diff --git a/src/xenia/kernel/objects/xkernel_module.cc b/src/xenia/kernel/objects/xkernel_module.cc index 91d8470fb..e46e88e16 100644 --- a/src/xenia/kernel/objects/xkernel_module.cc +++ b/src/xenia/kernel/objects/xkernel_module.cc @@ -34,5 +34,10 @@ void* XKernelModule::GetProcAddressByOrdinal(uint16_t ordinal) { return NULL; } +void* XKernelModule::GetProcAddressByName(const char* name) { + XELOGE("GetProcAddressByName not implemented"); + return NULL; +} + } // namespace kernel } // namespace xe diff --git a/src/xenia/kernel/objects/xkernel_module.h b/src/xenia/kernel/objects/xkernel_module.h index 98691f41d..a4117032c 100644 --- a/src/xenia/kernel/objects/xkernel_module.h +++ b/src/xenia/kernel/objects/xkernel_module.h @@ -24,6 +24,7 @@ class XKernelModule : public XModule { virtual ~XKernelModule(); virtual void* GetProcAddressByOrdinal(uint16_t ordinal); + virtual void* GetProcAddressByName(const char* name); protected: Emulator* emulator_; diff --git a/src/xenia/kernel/objects/xmodule.h b/src/xenia/kernel/objects/xmodule.h index 0abe66931..225a80987 100644 --- a/src/xenia/kernel/objects/xmodule.h +++ b/src/xenia/kernel/objects/xmodule.h @@ -27,6 +27,7 @@ class XModule : public XObject { const std::string& name() const { return name_; } virtual void* GetProcAddressByOrdinal(uint16_t ordinal) = 0; + virtual void* GetProcAddressByName(const char *name) = 0; virtual X_STATUS GetSection(const char* name, uint32_t* out_section_data, uint32_t* out_section_size); diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index dc7dc0c1e..86ecb025a 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -148,6 +148,17 @@ void* XUserModule::GetProcAddressByOrdinal(uint16_t ordinal) { return NULL; } +void* XUserModule::GetProcAddressByName(const char* name) { + PEExport export; + int ret = xe_xex2_lookup_export(xex_, name, export); + + // Failure. + if (ret) + return NULL; + + return (void *)export.addr; +} + X_STATUS XUserModule::GetSection(const char* name, uint32_t* out_section_data, uint32_t* out_section_size) { auto header = xe_xex2_get_header(xex_); diff --git a/src/xenia/kernel/objects/xuser_module.h b/src/xenia/kernel/objects/xuser_module.h index 590a0a5b4..19edc0c3b 100644 --- a/src/xenia/kernel/objects/xuser_module.h +++ b/src/xenia/kernel/objects/xuser_module.h @@ -33,6 +33,7 @@ class XUserModule : public XModule { X_STATUS LoadFromMemory(const void* addr, const size_t length); virtual void* GetProcAddressByOrdinal(uint16_t ordinal); + virtual void* GetProcAddressByName(const char* name); virtual X_STATUS GetSection(const char* name, uint32_t* out_section_data, uint32_t* out_section_size); diff --git a/src/xenia/kernel/util/xex2.cc b/src/xenia/kernel/util/xex2.cc index c2dfcb40b..4c6eefbd1 100644 --- a/src/xenia/kernel/util/xex2.cc +++ b/src/xenia/kernel/util/xex2.cc @@ -237,6 +237,11 @@ int xe_xex2_read_header(const uint8_t *addr, const size_t length, case XEX_HEADER_DEFAULT_HEAP_SIZE: header->exe_heap_size = opt_header->value; 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); + // 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); @@ -982,3 +987,41 @@ int xe_xex2_get_import_infos(xe_xex2_ref xex, *out_import_infos = xex->library_imports[library_index].infos; return 0; } + +int xe_xex2_lookup_export(xe_xex2_ref xex, const char *name, + PEExport &peexport) { + auto header = xe_xex2_get_header(xex); + + // no exports :( + if (!header->export_table_offset) { + return 1; + } + + uint64_t baseaddr = (uint64_t)xex->memory->TranslateVirtual(header->exe_address); + IMAGE_EXPORT_DIRECTORY *e = (PIMAGE_EXPORT_DIRECTORY)(baseaddr + header->export_table_offset); + + // e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY! + uint32_t* function_table = (uint32_t*)((uint64_t)e + e->AddressOfFunctions); // Functions relative to base + uint32_t* name_table = (uint32_t*)((uint64_t)e + e->AddressOfNames); // Names relative to directory + uint16_t* ordinal_table = (uint16_t*)((uint64_t)e + e->AddressOfNameOrdinals); // Table of ordinals + + const char* mod_name = (const char*)((uint64_t)e + e->Name); + + for (int 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]); + + if (!strcmp(name, fn_name)) { + // We have a match! + peexport.name = fn_name; + peexport.addr = addr; + peexport.ordinal = ordinal; + + return 0; + } + } + + // No match + return 1; +} \ No newline at end of file diff --git a/src/xenia/kernel/util/xex2.h b/src/xenia/kernel/util/xex2.h index 212badb7c..dbea70cf5 100644 --- a/src/xenia/kernel/util/xex2.h +++ b/src/xenia/kernel/util/xex2.h @@ -43,6 +43,13 @@ class PESection { uint32_t flags; // kXEPESection* }; +struct PEExport { + const char *name; + uint32_t ordinal; + + uint64_t addr; // Function address +}; + xe_xex2_ref xe_xex2_load(xe::Memory* memory, const void* addr, const size_t length, xe_xex2_options_t options); void xe_xex2_dealloc(xe_xex2_ref xex); @@ -55,4 +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); + #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 5256fa5cd..6109bd963 100644 --- a/src/xenia/kernel/util/xex2_info.h +++ b/src/xenia/kernel/util/xex2_info.h @@ -295,6 +295,17 @@ 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, @@ -397,7 +408,7 @@ typedef struct { uint8_t import_table_digest[20]; uint8_t media_id[16]; uint8_t file_key[16]; - uint32_t export_table; + uint32_t export_table; // address of the export table uint8_t header_digest[20]; xe_xex2_region_flags game_regions; xe_xex2_media_flags media_flags; @@ -434,6 +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 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 ff9b9a530..b7be63e9e 100644 --- a/src/xenia/kernel/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl_modules.cc @@ -246,10 +246,16 @@ 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); - XELOGD("XexGetProcedureAddress(%.8X, %.8X, %.8X)", module_handle, ordinal, - out_function_ptr); + if (ordinal < 0x10000) { + 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); @@ -263,11 +269,29 @@ SHIM_CALL XexGetProcedureAddress_shim(PPCContext* ppc_state, state->object_table()->GetObject(module_handle, (XObject**)&module); } + result = X_STATUS_UNSUCCESSFUL; + if (XSUCCEEDED(result)) { - // TODO(benvanik): implement. May need to create stub functions on the fly. - // module->GetProcAddressByOrdinal(ordinal); - result = X_STATUS_NOT_IMPLEMENTED; + if (ordinal < 0x10000) { + // Ordinal. + uint64_t ptr = (uint64_t)module->GetProcAddressByOrdinal(ordinal); + if (ptr) { + SHIM_SET_MEM_32(out_function_ptr, ptr); + result = X_STATUS_SUCCESS; + } + } else { + // It's a name pointer instead. + uint64_t ptr = (uint64_t)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; + } + } } + if (module) { module->Release(); }