diff --git a/src/xenia/cpu/backend/x64/x64_emitter.cc b/src/xenia/cpu/backend/x64/x64_emitter.cc index 5cd66c4ac..436949f39 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.cc +++ b/src/xenia/cpu/backend/x64/x64_emitter.cc @@ -36,6 +36,8 @@ DEFINE_bool(enable_debugprint_log, false, "Log debugprint traps to the active debugger"); +DEFINE_bool(ignore_undefined_externs, false, + "Don't exit when an undefined extern is called."); namespace xe { namespace cpu { @@ -299,6 +301,13 @@ uint64_t TrapDebugPrint(void* raw_context, uint64_t address) { return 0; } +uint64_t TrapDebugBreak(void* raw_context, uint64_t address) { + auto thread_state = *reinterpret_cast(raw_context); + XELOGE("Trap!"); + xe::debugging::Break(); + return 0; +} + void X64Emitter::Trap(uint16_t trap_type) { switch (trap_type) { case 20: @@ -310,6 +319,7 @@ void X64Emitter::Trap(uint16_t trap_type) { case 22: // Always trap? // TODO(benvanik): post software interrupt to debugger. + CallNative(TrapDebugBreak, 0); if (FLAGS_break_on_debugbreak) { db(0xCC); } @@ -417,8 +427,13 @@ void X64Emitter::CallIndirect(const hir::Instr* instr, uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) { auto function = reinterpret_cast(function_ptr); - XELOGW("undefined extern call to %.8X %s", function->address(), - function->name().c_str()); + if (!FLAGS_ignore_undefined_externs) { + xe::FatalError("undefined extern call to %.8X %s", function->address(), + function->name().c_str()); + } else { + XELOGE("undefined extern call to %.8X %s", function->address(), + function->name().c_str()); + } return 0; } void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) { diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index b9c420f2a..28ec1fa5f 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -399,7 +399,20 @@ bool XexModule::SetupLibraryImports(const char* name, function->set_end_address(record_addr + 16 - 4); function->set_name(import_name.GetString()); - if (kernel_export) { + if (user_export_addr) { + // Rewrite PPC code to set r11 to the target address + // So we'll have: + // lis r11, user_export_addr + // ori r11, r11, user_export_addr + // mtspr CTR, r11 + // bctr + uint16_t hi_addr = (user_export_addr >> 16) & 0xFFFF; + uint16_t low_addr = user_export_addr & 0xFFFF; + + uint8_t* p = memory()->TranslateVirtual(record_addr); + xe::store_and_swap(p + 0x0, 0x3D600000 | hi_addr); + xe::store_and_swap(p + 0x4, 0x616B0000 | low_addr); + } else { // On load we have something like this in memory: // li r3, 0 // li r4, 0x1F5 @@ -420,6 +433,8 @@ bool XexModule::SetupLibraryImports(const char* name, xe::store_and_swap(p + 0x8, 0x60000000); xe::store_and_swap(p + 0xC, 0x60000000); + // Note that we may not have a handler registered - if not, eventually + // we'll get directed to UndefinedImport. GuestFunction::ExternHandler handler = nullptr; if (kernel_export) { if (kernel_export->function_data.trampoline) { @@ -432,36 +447,9 @@ bool XexModule::SetupLibraryImports(const char* name, } else { XELOGW("WARNING: Imported kernel function %s is unimplemented!", import_name.GetString()); - handler = UndefinedImport; } static_cast(function)->SetupExtern(handler); - } else if (user_export_addr) { - // Rewrite PPC code to set r11 to the target address - // So we'll have: - // lis r11, user_export_addr - // ori r11, r11, user_export_addr - // mtspr CTR, r11 - // bctr - uint16_t hi_addr = (user_export_addr >> 16) & 0xFFFF; - uint16_t low_addr = user_export_addr & 0xFFFF; - - uint8_t* p = memory()->TranslateVirtual(record_addr); - xe::store_and_swap(p + 0x0, 0x3D600000 | hi_addr); - xe::store_and_swap(p + 0x4, 0x616B0000 | low_addr); - } else { - // Import not resolved. - // We're gonna rewrite the PPC to trigger a debug trap: - // trap - // blr - // nop - // nop - uint8_t* p = memory()->TranslateVirtual(record_addr); - xe::store_and_swap(p + 0x0, 0x7FE00008); - xe::store_and_swap(p + 0x4, 0x4E800020); - xe::store_and_swap(p + 0x8, 0x60000000); - xe::store_and_swap(p + 0xC, 0x60000000); } - function->set_status(Symbol::Status::kDeclared); } else { // Bad. diff --git a/src/xenia/kernel/xboxkrnl_io.cc b/src/xenia/kernel/xboxkrnl_io.cc index 2408de12f..598d67c27 100644 --- a/src/xenia/kernel/xboxkrnl_io.cc +++ b/src/xenia/kernel/xboxkrnl_io.cc @@ -197,9 +197,6 @@ dword_result_t NtReadFile(dword_t file_handle, dword_t event_handle, // Queue the APC callback. It must be delivered via the APC mechanism even // though were are completing immediately. if ((uint32_t)apc_routine_ptr & ~1) { - // Bejeweled 3 calls ReadFileEx with a NULL completion routine! - assert_not_zero((uint32_t)apc_context); - if (apc_context) { auto thread = XThread::GetCurrentThread(); thread->EnqueueApc((uint32_t)apc_routine_ptr & ~1, apc_context,