From e44ac54eed66de2d5dc84ee23d4bde7f9466e551 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sun, 28 Jun 2015 23:57:00 -0500 Subject: [PATCH 01/20] Remove kernel dependency on old xex header --- src/xenia/kernel/xboxkrnl_modules.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/xenia/kernel/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl_modules.cc index 468cb8c71..e31afa7ec 100644 --- a/src/xenia/kernel/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl_modules.cc @@ -141,9 +141,8 @@ SHIM_CALL XexCheckExecutablePrivilege_shim(PPCContext* ppc_context, SHIM_SET_RETURN_32(0); return; } - xe_xex2_ref xex = module->xex(); - const xe_xex2_header_t* header = xe_xex2_get_header(xex); + auto header = module->xex_header(); uint32_t result = (header->system_flags & mask) > 0; SHIM_SET_RETURN_32(result); From fe87c0842442c6db784686127507d200a840e1c7 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Mon, 29 Jun 2015 00:48:24 -0500 Subject: [PATCH 02/20] Shuffle some code around. --- src/xenia/cpu/xex_module.cc | 70 ++++++++++++++++++++++++ src/xenia/cpu/xex_module.h | 12 ++++ src/xenia/kernel/objects/xuser_module.cc | 42 ++++++-------- src/xenia/kernel/objects/xuser_module.h | 10 +++- src/xenia/kernel/util/xex2_info.h | 65 +++++++++++++++++++--- 5 files changed, 162 insertions(+), 37 deletions(-) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index ebfee1c5f..1a6a8e733 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -21,6 +21,8 @@ #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/objects/xmodule.h" +#include "third_party/crypto/rijndael-alg-fst.h" + namespace xe { namespace cpu { @@ -45,6 +47,74 @@ XexModule::XexModule(Processor* processor, KernelState* kernel_state) XexModule::~XexModule() { xe_xex2_dealloc(xex_); } +bool XexModule::GetOptHeader(const xex2_header* header, xe_xex2_header_keys key, + void** out_ptr) { + assert_not_null(header); + assert_not_null(out_ptr); + + for (uint32_t i = 0; i < header->header_count; i++) { + const xex2_opt_header& opt_header = header->headers[i]; + if (opt_header.key == key) { + // Match! + switch (key & 0xFF) { + case 0x00: { + // We just return the value of the optional header. + *out_ptr = (void*)((uint64_t)opt_header.value); + } break; + case 0x01: { + // Pointer to the value on the optional header. + *out_ptr = (void*)&opt_header.value; + } break; + default: { + // Pointer to the header. + *out_ptr = (void*)((uint8_t*)header + opt_header.offset); + } break; + } + + return true; + } + } + + return false; +} + +bool XexModule::ApplyPatch(XexModule* module) { + auto header = reinterpret_cast(module->xex_header()); + if (!(header->module_flags & + (XEX_MODULE_MODULE_PATCH | XEX_MODULE_PATCH_DELTA | + XEX_MODULE_PATCH_FULL))) { + // This isn't a XEX2 patch. + return false; + } + + // Grab the delta descriptor and get to work. + xex2_opt_delta_patch_descriptor* patch_header = nullptr; + GetOptHeader(header, XEX_HEADER_DELTA_PATCH_DESCRIPTOR, + (void**)&patch_header); + assert_not_null(patch_header); + + // TODO! + + return true; +} + +bool XexModule::Load(const std::string& name, const std::string& path, + const void* xex_addr, size_t xex_length) { + // TODO: Move loading code here + xex_ = xe_xex2_load(memory(), xex_addr, xex_length, {0}); + if (!xex_) { + return false; + } + + // Make a copy of the xex header. + auto src_header = reinterpret_cast(xex_addr); + xex_header_ = (xex2_header*)new char[src_header->header_size]; + + std::memcpy(xex_header_, src_header, src_header->header_size); + + return Load(name, path, xex_); +} + bool XexModule::Load(const std::string& name, const std::string& path, xe_xex2_ref xex) { xex_ = xex; diff --git a/src/xenia/cpu/xex_module.h b/src/xenia/cpu/xex_module.h index f61799e93..54e23d41e 100644 --- a/src/xenia/cpu/xex_module.h +++ b/src/xenia/cpu/xex_module.h @@ -14,6 +14,7 @@ #include "xenia/cpu/module.h" #include "xenia/kernel/util/xex2.h" +#include "xenia/kernel/util/xex2_info.h" namespace xe { @@ -32,7 +33,17 @@ class XexModule : public xe::cpu::Module { virtual ~XexModule(); xe_xex2_ref xex() const { return xex_; } + const xex2_header* xex_header() const { return xex_header_; } + // Gets an optional header. Returns NULL if not found. + // Special case: if key & 0xFF == 0x00, this function will return the value, + // not a pointer! + static bool GetOptHeader(const xex2_header* header, xe_xex2_header_keys key, + void** out_ptr); + + bool ApplyPatch(XexModule* module); + bool Load(const std::string& name, const std::string& path, + const void* xex_addr, size_t xex_length); bool Load(const std::string& name, const std::string& path, xe_xex2_ref xex); const std::string& name() const override { return name_; } @@ -50,6 +61,7 @@ class XexModule : public xe::cpu::Module { std::string name_; std::string path_; xe_xex2_ref xex_; + xex2_header* xex_header_; uint32_t base_address_; uint32_t low_address_; diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 504a2ab1a..6da97cd48 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -26,8 +26,6 @@ XUserModule::XUserModule(KernelState* kernel_state, const char* path) XUserModule::~XUserModule() { xe_xex2_dealloc(xex_); } -xe_xex2_ref XUserModule::xex() { return xex_; } - const xe_xex2_header_t* XUserModule::xex_header() { return xe_xex2_get_header(xex_); } @@ -82,39 +80,31 @@ X_STATUS XUserModule::LoadFromFile(std::string path) { X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) { Processor* processor = kernel_state()->processor(); - // Load the XEX into memory and decrypt. - xe_xex2_options_t xex_options = {0}; - xex_ = xe_xex2_load(memory(), addr, length, xex_options); - if (!xex_) { + // Prepare the module for execution. + // Runtime takes ownership. + auto xex_module = std::make_unique(processor, kernel_state()); + if (!xex_module->Load(name_, path_, addr, length)) { + return X_STATUS_UNSUCCESSFUL; + } + xex_ = xex_module->xex(); + processor_module_ = xex_module.get(); + if (!processor->AddModule(std::move(xex_module))) { return X_STATUS_UNSUCCESSFUL; } - // Copy the xex2 header into guest memory - const xex2_header* header = reinterpret_cast(addr); - uint32_t header_size = xex2_get_header_size(header); + // Copy the xex2 header into guest memory. + const xex2_header* header = this->xex_module()->xex_header(); + guest_xex_header_ = memory()->SystemHeapAlloc(header->header_size); - xex_header_ = memory()->SystemHeapAlloc(header_size); - - uint8_t* xex_header_ptr = memory()->TranslateVirtual(xex_header_); - std::memcpy(xex_header_ptr, header, header_size); + uint8_t* xex_header_ptr = memory()->TranslateVirtual(guest_xex_header_); + std::memcpy(xex_header_ptr, header, header->header_size); // Setup the loader data entry auto ldr_data = memory()->TranslateVirtual(hmodule_ptr_); ldr_data->dll_base = 0; // GetProcAddress will read this. - ldr_data->xex_header_base = xex_header_; - - // Prepare the module for execution. - // Runtime takes ownership. - auto xex_module = std::make_unique(processor, kernel_state()); - if (!xex_module->Load(name_, path_, xex_)) { - return X_STATUS_UNSUCCESSFUL; - } - processor_module_ = xex_module.get(); - if (!processor->AddModule(std::move(xex_module))) { - return X_STATUS_UNSUCCESSFUL; - } + ldr_data->xex_header_base = guest_xex_header_; OnLoad(); @@ -146,7 +136,7 @@ X_STATUS XUserModule::GetSection(const char* name, uint32_t* out_section_data, X_STATUS XUserModule::GetOptHeader(xe_xex2_header_keys key, uint32_t* out_header_guest_ptr) { - auto header = memory()->TranslateVirtual(xex_header_); + auto header = xex_module()->xex_header(); if (!header) { return X_STATUS_UNSUCCESSFUL; } diff --git a/src/xenia/kernel/objects/xuser_module.h b/src/xenia/kernel/objects/xuser_module.h index 18e2156ea..54ed496fe 100644 --- a/src/xenia/kernel/objects/xuser_module.h +++ b/src/xenia/kernel/objects/xuser_module.h @@ -18,6 +18,10 @@ #include "xenia/xbox.h" namespace xe { +namespace cpu { +class XexModule; +} // namespace cpu + namespace kernel { class XUserModule : public XModule { @@ -25,8 +29,10 @@ class XUserModule : public XModule { XUserModule(KernelState* kernel_state, const char* path); ~XUserModule() override; - xe_xex2_ref xex(); const xe_xex2_header_t* xex_header(); + const xe::cpu::XexModule* xex_module() const { + return reinterpret_cast(processor_module_); + } X_STATUS LoadFromFile(std::string path); X_STATUS LoadFromMemory(const void* addr, const size_t length); @@ -48,7 +54,7 @@ class XUserModule : public XModule { private: xe_xex2_ref xex_; - uint32_t xex_header_; + uint32_t guest_xex_header_; }; } // namespace kernel diff --git a/src/xenia/kernel/util/xex2_info.h b/src/xenia/kernel/util/xex2_info.h index 4d0a5409f..7a3ecd0b1 100644 --- a/src/xenia/kernel/util/xex2_info.h +++ b/src/xenia/kernel/util/xex2_info.h @@ -470,9 +470,8 @@ typedef struct { } xe_xex2_header_t; namespace xe { -namespace kernel { union xex2_version { - uint32_t value; + xe::be value; struct { uint32_t major : 4; uint32_t minor : 4; @@ -481,6 +480,22 @@ union xex2_version { }; }; +struct xex2_opt_delta_patch_descriptor { + xe::be size; // 0x0 + xex2_version target_version; // 0x4 + xex2_version source_version; // 0x8 + char digest_source[0x14]; // 0xC + char image_key_source[0x10]; // 0x20 + xe::be size_of_target_headers; // 0x30 + xe::be delta_headers_source_offset; // 0x34 + xe::be delta_headers_source_size; // 0x38 + xe::be delta_headers_target_offset; // 0x3C + xe::be delta_image_source_offset; // 0x40 + xe::be delta_image_source_size; // 0x44 + xe::be delta_image_target_offset; // 0x48 +}; +static_assert_size(xex2_opt_delta_patch_descriptor, 0x4C); + struct xex2_opt_execution_info { xe::be media_id; // 0x0 xe::be version; // 0x4 @@ -492,32 +507,64 @@ struct xex2_opt_execution_info { uint8_t disc_count; // 0x13 xe::be savegame_id; // 0x14 }; +static_assert_size(xex2_opt_execution_info, 0x18); + +struct xex2_opt_import_libraries { + xe::be section_size; // 0x0 + xe::be string_table_size; // 0x4 + xe::be library_count; // 0x8 + char string_table[1]; // 0xC string_table_size bytes +}; + +struct xex2_import_library { + xe::be size; // 0x0 + char next_import_digest[0x14]; // 0x4 + xe::be id; // 0x18 + xex2_version version; // 0x1C + xex2_version version_min; // 0x20 + xe::be name_index; // 0x24 + xe::be count; // 0x26 + xe::be import_table[1]; // 0x28 +}; struct xex2_opt_header { xe::be key; // 0x0 union { xe::be value; // 0x4 - xe::be offset; // 0x8 + xe::be offset; // 0x4 }; }; struct xex2_header { xe::be magic; // 0x0 'XEX2' xe::be module_flags; // 0x4 - xe::be exe_offset; // 0x8 + xe::be header_size; // 0x8 xe::be reserved; // 0xC - xe::be certificate_offset; // 0x10 + xe::be security_offset; // 0x10 xe::be header_count; // 0x14 xex2_opt_header headers[1]; // 0x18 }; -struct xex2_loader_info { - xe::be header_size; - xe::be image_size; +struct xex2_security_info { + xe::be header_size; // 0x0 + xe::be image_size; // 0x4 + char rsa_signature[0x100]; // 0x8 + xe::be unk_108; // 0x108 unk length + xe::be image_flags; // 0x10C + xe::be load_address; // 0x110 + char section_digest[0x14]; // 0x114 + xe::be import_table_count; // 0x128 + char import_table_digest[0x14]; // 0x12C + char xgd2_media_id[0x10]; // 0x140 + char aes_key[0x10]; // 0x150 + xe::be export_table; // 0x160 + char header_digest[0x14]; // 0x164 + xe::be region; // 0x178 + xe::be allowed_media_types; // 0x17C }; -} // namespace kernel +static_assert_size(xex2_security_info, 0x180); } // namespace xe #endif // XENIA_KERNEL_XEX2_INFO_H_ From 029babaf5d79229fa4fc37a288eaaf29e2801981 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Mon, 29 Jun 2015 01:39:07 -0500 Subject: [PATCH 03/20] Drop dependency on old-style xex2 headers --- src/xenia/cpu/xex_module.cc | 12 ++++ src/xenia/cpu/xex_module.h | 4 ++ src/xenia/kernel/kernel_state.cc | 37 ++++++++----- src/xenia/kernel/objects/xthread.cc | 28 ++++++---- src/xenia/kernel/objects/xuser_module.cc | 70 +++++++++++++++++------- src/xenia/kernel/objects/xuser_module.h | 19 ++++--- src/xenia/kernel/util/xex2_info.h | 20 +++++++ src/xenia/kernel/xboxkrnl_modules.cc | 6 +- src/xenia/kernel/xboxkrnl_threading.cc | 3 +- 9 files changed, 141 insertions(+), 58 deletions(-) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index 1a6a8e733..ca00c5bff 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -78,6 +78,18 @@ bool XexModule::GetOptHeader(const xex2_header* header, xe_xex2_header_keys key, return false; } +bool XexModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const { + return XexModule::GetOptHeader(xex_header_, key, out_ptr); +} + +uint32_t XexModule::GetProcAddress(uint16_t ordinal) const { + return xe_xex2_lookup_export(xex_, ordinal); +} + +uint32_t XexModule::GetProcAddress(const char* name) const { + return xe_xex2_lookup_export(xex_, name); +} + bool XexModule::ApplyPatch(XexModule* module) { auto header = reinterpret_cast(module->xex_header()); if (!(header->module_flags & diff --git a/src/xenia/cpu/xex_module.h b/src/xenia/cpu/xex_module.h index 54e23d41e..f15c03bd9 100644 --- a/src/xenia/cpu/xex_module.h +++ b/src/xenia/cpu/xex_module.h @@ -40,6 +40,10 @@ class XexModule : public xe::cpu::Module { // not a pointer! static bool GetOptHeader(const xex2_header* header, xe_xex2_header_keys key, void** out_ptr); + bool GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const; + + uint32_t GetProcAddress(uint16_t ordinal) const; + uint32_t GetProcAddress(const char* name) const; bool ApplyPatch(XexModule* module); bool Load(const std::string& name, const std::string& path, diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 86994216f..a3544b4ad 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -120,7 +120,16 @@ KernelState* KernelState::shared() { return shared_kernel_state_; } uint32_t KernelState::title_id() const { assert_not_null(executable_module_); - return executable_module_->xex_header()->execution_info.title_id; + + xex2_opt_execution_info* exec_info = 0; + executable_module_->GetOptHeader(XEX_HEADER_EXECUTION_INFO, + (void**)&exec_info); + + if (exec_info) { + return exec_info->title_id; + } + + return 0; } uint32_t KernelState::process_type() const { @@ -192,13 +201,14 @@ void KernelState::SetExecutableModule(object_ref module) { return; } - auto header = executable_module_->xex_header(); - if (header) { + xex2_opt_tls_info* tls_header = nullptr; + executable_module_->GetOptHeader(XEX_HEADER_TLS_INFO, (void**)&tls_header); + if (tls_header) { auto pib = memory_->TranslateVirtual( process_info_block_address_); - pib->tls_data_size = header->tls_info.data_size; - pib->tls_raw_data_size = header->tls_info.raw_data_size; - pib->tls_slot_size = header->tls_info.slot_count * 4; + pib->tls_data_size = tls_header->data_size; + pib->tls_raw_data_size = tls_header->raw_data_size; + pib->tls_slot_size = tls_header->slot_count * 4; } // Setup the kernel's XexExecutableModuleHandle field. @@ -275,8 +285,7 @@ object_ref KernelState::LoadUserModule(const char* raw_name) { module->Dump(); - auto xex_header = module->xex_header(); - if (xex_header->exe_entry_point) { + if (module->entry_point()) { // Call DllMain(DLL_PROCESS_ATTACH): // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx uint64_t args[] = { @@ -285,7 +294,7 @@ object_ref KernelState::LoadUserModule(const char* raw_name) { 0, // 0 because always dynamic }; auto thread_state = XThread::GetCurrentThread()->thread_state(); - processor()->Execute(thread_state, xex_header->exe_entry_point, args, + processor()->Execute(thread_state, module->entry_point(), args, xe::countof(args)); } @@ -323,14 +332,13 @@ void KernelState::OnThreadExecute(XThread* thread) { // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx auto thread_state = thread->thread_state(); for (auto user_module : user_modules_) { - auto xex_header = user_module->xex_header(); - if (xex_header->exe_entry_point) { + if (user_module->entry_point()) { uint64_t args[] = { user_module->handle(), 2, // DLL_THREAD_ATTACH 0, // 0 because always dynamic }; - processor()->Execute(thread_state, xex_header->exe_entry_point, args, + processor()->Execute(thread_state, user_module->entry_point(), args, xe::countof(args)); } } @@ -346,14 +354,13 @@ void KernelState::OnThreadExit(XThread* thread) { // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx auto thread_state = thread->thread_state(); for (auto user_module : user_modules_) { - auto xex_header = user_module->xex_header(); - if (xex_header->exe_entry_point) { + if (user_module->entry_point()) { uint64_t args[] = { user_module->handle(), 3, // DLL_THREAD_DETACH 0, // 0 because always dynamic }; - processor()->Execute(thread_state, xex_header->exe_entry_point, args, + processor()->Execute(thread_state, user_module->entry_point(), args, xe::countof(args)); } } diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index 5815ba212..471ba3c6b 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -164,14 +164,18 @@ X_STATUS XThread::Create() { // Allocate TLS block. // Games will specify a certain number of 4b slots that each thread will get. + xex2_opt_tls_info* tls_header = nullptr; + if (module) { + module->GetOptHeader(XEX_HEADER_TLS_INFO, (void**)&tls_header); + } + const uint32_t kDefaultTlsSlotCount = 32; uint32_t tls_slots = 0; uint32_t tls_extended_size = 0; - if (module && module->xex_header()) { - const xe_xex2_header_t* header = module->xex_header(); - tls_slots = header->tls_info.slot_count ? header->tls_info.slot_count - : kDefaultTlsSlotCount; - tls_extended_size = header->tls_info.data_size; + if (tls_header) { + tls_slots = + tls_header->slot_count ? tls_header->slot_count : kDefaultTlsSlotCount; + tls_extended_size = tls_header->data_size; } else { tls_slots = kDefaultTlsSlotCount; } @@ -192,10 +196,9 @@ X_STATUS XThread::Create() { memory()->Fill(tls_address_, tls_total_size, 0); if (tls_extended_size) { // If game has extended data, copy in the default values. - const xe_xex2_header_t* header = module->xex_header(); - assert_not_zero(header->tls_info.raw_data_address); - memory()->Copy(tls_address_, header->tls_info.raw_data_address, - header->tls_info.raw_data_size); + assert_not_zero(tls_header->raw_data_address); + memory()->Copy(tls_address_, tls_header->raw_data_address, + tls_header->raw_data_size); } // Allocate thread state block from heap. @@ -583,8 +586,11 @@ void XThread::DeliverAPCs(void* data) { // kernel_routine(apc_address, &normal_routine, &normal_context, // &system_arg1, &system_arg2) uint64_t kernel_args[] = { - apc_ptr, thread->scratch_address_ + 0, thread->scratch_address_ + 4, - thread->scratch_address_ + 8, thread->scratch_address_ + 12, + apc_ptr, + thread->scratch_address_ + 0, + thread->scratch_address_ + 4, + thread->scratch_address_ + 8, + thread->scratch_address_ + 12, }; processor->Execute(thread->thread_state(), apc->kernel_routine, kernel_args, xe::countof(kernel_args)); diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 6da97cd48..8a7b958d6 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -22,13 +22,9 @@ namespace kernel { using namespace xe::cpu; XUserModule::XUserModule(KernelState* kernel_state, const char* path) - : XModule(kernel_state, ModuleType::kUserModule, path), xex_(nullptr) {} + : XModule(kernel_state, ModuleType::kUserModule, path) {} -XUserModule::~XUserModule() { xe_xex2_dealloc(xex_); } - -const xe_xex2_header_t* XUserModule::xex_header() { - return xe_xex2_get_header(xex_); -} +XUserModule::~XUserModule() {} X_STATUS XUserModule::LoadFromFile(std::string path) { X_STATUS result = X_STATUS_UNSUCCESSFUL; @@ -86,7 +82,6 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) { if (!xex_module->Load(name_, path_, addr, length)) { return X_STATUS_UNSUCCESSFUL; } - xex_ = xex_module->xex(); processor_module_ = xex_module.get(); if (!processor->AddModule(std::move(xex_module))) { return X_STATUS_UNSUCCESSFUL; @@ -106,34 +101,60 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) { ldr_data->dll_base = 0; // GetProcAddress will read this. ldr_data->xex_header_base = guest_xex_header_; + // Cache some commonly used headers... + this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, + (void**)&entry_point_); + this->xex_module()->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE, + (void**)&stack_size_); + OnLoad(); return X_STATUS_SUCCESS; } uint32_t XUserModule::GetProcAddressByOrdinal(uint16_t ordinal) { - return xe_xex2_lookup_export(xex_, ordinal); + return xex_module()->GetProcAddress(ordinal); } uint32_t XUserModule::GetProcAddressByName(const char* name) { - return xe_xex2_lookup_export(xex_, name); + return xex_module()->GetProcAddress(name); } X_STATUS XUserModule::GetSection(const char* name, uint32_t* out_section_data, uint32_t* out_section_size) { - auto header = xe_xex2_get_header(xex_); - for (size_t n = 0; n < header->resource_info_count; n++) { - auto& res = header->resource_infos[n]; + xex2_opt_resource_info* resource_header = nullptr; + if (!XexModule::GetOptHeader(xex_header(), XEX_HEADER_RESOURCE_INFO, + (void**)&resource_header)) { + // No resources. + return X_STATUS_UNSUCCESSFUL; + } + + uint32_t count = (resource_header->size - 4) / 16; + for (uint32_t i = 0; i < count; i++) { + auto& res = resource_header->resources[i]; if (strcmp(name, res.name) == 0) { // Found! *out_section_data = res.address; *out_section_size = res.size; + return X_STATUS_SUCCESS; } } + return X_STATUS_UNSUCCESSFUL; } +X_STATUS XUserModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) { + assert_not_null(out_ptr); + + bool ret = xex_module()->GetOptHeader(key, out_ptr); + if (!ret) { + return X_STATUS_NOT_FOUND; + } + + return X_STATUS_SUCCESS; +} + X_STATUS XUserModule::GetOptHeader(xe_xex2_header_keys key, uint32_t* out_header_guest_ptr) { auto header = xex_module()->xex_header(); @@ -180,16 +201,21 @@ X_STATUS XUserModule::GetOptHeader(uint8_t* membase, const xex2_header* header, } X_STATUS XUserModule::Launch(uint32_t flags) { - const xe_xex2_header_t* header = xex_header(); - XELOGI("Launching module..."); - Dump(); + // Grab some important variables... + auto header = xex_header(); + uint32_t exe_stack_size = 0; + uint32_t exe_entry_point = 0; + XexModule::GetOptHeader(xex_header(), XEX_HEADER_DEFAULT_STACK_SIZE, + (void**)&exe_stack_size); + XexModule::GetOptHeader(xex_header(), XEX_HEADER_ENTRY_POINT, + (void**)&exe_entry_point); + // Create a thread to run in. - auto thread = - object_ref(new XThread(kernel_state(), header->exe_stack_size, 0, - header->exe_entry_point, 0, 0)); + auto thread = object_ref( + new XThread(kernel_state(), exe_stack_size, 0, exe_entry_point, 0, 0)); X_STATUS result = thread->Create(); if (XFAILED(result)) { @@ -206,11 +232,14 @@ X_STATUS XUserModule::Launch(uint32_t flags) { void XUserModule::Dump() { xe::cpu::ExportResolver* export_resolver = kernel_state_->emulator()->export_resolver(); - const xe_xex2_header_t* header = xe_xex2_get_header(xex_); + auto header = xex_header(); + + // TODO: Need to loop through the optional headers one-by-one. // XEX info. printf("Module %s:\n\n", path_.c_str()); printf(" Module Flags: %.8X\n", header->module_flags); + /* printf(" System Flags: %.8X\n", header->system_flags); printf("\n"); printf(" Address: %.8X\n", header->exe_address); @@ -249,7 +278,7 @@ void XUserModule::Dump() { printf("\n"); printf(" Headers:\n"); for (size_t n = 0; n < header->header_count; n++) { - const xe_xex2_opt_header_t* opt_header = &header->headers[n]; + const xex2_opt_header* opt_header = &header->headers[n]; printf(" %.8X (%.8X, %4db) %.8X = %11d\n", opt_header->key, opt_header->offset, opt_header->length, opt_header->value, opt_header->value); @@ -440,6 +469,7 @@ void XUserModule::Dump() { printf("\n"); } + */ } } // namespace kernel diff --git a/src/xenia/kernel/objects/xuser_module.h b/src/xenia/kernel/objects/xuser_module.h index 54ed496fe..588c7d875 100644 --- a/src/xenia/kernel/objects/xuser_module.h +++ b/src/xenia/kernel/objects/xuser_module.h @@ -13,15 +13,12 @@ #include #include "xenia/cpu/export_resolver.h" +#include "xenia/cpu/xex_module.h" #include "xenia/kernel/objects/xmodule.h" -#include "xenia/kernel/util/xex2.h" +#include "xenia/kernel/util/xex2_info.h" #include "xenia/xbox.h" namespace xe { -namespace cpu { -class XexModule; -} // namespace cpu - namespace kernel { class XUserModule : public XModule { @@ -29,11 +26,15 @@ class XUserModule : public XModule { XUserModule(KernelState* kernel_state, const char* path); ~XUserModule() override; - const xe_xex2_header_t* xex_header(); const xe::cpu::XexModule* xex_module() const { return reinterpret_cast(processor_module_); } + const xex2_header* xex_header() const { return xex_module()->xex_header(); } + + uint32_t entry_point() const { return entry_point_; } + uint32_t stack_size() const { return stack_size_; } + X_STATUS LoadFromFile(std::string path); X_STATUS LoadFromMemory(const void* addr, const size_t length); @@ -42,6 +43,8 @@ class XUserModule : public XModule { X_STATUS GetSection(const char* name, uint32_t* out_section_data, uint32_t* out_section_size) override; + X_STATUS GetOptHeader(xe_xex2_header_keys key, void** out_ptr); + X_STATUS GetOptHeader(xe_xex2_header_keys key, uint32_t* out_header_guest_ptr); static X_STATUS GetOptHeader(uint8_t* membase, const xex2_header* header, @@ -53,8 +56,10 @@ class XUserModule : public XModule { void Dump(); private: - xe_xex2_ref xex_; uint32_t guest_xex_header_; + + uint32_t entry_point_; + uint32_t stack_size_; }; } // namespace kernel diff --git a/src/xenia/kernel/util/xex2_info.h b/src/xenia/kernel/util/xex2_info.h index 7a3ecd0b1..afd1ea087 100644 --- a/src/xenia/kernel/util/xex2_info.h +++ b/src/xenia/kernel/util/xex2_info.h @@ -480,6 +480,26 @@ union xex2_version { }; }; +struct xex2_opt_tls_info { + xe::be slot_count; // 0x0 + xe::be raw_data_address; // 0x4 + xe::be data_size; // 0x8 + xe::be raw_data_size; // 0xC +}; +static_assert_size(xex2_opt_tls_info, 0x10); + +struct xex2_resource { + char name[8]; // 0x0 + xe::be address; // 0x8 + xe::be size; // 0xC +}; +static_assert_size(xex2_resource, 0x10); + +struct xex2_opt_resource_info { + xe::be size; // 0x0 Resource count is (size - 4) / 16 + xex2_resource resources[1]; // 0x4 +}; + struct xex2_opt_delta_patch_descriptor { xe::be size; // 0x0 xex2_version target_version; // 0x4 diff --git a/src/xenia/kernel/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl_modules.cc index e31afa7ec..9b1889e08 100644 --- a/src/xenia/kernel/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl_modules.cc @@ -142,10 +142,10 @@ SHIM_CALL XexCheckExecutablePrivilege_shim(PPCContext* ppc_context, return; } - auto header = module->xex_header(); - uint32_t result = (header->system_flags & mask) > 0; + uint32_t flags = 0; + module->GetOptHeader(XEX_HEADER_SYSTEM_FLAGS, (void **)&flags); - SHIM_SET_RETURN_32(result); + SHIM_SET_RETURN_32((flags & mask) > 0); } SHIM_CALL XexGetModuleHandle_shim(PPCContext* ppc_context, diff --git a/src/xenia/kernel/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl_threading.cc index 1eee343d7..d24aa08f4 100644 --- a/src/xenia/kernel/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl_threading.cc @@ -109,8 +109,7 @@ SHIM_CALL ExCreateThread_shim(PPCContext* ppc_context, // Inherit default stack size if (stack_size == 0) { - stack_size = - kernel_state->GetExecutableModule()->xex_header()->exe_stack_size; + stack_size = kernel_state->GetExecutableModule()->stack_size(); } // Stack must be aligned to 16kb pages From be5f8d3aa4e27fea69306657fba3594c382460ce Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Mon, 29 Jun 2015 01:54:13 -0500 Subject: [PATCH 04/20] Simplify some logic in parsing import libraries Change struct used in export lookups --- src/xenia/kernel/util/xex2.cc | 82 +++++++++++++++---------------- src/xenia/kernel/util/xex2.h | 3 -- src/xenia/kernel/util/xex2_info.h | 24 ++++----- 3 files changed, 52 insertions(+), 57 deletions(-) diff --git a/src/xenia/kernel/util/xex2.cc b/src/xenia/kernel/util/xex2.cc index 238ecb64d..94f2a387b 100644 --- a/src/xenia/kernel/util/xex2.cc +++ b/src/xenia/kernel/util/xex2.cc @@ -28,11 +28,6 @@ #include "xenia/base/platform.h" namespace xe { -namespace kernel { -uint32_t xex2_get_header_size(const xex2_header* header) { - return header->exe_offset; -} -} // namespace kernel } // namespace xe // TODO(benvanik): remove. @@ -251,56 +246,59 @@ int xe_xex2_read_header(const uint8_t* addr, const size_t length, // size = xe::load_and_swap(pp + 0x04); } break; case XEX_HEADER_IMPORT_LIBRARIES: { - const size_t max_count = xe::countof(header->import_libraries); - size_t count = xe::load_and_swap(pp + 0x08); + auto import_libraries = + reinterpret_cast(pp); + + const uint32_t max_count = (uint32_t)xe::countof(header->import_libraries); + uint32_t count = import_libraries->library_count; assert_true(count <= max_count); if (count > max_count) { XELOGW("ignoring %zu extra entries in XEX_HEADER_IMPORT_LIBRARIES", - (max_count - count)); + (max_count - import_libraries->library_count)); count = max_count; } header->import_library_count = count; - uint32_t string_table_size = xe::load_and_swap(pp + 0x04); - const char* string_table = (const char*)(pp + 0x0C); + uint32_t string_table_size = import_libraries->string_table_size; + const char *string_table[32]; // Pretend 32 is max_count + std::memset(string_table, 0, sizeof(string_table)); - pp += 12 + string_table_size; + // Parse the string table + for (size_t i = 0, j = 0; i < string_table_size; j++) { + const char* str = import_libraries->string_table + i; + + string_table[j] = str; + i += std::strlen(str) + 1; + + // Padding + if ((i % 4) != 0) { + i += 4 - (i % 4); + } + } + + pp += 12 + import_libraries->string_table_size; for (size_t m = 0; m < count; m++) { xe_xex2_import_library_t* library = &header->import_libraries[m]; + auto src_library = (xe::xex2_import_library *)pp; + memcpy(library->digest, pp + 0x04, 20); - library->import_id = xe::load_and_swap(pp + 0x18); - library->version.value = xe::load_and_swap(pp + 0x1C); - library->min_version.value = xe::load_and_swap(pp + 0x20); + library->import_id = src_library->id; + library->version.value = src_library->version.value; + library->min_version.value = src_library->version_min.value; - const uint16_t name_index = - xe::load_and_swap(pp + 0x24) & 0xFF; - for (size_t i = 0, j = 0; i < string_table_size;) { - assert_true(j <= 0xFF); - if (j == name_index) { - std::strncpy(library->name, string_table + i, - xe::countof(library->name)); - break; - } - if (string_table[i] == 0) { - i++; - if (i % 4) { - i += 4 - (i % 4); - } - j++; - } else { - i++; - } - } + std::strncpy(library->name, + string_table[src_library->name_index], + xe::countof(library->name)); - library->record_count = xe::load_and_swap(pp + 0x26); + library->record_count = src_library->count; library->records = (uint32_t*)calloc(library->record_count, sizeof(uint32_t)); XEEXPECTNOTNULL(library->records); - pp += 0x28; for (size_t i = 0; i < library->record_count; i++) { - library->records[i] = xe::load_and_swap(pp); - pp += 4; + library->records[i] = src_library->import_table[i]; } + + pp += src_library->size; } } break; case XEX_HEADER_STATIC_LIBRARIES: { @@ -1058,17 +1056,17 @@ uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, uint16_t ordinal) { // XEX-style export table. if (header->loader_info.export_table) { - auto export_table = reinterpret_cast( + auto export_table = reinterpret_cast( xex->memory->TranslateVirtual(header->loader_info.export_table)); - uint32_t ordinal_count = xe::byte_swap(export_table->count); - uint32_t ordinal_base = xe::byte_swap(export_table->base); + uint32_t ordinal_count = export_table->count; + uint32_t ordinal_base = export_table->base; if (ordinal > ordinal_count) { XELOGE("xe_xex2_lookup_export: ordinal out of bounds"); return 0; } uint32_t i = ordinal - ordinal_base; - uint32_t ordinal_offset = xe::byte_swap(export_table->ordOffset[i]); - ordinal_offset += xe::byte_swap(export_table->imagebaseaddr) << 16; + uint32_t ordinal_offset = export_table->ordOffset[i]; + ordinal_offset += export_table->imagebaseaddr << 16; return ordinal_offset; } diff --git a/src/xenia/kernel/util/xex2.h b/src/xenia/kernel/util/xex2.h index 85a674b95..77493087f 100644 --- a/src/xenia/kernel/util/xex2.h +++ b/src/xenia/kernel/util/xex2.h @@ -14,9 +14,6 @@ #include "xenia/memory.h" namespace xe { -namespace kernel { -uint32_t xex2_get_header_size(const xex2_header* header); -} // namespace kernel } // namespace xe typedef struct { int reserved; } xe_xex2_options_t; diff --git a/src/xenia/kernel/util/xex2_info.h b/src/xenia/kernel/util/xex2_info.h index afd1ea087..21dece244 100644 --- a/src/xenia/kernel/util/xex2_info.h +++ b/src/xenia/kernel/util/xex2_info.h @@ -297,18 +297,6 @@ typedef struct { xe_xex2_approval_type approval; } xe_xex2_static_library_t; -// credits: some obscure pastebin (http://pastebin.com/ZRvr3Sgj) -typedef struct { - uint32_t magic[3]; - uint32_t modulenumber[2]; - uint32_t version[3]; - uint32_t imagebaseaddr; // must be <<16 to be accurate - uint32_t count; - uint32_t base; - uint32_t ordOffset[1]; // ordOffset[0] + (imagebaseaddr << 16) = function - // offset of ordinal 1 -} xe_xex2_export_table; - typedef enum { XEX_ENCRYPTION_NONE = 0, XEX_ENCRYPTION_NORMAL = 1, @@ -585,6 +573,18 @@ struct xex2_security_info { xe::be allowed_media_types; // 0x17C }; static_assert_size(xex2_security_info, 0x180); + +struct xex2_export_table { + xe::be magic[3]; // 0x0 + xe::be modulenumber[2]; // 0xC + xe::be version[3]; // 0x14 + xe::be imagebaseaddr; // 0x20 must be <<16 to be accurate + xe::be count; // 0x24 + xe::be base; // 0x28 + xe::be + ordOffset[1]; // 0x2C ordOffset[0] + (imagebaseaddr << 16) = function +}; + } // namespace xe #endif // XENIA_KERNEL_XEX2_INFO_H_ From 362a521c7942754ec02157df4cb3c67a8de798c0 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Thu, 2 Jul 2015 21:58:47 -0500 Subject: [PATCH 05/20] Rewrite XexModule to drop dependency on old xex2 headers for imports --- src/xenia/base/byte_order.h | 1 + src/xenia/cpu/xex_module.cc | 253 ++++++++++++++++++++---------------- src/xenia/cpu/xex_module.h | 23 +++- 3 files changed, 162 insertions(+), 115 deletions(-) diff --git a/src/xenia/base/byte_order.h b/src/xenia/base/byte_order.h index da74c0be0..bb0fc145d 100644 --- a/src/xenia/base/byte_order.h +++ b/src/xenia/base/byte_order.h @@ -12,6 +12,7 @@ #include +#include "xenia/base/assert.h" #include "xenia/base/platform.h" #if XE_PLATFORM_MAC diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index ca00c5bff..18f5ab6f6 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -82,6 +82,12 @@ bool XexModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const { return XexModule::GetOptHeader(xex_header_, key, out_ptr); } +const xex2_security_info* XexModule::GetSecurityInfo( + const xex2_header* header) { + return reinterpret_cast((uint8_t*)header + + header->security_offset); +} + uint32_t XexModule::GetProcAddress(uint16_t ordinal) const { return xe_xex2_lookup_export(xex_, ordinal); } @@ -130,16 +136,17 @@ bool XexModule::Load(const std::string& name, const std::string& path, bool XexModule::Load(const std::string& name, const std::string& path, xe_xex2_ref xex) { xex_ = xex; - const xe_xex2_header_t* header = xe_xex2_get_header(xex); + auto header = xex_header_; + auto old_header = xe_xex2_get_header(xex_); // Scan and find the low/high addresses. // All code sections are continuous, so this should be easy. low_address_ = UINT_MAX; high_address_ = 0; - for (uint32_t n = 0, i = 0; n < header->section_count; n++) { - const xe_xex2_section_t* section = &header->sections[n]; + for (uint32_t n = 0, i = 0; n < old_header->section_count; n++) { + const xe_xex2_section_t* section = &old_header->sections[n]; const uint32_t start_address = - header->exe_address + (i * section->page_size); + old_header->exe_address + (i * section->page_size); const uint32_t end_address = start_address + (section->info.page_count * section->page_size); if (section->info.type == XEX_SECTION_CODE) { @@ -153,12 +160,37 @@ bool XexModule::Load(const std::string& name, const std::string& path, processor_->backend()->CommitExecutableRange(low_address_, high_address_); // Add all imports (variables/functions). - for (size_t n = 0; n < header->import_library_count; n++) { - if (!SetupLibraryImports(&header->import_libraries[n])) { - return false; + xex2_opt_import_libraries* opt_import_header = nullptr; + GetOptHeader(XEX_HEADER_IMPORT_LIBRARIES, &opt_import_header); + assert_not_null(opt_import_header); + + // FIXME: Don't know if 32 is the actual limit, but haven't seen more than 2. + const char* string_table[32]; + std::memset(string_table, 0, sizeof(string_table)); + + // Parse the string table + for (size_t i = 0, j = 0; i < opt_import_header->string_table_size; j++) { + const char* str = opt_import_header->string_table + i; + + string_table[j] = str; + i += std::strlen(str) + 1; + + // Padding + if ((i % 4) != 0) { + i += 4 - (i % 4); } } + auto libraries = + (uint8_t*)opt_import_header + opt_import_header->string_table_size + 12; + uint32_t library_offset = 0; + for (uint32_t i = 0; i < opt_import_header->library_count; i++) { + auto library = reinterpret_cast((uint8_t*)libraries + library_offset); + SetupLibraryImports(string_table[library->name_index], library); + + library_offset += library->size; + } + // Find __savegprlr_* and __restgprlr_* and the others. // We can flag these for special handling (inlining/etc). if (!FindSaveRest()) { @@ -180,121 +212,103 @@ bool XexModule::Load(const std::string& name, const std::string& path, return true; } -bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) { - ExportResolver* export_resolver = processor_->export_resolver(); - - xe_xex2_import_info_t* import_infos; - size_t import_info_count; - if (xe_xex2_get_import_infos(xex_, library, &import_infos, - &import_info_count)) { - return false; +bool XexModule::SetupLibraryImports(const char* name, + const xex2_import_library* library) { + ExportResolver* kernel_resolver = nullptr; + if (kernel_state_->IsKernelModule(name)) { + kernel_resolver = processor_->export_resolver(); } - char name[128]; - for (size_t n = 0; n < import_info_count; n++) { - const xe_xex2_import_info_t* info = &import_infos[n]; + auto user_module = kernel_state_->GetModule(name); - // Strip off the extension (for the symbol name) - std::string libname = library->name; - auto dot = libname.find_last_of('.'); - if (dot != libname.npos) { - libname = libname.substr(0, dot); - } + std::string libbasename = name; + auto dot = libbasename.find_last_of('.'); + if (dot != libbasename.npos) { + libbasename = libbasename.substr(0, dot); + } - Export* kernel_export = nullptr; // kernel export info - uint32_t user_export_addr = 0; // user export address + // Imports are stored as {import descriptor, thunk addr, import desc, ...} + for (uint32_t i = 0; i < library->count; i++) { + uint32_t record_addr = library->import_table[i]; + assert_not_zero(record_addr); - if (kernel_state_->IsKernelModule(library->name)) { - kernel_export = - export_resolver->GetExportByOrdinal(library->name, info->ordinal); + auto record_slot = + memory()->TranslateVirtual*>(record_addr); + uint32_t record_value = *record_slot; + + uint16_t record_type = (record_value & 0xFF000000) >> 24; + uint16_t ordinal = record_value & 0xFFFF; + + Export* kernel_export = nullptr; + uint32_t user_export_addr = 0; + + if (kernel_resolver) { + kernel_export = kernel_resolver->GetExportByOrdinal(name, ordinal); } else { - auto module = kernel_state_->GetModule(library->name); - if (module) { - user_export_addr = module->GetProcAddressByOrdinal(info->ordinal); - } + user_export_addr = user_module->GetProcAddressByOrdinal(ordinal); } - if (kernel_export) { - if (info->thunk_address) { - snprintf(name, xe::countof(name), "__imp_%s", kernel_export->name); - } else { - snprintf(name, xe::countof(name), "%s", kernel_export->name); - } - } else { - snprintf(name, xe::countof(name), "__imp_%s_%.3X", libname.c_str(), - info->ordinal); + // Import not resolved? + assert_not_zero(kernel_export || user_export_addr); + if (!kernel_export && !user_export_addr) { + XELOGW( + "WARNING: an import variable was not resolved! (import lib: %s, " + "ordinal: %.3X)", + name, ordinal); } - VariableInfo* var_info; - DeclareVariable(info->value_address, &var_info); - // var->set_name(name); - var_info->set_status(SymbolStatus::kDeclared); - DefineVariable(var_info); - // var->kernel_export = kernel_export; - var_info->set_status(SymbolStatus::kDefined); - - // Grab, if available. - auto slot = memory_->TranslateVirtual(info->value_address); - if (kernel_export) { - if (kernel_export->type == Export::Type::kFunction) { - // Not exactly sure what this should be... - if (info->thunk_address) { - *slot = xe::byte_swap(info->thunk_address); - } else { - // TODO(benvanik): find out what import variables are. - XELOGW("kernel import variable not defined %.8X %s", - info->value_address, kernel_export->name); - *slot = xe::byte_swap(0xF00DF00D); - } - } else { - if (kernel_export->is_implemented()) { - // Implemented - replace with pointer. - xe::store_and_swap(slot, kernel_export->variable_ptr); - } else { - // Not implemented - write with a dummy value. - xe::store_and_swap( - slot, 0xD000BEEF | (kernel_export->ordinal & 0xFFF) << 16); - XELOGCPU("WARNING: imported a variable with no value: %s", - kernel_export->name); - } - } - } else if (user_export_addr) { - xe::store_and_swap(slot, user_export_addr); - } else { - // No module found. - XELOGE("kernel import not found: %s", name); - if (info->thunk_address) { - *slot = xe::byte_swap(info->thunk_address); - } else { - *slot = xe::byte_swap(0xF00DF00D); - } - } - - if (info->thunk_address) { + StringBuffer import_name; + if (record_type == 0) { + // Variable. + import_name.AppendFormat("__imp__"); if (kernel_export) { - snprintf(name, xe::countof(name), "%s", kernel_export->name); + import_name.AppendFormat("%s", kernel_export->name); } else if (user_export_addr) { - snprintf(name, xe::countof(name), "__%s_%.3X", libname.c_str(), - info->ordinal); - } else { - snprintf(name, xe::countof(name), "__kernel_%s_%.3X", libname.c_str(), - info->ordinal); + import_name.AppendFormat("%s_%.3X", libbasename.c_str(), ordinal); } - 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; + if (kernel_export) { + if (kernel_export->type == Export::Type::kFunction) { + // Not exactly sure what this should be... + // Appears to be ignored. + *record_slot = 0xDEADC0DE; + } else if (kernel_export->type == Export::Type::kVariable) { + // Kernel import variable + if (kernel_export->is_implemented()) { + // Implemented - replace with pointer. + *record_slot = kernel_export->variable_ptr; + } else { + // Not implemented - write with a dummy value. + *record_slot = 0xD000BEEF | (kernel_export->ordinal & 0xFFF) << 16; + XELOGCPU("WARNING: imported a variable with no value: %s", + kernel_export->name); + } + } + } else if (user_export_addr) { + *record_slot = user_export_addr; + } - uint8_t* p = memory()->TranslateVirtual(info->thunk_address); - xe::store_and_swap(p + 0x0, 0x3D600000 | hi_addr); - xe::store_and_swap(p + 0x4, 0x616B0000 | low_addr); - } else { + // Setup a variable and define it. + VariableInfo* var_info; + DeclareVariable(record_addr, &var_info); + var_info->set_name(import_name.GetString()); + var_info->set_status(SymbolStatus::kDeclared); + DefineVariable(var_info); + var_info->set_status(SymbolStatus::kDefined); + } else if (record_type == 1) { + // Thunk. + if (kernel_export) { + import_name.AppendFormat("%s", kernel_export->name); + } else if (user_export_addr) { + import_name.AppendFormat("__%s_%.3X", libbasename.c_str(), ordinal); + } + + FunctionInfo* fn_info; + DeclareFunction(record_addr, &fn_info); + fn_info->set_end_address(record_addr + 16 - 4); + fn_info->set_name(import_name.GetString()); + + if (kernel_export) { // On load we have something like this in memory: // li r3, 0 // li r4, 0x1F5 @@ -309,7 +323,7 @@ bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) { // blr // nop // nop - uint8_t* p = memory()->TranslateVirtual(info->thunk_address); + uint8_t* p = memory()->TranslateVirtual(record_addr); xe::store_and_swap(p + 0x0, 0x44000002); xe::store_and_swap(p + 0x4, 0x4E800020); xe::store_and_swap(p + 0x8, 0x60000000); @@ -328,13 +342,26 @@ bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) { handler = UndefinedImport; } - 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); - fn_info->set_status(SymbolStatus::kDeclared); + } 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); } + + fn_info->set_status(SymbolStatus::kDeclared); + } else { + // Bad. + assert_always(); } } diff --git a/src/xenia/cpu/xex_module.h b/src/xenia/cpu/xex_module.h index f15c03bd9..f15eff3b8 100644 --- a/src/xenia/cpu/xex_module.h +++ b/src/xenia/cpu/xex_module.h @@ -34,6 +34,9 @@ class XexModule : public xe::cpu::Module { xe_xex2_ref xex() const { return xex_; } const xex2_header* xex_header() const { return xex_header_; } + const xex2_security_info* xex_security_info() const { + return GetSecurityInfo(xex_header_); + } // Gets an optional header. Returns NULL if not found. // Special case: if key & 0xFF == 0x00, this function will return the value, @@ -42,6 +45,22 @@ class XexModule : public xe::cpu::Module { void** out_ptr); bool GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const; + // Ultra-cool templated version + // Special case: if key & 0xFF == 0x00, this function will return the value, + // not a pointer! + template + static bool GetOptHeader(const xex2_header* header, xe_xex2_header_keys key, + T* out_ptr) { + return GetOptHeader(header, key, reinterpret_cast(out_ptr)); + } + + template + bool GetOptHeader(xe_xex2_header_keys key, T* out_ptr) const { + return GetOptHeader(key, reinterpret_cast(out_ptr)); + } + + static const xex2_security_info* GetSecurityInfo(const xex2_header* header); + uint32_t GetProcAddress(uint16_t ordinal) const; uint32_t GetProcAddress(const char* name) const; @@ -55,8 +74,8 @@ class XexModule : public xe::cpu::Module { bool ContainsAddress(uint32_t address) override; private: - bool SetupImports(xe_xex2_ref xex); - bool SetupLibraryImports(const xe_xex2_import_library_t* library); + bool SetupLibraryImports(const char* name, + const xex2_import_library* library); bool FindSaveRest(); private: From 82ec1c345d06593ddbfcc9b21a55511b4c3dbbf5 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Thu, 2 Jul 2015 22:00:45 -0500 Subject: [PATCH 06/20] Dump all xex2 headers in XUserModule (minus a couple) --- src/xenia/kernel/objects/xuser_module.cc | 141 ++++++++++++++++++++--- src/xenia/kernel/objects/xuser_module.h | 3 + src/xenia/kernel/util/xex2_info.h | 31 +++++ 3 files changed, 159 insertions(+), 16 deletions(-) diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 8a7b958d6..182d4ab33 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -103,9 +103,9 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) { // Cache some commonly used headers... this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, - (void**)&entry_point_); + &entry_point_); this->xex_module()->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE, - (void**)&stack_size_); + &stack_size_); OnLoad(); @@ -124,7 +124,7 @@ X_STATUS XUserModule::GetSection(const char* name, uint32_t* out_section_data, uint32_t* out_section_size) { xex2_opt_resource_info* resource_header = nullptr; if (!XexModule::GetOptHeader(xex_header(), XEX_HEADER_RESOURCE_INFO, - (void**)&resource_header)) { + &resource_header)) { // No resources. return X_STATUS_UNSUCCESSFUL; } @@ -204,18 +204,9 @@ X_STATUS XUserModule::Launch(uint32_t flags) { XELOGI("Launching module..."); Dump(); - // Grab some important variables... - auto header = xex_header(); - uint32_t exe_stack_size = 0; - uint32_t exe_entry_point = 0; - XexModule::GetOptHeader(xex_header(), XEX_HEADER_DEFAULT_STACK_SIZE, - (void**)&exe_stack_size); - XexModule::GetOptHeader(xex_header(), XEX_HEADER_ENTRY_POINT, - (void**)&exe_entry_point); - // Create a thread to run in. auto thread = object_ref( - new XThread(kernel_state(), exe_stack_size, 0, exe_entry_point, 0, 0)); + new XThread(kernel_state(), stack_size_, 0, entry_point_, 0, 0)); X_STATUS result = thread->Create(); if (XFAILED(result)) { @@ -236,9 +227,127 @@ void XUserModule::Dump() { // TODO: Need to loop through the optional headers one-by-one. - // XEX info. - printf("Module %s:\n\n", path_.c_str()); - printf(" Module Flags: %.8X\n", header->module_flags); + // XEX header. + printf("Module %s:\n", path_.c_str()); + printf(" Module Flags: %.8X\n", (uint32_t)header->module_flags); + + // Security header + auto security_info = xex_module()->xex_security_info(); + printf("Security Header:\n"); + printf(" Image Flags: %.8X\n", (uint32_t)security_info->image_flags); + printf(" Load Address: %.8X\n", (uint32_t)security_info->load_address); + printf(" Image Size: %.8X\n", (uint32_t)security_info->image_size); + printf(" Export Table: %.8X\n", (uint32_t)security_info->export_table); + + // Optional headers + printf("Optional Header Count: %d\n", (uint32_t)header->header_count); + + for (uint32_t i = 0; i < header->header_count; i++) { + auto& opt_header = header->headers[i]; + + // Stash a pointer (although this isn't used in every case) + void* opt_header_ptr = (uint8_t*)header + opt_header.offset; + switch (opt_header.key) { + case XEX_HEADER_RESOURCE_INFO: { + printf(" XEX_HEADER_RESOURCE_INFO (TODO):\n"); + auto opt_resource_info = + reinterpret_cast(opt_header_ptr); + + } break; + case XEX_HEADER_FILE_FORMAT_INFO: { + printf(" XEX_HEADER_FILE_FORMAT_INFO (TODO):\n"); + } break; + case XEX_HEADER_DELTA_PATCH_DESCRIPTOR: { + printf(" XEX_HEADER_DELTA_PATCH_DESCRIPTOR (TODO):\n"); + } break; + case XEX_HEADER_BOUNDING_PATH: { + auto opt_bound_path = + reinterpret_cast(opt_header_ptr); + printf(" XEX_HEADER_BOUNDING_PATH: %s\n", opt_bound_path->path); + } break; + case XEX_HEADER_ORIGINAL_BASE_ADDRESS: { + printf(" XEX_HEADER_ORIGINAL_BASE_ADDRESS: %.8X\n", (uint32_t)opt_header.value); + } break; + case XEX_HEADER_ENTRY_POINT: { + printf(" XEX_HEADER_ENTRY_POINT: %.8X\n", (uint32_t)opt_header.value); + } break; + case XEX_HEADER_IMAGE_BASE_ADDRESS: { + printf(" XEX_HEADER_IMAGE_BASE_ADDRESS: %.8X\n", (uint32_t)opt_header.value); + } break; + case XEX_HEADER_ORIGINAL_PE_NAME: { + auto opt_pe_name = + reinterpret_cast(opt_header_ptr); + printf(" XEX_HEADER_ORIGINAL_PE_NAME: %s\n", opt_pe_name->name); + } break; + case XEX_HEADER_STATIC_LIBRARIES: { + printf(" XEX_HEADER_STATIC_LIBRARIES:\n"); + auto opt_static_libraries = + reinterpret_cast(opt_header_ptr); + + uint32_t count = (opt_static_libraries->size - 4) / 0x10; + for (uint32_t i = 0; i < count; i++) { + auto& library = opt_static_libraries->libraries[i]; + printf( + " %-8s : %d.%d.%d.%d\n", library.name, + (uint16_t)library.version_major, (uint16_t)library.version_minor, + (uint16_t)library.version_build, (uint16_t)library.version_qfe); + } + } break; + case XEX_HEADER_TLS_INFO: { + printf(" XEX_HEADER_TLS_INFO:\n"); + auto opt_tls_info = + reinterpret_cast(opt_header_ptr); + + printf(" Slot Count: %d\n", (uint32_t)opt_tls_info->slot_count); + printf(" Raw Data Address: %.8X\n", (uint32_t)opt_tls_info->raw_data_address); + printf(" Data Size: %d\n", (uint32_t)opt_tls_info->data_size); + printf(" Raw Data Size: %d\n", (uint32_t)opt_tls_info->raw_data_size); + } break; + case XEX_HEADER_DEFAULT_STACK_SIZE: { + printf(" XEX_HEADER_DEFAULT_STACK_SIZE: %d\n", (uint32_t)opt_header.value); + } break; + case XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: { + printf(" XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: %d\n", (uint32_t)opt_header.value); + } break; + case XEX_HEADER_DEFAULT_HEAP_SIZE: { + printf(" XEX_HEADER_DEFAULT_HEAP_SIZE: %d\n", (uint32_t)opt_header.value); + } break; + case XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS: { + printf(" XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS (TODO):\n"); + } break; + case XEX_HEADER_SYSTEM_FLAGS: { + printf(" XEX_HEADER_SYSTEM_FLAGS (TODO):\n"); + } break; + case XEX_HEADER_EXECUTION_INFO: { + printf(" XEX_HEADER_EXECUTION_INFO (TODO):\n"); + } break; + case XEX_HEADER_TITLE_WORKSPACE_SIZE: { + printf(" XEX_HEADER_TITLE_WORKSPACE_SIZE (TODO):\n"); + } break; + case XEX_HEADER_GAME_RATINGS: { + printf(" XEX_HEADER_GAME_RATINGS (TODO):\n"); + } break; + case XEX_HEADER_LAN_KEY: { + printf(" XEX_HEADER_LAN_KEY (TODO):\n"); + } break; + case XEX_HEADER_XBOX360_LOGO: { + printf(" XEX_HEADER_XBOX360_LOGO (TODO):\n"); + } break; + case XEX_HEADER_MULTIDISC_MEDIA_IDS: { + printf(" XEX_HEADER_MULTIDISC_MEDIA_IDS (TODO):\n"); + } break; + case XEX_HEADER_ALTERNATE_TITLE_IDS: { + printf(" XEX_HEADER_ALTERNATE_TITLE_IDS (TODO):\n"); + } break; + case XEX_HEADER_ADDITIONAL_TITLE_MEMORY: { + printf(" XEX_HEADER_ADDITIONAL_TITLE_MEMORY (TODO):\n"); + } break; + case XEX_HEADER_EXPORTS_BY_NAME: { + printf(" XEX_HEADER_EXPORTS_BY_NAME (TODO):\n"); + } break; + } + } + /* printf(" System Flags: %.8X\n", header->system_flags); printf("\n"); diff --git a/src/xenia/kernel/objects/xuser_module.h b/src/xenia/kernel/objects/xuser_module.h index 588c7d875..36df80282 100644 --- a/src/xenia/kernel/objects/xuser_module.h +++ b/src/xenia/kernel/objects/xuser_module.h @@ -31,6 +31,7 @@ class XUserModule : public XModule { } const xex2_header* xex_header() const { return xex_module()->xex_header(); } + uint32_t guest_xex_header() const { return guest_xex_header_; } uint32_t entry_point() const { return entry_point_; } uint32_t stack_size() const { return stack_size_; } @@ -43,8 +44,10 @@ class XUserModule : public XModule { X_STATUS GetSection(const char* name, uint32_t* out_section_data, uint32_t* out_section_size) override; + // Get optional header - FOR HOST USE ONLY! X_STATUS GetOptHeader(xe_xex2_header_keys key, void** out_ptr); + // Get optional header that can safely be returned to guest code. X_STATUS GetOptHeader(xe_xex2_header_keys key, uint32_t* out_header_guest_ptr); static X_STATUS GetOptHeader(uint8_t* membase, const xex2_header* header, diff --git a/src/xenia/kernel/util/xex2_info.h b/src/xenia/kernel/util/xex2_info.h index 21dece244..649bdd118 100644 --- a/src/xenia/kernel/util/xex2_info.h +++ b/src/xenia/kernel/util/xex2_info.h @@ -468,6 +468,37 @@ union xex2_version { }; }; +struct xex2_opt_bound_path { + xe::be size; + char path[1]; +}; + +struct xex2_opt_static_library { + char name[8]; // 0x0 + xe::be version_major; // 0x8 + xe::be version_minor; // 0xA + xe::be version_build; // 0xC + xe::be approval_type; // 0xE + xe::be version_qfe; // 0xF +}; +static_assert_size(xex2_opt_static_library, 0x10); + +struct xex2_opt_static_libraries { + xe::be size; // 0x0 + xex2_opt_static_library libraries[1]; // 0x4 +}; + +struct xex2_opt_original_pe_name { + xe::be size; + char name[1]; +}; + +struct xex2_opt_data_directory { + xe::be offset; // 0x0 + xe::be size; // 0x4 +}; +static_assert_size(xex2_opt_data_directory, 0x8); + struct xex2_opt_tls_info { xe::be slot_count; // 0x0 xe::be raw_data_address; // 0x4 From a25090f3ca63b252f1cb7688022f0fefe152859a Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 10:24:11 -0500 Subject: [PATCH 07/20] Templated XUserModule::GetOptHeader --- src/xenia/kernel/kernel_state.cc | 4 ++-- src/xenia/kernel/objects/xthread.cc | 2 +- src/xenia/kernel/objects/xuser_module.h | 6 ++++++ src/xenia/kernel/xboxkrnl_modules.cc | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index a3544b4ad..5435e45c0 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -123,7 +123,7 @@ uint32_t KernelState::title_id() const { xex2_opt_execution_info* exec_info = 0; executable_module_->GetOptHeader(XEX_HEADER_EXECUTION_INFO, - (void**)&exec_info); + &exec_info); if (exec_info) { return exec_info->title_id; @@ -202,7 +202,7 @@ void KernelState::SetExecutableModule(object_ref module) { } xex2_opt_tls_info* tls_header = nullptr; - executable_module_->GetOptHeader(XEX_HEADER_TLS_INFO, (void**)&tls_header); + executable_module_->GetOptHeader(XEX_HEADER_TLS_INFO, &tls_header); if (tls_header) { auto pib = memory_->TranslateVirtual( process_info_block_address_); diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index 471ba3c6b..8330a4244 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -166,7 +166,7 @@ X_STATUS XThread::Create() { // Games will specify a certain number of 4b slots that each thread will get. xex2_opt_tls_info* tls_header = nullptr; if (module) { - module->GetOptHeader(XEX_HEADER_TLS_INFO, (void**)&tls_header); + module->GetOptHeader(XEX_HEADER_TLS_INFO, &tls_header); } const uint32_t kDefaultTlsSlotCount = 32; diff --git a/src/xenia/kernel/objects/xuser_module.h b/src/xenia/kernel/objects/xuser_module.h index 36df80282..e8896fede 100644 --- a/src/xenia/kernel/objects/xuser_module.h +++ b/src/xenia/kernel/objects/xuser_module.h @@ -47,6 +47,12 @@ class XUserModule : public XModule { // Get optional header - FOR HOST USE ONLY! X_STATUS GetOptHeader(xe_xex2_header_keys key, void** out_ptr); + // Get optional header - FOR HOST USE ONLY! + template + X_STATUS GetOptHeader(xe_xex2_header_keys key, T* out_ptr) { + return GetOptHeader(key, reinterpret_cast(out_ptr)); + } + // Get optional header that can safely be returned to guest code. X_STATUS GetOptHeader(xe_xex2_header_keys key, uint32_t* out_header_guest_ptr); diff --git a/src/xenia/kernel/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl_modules.cc index 9b1889e08..6f95ef3a1 100644 --- a/src/xenia/kernel/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl_modules.cc @@ -143,7 +143,7 @@ SHIM_CALL XexCheckExecutablePrivilege_shim(PPCContext* ppc_context, } uint32_t flags = 0; - module->GetOptHeader(XEX_HEADER_SYSTEM_FLAGS, (void **)&flags); + module->GetOptHeader(XEX_HEADER_SYSTEM_FLAGS, &flags); SHIM_SET_RETURN_32((flags & mask) > 0); } From 0211135fd6382b5ffd483b7ac3a3bd1507390525 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 10:41:43 -0500 Subject: [PATCH 08/20] Fix potential corruption for GetOptHeader --- src/xenia/cpu/xex_module.cc | 25 +++++++++++++++---------- src/xenia/cpu/xex_module.h | 4 ++-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index 18f5ab6f6..2748687c9 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -59,7 +59,8 @@ bool XexModule::GetOptHeader(const xex2_header* header, xe_xex2_header_keys key, switch (key & 0xFF) { case 0x00: { // We just return the value of the optional header. - *out_ptr = (void*)((uint64_t)opt_header.value); + // Assume that the output pointer points to a uint32_t. + *(uint32_t*)out_ptr = (uint32_t)opt_header.value; } break; case 0x01: { // Pointer to the value on the optional header. @@ -139,8 +140,14 @@ bool XexModule::Load(const std::string& name, const std::string& path, auto header = xex_header_; auto old_header = xe_xex2_get_header(xex_); + // Setup debug info. + name_ = std::string(name); + path_ = std::string(path); + // TODO(benvanik): debug info + // Scan and find the low/high addresses. // All code sections are continuous, so this should be easy. + // TODO: Use the new xex header to do this. low_address_ = UINT_MAX; high_address_ = 0; for (uint32_t n = 0, i = 0; n < old_header->section_count; n++) { @@ -170,6 +177,7 @@ bool XexModule::Load(const std::string& name, const std::string& path, // Parse the string table for (size_t i = 0, j = 0; i < opt_import_header->string_table_size; j++) { + assert_true(j < xe::countof(string_table)); const char* str = opt_import_header->string_table + i; string_table[j] = str; @@ -185,7 +193,8 @@ bool XexModule::Load(const std::string& name, const std::string& path, (uint8_t*)opt_import_header + opt_import_header->string_table_size + 12; uint32_t library_offset = 0; for (uint32_t i = 0; i < opt_import_header->library_count; i++) { - auto library = reinterpret_cast((uint8_t*)libraries + library_offset); + auto library = reinterpret_cast((uint8_t*)libraries + + library_offset); SetupLibraryImports(string_table[library->name_index], library); library_offset += library->size; @@ -197,11 +206,6 @@ bool XexModule::Load(const std::string& name, const std::string& path, return false; } - // Setup debug info. - name_ = std::string(name); - path_ = std::string(path); - // TODO(benvanik): debug info - // Load a specified module map and diff. if (FLAGS_load_module_map.size()) { if (!ReadMap(FLAGS_load_module_map.c_str())) { @@ -228,6 +232,7 @@ bool XexModule::SetupLibraryImports(const char* name, } // Imports are stored as {import descriptor, thunk addr, import desc, ...} + // Even thunks have an import descriptor (albeit unused/useless) for (uint32_t i = 0; i < library->count; i++) { uint32_t record_addr = library->import_table[i]; assert_not_zero(record_addr); @@ -252,9 +257,9 @@ bool XexModule::SetupLibraryImports(const char* name, assert_not_zero(kernel_export || user_export_addr); if (!kernel_export && !user_export_addr) { XELOGW( - "WARNING: an import variable was not resolved! (import lib: %s, " - "ordinal: %.3X)", - name, ordinal); + "WARNING: an import variable was not resolved! (library: %s, import " + "lib: %s, ordinal: %.3X)", + name_.c_str(), name, ordinal); } StringBuffer import_name; diff --git a/src/xenia/cpu/xex_module.h b/src/xenia/cpu/xex_module.h index f15eff3b8..f4723a6cb 100644 --- a/src/xenia/cpu/xex_module.h +++ b/src/xenia/cpu/xex_module.h @@ -40,14 +40,14 @@ class XexModule : public xe::cpu::Module { // Gets an optional header. Returns NULL if not found. // Special case: if key & 0xFF == 0x00, this function will return the value, - // not a pointer! + // not a pointer! This assumes out_ptr points to uint32_t. static bool GetOptHeader(const xex2_header* header, xe_xex2_header_keys key, void** out_ptr); bool GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const; // Ultra-cool templated version // Special case: if key & 0xFF == 0x00, this function will return the value, - // not a pointer! + // not a pointer! This assumes out_ptr points to uint32_t. template static bool GetOptHeader(const xex2_header* header, xe_xex2_header_keys key, T* out_ptr) { From 307feaf893e19bb3823bd0bdb438224e761f40b4 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 10:51:35 -0500 Subject: [PATCH 09/20] Formatting. --- src/xenia/kernel/objects/xuser_module.cc | 262 ++--------------------- 1 file changed, 18 insertions(+), 244 deletions(-) diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 182d4ab33..0b79720b0 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -102,10 +102,8 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) { ldr_data->xex_header_base = guest_xex_header_; // Cache some commonly used headers... - this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, - &entry_point_); - this->xex_module()->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE, - &stack_size_); + this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, &entry_point_); + this->xex_module()->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE, &stack_size_); OnLoad(); @@ -266,13 +264,15 @@ void XUserModule::Dump() { printf(" XEX_HEADER_BOUNDING_PATH: %s\n", opt_bound_path->path); } break; case XEX_HEADER_ORIGINAL_BASE_ADDRESS: { - printf(" XEX_HEADER_ORIGINAL_BASE_ADDRESS: %.8X\n", (uint32_t)opt_header.value); + printf(" XEX_HEADER_ORIGINAL_BASE_ADDRESS: %.8X\n", + (uint32_t)opt_header.value); } break; case XEX_HEADER_ENTRY_POINT: { printf(" XEX_HEADER_ENTRY_POINT: %.8X\n", (uint32_t)opt_header.value); } break; case XEX_HEADER_IMAGE_BASE_ADDRESS: { - printf(" XEX_HEADER_IMAGE_BASE_ADDRESS: %.8X\n", (uint32_t)opt_header.value); + printf(" XEX_HEADER_IMAGE_BASE_ADDRESS: %.8X\n", + (uint32_t)opt_header.value); } break; case XEX_HEADER_ORIGINAL_PE_NAME: { auto opt_pe_name = @@ -298,19 +298,25 @@ void XUserModule::Dump() { auto opt_tls_info = reinterpret_cast(opt_header_ptr); - printf(" Slot Count: %d\n", (uint32_t)opt_tls_info->slot_count); - printf(" Raw Data Address: %.8X\n", (uint32_t)opt_tls_info->raw_data_address); + printf(" Slot Count: %d\n", + (uint32_t)opt_tls_info->slot_count); + printf(" Raw Data Address: %.8X\n", + (uint32_t)opt_tls_info->raw_data_address); printf(" Data Size: %d\n", (uint32_t)opt_tls_info->data_size); - printf(" Raw Data Size: %d\n", (uint32_t)opt_tls_info->raw_data_size); + printf(" Raw Data Size: %d\n", + (uint32_t)opt_tls_info->raw_data_size); } break; case XEX_HEADER_DEFAULT_STACK_SIZE: { - printf(" XEX_HEADER_DEFAULT_STACK_SIZE: %d\n", (uint32_t)opt_header.value); + printf(" XEX_HEADER_DEFAULT_STACK_SIZE: %d\n", + (uint32_t)opt_header.value); } break; case XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: { - printf(" XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: %d\n", (uint32_t)opt_header.value); + printf(" XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: %d\n", + (uint32_t)opt_header.value); } break; case XEX_HEADER_DEFAULT_HEAP_SIZE: { - printf(" XEX_HEADER_DEFAULT_HEAP_SIZE: %d\n", (uint32_t)opt_header.value); + printf(" XEX_HEADER_DEFAULT_HEAP_SIZE: %d\n", + (uint32_t)opt_header.value); } break; case XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS: { printf(" XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS (TODO):\n"); @@ -347,238 +353,6 @@ void XUserModule::Dump() { } break; } } - - /* - printf(" System Flags: %.8X\n", header->system_flags); - printf("\n"); - printf(" Address: %.8X\n", header->exe_address); - printf(" Entry Point: %.8X\n", header->exe_entry_point); - printf(" Stack Size: %.8X\n", header->exe_stack_size); - printf(" Heap Size: %.8X\n", header->exe_heap_size); - printf("\n"); - printf(" Execution Info:\n"); - printf(" Media ID: %.8X\n", header->execution_info.media_id); - printf( - " Version: %d.%d.%d.%d\n", header->execution_info.version.major, - header->execution_info.version.minor, - header->execution_info.version.build, header->execution_info.version.qfe); - printf(" Base Version: %d.%d.%d.%d\n", - header->execution_info.base_version.major, - header->execution_info.base_version.minor, - header->execution_info.base_version.build, - header->execution_info.base_version.qfe); - printf(" Title ID: %.8X\n", header->execution_info.title_id); - printf(" Platform: %.8X\n", header->execution_info.platform); - printf(" Exec Table: %.8X\n", header->execution_info.executable_table); - printf(" Disc Number: %d\n", header->execution_info.disc_number); - printf(" Disc Count: %d\n", header->execution_info.disc_count); - printf(" Savegame ID: %.8X\n", header->execution_info.savegame_id); - printf("\n"); - printf(" Loader Info:\n"); - printf(" Image Flags: %.8X\n", header->loader_info.image_flags); - printf(" Game Regions: %.8X\n", header->loader_info.game_regions); - printf(" Media Flags: %.8X\n", header->loader_info.media_flags); - printf("\n"); - printf(" TLS Info:\n"); - printf(" Slot Count: %d\n", header->tls_info.slot_count); - printf(" Data Size: %db\n", header->tls_info.data_size); - printf(" Address: %.8X, %db\n", header->tls_info.raw_data_address, - header->tls_info.raw_data_size); - printf("\n"); - printf(" Headers:\n"); - for (size_t n = 0; n < header->header_count; n++) { - const xex2_opt_header* opt_header = &header->headers[n]; - printf(" %.8X (%.8X, %4db) %.8X = %11d\n", opt_header->key, - opt_header->offset, opt_header->length, opt_header->value, - opt_header->value); - } - printf("\n"); - - // Resources. - printf("Resources:\n"); - for (size_t n = 0; n < header->resource_info_count; n++) { - auto& res = header->resource_infos[n]; - printf(" %-8s %.8X-%.8X, %db\n", res.name, res.address, - res.address + res.size, res.size); - } - printf("\n"); - - // Section info. - printf("Sections:\n"); - for (size_t n = 0, i = 0; n < header->section_count; n++) { - const xe_xex2_section_t* section = &header->sections[n]; - const char* type = "UNKNOWN"; - switch (section->info.type) { - case XEX_SECTION_CODE: - type = "CODE "; - break; - case XEX_SECTION_DATA: - type = "RWDATA "; - break; - case XEX_SECTION_READONLY_DATA: - type = "RODATA "; - break; - } - const size_t start_address = header->exe_address + (i * section->page_size); - const size_t end_address = - start_address + (section->info.page_count * section->page_size); - printf(" %3d %s %3d pages %.8X - %.8X (%d bytes)\n", (int)n, type, - section->info.page_count, (int)start_address, (int)end_address, - section->info.page_count * section->page_size); - i += section->info.page_count; - } - printf("\n"); - - // Static libraries. - printf("Static Libraries:\n"); - for (size_t n = 0; n < header->static_library_count; n++) { - const xe_xex2_static_library_t* library = &header->static_libraries[n]; - printf(" %-8s : %d.%d.%d.%d\n", library->name, library->major, - library->minor, library->build, library->qfe); - } - printf("\n"); - - // Exports. - printf("Exports:\n"); - if (header->loader_info.export_table) { - printf(" XEX-style Ordinal Exports:\n"); - auto export_table = reinterpret_cast( - memory()->TranslateVirtual(header->loader_info.export_table)); - uint32_t ordinal_count = xe::byte_swap(export_table->count); - uint32_t ordinal_base = xe::byte_swap(export_table->base); - for (uint32_t i = 0, ordinal = ordinal_base; i < ordinal_count; - ++i, ++ordinal) { - uint32_t ordinal_offset = xe::byte_swap(export_table->ordOffset[i]); - ordinal_offset += xe::byte_swap(export_table->imagebaseaddr) << 16; - printf(" %.8X %.3X (%3d)\n", ordinal_offset, ordinal, - ordinal); - } - } - if (header->pe_export_table_offset) { - auto e = reinterpret_cast( - memory()->TranslateVirtual(header->exe_address + - header->pe_export_table_offset)); - uint32_t* function_table = (uint32_t*)((uint64_t)e + e->AddressOfFunctions); - uint32_t* name_table = (uint32_t*)((uint64_t)e + e->AddressOfNames); - uint16_t* ordinal_table = - (uint16_t*)((uint64_t)e + e->AddressOfNameOrdinals); - const char* mod_name = (const char*)((uint64_t)e + e->Name); - printf(" PE %s:\n", mod_name); - for (uint32_t i = 0; i < e->NumberOfNames; i++) { - const char* fn_name = (const char*)((uint64_t)e + name_table[i]); - uint16_t ordinal = ordinal_table[i]; - uint32_t addr = header->exe_address + function_table[ordinal]; - printf(" %.8X %.3X (%3d) %s\n", addr, ordinal, ordinal, - fn_name); - } - } - if (!header->loader_info.export_table && !header->pe_export_table_offset) { - printf(" No exports\n"); - } - printf("\n"); - - // Imports. - printf("Imports:\n"); - for (size_t n = 0; n < header->import_library_count; n++) { - const xe_xex2_import_library_t* library = &header->import_libraries[n]; - - xe_xex2_import_info_t* import_infos; - size_t import_info_count; - if (!xe_xex2_get_import_infos(xex_, library, &import_infos, - &import_info_count)) { - printf(" %s - %d imports\n", library->name, (int)import_info_count); - printf(" Version: %d.%d.%d.%d\n", library->version.major, - library->version.minor, library->version.build, - library->version.qfe); - printf(" Min Version: %d.%d.%d.%d\n", library->min_version.major, - library->min_version.minor, library->min_version.build, - library->min_version.qfe); - printf("\n"); - - // Counts. - int known_count = 0; - int unknown_count = 0; - int impl_count = 0; - int unimpl_count = 0; - for (size_t m = 0; m < import_info_count; m++) { - const xe_xex2_import_info_t* info = &import_infos[m]; - - if (kernel_state_->IsKernelModule(library->name)) { - auto kernel_export = - export_resolver->GetExportByOrdinal(library->name, info->ordinal); - if (kernel_export) { - known_count++; - if (kernel_export->is_implemented()) { - impl_count++; - } else { - unimpl_count++; - } - } else { - unknown_count++; - unimpl_count++; - } - } else { - auto module = kernel_state_->GetModule(library->name); - if (module) { - uint32_t export_addr = - module->GetProcAddressByOrdinal(info->ordinal); - if (export_addr) { - impl_count++; - known_count++; - } else { - unimpl_count++; - unknown_count++; - } - } else { - unimpl_count++; - unknown_count++; - } - } - } - printf(" Total: %4u\n", uint32_t(import_info_count)); - printf(" Known: %3d%% (%d known, %d unknown)\n", - (int)(known_count / (float)import_info_count * 100.0f), - known_count, unknown_count); - printf(" Implemented: %3d%% (%d implemented, %d unimplemented)\n", - (int)(impl_count / (float)import_info_count * 100.0f), impl_count, - unimpl_count); - printf("\n"); - - // Listing. - for (size_t m = 0; m < import_info_count; m++) { - const xe_xex2_import_info_t* info = &import_infos[m]; - const char* name = "UNKNOWN"; - bool implemented = false; - - Export* kernel_export = nullptr; - if (kernel_state_->IsKernelModule(library->name)) { - kernel_export = - export_resolver->GetExportByOrdinal(library->name, info->ordinal); - if (kernel_export) { - name = kernel_export->name; - implemented = kernel_export->is_implemented(); - } - } else { - auto module = kernel_state_->GetModule(library->name); - if (module && module->GetProcAddressByOrdinal(info->ordinal)) { - // TODO: Name lookup - implemented = true; - } - } - if (kernel_export && kernel_export->type == Export::Type::kVariable) { - printf(" V %.8X %.3X (%3d) %s %s\n", info->value_address, - info->ordinal, info->ordinal, implemented ? " " : "!!", name); - } else if (info->thunk_address) { - printf(" F %.8X %.8X %.3X (%3d) %s %s\n", info->value_address, - info->thunk_address, info->ordinal, info->ordinal, - implemented ? " " : "!!", name); - } - } - } - - printf("\n"); - } - */ } } // namespace kernel From c32a94599e24ca5d29c7ce65bda5bf1b4fe76dfc Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 10:51:56 -0500 Subject: [PATCH 10/20] Use the guest copy of the xex header for guest code. --- src/xenia/kernel/objects/xuser_module.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 0b79720b0..5742e7175 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -155,7 +155,8 @@ X_STATUS XUserModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) { X_STATUS XUserModule::GetOptHeader(xe_xex2_header_keys key, uint32_t* out_header_guest_ptr) { - auto header = xex_module()->xex_header(); + auto header = + memory()->TranslateVirtual(guest_xex_header_); if (!header) { return X_STATUS_UNSUCCESSFUL; } From 800f982a4d334435dcfbc7532d0f043e878cda4b Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 10:58:33 -0500 Subject: [PATCH 11/20] XEX2 Security Page Descriptors --- src/xenia/kernel/util/xex2_info.h | 46 +++++++++++++++++++------------ 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/xenia/kernel/util/xex2_info.h b/src/xenia/kernel/util/xex2_info.h index 649bdd118..569645cef 100644 --- a/src/xenia/kernel/util/xex2_info.h +++ b/src/xenia/kernel/util/xex2_info.h @@ -586,24 +586,36 @@ struct xex2_header { xex2_opt_header headers[1]; // 0x18 }; -struct xex2_security_info { - xe::be header_size; // 0x0 - xe::be image_size; // 0x4 - char rsa_signature[0x100]; // 0x8 - xe::be unk_108; // 0x108 unk length - xe::be image_flags; // 0x10C - xe::be load_address; // 0x110 - char section_digest[0x14]; // 0x114 - xe::be import_table_count; // 0x128 - char import_table_digest[0x14]; // 0x12C - char xgd2_media_id[0x10]; // 0x140 - char aes_key[0x10]; // 0x150 - xe::be export_table; // 0x160 - char header_digest[0x14]; // 0x164 - xe::be region; // 0x178 - xe::be allowed_media_types; // 0x17C +struct xex2_page_descriptor { + union { + struct { + uint32_t info : 4; + uint32_t size : 28; + }; + xe::be value; // 0x0 + }; + char data_digest[0x14]; // 0x4 +}; + +struct xex2_security_info { + xe::be header_size; // 0x0 + xe::be image_size; // 0x4 + char rsa_signature[0x100]; // 0x8 + xe::be unk_108; // 0x108 unk length + xe::be image_flags; // 0x10C + xe::be load_address; // 0x110 + char section_digest[0x14]; // 0x114 + xe::be import_table_count; // 0x128 + char import_table_digest[0x14]; // 0x12C + char xgd2_media_id[0x10]; // 0x140 + char aes_key[0x10]; // 0x150 + xe::be export_table; // 0x160 + char header_digest[0x14]; // 0x164 + xe::be region; // 0x178 + xe::be allowed_media_types; // 0x17C + xe::be page_descriptor_count; // 0x180 + xex2_page_descriptor page_descriptors[1]; // 0x184 }; -static_assert_size(xex2_security_info, 0x180); struct xex2_export_table { xe::be magic[3]; // 0x0 From d616231c8b111bbcf2eff4c9d14641114e6fd0bf Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 18:02:17 -0500 Subject: [PATCH 12/20] Trigger a PPC debug trap if an unresolved import is called. --- src/xenia/cpu/xex_module.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index 2748687c9..bff224435 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -361,6 +361,18 @@ bool XexModule::SetupLibraryImports(const char* name, 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); } fn_info->set_status(SymbolStatus::kDeclared); From e58bb5ad9f8124d6c869dd6d2c1c603b5ebcbe80 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 18:14:50 -0500 Subject: [PATCH 13/20] ReImplement XexModule::GetProcAddress --- src/xenia/cpu/xex_module.cc | 75 ++++++++++++++++++++++++++++++- src/xenia/kernel/util/xex2_info.h | 15 +++++++ 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index bff224435..1a7f451b0 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -90,11 +90,82 @@ const xex2_security_info* XexModule::GetSecurityInfo( } uint32_t XexModule::GetProcAddress(uint16_t ordinal) const { - return xe_xex2_lookup_export(xex_, ordinal); + // First: Check the xex2 export table. + if (xex_security_info()->export_table) { + auto export_table = memory()->TranslateVirtual( + xex_security_info()->export_table); + + if (ordinal > export_table->count) { + XELOGE("GetProcAddress(%.3X): ordinal out of bounds", ordinal); + return 0; + } + + uint32_t num = ordinal - export_table->base; + uint32_t ordinal_offset = export_table->ordOffset[num]; + ordinal_offset += export_table->imagebaseaddr << 16; + return ordinal_offset; + } + + // Second: Check the PE exports. + xe::be* exe_address = nullptr; + GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &exe_address); + assert_not_null(exe_address); + + xex2_opt_data_directory* pe_export_directory = 0; + if (GetOptHeader(XEX_HEADER_EXPORTS_BY_NAME, &pe_export_directory)) { + auto e = memory()->TranslateVirtual( + *exe_address + pe_export_directory->offset); + assert_not_null(e); + + uint32_t* function_table = (uint32_t*)((uint8_t*)e + e->AddressOfFunctions); + + if (ordinal < e->NumberOfFunctions) { + return xex_security_info()->load_address + function_table[ordinal]; + } + } + + return 0; } uint32_t XexModule::GetProcAddress(const char* name) const { - return xe_xex2_lookup_export(xex_, name); + xe::be* exe_address = nullptr; + GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &exe_address); + assert_not_null(exe_address); + + xex2_opt_data_directory* pe_export_directory = 0; + if (!GetOptHeader(XEX_HEADER_EXPORTS_BY_NAME, &pe_export_directory)) { + // No exports by name. + return 0; + } + + auto e = memory()->TranslateVirtual( + *exe_address + pe_export_directory->offset); + assert_not_null(e); + + // e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY! + uint32_t* function_table = (uint32_t*)((uint64_t)e + e->AddressOfFunctions); + + // Names relative to directory + uint32_t* name_table = (uint32_t*)((uint64_t)e + e->AddressOfNames); + + // Table of ordinals (by name) + uint16_t* ordinal_table = (uint16_t*)((uint64_t)e + e->AddressOfNameOrdinals); + + const char* mod_name = (const char*)((uint64_t)e + e->Name); + + for (uint32_t i = 0; i < e->NumberOfNames; i++) { + const char* fn_name = (const char*)((uint64_t)e + name_table[i]); + uint16_t ordinal = ordinal_table[i]; + uint32_t addr = *exe_address + function_table[ordinal]; + + if (!strcmp(name, fn_name)) { + // We have a match! + return addr; + } + } + + // No match + return 0; } bool XexModule::ApplyPatch(XexModule* module) { diff --git a/src/xenia/kernel/util/xex2_info.h b/src/xenia/kernel/util/xex2_info.h index 569645cef..2df16edf6 100644 --- a/src/xenia/kernel/util/xex2_info.h +++ b/src/xenia/kernel/util/xex2_info.h @@ -628,6 +628,21 @@ struct xex2_export_table { ordOffset[1]; // 0x2C ordOffset[0] + (imagebaseaddr << 16) = function }; +// Little endian PE export directory (from winnt.h) +struct X_IMAGE_EXPORT_DIRECTORY { + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t Name; + uint32_t Base; + uint32_t NumberOfFunctions; + uint32_t NumberOfNames; + uint32_t AddressOfFunctions; // RVA from base of image + uint32_t AddressOfNames; // RVA from base of image + uint32_t AddressOfNameOrdinals; // RVA from base of image +}; + } // namespace xe #endif // XENIA_KERNEL_XEX2_INFO_H_ From 74d57cfd0a460f474021a4103536f46341aa4f1e Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 18:45:11 -0500 Subject: [PATCH 14/20] Dump exports by name in debug log --- src/xenia/kernel/objects/xuser_module.cc | 36 ++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 5742e7175..482d39994 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -224,8 +224,6 @@ void XUserModule::Dump() { kernel_state_->emulator()->export_resolver(); auto header = xex_header(); - // TODO: Need to loop through the optional headers one-by-one. - // XEX header. printf("Module %s:\n", path_.c_str()); printf(" Module Flags: %.8X\n", (uint32_t)header->module_flags); @@ -275,6 +273,12 @@ void XUserModule::Dump() { printf(" XEX_HEADER_IMAGE_BASE_ADDRESS: %.8X\n", (uint32_t)opt_header.value); } break; + case XEX_HEADER_IMPORT_LIBRARIES: { + printf(" XEX_HEADER_IMPORT_LIBRARIES (TODO):\n"); + } break; + case XEX_HEADER_CHECKSUM_TIMESTAMP: { + printf(" XEX_HEADER_CHECKSUM_TIMESTAMP (TODO):\n"); + } break; case XEX_HEADER_ORIGINAL_PE_NAME: { auto opt_pe_name = reinterpret_cast(opt_header_ptr); @@ -350,7 +354,33 @@ void XUserModule::Dump() { printf(" XEX_HEADER_ADDITIONAL_TITLE_MEMORY (TODO):\n"); } break; case XEX_HEADER_EXPORTS_BY_NAME: { - printf(" XEX_HEADER_EXPORTS_BY_NAME (TODO):\n"); + printf(" XEX_HEADER_EXPORTS_BY_NAME:\n"); + auto dir = + reinterpret_cast(opt_header_ptr); + + auto exe_address = xex_module()->xex_security_info()->load_address; + auto e = memory()->TranslateVirtual( + exe_address + dir->offset); + + // e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY! + uint32_t* function_table = (uint32_t*)((uint64_t)e + e->AddressOfFunctions); + + // Names relative to directory + uint32_t* name_table = (uint32_t*)((uint64_t)e + e->AddressOfNames); + + // Table of ordinals (by name) + uint16_t* ordinal_table = (uint16_t*)((uint64_t)e + e->AddressOfNameOrdinals); + + for (uint32_t i = 0; i < e->NumberOfNames; i++) { + const char* name = (const char*)((uint8_t*)e + name_table[i]); + uint16_t ordinal = ordinal_table[i]; + uint32_t addr = exe_address + function_table[ordinal]; + + printf(" %-28s - %.3X - %.8X\n", name, ordinal, addr); + } + } break; + default: { + printf(" Unknown Header %.8X\n", (uint32_t)opt_header.key); } break; } } From b852ce75df27d0c5686c1e22d5ec4b95c124bc59 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 19:00:07 -0500 Subject: [PATCH 15/20] Write a dummy value into unresolved import variables. --- src/xenia/cpu/xex_module.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index 1a7f451b0..d2814854b 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -362,6 +362,8 @@ bool XexModule::SetupLibraryImports(const char* name, } } else if (user_export_addr) { *record_slot = user_export_addr; + } else { + *record_slot = 0xF00DF00D; } // Setup a variable and define it. From ed3db906662c0538292849a39080c6d134080d3c Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 19:16:36 -0500 Subject: [PATCH 16/20] Dump a few more headers (incl. execution header) --- src/xenia/kernel/objects/xuser_module.cc | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 482d39994..86bad7a60 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -327,13 +327,26 @@ void XUserModule::Dump() { printf(" XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS (TODO):\n"); } break; case XEX_HEADER_SYSTEM_FLAGS: { - printf(" XEX_HEADER_SYSTEM_FLAGS (TODO):\n"); + printf(" XEX_HEADER_SYSTEM_FLAGS: %.8X\n", (uint32_t)opt_header.value); } break; case XEX_HEADER_EXECUTION_INFO: { - printf(" XEX_HEADER_EXECUTION_INFO (TODO):\n"); + printf(" XEX_HEADER_EXECUTION_INFO:\n"); + auto opt_exec_info = + reinterpret_cast(opt_header_ptr); + + printf(" Media ID: %.8X\n", + (uint32_t)opt_exec_info->media_id); + printf(" Title ID: %.8X\n", + (uint32_t)opt_exec_info->title_id); + printf(" Savegame ID: %.8X\n", + (uint32_t)opt_exec_info->title_id); + printf(" Disc Number / Total: %d / %d\n", + (uint8_t)opt_exec_info->disc_number, + (uint8_t)opt_exec_info->disc_count); } break; case XEX_HEADER_TITLE_WORKSPACE_SIZE: { - printf(" XEX_HEADER_TITLE_WORKSPACE_SIZE (TODO):\n"); + printf(" XEX_HEADER_TITLE_WORKSPACE_SIZE: %d\n", + (uint32_t)opt_header.value); } break; case XEX_HEADER_GAME_RATINGS: { printf(" XEX_HEADER_GAME_RATINGS (TODO):\n"); @@ -351,7 +364,7 @@ void XUserModule::Dump() { printf(" XEX_HEADER_ALTERNATE_TITLE_IDS (TODO):\n"); } break; case XEX_HEADER_ADDITIONAL_TITLE_MEMORY: { - printf(" XEX_HEADER_ADDITIONAL_TITLE_MEMORY (TODO):\n"); + printf(" XEX_HEADER_ADDITIONAL_TITLE_MEMORY: %d\n", opt_header.value); } break; case XEX_HEADER_EXPORTS_BY_NAME: { printf(" XEX_HEADER_EXPORTS_BY_NAME:\n"); From 8cb8098a6b9f968f533d7d374bc65d24715468b6 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 19:43:43 -0500 Subject: [PATCH 17/20] Dump import libraries --- src/xenia/kernel/objects/xuser_module.cc | 48 +++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 86bad7a60..f1c470c2e 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -274,7 +274,53 @@ void XUserModule::Dump() { (uint32_t)opt_header.value); } break; case XEX_HEADER_IMPORT_LIBRARIES: { - printf(" XEX_HEADER_IMPORT_LIBRARIES (TODO):\n"); + printf(" XEX_HEADER_IMPORT_LIBRARIES:\n"); + auto opt_import_libraries = + reinterpret_cast(opt_header_ptr); + + // FIXME: Don't know if 32 is the actual limit, but haven't seen more + // than 2. + const char* string_table[32]; + std::memset(string_table, 0, sizeof(string_table)); + + // Parse the string table + for (size_t i = 0, j = 0; i < opt_import_libraries->string_table_size; + j++) { + assert_true(j < xe::countof(string_table)); + const char* str = opt_import_libraries->string_table + i; + + string_table[j] = str; + i += std::strlen(str) + 1; + + // Padding + if ((i % 4) != 0) { + i += 4 - (i % 4); + } + } + + auto libraries = (uint8_t*)opt_import_libraries + + opt_import_libraries->string_table_size + 12; + uint32_t library_offset = 0; + for (uint32_t i = 0; i < opt_import_libraries->library_count; i++) { + auto library = reinterpret_cast( + (uint8_t*)libraries + library_offset); + auto name = string_table[library->name_index]; + + // Okay. Dump it. + printf(" %s - %d imports\n", name, library->count); + + // Manually byteswap these because of the bitfields. + xex2_version version, version_min; + version.value = xe::byte_swap(library->version.value); + version_min.value = + xe::byte_swap(library->version_min.value); + printf(" Version: %d.%d.%d.%d\n", version.major, version.minor, + version.build, version.qfe); + printf(" Min Version: %d.%d.%d.%d\n", version_min.major, + version_min.minor, version_min.build, version_min.qfe); + + library_offset += library->size; + } } break; case XEX_HEADER_CHECKSUM_TIMESTAMP: { printf(" XEX_HEADER_CHECKSUM_TIMESTAMP (TODO):\n"); From 03ffb20a433172447c43f7bfcdc033b0ec2265f2 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 19:44:11 -0500 Subject: [PATCH 18/20] Warn when importing an unimplemented kernel function --- src/xenia/cpu/xex_module.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index d2814854b..3c658cd87 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -417,6 +417,8 @@ bool XexModule::SetupLibraryImports(const char* name, (FunctionInfo::ExternHandler)kernel_export->function_data.shim; } } else { + XELOGW("WARNING: Imported kernel function %s is unimplemented!", + import_name.GetString()); handler = UndefinedImport; } From 1788ee158913dc7364ce7b2e84dcdc7b22be68e9 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 19:57:18 -0500 Subject: [PATCH 19/20] Linting. --- src/xenia/cpu/xex_module.cc | 2 +- src/xenia/kernel/kernel_state.cc | 3 +-- src/xenia/kernel/objects/xthread.cc | 7 ++----- src/xenia/kernel/objects/xuser_module.cc | 15 +++++++-------- src/xenia/kernel/util/xex2.cc | 17 ++++++++--------- src/xenia/kernel/util/xex2.h | 3 +-- 6 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index 3c658cd87..af6f6e588 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -114,7 +114,7 @@ uint32_t XexModule::GetProcAddress(uint16_t ordinal) const { xex2_opt_data_directory* pe_export_directory = 0; if (GetOptHeader(XEX_HEADER_EXPORTS_BY_NAME, &pe_export_directory)) { auto e = memory()->TranslateVirtual( - *exe_address + pe_export_directory->offset); + *exe_address + pe_export_directory->offset); assert_not_null(e); uint32_t* function_table = (uint32_t*)((uint8_t*)e + e->AddressOfFunctions); diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 5435e45c0..5c1f32ea8 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -122,8 +122,7 @@ uint32_t KernelState::title_id() const { assert_not_null(executable_module_); xex2_opt_execution_info* exec_info = 0; - executable_module_->GetOptHeader(XEX_HEADER_EXECUTION_INFO, - &exec_info); + executable_module_->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &exec_info); if (exec_info) { return exec_info->title_id; diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index 8330a4244..494086b85 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -586,11 +586,8 @@ void XThread::DeliverAPCs(void* data) { // kernel_routine(apc_address, &normal_routine, &normal_context, // &system_arg1, &system_arg2) uint64_t kernel_args[] = { - apc_ptr, - thread->scratch_address_ + 0, - thread->scratch_address_ + 4, - thread->scratch_address_ + 8, - thread->scratch_address_ + 12, + apc_ptr, thread->scratch_address_ + 0, thread->scratch_address_ + 4, + thread->scratch_address_ + 8, thread->scratch_address_ + 12, }; processor->Execute(thread->thread_state(), apc->kernel_routine, kernel_args, xe::countof(kernel_args)); diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index f1c470c2e..df8b6e3f7 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -380,12 +380,9 @@ void XUserModule::Dump() { auto opt_exec_info = reinterpret_cast(opt_header_ptr); - printf(" Media ID: %.8X\n", - (uint32_t)opt_exec_info->media_id); - printf(" Title ID: %.8X\n", - (uint32_t)opt_exec_info->title_id); - printf(" Savegame ID: %.8X\n", - (uint32_t)opt_exec_info->title_id); + printf(" Media ID: %.8X\n", (uint32_t)opt_exec_info->media_id); + printf(" Title ID: %.8X\n", (uint32_t)opt_exec_info->title_id); + printf(" Savegame ID: %.8X\n", (uint32_t)opt_exec_info->title_id); printf(" Disc Number / Total: %d / %d\n", (uint8_t)opt_exec_info->disc_number, (uint8_t)opt_exec_info->disc_count); @@ -422,13 +419,15 @@ void XUserModule::Dump() { exe_address + dir->offset); // e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY! - uint32_t* function_table = (uint32_t*)((uint64_t)e + e->AddressOfFunctions); + uint32_t* function_table = + (uint32_t*)((uint64_t)e + e->AddressOfFunctions); // Names relative to directory uint32_t* name_table = (uint32_t*)((uint64_t)e + e->AddressOfNames); // Table of ordinals (by name) - uint16_t* ordinal_table = (uint16_t*)((uint64_t)e + e->AddressOfNameOrdinals); + uint16_t* ordinal_table = + (uint16_t*)((uint64_t)e + e->AddressOfNameOrdinals); for (uint32_t i = 0; i < e->NumberOfNames; i++) { const char* name = (const char*)((uint8_t*)e + name_table[i]); diff --git a/src/xenia/kernel/util/xex2.cc b/src/xenia/kernel/util/xex2.cc index 94f2a387b..f44945ed9 100644 --- a/src/xenia/kernel/util/xex2.cc +++ b/src/xenia/kernel/util/xex2.cc @@ -27,8 +27,7 @@ #include "xenia/base/memory.h" #include "xenia/base/platform.h" -namespace xe { -} // namespace xe +namespace xe {} // namespace xe // TODO(benvanik): remove. #define XEEXPECTZERO(expr) \ @@ -247,9 +246,10 @@ int xe_xex2_read_header(const uint8_t* addr, const size_t length, } break; case XEX_HEADER_IMPORT_LIBRARIES: { auto import_libraries = - reinterpret_cast(pp); + reinterpret_cast(pp); - const uint32_t max_count = (uint32_t)xe::countof(header->import_libraries); + const uint32_t max_count = + (uint32_t)xe::countof(header->import_libraries); uint32_t count = import_libraries->library_count; assert_true(count <= max_count); if (count > max_count) { @@ -260,7 +260,7 @@ int xe_xex2_read_header(const uint8_t* addr, const size_t length, header->import_library_count = count; uint32_t string_table_size = import_libraries->string_table_size; - const char *string_table[32]; // Pretend 32 is max_count + const char* string_table[32]; // Pretend 32 is max_count std::memset(string_table, 0, sizeof(string_table)); // Parse the string table @@ -279,15 +279,14 @@ int xe_xex2_read_header(const uint8_t* addr, const size_t length, pp += 12 + import_libraries->string_table_size; for (size_t m = 0; m < count; m++) { xe_xex2_import_library_t* library = &header->import_libraries[m]; - auto src_library = (xe::xex2_import_library *)pp; + auto src_library = (xe::xex2_import_library*)pp; memcpy(library->digest, pp + 0x04, 20); library->import_id = src_library->id; library->version.value = src_library->version.value; library->min_version.value = src_library->version_min.value; - std::strncpy(library->name, - string_table[src_library->name_index], + std::strncpy(library->name, string_table[src_library->name_index], xe::countof(library->name)); library->record_count = src_library->count; @@ -1056,7 +1055,7 @@ uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, uint16_t ordinal) { // XEX-style export table. if (header->loader_info.export_table) { - auto export_table = reinterpret_cast( + auto export_table = reinterpret_cast( xex->memory->TranslateVirtual(header->loader_info.export_table)); uint32_t ordinal_count = export_table->count; uint32_t ordinal_base = export_table->base; diff --git a/src/xenia/kernel/util/xex2.h b/src/xenia/kernel/util/xex2.h index 77493087f..f4499ef11 100644 --- a/src/xenia/kernel/util/xex2.h +++ b/src/xenia/kernel/util/xex2.h @@ -13,8 +13,7 @@ #include "xenia/kernel/util/xex2_info.h" #include "xenia/memory.h" -namespace xe { -} // namespace xe +namespace xe {} // namespace xe typedef struct { int reserved; } xe_xex2_options_t; From 9186d011f07322f8220b33d7c13f01aa0a1ccdff Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 3 Jul 2015 21:25:06 -0500 Subject: [PATCH 20/20] Bring back some important debug dumps --- src/xenia/kernel/objects/xuser_module.cc | 151 ++++++++++++++++++++++- 1 file changed, 150 insertions(+), 1 deletion(-) diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index df8b6e3f7..2adb3af1b 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -246,10 +246,22 @@ void XUserModule::Dump() { void* opt_header_ptr = (uint8_t*)header + opt_header.offset; switch (opt_header.key) { case XEX_HEADER_RESOURCE_INFO: { - printf(" XEX_HEADER_RESOURCE_INFO (TODO):\n"); + printf(" XEX_HEADER_RESOURCE_INFO:\n"); auto opt_resource_info = reinterpret_cast(opt_header_ptr); + uint32_t count = (opt_resource_info->size - 4) / 16; + for (uint32_t j = 0; j < count; j++) { + auto& res = opt_resource_info->resources[j]; + + // Manually NULL-terminate the name. + char name[9]; + std::memcpy(name, res.name, sizeof(res.name)); + name[8] = 0; + + printf(" %-8s %.8X-%.8X, %db\n", name, res.address, + res.address + res.size, res.size); + } } break; case XEX_HEADER_FILE_FORMAT_INFO: { printf(" XEX_HEADER_FILE_FORMAT_INFO (TODO):\n"); @@ -442,6 +454,143 @@ void XUserModule::Dump() { } break; } } + + printf("Sections:\n"); + for (uint32_t i = 0, page = 0; i < security_info->page_descriptor_count; + i++) { + // Manually byteswap the bitfield data. + xex2_page_descriptor page_descriptor; + page_descriptor.value = + xe::byte_swap(security_info->page_descriptors[i].value); + + const char* type = "UNKNOWN"; + switch (page_descriptor.info) { + case XEX_SECTION_CODE: + type = "CODE "; + break; + case XEX_SECTION_DATA: + type = "RWDATA "; + break; + case XEX_SECTION_READONLY_DATA: + type = "RODATA "; + break; + } + + const uint32_t page_size = + security_info->load_address < 0x90000000 ? 64 * 1024 : 4 * 1024; + uint32_t start_address = security_info->load_address + (page * page_size); + uint32_t end_address = start_address + (page_descriptor.size * page_size); + + printf(" %3d %s %3d pages %.8X - %.8X (%d bytes)\n", (int)page, type, + page_descriptor.size, (int)start_address, (int)end_address, + page_descriptor.size * page_size); + page += page_descriptor.size; + } + + // Print out imports. + // TODO: Figure out a way to remove dependency on old xex header. + auto old_header = xe_xex2_get_header(xex_module()->xex()); + + printf("Imports:\n"); + for (size_t n = 0; n < old_header->import_library_count; n++) { + const xe_xex2_import_library_t* library = &old_header->import_libraries[n]; + + xe_xex2_import_info_t* import_infos; + size_t import_info_count; + if (!xe_xex2_get_import_infos(xex_module()->xex(), library, &import_infos, + &import_info_count)) { + printf(" %s - %d imports\n", library->name, (int)import_info_count); + printf(" Version: %d.%d.%d.%d\n", library->version.major, + library->version.minor, library->version.build, + library->version.qfe); + printf(" Min Version: %d.%d.%d.%d\n", library->min_version.major, + library->min_version.minor, library->min_version.build, + library->min_version.qfe); + printf("\n"); + + // Counts. + int known_count = 0; + int unknown_count = 0; + int impl_count = 0; + int unimpl_count = 0; + for (size_t m = 0; m < import_info_count; m++) { + const xe_xex2_import_info_t* info = &import_infos[m]; + + if (kernel_state_->IsKernelModule(library->name)) { + auto kernel_export = + export_resolver->GetExportByOrdinal(library->name, info->ordinal); + if (kernel_export) { + known_count++; + if (kernel_export->is_implemented()) { + impl_count++; + } else { + unimpl_count++; + } + } else { + unknown_count++; + unimpl_count++; + } + } else { + auto module = kernel_state_->GetModule(library->name); + if (module) { + uint32_t export_addr = + module->GetProcAddressByOrdinal(info->ordinal); + if (export_addr) { + impl_count++; + known_count++; + } else { + unimpl_count++; + unknown_count++; + } + } else { + unimpl_count++; + unknown_count++; + } + } + } + printf(" Total: %4u\n", uint32_t(import_info_count)); + printf(" Known: %3d%% (%d known, %d unknown)\n", + (int)(known_count / (float)import_info_count * 100.0f), + known_count, unknown_count); + printf(" Implemented: %3d%% (%d implemented, %d unimplemented)\n", + (int)(impl_count / (float)import_info_count * 100.0f), impl_count, + unimpl_count); + printf("\n"); + + // Listing. + for (size_t m = 0; m < import_info_count; m++) { + const xe_xex2_import_info_t* info = &import_infos[m]; + const char* name = "UNKNOWN"; + bool implemented = false; + + Export* kernel_export = nullptr; + if (kernel_state_->IsKernelModule(library->name)) { + kernel_export = + export_resolver->GetExportByOrdinal(library->name, info->ordinal); + if (kernel_export) { + name = kernel_export->name; + implemented = kernel_export->is_implemented(); + } + } else { + auto module = kernel_state_->GetModule(library->name); + if (module && module->GetProcAddressByOrdinal(info->ordinal)) { + // TODO: Name lookup + implemented = true; + } + } + if (kernel_export && kernel_export->type == Export::Type::kVariable) { + printf(" V %.8X %.3X (%3d) %s %s\n", info->value_address, + info->ordinal, info->ordinal, implemented ? " " : "!!", name); + } else if (info->thunk_address) { + printf(" F %.8X %.8X %.3X (%3d) %s %s\n", info->value_address, + info->thunk_address, info->ordinal, info->ordinal, + implemented ? " " : "!!", name); + } + } + } + + printf("\n"); + } } } // namespace kernel