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,
|
DEFINE_bool(enable_debugprint_log, false,
|
||||||
"Log debugprint traps to the active debugger");
|
"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 xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
@ -299,6 +301,13 @@ uint64_t TrapDebugPrint(void* raw_context, uint64_t address) {
|
||||||
return 0;
|
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) {
|
void X64Emitter::Trap(uint16_t trap_type) {
|
||||||
switch (trap_type) {
|
switch (trap_type) {
|
||||||
case 20:
|
case 20:
|
||||||
|
@ -310,6 +319,7 @@ void X64Emitter::Trap(uint16_t trap_type) {
|
||||||
case 22:
|
case 22:
|
||||||
// Always trap?
|
// Always trap?
|
||||||
// TODO(benvanik): post software interrupt to debugger.
|
// TODO(benvanik): post software interrupt to debugger.
|
||||||
|
CallNative(TrapDebugBreak, 0);
|
||||||
if (FLAGS_break_on_debugbreak) {
|
if (FLAGS_break_on_debugbreak) {
|
||||||
db(0xCC);
|
db(0xCC);
|
||||||
}
|
}
|
||||||
|
@ -417,8 +427,13 @@ void X64Emitter::CallIndirect(const hir::Instr* instr,
|
||||||
|
|
||||||
uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) {
|
uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) {
|
||||||
auto function = reinterpret_cast<Function*>(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());
|
function->name().c_str());
|
||||||
|
} else {
|
||||||
|
XELOGE("undefined extern call to %.8X %s", function->address(),
|
||||||
|
function->name().c_str());
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) {
|
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_end_address(record_addr + 16 - 4);
|
||||||
function->set_name(import_name.GetString());
|
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:
|
// On load we have something like this in memory:
|
||||||
// li r3, 0
|
// li r3, 0
|
||||||
// li r4, 0x1F5
|
// 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 + 0x8, 0x60000000);
|
||||||
xe::store_and_swap<uint32_t>(p + 0xC, 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;
|
GuestFunction::ExternHandler handler = nullptr;
|
||||||
if (kernel_export) {
|
if (kernel_export) {
|
||||||
if (kernel_export->function_data.trampoline) {
|
if (kernel_export->function_data.trampoline) {
|
||||||
|
@ -432,36 +447,9 @@ bool XexModule::SetupLibraryImports(const char* name,
|
||||||
} else {
|
} else {
|
||||||
XELOGW("WARNING: Imported kernel function %s is unimplemented!",
|
XELOGW("WARNING: Imported kernel function %s is unimplemented!",
|
||||||
import_name.GetString());
|
import_name.GetString());
|
||||||
handler = UndefinedImport;
|
|
||||||
}
|
}
|
||||||
static_cast<GuestFunction*>(function)->SetupExtern(handler);
|
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);
|
function->set_status(Symbol::Status::kDeclared);
|
||||||
} else {
|
} else {
|
||||||
// Bad.
|
// 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
|
// Queue the APC callback. It must be delivered via the APC mechanism even
|
||||||
// though were are completing immediately.
|
// though were are completing immediately.
|
||||||
if ((uint32_t)apc_routine_ptr & ~1) {
|
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) {
|
if (apc_context) {
|
||||||
auto thread = XThread::GetCurrentThread();
|
auto thread = XThread::GetCurrentThread();
|
||||||
thread->EnqueueApc((uint32_t)apc_routine_ptr & ~1, apc_context,
|
thread->EnqueueApc((uint32_t)apc_routine_ptr & ~1, apc_context,
|
||||||
|
|
Loading…
Reference in New Issue