diff --git a/src/xenia/kernel/objects/xmodule.cc b/src/xenia/kernel/objects/xmodule.cc index db2dd1e87..28d5e1d3d 100644 --- a/src/xenia/kernel/objects/xmodule.cc +++ b/src/xenia/kernel/objects/xmodule.cc @@ -20,7 +20,8 @@ XModule::XModule(KernelState* kernel_state, ModuleType module_type, : XObject(kernel_state, kTypeModule), module_type_(module_type), path_(path), - processor_module_(nullptr) { + processor_module_(nullptr), + hmodule_ptr_(0) { auto last_slash = path.find_last_of('/'); if (last_slash == path.npos) { last_slash = path.find_last_of('\\'); @@ -34,9 +35,22 @@ XModule::XModule(KernelState* kernel_state, ModuleType module_type, if (dot != name_.npos) { name_ = name_.substr(0, dot); } + + // Loader data (HMODULE) + hmodule_ptr_ = memory()->SystemHeapAlloc(sizeof(X_LDR_DATA_TABLE_ENTRY)); + + // Hijack the checksum field to store our kernel object handle. + auto ldr_data = + memory()->TranslateVirtual(hmodule_ptr_); + ldr_data->checksum = handle(); } -XModule::~XModule() { kernel_state_->UnregisterModule(this); } +XModule::~XModule() { + kernel_state_->UnregisterModule(this); + + // Destroy the loader data. + memory()->SystemHeapFree(hmodule_ptr_); +} bool XModule::Matches(const std::string& name) const { if (strcasecmp(xe::find_name_from_path(path_).c_str(), name.c_str()) == 0) { @@ -58,5 +72,17 @@ X_STATUS XModule::GetSection(const char* name, uint32_t* out_section_data, return X_STATUS_UNSUCCESSFUL; } +object_ref XModule::GetFromHModule(KernelState* kernel_state, + void* hmodule) { + // Grab the object from our stashed kernel handle + return kernel_state->object_table()->LookupObject( + GetHandleFromHModule(hmodule)); +} + +uint32_t XModule::GetHandleFromHModule(void* hmodule) { + auto ldr_data = reinterpret_cast(hmodule); + return ldr_data->checksum; +} + } // namespace kernel } // namespace xe diff --git a/src/xenia/kernel/objects/xmodule.h b/src/xenia/kernel/objects/xmodule.h index 5c62c2d50..b6cc047a2 100644 --- a/src/xenia/kernel/objects/xmodule.h +++ b/src/xenia/kernel/objects/xmodule.h @@ -19,6 +19,42 @@ namespace xe { namespace kernel { +// http://www.nirsoft.net/kernel_struct/vista/LDR_DATA_TABLE_ENTRY.html +// HMODULE points to this struct! +struct X_LDR_DATA_TABLE_ENTRY { + X_LIST_ENTRY in_load_order_links; // 0x0 + X_LIST_ENTRY in_memory_order_links; // 0x8 + X_LIST_ENTRY in_initialization_order_links; // 0x10 + + xe::be dll_base; // 0x18 + xe::be image_base; // 0x1C + xe::be image_size; // 0x20 + + X_UNICODE_STRING full_dll_name; // 0x24 + X_UNICODE_STRING base_dll_name; // 0x2C + + xe::be flags; // 0x34 + xe::be full_image_size; // 0x38 + xe::be entry_point; // 0x3C + xe::be load_count; // 0x40 + xe::be module_index; // 0x42 + xe::be dll_base_original; // 0x44 + xe::be checksum; // 0x48 hijacked to hold kernel handle + xe::be load_flags; // 0x4C + xe::be time_date_stamp; // 0x50 + xe::be loaded_imports; // 0x54 + xe::be xex_header_base; // 0x58 + + union { + X_ANSI_STRING load_file_name; // 0x5C + + struct { + xe::be closure_root; // 0x5C + xe::be traversal_parent; // 0x60 + }; + }; +}; + class XModule : public XObject { public: enum class ModuleType { @@ -37,12 +73,16 @@ class XModule : public XObject { bool Matches(const std::string& name) const; xe::cpu::Module* processor_module() const { return processor_module_; } + uint32_t hmodule_ptr() const { return hmodule_ptr_; } virtual uint32_t GetProcAddressByOrdinal(uint16_t ordinal) = 0; virtual uint32_t GetProcAddressByName(const char* name) = 0; virtual X_STATUS GetSection(const char* name, uint32_t* out_section_data, uint32_t* out_section_size); + static object_ref GetFromHModule(KernelState* kernel_state, void* hmodule); + static uint32_t GetHandleFromHModule(void* hmodule); + protected: void OnLoad(); @@ -51,6 +91,8 @@ class XModule : public XObject { std::string path_; xe::cpu::Module* processor_module_; + + uint32_t hmodule_ptr_; // This points to LDR_DATA_TABLE_ENTRY. }; } // namespace kernel diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index a8b533e2b..e52d2d319 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -101,20 +101,21 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) { return X_STATUS_UNSUCCESSFUL; } - // Store execution info for later use. - // TODO(benvanik): just put entire xex header in memory somewhere? - execution_info_ptr_ = memory()->SystemHeapAlloc(24); - auto eip = memory()->TranslateVirtual(execution_info_ptr_); - const auto& ex = xe_xex2_get_header(xex_)->execution_info; - xe::store_and_swap(eip + 0x00, ex.media_id); - xe::store_and_swap(eip + 0x04, ex.version.value); - xe::store_and_swap(eip + 0x08, ex.base_version.value); - xe::store_and_swap(eip + 0x0C, ex.title_id); - xe::store_and_swap(eip + 0x10, ex.platform); - xe::store_and_swap(eip + 0x11, ex.executable_table); - xe::store_and_swap(eip + 0x12, ex.disc_number); - xe::store_and_swap(eip + 0x13, ex.disc_count); - xe::store_and_swap(eip + 0x14, ex.savegame_id); + // Copy the xex2 header into guest memory + const xex2_header* header = reinterpret_cast(addr); + uint32_t header_size = xex2_get_header_size(header); + + xex_header_ = memory()->SystemHeapAlloc(header_size); + + uint8_t* xex_header_ptr = memory()->TranslateVirtual(xex_header_); + std::memcpy(xex_header_ptr, 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. diff --git a/src/xenia/kernel/objects/xuser_module.h b/src/xenia/kernel/objects/xuser_module.h index 3ea84b315..c312433e1 100644 --- a/src/xenia/kernel/objects/xuser_module.h +++ b/src/xenia/kernel/objects/xuser_module.h @@ -44,6 +44,7 @@ class XUserModule : public XModule { private: xe_xex2_ref xex_; + uint32_t xex_header_; uint32_t execution_info_ptr_; }; diff --git a/src/xenia/kernel/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl_modules.cc index ad9c1786d..ac03cce32 100644 --- a/src/xenia/kernel/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl_modules.cc @@ -153,9 +153,9 @@ SHIM_CALL XexGetModuleHandle_shim(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t module_name_ptr = SHIM_GET_ARG_32(0); const char* module_name = (const char*)SHIM_MEM_ADDR(module_name_ptr); - uint32_t module_handle_ptr = SHIM_GET_ARG_32(1); + uint32_t hmodule_ptr = SHIM_GET_ARG_32(1); - XELOGD("XexGetModuleHandle(%s, %.8X)", module_name, module_handle_ptr); + XELOGD("XexGetModuleHandle(%s, %.8X)", module_name, hmodule_ptr); object_ref module; if (!module_name) { @@ -164,31 +164,31 @@ SHIM_CALL XexGetModuleHandle_shim(PPCContext* ppc_context, module = kernel_state->GetModule(module_name); } if (!module) { - SHIM_SET_MEM_32(module_handle_ptr, 0); + SHIM_SET_MEM_32(hmodule_ptr, 0); SHIM_SET_RETURN_32(X_ERROR_NOT_FOUND); return; } // NOTE: we don't retain the handle for return. - SHIM_SET_MEM_32(module_handle_ptr, module->handle()); + SHIM_SET_MEM_32(hmodule_ptr, module->hmodule_ptr()); SHIM_SET_RETURN_32(X_ERROR_SUCCESS); } SHIM_CALL XexGetModuleSection_shim(PPCContext* ppc_context, KernelState* kernel_state) { - uint32_t handle = SHIM_GET_ARG_32(0); + uint32_t hmodule = SHIM_GET_ARG_32(0); uint32_t name_ptr = SHIM_GET_ARG_32(1); const char* name = (const char*)SHIM_MEM_ADDR(name_ptr); uint32_t data_ptr = SHIM_GET_ARG_32(2); uint32_t size_ptr = SHIM_GET_ARG_32(3); - XELOGD("XexGetModuleSection(%.8X, %s, %.8X, %.8X)", handle, name, data_ptr, + XELOGD("XexGetModuleSection(%.8X, %s, %.8X, %.8X)", hmodule, name, data_ptr, size_ptr); X_STATUS result = X_STATUS_SUCCESS; - auto module = kernel_state->object_table()->LookupObject(handle); + auto module = XModule::GetFromHModule(kernel_state, SHIM_MEM_ADDR(hmodule)); if (module) { uint32_t section_data = 0; uint32_t section_size = 0; @@ -210,49 +210,64 @@ SHIM_CALL XexLoadImage_shim(PPCContext* ppc_context, const char* module_name = (const char*)SHIM_MEM_ADDR(module_name_ptr); uint32_t module_flags = SHIM_GET_ARG_32(1); uint32_t min_version = SHIM_GET_ARG_32(2); - uint32_t handle_ptr = SHIM_GET_ARG_32(3); + uint32_t hmodule_ptr = SHIM_GET_ARG_32(3); XELOGD("XexLoadImage(%s, %.8X, %.8X, %.8X)", module_name, module_flags, - min_version, handle_ptr); + min_version, hmodule_ptr); X_STATUS result = X_STATUS_NO_SUCH_FILE; - X_HANDLE module_handle = X_INVALID_HANDLE_VALUE; + uint32_t hmodule = 0; auto module = kernel_state->GetModule(module_name); if (module) { - // Existing module found, just add a reference and obtain a handle. - result = - kernel_state->object_table()->AddHandle(module.get(), &module_handle); + // Existing module found. + hmodule = module->hmodule_ptr(); } else { // Not found; attempt to load as a user module. auto user_module = kernel_state->LoadUserModule(module_name); if (user_module) { user_module->RetainHandle(); - module_handle = user_module->handle(); + hmodule = user_module->hmodule_ptr(); result = X_STATUS_SUCCESS; } } - SHIM_SET_MEM_32(handle_ptr, module_handle); + + // Increment the module's load count. + auto ldr_data = + kernel_memory()->TranslateVirtual(hmodule); + ldr_data->load_count++; + + SHIM_SET_MEM_32(hmodule_ptr, hmodule); SHIM_SET_RETURN_32(result); } SHIM_CALL XexUnloadImage_shim(PPCContext* ppc_context, KernelState* kernel_state) { - uint32_t module_handle = SHIM_GET_ARG_32(0); + uint32_t hmodule = SHIM_GET_ARG_32(0); - XELOGD("XexUnloadImage(%.8X)", module_handle); + XELOGD("XexUnloadImage(%.8X)", hmodule); - X_STATUS result = X_STATUS_INVALID_HANDLE; + auto module = XModule::GetFromHModule(kernel_state, SHIM_MEM_ADDR(hmodule)); + if (!module) { + SHIM_SET_RETURN_32(X_STATUS_INVALID_HANDLE); + return; + } - result = kernel_state->object_table()->RemoveHandle(module_handle); + auto ldr_data = + kernel_state->memory()->TranslateVirtual( + hmodule); + if (ldr_data->load_count-- <= 0) { + // No more references, free it. + kernel_state->object_table()->RemoveHandle(module->handle()); + } - SHIM_SET_RETURN_32(result); + SHIM_SET_RETURN_32(X_STATUS_SUCCESS); } SHIM_CALL XexGetProcedureAddress_shim(PPCContext* ppc_context, KernelState* kernel_state) { - uint32_t module_handle = SHIM_GET_ARG_32(0); + uint32_t hmodule = SHIM_GET_ARG_32(0); uint32_t ordinal = SHIM_GET_ARG_32(1); uint32_t out_function_ptr = SHIM_GET_ARG_32(2); @@ -263,20 +278,20 @@ SHIM_CALL XexGetProcedureAddress_shim(PPCContext* ppc_context, auto string_name = reinterpret_cast(SHIM_MEM_ADDR(ordinal)); if (is_string_name) { - XELOGD("XexGetProcedureAddress(%.8X, %.8X(%s), %.8X)", module_handle, + XELOGD("XexGetProcedureAddress(%.8X, %.8X(%s), %.8X)", hmodule, ordinal, string_name, out_function_ptr); } else { - XELOGD("XexGetProcedureAddress(%.8X, %.8X, %.8X)", module_handle, ordinal, + XELOGD("XexGetProcedureAddress(%.8X, %.8X, %.8X)", hmodule, ordinal, out_function_ptr); } X_STATUS result = X_STATUS_INVALID_HANDLE; object_ref module; - if (!module_handle) { + if (!hmodule) { module = kernel_state->GetExecutableModule(); } else { - module = kernel_state->object_table()->LookupObject(module_handle); + module = XModule::GetFromHModule(kernel_state, SHIM_MEM_ADDR(hmodule)); } if (module) { uint32_t ptr;