XexModule: Resolve user library imports

This commit is contained in:
Dr. Chat 2015-05-18 01:31:59 -05:00
parent 88733bf8ba
commit 589e672d20
3 changed files with 91 additions and 43 deletions

View File

@ -18,21 +18,25 @@
#include "xenia/cpu/cpu-private.h" #include "xenia/cpu/cpu-private.h"
#include "xenia/cpu/export_resolver.h" #include "xenia/cpu/export_resolver.h"
#include "xenia/cpu/processor.h" #include "xenia/cpu/processor.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/objects/xmodule.h"
namespace xe { namespace xe {
namespace cpu { namespace cpu {
using namespace xe::cpu; using namespace xe::cpu;
using namespace xe::kernel;
using PPCContext = xe::cpu::frontend::PPCContext; using PPCContext = xe::cpu::frontend::PPCContext;
void UndefinedImport(PPCContext* ppc_state, void* arg0, void* arg1) { void UndefinedImport(PPCContext* ppc_state, void* arg0, void* arg1) {
XELOGE("call to undefined kernel import"); XELOGE("call to undefined import");
} }
XexModule::XexModule(Processor* processor) XexModule::XexModule(Processor* processor, KernelState* state)
: Module(processor), : Module(processor),
processor_(processor), processor_(processor),
kernel_state_(state),
xex_(nullptr), xex_(nullptr),
base_address_(0), base_address_(0),
low_address_(0), low_address_(0),
@ -104,8 +108,23 @@ bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) {
for (size_t n = 0; n < import_info_count; n++) { for (size_t n = 0; n < import_info_count; n++) {
const xe_xex2_import_info_t* info = &import_infos[n]; const xe_xex2_import_info_t* info = &import_infos[n];
KernelExport* kernel_export = // Strip off the extension (for the symbol name)
export_resolver->GetExportByOrdinal(library->name, info->ordinal); std::string libname = library->name;
auto dot = libname.find_last_of('.');
if (dot != libname.npos) {
libname = libname.substr(0, dot);
}
KernelExport* kernel_export = NULL; // kernel export info
uint32_t user_export_addr = 0; // user export address
if (kernel_state_->IsKernelModule(library->name)) {
kernel_export =
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
} else {
XModule* module = kernel_state_->GetModule(library->name);
user_export_addr = module->GetProcAddressByOrdinal(info->ordinal);
}
if (kernel_export) { if (kernel_export) {
if (info->thunk_address) { if (info->thunk_address) {
@ -114,7 +133,7 @@ bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) {
snprintf(name, xe::countof(name), "%s", kernel_export->name); snprintf(name, xe::countof(name), "%s", kernel_export->name);
} }
} else { } else {
snprintf(name, xe::countof(name), "__imp_%s_%.3X", library->name, snprintf(name, xe::countof(name), "__imp_%s_%.3X", libname,
info->ordinal); info->ordinal);
} }
@ -151,53 +170,77 @@ bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) {
kernel_export->name); kernel_export->name);
} }
} }
} else {
auto slot = memory_->TranslateVirtual<uint32_t*>(info->value_address);
// Assuming this is correct...
xe::store_and_swap<uint32_t>(slot, user_export_addr);
} }
if (info->thunk_address) { if (info->thunk_address) {
if (kernel_export) { if (kernel_export) {
snprintf(name, xe::countof(name), "%s", kernel_export->name); snprintf(name, xe::countof(name), "%s", kernel_export->name);
} else if (user_export_addr) {
snprintf(name, xe::countof(name), "__%s_%.3X", libname,
info->ordinal);
} else { } else {
snprintf(name, xe::countof(name), "__kernel_%s_%.3X", library->name, snprintf(name, xe::countof(name), "__kernel_%s_%.3X", libname,
info->ordinal); info->ordinal);
} }
// On load we have something like this in memory: // Kernel exports go up to the host shim functions
// li r3, 0
// li r4, 0x1F5
// mtspr CTR, r11
// bctr
// Real consoles rewrite this with some code that sets r11.
// If we did that we'd still have to put a thunk somewhere and do the
// dynamic lookup. Instead, we rewrite it to use syscalls, as they
// aren't used on the 360. CPU backends can either take the syscall
// or do something smarter.
// sc
// blr
// nop
// nop
uint8_t* p = memory()->TranslateVirtual(info->thunk_address);
xe::store_and_swap<uint32_t>(p + 0x0, 0x44000002);
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);
FunctionInfo::ExternHandler handler = 0;
void* handler_data = 0;
if (kernel_export) { if (kernel_export) {
handler = // On load we have something like this in memory:
(FunctionInfo::ExternHandler)kernel_export->function_data.shim; // li r3, 0
handler_data = kernel_export->function_data.shim_data; // li r4, 0x1F5
} else { // mtspr CTR, r11
handler = (FunctionInfo::ExternHandler)UndefinedImport; // bctr
handler_data = this; // Real consoles rewrite this with some code that sets r11.
} // If we did that we'd still have to put a thunk somewhere and do the
// dynamic lookup. Instead, we rewrite it to use syscalls, as they
// aren't used on the 360. CPU backends can either take the syscall
// or do something smarter.
// sc
// blr
// nop
// nop
uint8_t* p = memory()->TranslateVirtual(info->thunk_address);
xe::store_and_swap<uint32_t>(p + 0x0, 0x44000002);
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);
FunctionInfo* fn_info; FunctionInfo::ExternHandler handler = 0;
DeclareFunction(info->thunk_address, &fn_info); void* handler_data = 0;
fn_info->set_end_address(info->thunk_address + 16 - 4); if (kernel_export) {
fn_info->set_name(name); handler =
fn_info->SetupExtern(handler, handler_data, NULL); (FunctionInfo::ExternHandler)kernel_export->function_data.shim;
fn_info->set_status(SymbolInfo::STATUS_DECLARED); handler_data = kernel_export->function_data.shim_data;
} else {
handler = (FunctionInfo::ExternHandler)UndefinedImport;
handler_data = this;
}
FunctionInfo* fn_info;
DeclareFunction(info->thunk_address, &fn_info);
fn_info->set_end_address(info->thunk_address + 16 - 4);
fn_info->set_name(name);
fn_info->SetupExtern(handler, handler_data, NULL);
fn_info->set_status(SymbolInfo::STATUS_DECLARED);
} 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(info->thunk_address);
xe::store_and_swap<uint32_t>(p + 0x0, 0x3D600000 | hi_addr);
xe::store_and_swap<uint32_t>(p + 0x4, 0x616B0000 | low_addr);
}
} }
} }

