Much better errors on undefined externs.
This commit is contained in:
parent
87094b8257
commit
b80a028589
|
@ -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<ThreadState**>(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*>(function_ptr);
|
||||
XELOGW("undefined extern call to %.8X %s", function->address(),
|
||||
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) {
|
||||
|
|
|
@ -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<uint32_t>(p + 0x0, 0x3D600000 | hi_addr);
|
||||
xe::store_and_swap<uint32_t>(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<uint32_t>(p + 0x8, 0x60000000);
|
||||
xe::store_and_swap<uint32_t>(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<GuestFunction*>(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<uint32_t>(p + 0x0, 0x3D600000 | hi_addr);
|
||||
xe::store_and_swap<uint32_t>(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<uint32_t>(p + 0x0, 0x7FE00008);
|
||||
xe::store_and_swap<uint32_t>(p + 0x4, 0x4E800020);
|
||||
xe::store_and_swap<uint32_t>(p + 0x8, 0x60000000);
|
||||
xe::store_and_swap<uint32_t>(p + 0xC, 0x60000000);
|
||||
}
|
||||
|
||||
function->set_status(Symbol::Status::kDeclared);
|
||||
} else {
|
||||
// Bad.
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue