From 4411a4499d13565347b241492693b8ea0fea0b3a Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Sat, 9 May 2015 00:59:03 -0700 Subject: [PATCH] XEX-style exports, dumping exports, and prepping kernel export lookup. --- src/xenia/cpu/export_resolver.cc | 2 +- src/xenia/cpu/export_resolver.h | 10 ++- src/xenia/kernel/objects/xkernel_module.cc | 32 ++++++++- src/xenia/kernel/objects/xuser_module.cc | 61 ++++++++++------ src/xenia/kernel/util/xex2.cc | 84 +++++++++++----------- src/xenia/kernel/util/xex2.h | 6 +- src/xenia/kernel/util/xex2_info.h | 2 +- src/xenia/kernel/xboxkrnl_modules.cc | 45 ++++++------ src/xenia/xbox.h | 2 + 9 files changed, 150 insertions(+), 94 deletions(-) diff --git a/src/xenia/cpu/export_resolver.cc b/src/xenia/cpu/export_resolver.cc index 95bb9ce8f..cabdd4818 100644 --- a/src/xenia/cpu/export_resolver.cc +++ b/src/xenia/cpu/export_resolver.cc @@ -34,7 +34,7 @@ void ExportResolver::RegisterTable(const std::string& library_name, KernelExport* ExportResolver::GetExportByOrdinal( const std::string& library_name, const uint32_t ordinal) { for (const auto& table : tables_) { - if (table.name == library_name) { + if (table.name == library_name || table.simple_name == library_name) { // TODO(benvanik): binary search? for (size_t n = 0; n < table.count; n++) { if (table.exports[n].ordinal == ordinal) { diff --git a/src/xenia/cpu/export_resolver.h b/src/xenia/cpu/export_resolver.h index d08cfe219..194e63ac6 100644 --- a/src/xenia/cpu/export_resolver.h +++ b/src/xenia/cpu/export_resolver.h @@ -70,10 +70,18 @@ class ExportResolver { private: struct ExportTable { std::string name; + std::string simple_name; // without extension KernelExport* exports; size_t count; ExportTable(const std::string& name, KernelExport* exports, size_t count) - : name(name), exports(exports), count(count) {} + : name(name), exports(exports), count(count) { + auto dot_pos = name.find_last_of('.'); + if (dot_pos != std::string::npos) { + simple_name = name.substr(0, dot_pos); + } else { + simple_name = name; + } + } }; std::vector tables_; }; diff --git a/src/xenia/kernel/objects/xkernel_module.cc b/src/xenia/kernel/objects/xkernel_module.cc index 51b2b130c..b31c5bbdf 100644 --- a/src/xenia/kernel/objects/xkernel_module.cc +++ b/src/xenia/kernel/objects/xkernel_module.cc @@ -29,9 +29,35 @@ XKernelModule::XKernelModule(KernelState* kernel_state, const char* path) XKernelModule::~XKernelModule() {} uint32_t XKernelModule::GetProcAddressByOrdinal(uint16_t ordinal) { - // TODO(benvanik): check export tables. - XELOGE("GetProcAddressByOrdinal not implemented"); - return 0; + auto export = export_resolver_->GetExportByOrdinal(name(), ordinal); + if (!export) { + // Export (or its parent library) not found. + return 0; + } + if (export->type == cpu::KernelExport::ExportType::Variable) { + if (export->variable_ptr) { + return export->variable_ptr; + } else { + XELOGW( + "ERROR: var export referenced GetProcAddressByOrdinal(%.4X(%s)) is " + "not implemented", + ordinal, export->name); + return 0; + } + } else { + if (export->function_data.shim) { + // Implemented. Dynamically generate trampoline. + XELOGE("GetProcAddressByOrdinal not implemented"); + return 0; + } else { + // Not implemented. + XELOGW( + "ERROR: fn export referenced GetProcAddressByOrdinal(%.4X(%s)) is " + "not implemented", + ordinal, export->name); + return 0; + } + } } uint32_t XKernelModule::GetProcAddressByName(const char* name) { diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 265898daa..3d83a1b48 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -143,24 +143,11 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) { } uint32_t XUserModule::GetProcAddressByOrdinal(uint16_t ordinal) { - PEExport export; - int ret = xe_xex2_lookup_export(xex_, ordinal, export); - if (ret) { - XELOGE("XUserModule::GetProcAddressByOrdinal(%d) not found", ordinal); - return 0; - } - - return uint32_t(export.addr); + return xe_xex2_lookup_export(xex_, ordinal); } uint32_t XUserModule::GetProcAddressByName(const char* name) { - PEExport export; - int ret = xe_xex2_lookup_export(xex_, name, export); - if (ret) { - XELOGE("XUserModule::GetProcAddressByName(%s) not found", name); - return 0; - } - return uint32_t(export.addr); + return xe_xex2_lookup_export(xex_, name); } X_STATUS XUserModule::GetSection(const char* name, uint32_t* out_section_data, @@ -300,6 +287,45 @@ void XUserModule::Dump() { } 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++) { @@ -372,11 +398,6 @@ void XUserModule::Dump() { printf("\n"); } - - // Exports. - printf("Exports:\n"); - printf(" TODO\n"); - printf("\n"); } } // namespace kernel diff --git a/src/xenia/kernel/util/xex2.cc b/src/xenia/kernel/util/xex2.cc index 5e1e1ba5f..bfdd40a7f 100644 --- a/src/xenia/kernel/util/xex2.cc +++ b/src/xenia/kernel/util/xex2.cc @@ -239,7 +239,7 @@ int xe_xex2_read_header(const uint8_t *addr, const size_t length, break; case XEX_HEADER_EXPORTS_BY_NAME: { // IMAGE_DATA_DIRECTORY (w/ offset from PE file base) - header->export_table_offset = xe::load_and_swap(pp); + header->pe_export_table_offset = xe::load_and_swap(pp); // size = xe::load_and_swap(pp + 0x04); } break; case XEX_HEADER_IMPORT_LIBRARIES: { @@ -988,27 +988,24 @@ int xe_xex2_get_import_infos(xe_xex2_ref xex, return 0; } -int xe_xex2_lookup_export(xe_xex2_ref xex, const char *name, - PEExport &peexport) { +uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, const char *name) { auto header = xe_xex2_get_header(xex); // no exports :( - if (!header->export_table_offset) { - return 1; + if (!header->pe_export_table_offset) { + XELOGE("xe_xex2_lookup_export(%s) failed: no PE export table", name); + return 0; } - uint64_t baseaddr = - (uint64_t)xex->memory->TranslateVirtual(header->exe_address); - IMAGE_EXPORT_DIRECTORY *e = - (PIMAGE_EXPORT_DIRECTORY)(baseaddr + header->export_table_offset); + auto e = reinterpret_cast( + xex->memory->TranslateVirtual(header->exe_address + + header->pe_export_table_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); + uint32_t *name_table = (uint32_t *)((uint64_t)e + e->AddressOfNames); // Table of ordinals (by name) uint16_t *ordinal_table = @@ -1019,44 +1016,53 @@ int xe_xex2_lookup_export(xe_xex2_ref xex, const char *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]; - uint64_t addr = (uint64_t)(baseaddr + function_table[ordinal]); + uint32_t addr = header->exe_address + function_table[ordinal]; if (!strcmp(name, fn_name)) { // We have a match! - peexport.name = fn_name; - peexport.addr = addr; - peexport.ordinal = ordinal; - - return 0; + return addr; } } // No match - return 1; + return 0; } -int xe_xex2_lookup_export(xe_xex2_ref xex, int ordinal, - PEExport &peexport) { - auto header = xe_xex2_get_header(xex); +uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, uint16_t ordinal) { + auto header = xe_xex2_get_header(xex); - // no exports :( - if (!header->export_table_offset) { - return 1; + // XEX-style export table. + if (header->loader_info.export_table) { + 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); + 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; + return ordinal_offset; } - uint64_t baseaddr = - (uint64_t)xex->memory->TranslateVirtual(header->exe_address); - IMAGE_EXPORT_DIRECTORY *e = - (PIMAGE_EXPORT_DIRECTORY)(baseaddr + header->export_table_offset); + // Check for PE-style export table. + if (!header->pe_export_table_offset) { + XELOGE("xe_xex2_lookup_export(%.4X) failed: no XEX or PE export table"); + return 0; + } + + auto e = reinterpret_cast( + xex->memory->TranslateVirtual(header->exe_address + + header->pe_export_table_offset)); // e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY! // Functions relative to base - 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); + uint32_t *name_table = (uint32_t *)((uint64_t)e + e->AddressOfNames); // Table of ordinals (by name) uint16_t *ordinal_table = @@ -1064,14 +1070,10 @@ int xe_xex2_lookup_export(xe_xex2_ref xex, int ordinal, const char *mod_name = (const char *)((uint64_t)e + e->Name); - if (ordinal < int(e->NumberOfFunctions)) { - peexport.name = nullptr; // TODO: Backwards conversion to get this - peexport.ordinal = ordinal; - peexport.addr = (uint64_t)(baseaddr + function_table[ordinal]); - - return 0; + if (ordinal < e->NumberOfFunctions) { + return header->exe_address + function_table[ordinal]; } // No match - return 1; + return 0; } \ No newline at end of file diff --git a/src/xenia/kernel/util/xex2.h b/src/xenia/kernel/util/xex2.h index a48ea6100..c2c808c6e 100644 --- a/src/xenia/kernel/util/xex2.h +++ b/src/xenia/kernel/util/xex2.h @@ -62,9 +62,7 @@ int xe_xex2_get_import_infos(xe_xex2_ref xex, xe_xex2_import_info_t** out_import_infos, size_t* out_import_info_count); -int xe_xex2_lookup_export(xe_xex2_ref xex, const char* name, - PEExport& peexport); -int xe_xex2_lookup_export(xe_xex2_ref xex, int ordinal, - PEExport& peexport); +uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, const char* name); +uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, uint16_t ordinal); #endif // XENIA_KERNEL_UTIL_XEX2_H_ diff --git a/src/xenia/kernel/util/xex2_info.h b/src/xenia/kernel/util/xex2_info.h index 6109bd963..9933ea234 100644 --- a/src/xenia/kernel/util/xex2_info.h +++ b/src/xenia/kernel/util/xex2_info.h @@ -445,7 +445,7 @@ typedef struct { xe_xex2_tls_info_t tls_info; size_t import_library_count; xe_xex2_import_library_t import_libraries[32]; - size_t export_table_offset; // PE Export Directory + uint32_t pe_export_table_offset; // PE Export Directory size_t static_library_count; xe_xex2_static_library_t static_libraries[32]; xe_xex2_file_format_info_t file_format_info; diff --git a/src/xenia/kernel/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl_modules.cc index f105a8436..10642011a 100644 --- a/src/xenia/kernel/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl_modules.cc @@ -252,22 +252,25 @@ SHIM_CALL XexGetProcedureAddress_shim(PPCContext* ppc_state, KernelState* state) { uint32_t module_handle = SHIM_GET_ARG_32(0); uint32_t ordinal = SHIM_GET_ARG_32(1); - const char* name = (const char*)SHIM_MEM_ADDR(ordinal); uint32_t out_function_ptr = SHIM_GET_ARG_32(2); - if (ordinal < 0x10000) { + // May be entry point? + assert_not_zero(ordinal); + + bool is_string_name = (ordinal & 0xFFFF0000) != 0; + auto string_name = reinterpret_cast(SHIM_MEM_ADDR(ordinal)); + + if (is_string_name) { + XELOGD("XexGetProcedureAddress(%.8X, %.8X(%s), %.8X)", module_handle, + ordinal, string_name, out_function_ptr); + } else { XELOGD("XexGetProcedureAddress(%.8X, %.8X, %.8X)", module_handle, ordinal, out_function_ptr); - } else { - XELOGD("XexGetProcedureAddress(%.8X, %.8X(%s), %.8X)", module_handle, - ordinal, name, out_function_ptr); } X_STATUS result = X_STATUS_INVALID_HANDLE; - SHIM_SET_MEM_32(out_function_ptr, 0xDEADF00D); XModule* module = NULL; - if (!module_handle) { module = state->GetExecutableModule(); } else { @@ -276,23 +279,19 @@ SHIM_CALL XexGetProcedureAddress_shim(PPCContext* ppc_state, } if (XSUCCEEDED(result)) { - if (ordinal < 0x10000) { - // Ordinal. - uint32_t ptr = module->GetProcAddressByOrdinal(ordinal); - if (ptr) { - SHIM_SET_MEM_32(out_function_ptr, ptr); - result = X_STATUS_SUCCESS; - } + uint32_t ptr; + if (is_string_name) { + ptr = module->GetProcAddressByName(string_name); } else { - // It's a name pointer instead. - uint32_t ptr = module->GetProcAddressByName(name); - - // FYI: We don't need to generate this function now. It'll - // be done automatically by xenia when it gets called. - if (ptr) { - SHIM_SET_MEM_32(out_function_ptr, ptr); - result = X_STATUS_SUCCESS; - } + ptr = module->GetProcAddressByOrdinal(ordinal); + } + if (ptr) { + SHIM_SET_MEM_32(out_function_ptr, ptr); + result = X_STATUS_SUCCESS; + } else { + XELOGW("ERROR: XexGetProcedureAddress ordinal not found!"); + SHIM_SET_MEM_32(out_function_ptr, 0); + result = X_STATUS_DRIVER_ORDINAL_NOT_FOUND; } } diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index a1412de02..7d03fab39 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -60,6 +60,8 @@ typedef uint32_t X_STATUS; #define X_STATUS_INVALID_PARAMETER_1 ((X_STATUS)0xC00000EFL) #define X_STATUS_INVALID_PARAMETER_2 ((X_STATUS)0xC00000F0L) #define X_STATUS_INVALID_PARAMETER_3 ((X_STATUS)0xC00000F1L) +#define X_STATUS_DRIVER_ORDINAL_NOT_FOUND ((X_STATUS)0xC0000262L) +#define X_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND ((X_STATUS)0xC0000263L) // HRESULT (ERROR_*) // Adding as needed.