View File

@ -16,13 +16,17 @@
#include "xenia/kernel/util/xex2.h" #include "xenia/kernel/util/xex2.h"
namespace xe { namespace xe {
// KernelState forward decl.
namespace kernel { class KernelState; }
namespace cpu { namespace cpu {
class Runtime; class Runtime;
class XexModule : public xe::cpu::Module { class XexModule : public xe::cpu::Module {
public: public:
XexModule(Processor* processor); XexModule(Processor* processor, kernel::KernelState* state);
virtual ~XexModule(); virtual ~XexModule();
xe_xex2_ref xex() const { return xex_; } xe_xex2_ref xex() const { return xex_; }
@ -40,6 +44,7 @@ class XexModule : public xe::cpu::Module {
private: private:
Processor* processor_; Processor* processor_;
kernel::KernelState* kernel_state_;
std::string name_; std::string name_;
std::string path_; std::string path_;
xe_xex2_ref xex_; xe_xex2_ref xex_;

View File

@ -129,7 +129,7 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) {
// Prepare the module for execution. // Prepare the module for execution.
// Runtime takes ownership. // Runtime takes ownership.
auto xex_module = std::make_unique<XexModule>(processor); auto xex_module = std::make_unique<XexModule>(processor, kernel_state());
if (!xex_module->Load(name_, path_, xex_)) { if (!xex_module->Load(name_, path_, xex_)) {
return X_STATUS_UNSUCCESSFUL; return X_STATUS_UNSUCCESSFUL;
} }