diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 57097148e..12923b9ae 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -367,8 +367,7 @@ object_ref KernelState::LoadUserModule(const char* raw_name, // See if we've already loaded it for (auto& existing_module : user_modules_) { if (existing_module->path() == path) { - existing_module->Retain(); - return retain_object(existing_module.get()); + return existing_module; } } @@ -384,8 +383,7 @@ object_ref KernelState::LoadUserModule(const char* raw_name, global_lock.lock(); - // Retain when putting into the listing. - module->Retain(); + // Putting into the listing automatically retains. user_modules_.push_back(module); } @@ -407,6 +405,40 @@ object_ref KernelState::LoadUserModule(const char* raw_name, return module; } +void KernelState::UnloadUserModule(const object_ref& module, + bool call_entry) { + auto global_lock = global_critical_region_.Acquire(); + + if (module->is_dll_module() && module->entry_point() && call_entry) { + // Call DllMain(DLL_PROCESS_DETACH): + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx + uint64_t args[] = { + module->handle(), + 0, // DLL_PROCESS_DETACH + 0, // 0 for now, assume XexUnloadImage is like FreeLibrary + }; + auto thread_state = XThread::GetCurrentThread()->thread_state(); + processor()->Execute(thread_state, module->entry_point(), args, + xe::countof(args)); + } + + auto iter = std::find_if( + user_modules_.begin(), user_modules_.end(), + [&module](const auto& e) { return e->path() == module->path(); }); + assert_true(iter != user_modules_.end()); // Unloading an unregistered module + // is probably really bad + user_modules_.erase(iter); + + // Ensure this module was not somehow registered twice + assert_true(std::find_if(user_modules_.begin(), user_modules_.end(), + [&module](const auto& e) { + return e->path() == module->path(); + }) == user_modules_.end()); + + global_lock.unlock(); + object_table()->RemoveHandle(module->handle()); +} + void KernelState::TerminateTitle() { XELOGD("KernelState::TerminateTitle"); auto global_lock = global_critical_region_.Acquire(); diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h index 19fbc5825..95ce08fb9 100644 --- a/src/xenia/kernel/kernel_state.h +++ b/src/xenia/kernel/kernel_state.h @@ -131,6 +131,8 @@ class KernelState { void SetExecutableModule(object_ref module); object_ref LoadUserModule(const char* name, bool call_entry = true); + void UnloadUserModule(const object_ref& module, + bool call_entry = true); object_ref GetKernelModule(const char* name); template diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc index 0adb82673..cd7dd6b75 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc @@ -217,8 +217,10 @@ dword_result_t XexLoadImage(lpstring_t module_name, dword_t module_flags, // Not found; attempt to load as a user module. auto user_module = kernel_state()->LoadUserModule(module_name); if (user_module) { - user_module->Retain(); - hmodule = user_module->hmodule_ptr(); + // Give up object ownership, this reference will be released by the last + // XexUnloadImage call + auto user_module_raw = user_module.release(); + hmodule = user_module_raw->hmodule_ptr(); result = X_STATUS_SUCCESS; } } @@ -248,7 +250,8 @@ dword_result_t XexUnloadImage(lpvoid_t hmodule) { if (--ldr_data->load_count == 0) { // No more references, free it. module->Release(); - kernel_state()->object_table()->RemoveHandle(module->handle()); + kernel_state()->UnloadUserModule(object_ref( + reinterpret_cast(module.release()))); } }