From 362a521c7942754ec02157df4cb3c67a8de798c0 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Thu, 2 Jul 2015 21:58:47 -0500 Subject: [PATCH] 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: