From 0e56538cb1d492a4ce9efafb68d3beebbd268f49 Mon Sep 17 00:00:00 2001 From: Maschell Date: Wed, 5 Oct 2022 20:08:49 +0200 Subject: [PATCH] Keep track of acquired RPLs and release them on exit --- source/PluginManagement.cpp | 35 +++++++++++++++++++++++++++-------- source/PluginManagement.h | 14 +++++++++++--- source/globals.cpp | 2 ++ source/globals.h | 5 ++++- source/main.cpp | 18 +++++++++++++++++- source/utils/utils.cpp | 12 ++++++++++-- 6 files changed, 71 insertions(+), 15 deletions(-) diff --git a/source/PluginManagement.cpp b/source/PluginManagement.cpp index cc1aead..321246d 100644 --- a/source/PluginManagement.cpp +++ b/source/PluginManagement.cpp @@ -13,8 +13,11 @@ #include #include -bool PluginManagement::doRelocation(const std::vector> &relocData, relocation_trampoline_entry_t *tramp_data, uint32_t tramp_length, uint32_t trampolineID) { - std::map moduleHandleCache; +bool PluginManagement::doRelocation(const std::vector> &relocData, + relocation_trampoline_entry_t *tramp_data, + uint32_t tramp_length, + uint32_t trampolineID, + std::map &usedRPls) { for (auto const &cur : relocData) { uint32_t functionAddress = 0; const std::string &functionName = cur->getName(); @@ -37,12 +40,22 @@ bool PluginManagement::doRelocation(const std::vectorgetImportRPLInformation()->getRPLName(); int32_t isData = cur->getImportRPLInformation()->isData(); OSDynLoad_Module rplHandle = nullptr; - if (moduleHandleCache.count(rplName) > 0) { - rplHandle = moduleHandleCache[rplName]; + + if (!usedRPls.contains(rplName)) { + DEBUG_FUNCTION_LINE_VERBOSE("Acquire %s", rplName.c_str()); + // Always acquire to increase refcount and make sure it won't get unloaded while we're using it. + OSDynLoad_Error err = OSDynLoad_Acquire(rplName.c_str(), &rplHandle); + if (err != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("Failed to acquire %s", rplName.c_str()); + return false; + } + // Keep track RPLs we are using. + // They will be released on exit + usedRPls[rplName] = rplHandle; } else { - OSDynLoad_Acquire(rplName.c_str(), &rplHandle); - moduleHandleCache[rplName] = rplHandle; + rplHandle = usedRPls[rplName]; } + OSDynLoad_FindExport(rplHandle, isData, functionName.c_str(), (void **) &functionAddress); } @@ -74,7 +87,9 @@ bool PluginManagement::doRelocation(const std::vector> &plugins, relocation_trampoline_entry_t *trampData, uint32_t tramp_size) { +bool PluginManagement::doRelocations(const std::vector> &plugins, + relocation_trampoline_entry_t *trampData, uint32_t tramp_size, + std::map &usedRPls) { for (uint32_t i = 0; i < tramp_size; i++) { if (trampData[i].status == RELOC_TRAMP_IMPORT_DONE) { trampData[i].status = RELOC_TRAMP_FREE; @@ -89,7 +104,11 @@ bool PluginManagement::doRelocations(const std::vectorgetMetaInformation()->getName().c_str()); - if (!PluginManagement::doRelocation(pluginContainer->getPluginInformation()->getRelocationDataList(), trampData, tramp_size, pluginContainer->getPluginInformation()->getTrampolineId())) { + if (!PluginManagement::doRelocation(pluginContainer->getPluginInformation()->getRelocationDataList(), + trampData, + tramp_size, + pluginContainer->getPluginInformation()->getTrampolineId(), + usedRPls)) { return false; } } diff --git a/source/PluginManagement.h b/source/PluginManagement.h index d9eb339..bca2137 100644 --- a/source/PluginManagement.h +++ b/source/PluginManagement.h @@ -1,9 +1,10 @@ #pragma once #include "plugin/PluginContainer.h" +#include #include +#include #include -#include #include class PluginManagement { @@ -12,9 +13,16 @@ public: static void callInitHooks(const std::vector> &plugins); - static bool doRelocations(const std::vector> &plugins, relocation_trampoline_entry_t *trampData, uint32_t tramp_size); + static bool doRelocations(const std::vector> &plugins, + relocation_trampoline_entry_t *trampData, + uint32_t tramp_size, + std::map &usedRPls); - static bool doRelocation(const std::vector> &relocData, relocation_trampoline_entry_t *tramp_data, uint32_t tramp_length, uint32_t trampolineID); + static bool doRelocation(const std::vector> &relocData, + relocation_trampoline_entry_t *tramp_data, + uint32_t tramp_length, + uint32_t trampolineID, + std::map &usedRPls); static bool DoFunctionPatches(const std::vector> &plugins); diff --git a/source/globals.cpp b/source/globals.cpp index 94769a3..b28b3aa 100644 --- a/source/globals.cpp +++ b/source/globals.cpp @@ -9,3 +9,5 @@ relocation_trampoline_entry_t *gTrampData __attribute__((section(".data"))) = nu std::forward_list> gLoadedData __attribute__((section(".data"))); std::forward_list> gLoadOnNextLaunch __attribute__((section(".data"))); std::mutex gLoadedDataMutex __attribute__((section(".data"))); +std::map gUsedRPLs __attribute__((section(".data"))); +std::vector gAllocatedAddresses __attribute__((section(".data"))); \ No newline at end of file diff --git a/source/globals.h b/source/globals.h index d6a6448..a6d0b03 100644 --- a/source/globals.h +++ b/source/globals.h @@ -2,6 +2,7 @@ #include "plugin/PluginContainer.h" #include "utils/ConfigUtils.h" #include "version.h" +#include #include #include #include @@ -20,4 +21,6 @@ extern std::vector> gLoadedPlugins; extern std::forward_list> gLoadedData; extern std::forward_list> gLoadOnNextLaunch; -extern std::mutex gLoadedDataMutex; \ No newline at end of file +extern std::mutex gLoadedDataMutex; +extern std::map gUsedRPLs; +extern std::vector gAllocatedAddresses; \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp index 54aa9de..f130f4c 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -39,6 +39,11 @@ WUMS_APPLICATION_ENDS() { CallHook(gLoadedPlugins, WUPS_LOADER_HOOK_FINI_WUT_SOCKETS); CallHook(gLoadedPlugins, WUPS_LOADER_HOOK_FINI_WUT_DEVOPTAB); + for (auto &pair : gUsedRPLs) { + OSDynLoad_Release(pair.second); + } + gUsedRPLs.clear(); + deinitLogging(); } @@ -50,6 +55,17 @@ WUMS_APPLICATION_STARTS() { } OSReport("Running WiiUPluginLoaderBackend " VERSION_FULL "\n"); + + gUsedRPLs.clear(); + + // If an allocated rpl was not released properly (e.g. if something else calls OSDynload_Acquire without releasing it) memory get leaked. + // Let's clean this up! + for (auto &addr : gAllocatedAddresses) { + DEBUG_FUNCTION_LINE_WARN("Memory allocated by OSDynload was not freed properly, let's clean it up! (%08X)", addr); + free((void *) addr); + } + gAllocatedAddresses.clear(); + initLogging(); bool initNeeded = false; @@ -93,7 +109,7 @@ WUMS_APPLICATION_STARTS() { gLoadedData.clear(); if (!gLoadedPlugins.empty()) { - if (!PluginManagement::doRelocations(gLoadedPlugins, gTrampData, TRAMP_DATA_SIZE)) { + if (!PluginManagement::doRelocations(gLoadedPlugins, gTrampData, TRAMP_DATA_SIZE, gUsedRPLs)) { DEBUG_FUNCTION_LINE_ERR("Relocations failed"); OSFatal("Relocations failed"); } diff --git a/source/utils/utils.cpp b/source/utils/utils.cpp index 9a8311b..c1c7059 100644 --- a/source/utils/utils.cpp +++ b/source/utils/utils.cpp @@ -1,8 +1,8 @@ #include "utils.h" +#include "globals.h" #include "logger.h" +#include #include -#include -#include #include std::string getPluginPath() { @@ -69,10 +69,18 @@ OSDynLoad_Error CustomDynLoadAlloc(int32_t size, int32_t align, void **outAddr) if (!(*outAddr = memalign(align, size))) { return OS_DYNLOAD_OUT_OF_MEMORY; } + // keep track of allocated memory to clean it up if RPLs won't get unloaded properly + gAllocatedAddresses.push_back(*outAddr); return OS_DYNLOAD_OK; } void CustomDynLoadFree(void *addr) { free(addr); + + // Remove from list + auto it = std::find(gAllocatedAddresses.begin(), gAllocatedAddresses.end(), addr); + if (it != gAllocatedAddresses.end()) { + gAllocatedAddresses.erase(it); + } } \ No newline at end of file