From 6f34565b9910ccf1fad013cea5301f67330b297a Mon Sep 17 00:00:00 2001 From: Maschell Date: Sun, 8 Mar 2026 21:10:31 +0100 Subject: [PATCH] Rewrite module loading to load some module into custom memory mapped heap. --- .clang-format | 2 +- source/kernel.cpp | 2 +- source/main.cpp | 13 +- wumsloader/.clangd | 2 + wumsloader/src/defines.h | 35 ++ wumsloader/src/entry.cpp | 289 ++------- wumsloader/src/fs/DirList.cpp | 218 ------- wumsloader/src/fs/DirList.h | 122 ---- wumsloader/src/fs/FileUtils.cpp | 62 -- wumsloader/src/fs/FileUtils.h | 4 - wumsloader/src/globals.cpp | 10 +- wumsloader/src/globals.h | 37 +- wumsloader/src/link.ld | 2 +- wumsloader/src/{utils => module}/ElfUtils.cpp | 38 +- wumsloader/src/module/ElfUtils.h | 40 ++ wumsloader/src/module/ExportData.h | 35 +- wumsloader/src/module/FunctionSymbolData.cpp | 26 - wumsloader/src/module/FunctionSymbolData.h | 29 +- wumsloader/src/module/HookData.cpp | 16 - wumsloader/src/module/HookData.h | 17 +- wumsloader/src/module/HooksManagement.cpp | 167 ++++++ wumsloader/src/module/HooksManagement.h | 22 + .../src/module/ImportRPLInformation.cpp | 20 +- wumsloader/src/module/ImportRPLInformation.h | 4 +- wumsloader/src/module/ModuleAllocator.cpp | 60 ++ wumsloader/src/module/ModuleAllocator.h | 20 + wumsloader/src/module/ModuleContainer.cpp | 35 ++ wumsloader/src/module/ModuleContainer.h | 41 ++ wumsloader/src/module/ModuleData.h | 197 ------- wumsloader/src/module/ModuleDataFactory.cpp | 498 ---------------- .../src/module/ModuleDataPersistence.cpp | 381 ++++++------ wumsloader/src/module/ModuleDataPersistence.h | 53 +- .../src/module/ModuleLinkInformation.cpp | 129 ++++ wumsloader/src/module/ModuleLinkInformation.h | 87 +++ .../module/ModuleLinkInformationFactory.cpp | 557 ++++++++++++++++++ .../src/module/ModuleLinkInformationFactory.h | 83 +++ wumsloader/src/module/ModuleManagement.cpp | 343 +++++++++++ wumsloader/src/module/ModuleManagement.h | 18 + .../src/module/ModuleMetaInformation.cpp | 59 ++ wumsloader/src/module/ModuleMetaInformation.h | 40 ++ .../module/ModuleMetaInformationFactory.cpp | 165 ++++++ ...ctory.h => ModuleMetaInformationFactory.h} | 41 +- wumsloader/src/module/RelocationData.cpp | 7 +- wumsloader/src/module/RelocationData.h | 7 +- wumsloader/src/module/RelocationUtils.cpp | 203 +++++++ wumsloader/src/module/RelocationUtils.h | 27 + wumsloader/src/module/SectionInfo.cpp | 4 +- wumsloader/src/module/SectionInfo.h | 4 +- wumsloader/src/module/WUMSVersion.cpp | 44 ++ wumsloader/src/module/WUMSVersion.h | 26 + wumsloader/src/utils/ElfUtils.h | 47 -- wumsloader/src/utils/HeapMemoryFixedSize.cpp | 92 +++ wumsloader/src/utils/HeapMemoryFixedSize.h | 64 ++ wumsloader/src/utils/LoadedFile.cpp | 122 ++++ wumsloader/src/utils/LoadedFile.h | 32 + wumsloader/src/utils/OnLeavingScope.h | 4 +- wumsloader/src/utils/RelocationUtils.cpp | 160 ----- wumsloader/src/utils/RelocationUtils.h | 16 - wumsloader/src/utils/SegmentedTimer.h | 134 +++++ wumsloader/src/utils/StringTools.cpp | 51 -- wumsloader/src/utils/StringTools.h | 66 --- wumsloader/src/utils/hooks.cpp | 129 ---- wumsloader/src/utils/hooks.h | 14 - wumsloader/src/utils/imports.h | 2 + wumsloader/src/utils/logger.h | 22 +- wumsloader/src/utils/memory.cpp | 2 - wumsloader/src/utils/utils.h | 12 +- 67 files changed, 3111 insertions(+), 2199 deletions(-) create mode 100644 wumsloader/.clangd create mode 100644 wumsloader/src/defines.h delete mode 100644 wumsloader/src/fs/DirList.cpp delete mode 100644 wumsloader/src/fs/DirList.h delete mode 100644 wumsloader/src/fs/FileUtils.cpp delete mode 100644 wumsloader/src/fs/FileUtils.h rename wumsloader/src/{utils => module}/ElfUtils.cpp (82%) create mode 100644 wumsloader/src/module/ElfUtils.h delete mode 100644 wumsloader/src/module/FunctionSymbolData.cpp delete mode 100644 wumsloader/src/module/HookData.cpp create mode 100644 wumsloader/src/module/HooksManagement.cpp create mode 100644 wumsloader/src/module/HooksManagement.h create mode 100644 wumsloader/src/module/ModuleAllocator.cpp create mode 100644 wumsloader/src/module/ModuleAllocator.h create mode 100644 wumsloader/src/module/ModuleContainer.cpp create mode 100644 wumsloader/src/module/ModuleContainer.h delete mode 100644 wumsloader/src/module/ModuleData.h delete mode 100644 wumsloader/src/module/ModuleDataFactory.cpp create mode 100644 wumsloader/src/module/ModuleLinkInformation.cpp create mode 100644 wumsloader/src/module/ModuleLinkInformation.h create mode 100644 wumsloader/src/module/ModuleLinkInformationFactory.cpp create mode 100644 wumsloader/src/module/ModuleLinkInformationFactory.h create mode 100644 wumsloader/src/module/ModuleManagement.cpp create mode 100644 wumsloader/src/module/ModuleManagement.h create mode 100644 wumsloader/src/module/ModuleMetaInformation.cpp create mode 100644 wumsloader/src/module/ModuleMetaInformation.h create mode 100644 wumsloader/src/module/ModuleMetaInformationFactory.cpp rename wumsloader/src/module/{ModuleDataFactory.h => ModuleMetaInformationFactory.h} (50%) create mode 100644 wumsloader/src/module/RelocationUtils.cpp create mode 100644 wumsloader/src/module/RelocationUtils.h create mode 100644 wumsloader/src/module/WUMSVersion.cpp create mode 100644 wumsloader/src/module/WUMSVersion.h delete mode 100644 wumsloader/src/utils/ElfUtils.h create mode 100644 wumsloader/src/utils/HeapMemoryFixedSize.cpp create mode 100644 wumsloader/src/utils/HeapMemoryFixedSize.h create mode 100644 wumsloader/src/utils/LoadedFile.cpp create mode 100644 wumsloader/src/utils/LoadedFile.h delete mode 100644 wumsloader/src/utils/RelocationUtils.cpp delete mode 100644 wumsloader/src/utils/RelocationUtils.h create mode 100644 wumsloader/src/utils/SegmentedTimer.h delete mode 100644 wumsloader/src/utils/StringTools.cpp delete mode 100644 wumsloader/src/utils/hooks.cpp delete mode 100644 wumsloader/src/utils/hooks.h diff --git a/.clang-format b/.clang-format index 56cc685..34c35a0 100644 --- a/.clang-format +++ b/.clang-format @@ -42,7 +42,7 @@ IndentPPDirectives: None IndentWidth: 4 KeepEmptyLinesAtTheStartOfBlocks: true MaxEmptyLinesToKeep: 2 -NamespaceIndentation: All +NamespaceIndentation: None ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PointerAlignment: Right diff --git a/source/kernel.cpp b/source/kernel.cpp index 5e6b44c..2aa1b32 100644 --- a/source/kernel.cpp +++ b/source/kernel.cpp @@ -1,5 +1,5 @@ #include "kernel.h" -#include "../wumsloader/src/globals.h" +#include "../wumsloader/src/defines.h" #include "ElfUtils.h" #include "wumsloader_elf.h" #include diff --git a/source/main.cpp b/source/main.cpp index 13b929b..aa91728 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,7 +1,9 @@ -#include "../wumsloader/src/globals.h" +#include "../wumsloader/src/defines.h" #include "kernel.h" #include "utils/logger.h" + #include +#include #include extern "C" void __fini(); @@ -20,13 +22,10 @@ int main(int argc, char **argv) { "See https://wiiu.hacks.guide/ for instructions on how to update!"); } - memcpy(MEMORY_REGION_USABLE_MEM_REGION_END_VALUE_PTR, &argv[3], sizeof(uint32_t)); + memcpy(reinterpret_cast(MEMORY_REGION_USABLE_MEM_REGION_END_VALUE_PTR), &argv[3], sizeof(uint32_t)); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" -#pragma GCC diagnostic ignored "-Wstringop-overflow=" - strncpy(ENVRIONMENT_STRING, basePath.c_str(), ENVIRONMENT_PATH_LENGTH - 1); -#pragma GCC diagnostic pop + auto envBuffer = getEnvironmentStringBuffer(); + snprintf(envBuffer.data(), envBuffer.size(), "%s", basePath.c_str()); DEBUG_FUNCTION_LINE("Setup wumsloader"); SetupWUMSLoader(); diff --git a/wumsloader/.clangd b/wumsloader/.clangd new file mode 100644 index 0000000..d02e6c5 --- /dev/null +++ b/wumsloader/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + Add: [-m32] \ No newline at end of file diff --git a/wumsloader/src/defines.h b/wumsloader/src/defines.h new file mode 100644 index 0000000..d1cc9e3 --- /dev/null +++ b/wumsloader/src/defines.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include +#include + + +constexpr std::uint32_t MEMORY_REGION_START = 0x00800000; +constexpr std::uint32_t MEMORY_REGION_SIZE = 0x00800000; + +// Maximum size of the wumsloader, needs to match the one defined in link.ld +constexpr std::uint32_t MEMORY_REGION_RELOCATOR_SIZE = 0x67000; +// Length of the EnvironmentPath. +constexpr std::uint32_t MEMORY_REGION_USABLE_MEM_REGION_END_LENGTH = sizeof(std::uint32_t); // 0x04 + +constexpr std::uint32_t MEMORY_REGION_ENVIRONMENT_PATH_LENGTH = 0x100; +constexpr std::uint32_t MEMORY_REGION_ENVIRONMENT_STRING_ADRR = MEMORY_REGION_START + MEMORY_REGION_RELOCATOR_SIZE; + +constexpr std::uint32_t MEMORY_REGION_USABLE_MEM_REGION_END_VALUE_PTR = MEMORY_REGION_ENVIRONMENT_STRING_ADRR + MEMORY_REGION_ENVIRONMENT_PATH_LENGTH; +#define MEMORY_REGION_USABLE_MEM_REGION_END_VALUE (*MEMORY_REGION_USABLE_MEM_REGION_END_VALUE_PTR) + +// Technically we overwrite the CustomRPXLoader that is still loaded at 0x00800000... +// We can get away with it because the EnvironmentLoader exits instead of returning to the CustomRPXLoader. +constexpr std::uint32_t MEMORY_REGION_USABLE_HEAP_START = MEMORY_REGION_USABLE_MEM_REGION_END_VALUE_PTR + MEMORY_REGION_USABLE_MEM_REGION_END_LENGTH; + +constexpr std::uint32_t WUMS_LOADER_SETUP_MAGIC_WORD = 0x13371337; + +inline std::span getMemoryForHeap() { + return std::span(reinterpret_cast(MEMORY_REGION_USABLE_HEAP_START), (*reinterpret_cast(MEMORY_REGION_USABLE_MEM_REGION_END_VALUE_PTR)) - MEMORY_REGION_USABLE_HEAP_START); +} + +inline std::span getEnvironmentStringBuffer() { + return std::span( + reinterpret_cast(MEMORY_REGION_ENVIRONMENT_STRING_ADRR), + MEMORY_REGION_ENVIRONMENT_PATH_LENGTH); +} diff --git a/wumsloader/src/entry.cpp b/wumsloader/src/entry.cpp index 5a6e18e..e88bff8 100644 --- a/wumsloader/src/entry.cpp +++ b/wumsloader/src/entry.cpp @@ -1,28 +1,28 @@ #include "entry.h" -#include "fs/DirList.h" + +#include "defines.h" #include "globals.h" -#include "module/ModuleDataFactory.h" +#include "module/HooksManagement.h" #include "module/ModuleDataPersistence.h" -#include "utils/ElfUtils.h" -#include "utils/RelocationUtils.h" -#include "utils/dynamic.h" -#include "utils/hooks.h" -#include "utils/logger.h" -#include "utils/utils.h" +#include "module/ModuleManagement.h" #include "version.h" + +#include "module/RelocationUtils.h" +#include "utils/SegmentedTimer.h" +#include "utils/dynamic.h" +#include "utils/logger.h" + #include #include #include #include +#include + +#include + #include -#include -#define VERSION "v0.2.9" - -void CallInitHooksForModule(const std::shared_ptr &curModule); - -bool CheckModulesByDependencies(const std::vector> &loadedModules); -std::vector> OrderModulesByDependencies(const std::vector> &loadedModules); +#define VERSION "v0.3.0" void wipeStack(void *stackEnd) { volatile char current_stack_marker = 0; @@ -50,7 +50,8 @@ extern "C" int _start(int argc, char **argv) { static uint8_t ucSetupRequired = 1; if (ucSetupRequired) { - gHeapHandle = MEMCreateExpHeapEx((void *) (MEMORY_REGION_USABLE_HEAP_START), MEMORY_REGION_USABLE_HEAP_END - MEMORY_REGION_USABLE_HEAP_START, 1); + const auto heapSpan = getMemoryForHeap(); + gHeapHandle = MEMCreateExpHeapEx(heapSpan.data(), heapSpan.size_bytes(), 1); if (!gHeapHandle) { OSFatal("Failed to alloc heap"); } @@ -85,27 +86,10 @@ extern "C" int _start(int argc, char **argv) { return ((int (*)(int, char **))(*(unsigned int *) 0x1005E040))(argc, argv); } -void SaveLoadedRPLsInGlobalInformation(module_information_t *globalInformation, - std::map &usedRPls) { - // free previous allocations. - if (globalInformation->acquired_rpls) { - free(globalInformation->acquired_rpls); - } - globalInformation->number_acquired_rpls = usedRPls.size(); - globalInformation->acquired_rpls = (uint32_t *) malloc(usedRPls.size() * sizeof(uint32_t)); - if (!globalInformation->acquired_rpls) { - OSFatal("Failed to allocate memory"); - } - uint32_t i = 0; - for (auto &rpl : usedRPls) { - globalInformation->acquired_rpls[i] = (uint32_t) rpl.second; - ++i; - } -} - extern "C" uint32_t OSGetBootPMFlags(void); void doStart(int argc, char **argv) { + using namespace WUMSLoader; uint32_t bootFlags = OSGetBootPMFlags(); /* * Bit 13 - OS relaunch (OSForceFullRelaunch()). @@ -119,6 +103,11 @@ void doStart(int argc, char **argv) { OSReport("Running WUMSLoader " VERSION VERSION_EXTRA "\n"); +#ifdef WUMS_ENABLE_PROFILING + Utils::SegmentedTimer mainTimer("WUMSLoader Main"); + Utils::SegmentedTimer::globalTimer = &mainTimer; +#endif + gUsedRPLs.clear(); // If an allocated rpl was not released properly (e.g. if something else calls OSDynload_Acquire without releasing it) // memory gets leaked. Let's clean this up! @@ -129,226 +118,32 @@ void doStart(int argc, char **argv) { gAllocatedAddresses.clear(); if (!gInitCalled) { - gInitCalled = 1; + PROFILE_BLOCK("Init modules"); + gInitCalled = 1; + gModuleInformation = Modules::PersistedModuleData::create(); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstringop-overread" - std::string basePath = ENVRIONMENT_STRING; -#pragma GCC diagnostic pop - - DEBUG_FUNCTION_LINE("We need to load the modules. basePath %s", basePath.c_str()); - DirList modules(basePath + "/modules", ".wms", DirList::Files, 1); - modules.SortList(); - for (int i = 0; i < modules.GetFilecount(); i++) { - std::string_view asView(modules.GetFilename(i)); - if (asView.starts_with('.') || asView.starts_with('_')) { - DEBUG_FUNCTION_LINE_WARN("Skip file %s", modules.GetFilename(i)); - continue; - } - DEBUG_FUNCTION_LINE("Loading module %s", modules.GetFilepath(i)); - if (auto moduleData = ModuleDataFactory::load(modules.GetFilepath(i))) { - DEBUG_FUNCTION_LINE("Successfully loaded %s", modules.GetFilepath(i)); - gLoadedModules.push_back(std::move(moduleData.value())); - } else { - DEBUG_FUNCTION_LINE_ERR("Failed to load %s", modules.GetFilepath(i)); - } - } - - gModuleInformation = {.version = MODULE_INFORMATION_VERSION}; - ModuleDataPersistence::saveModuleData(&gModuleInformation, gLoadedModules); - - // Order modules list by dependencies. - gLoadedModules = OrderModulesByDependencies(gLoadedModules); - - { - // make sure the plugin backend module is at the end. - const auto it = std::ranges::find_if(gLoadedModules, - [](auto &cur) { return std::string_view(cur->getExportName()) == "homebrew_wupsbackend"; }); - if (it != gLoadedModules.end()) { - auto module = *it; - gLoadedModules.erase(it); - gLoadedModules.push_back(module); - } - } - - bool aromaBaseModuleLoaded = false; - for (auto &curModule : gLoadedModules) { - if (std::string_view(curModule->getExportName()) == "homebrew_basemodule") { - DEBUG_FUNCTION_LINE_VERBOSE("We have AromaBaseModule!"); - aromaBaseModuleLoaded = true; - break; - } - } - - // Make sure aromaBaseModuleLoaded is loaded when WUMS_HOOK_APPLICATION_ENDS and WUMS_HOOK_FINI_WUT are called - for (auto &curModule : gLoadedModules) { - for (auto &curHook : curModule->getHookDataList()) { - if (curHook->getType() == WUMS_HOOK_APPLICATION_ENDS || curHook->getType() == WUMS_HOOK_FINI_WUT_DEVOPTAB) { - if (!aromaBaseModuleLoaded) { - DEBUG_FUNCTION_LINE_ERR("%s requires module homebrew_basemodule", curModule->getExportName().c_str()); - OSFatal("module requires module homebrew_basemodule"); - } - } - } - } - - // Make sure the base module is called first of the "regular" modules (except the "InitBeforeRelocationDoneHook" hooks) - if (aromaBaseModuleLoaded) { - // Create a copy of all modules which do not call init before RelocationDoneHook - std::list> gLoadedModulesCopy; - for (auto &curModule : gLoadedModules) { - if (!curModule->isInitBeforeRelocationDoneHook()) { - gLoadedModulesCopy.push_back(curModule); - } - } - - // move homebrew_basemodule to the front - const auto it = std::ranges::find_if(gLoadedModulesCopy, - [](auto &cur) { return std::string_view(cur->getExportName()) == "homebrew_basemodule"; }); - if (it != gLoadedModulesCopy.end()) { - auto module = *it; - gLoadedModulesCopy.erase(it); - gLoadedModulesCopy.push_front(module); - } - - // Move all modules which do not call init before RelocationDoneHook to the end, but keep homebrew_basemodule at the front. - for (auto &curModule : gLoadedModulesCopy) { - if (remove_first_if(gLoadedModules, [curModule](auto &cur) { return cur->getExportName() == curModule->getExportName(); })) { - gLoadedModules.push_back(curModule); - } - } - } - - // Check if dependencies are still resolved. - if (!CheckModulesByDependencies(gLoadedModules)) { - OSFatal("Module order is impossible"); - } - -#ifdef VERBOSE_DEBUG - DEBUG_FUNCTION_LINE_VERBOSE("Final order of modules"); - for (auto &curModule : gLoadedModules) { - DEBUG_FUNCTION_LINE_VERBOSE("%s", curModule->getExportName().c_str()); - } -#endif - - DEBUG_FUNCTION_LINE_VERBOSE("Resolve relocations without loading additonal rpls"); - ResolveRelocations(gLoadedModules, true, gUsedRPLs); - - for (auto &curModule : gLoadedModules) { - if (curModule->isInitBeforeRelocationDoneHook()) { - CallInitHooksForModule(curModule); - } - } - - // Hacky workaround for modules to allow register a custom rpl allocator function - CallHook(gLoadedModules, WUMS_HOOK_GET_CUSTOM_RPL_ALLOCATOR); - - // Now we can load unloaded rpls. - ResolveRelocations(gLoadedModules, false, gUsedRPLs); - - DEBUG_FUNCTION_LINE_VERBOSE("Call Relocations done hook"); - CallHook(gLoadedModules, WUMS_HOOK_RELOCATIONS_DONE); - - for (auto &curModule : gLoadedModules) { - if (!curModule->isInitBeforeRelocationDoneHook()) { - CallInitHooksForModule(curModule); - } - } + std::string modulesBasePath = std::string(getEnvironmentStringBuffer().data()) + "/modules"; + DEBUG_FUNCTION_LINE("Trying to load module from: %s", modulesBasePath.c_str()); + gLoadedModules = Modules::ModuleManagement::loadAndLinkModules(modulesBasePath, gUsedRPLs, gHeapHandle, *gModuleInformation); } else { + PROFILE_BLOCK("Relocate modules"); DEBUG_FUNCTION_LINE("Resolve relocations and load unloaded rpls functions"); - CallHook(gLoadedModules, WUMS_HOOK_CLEAR_ALLOCATED_RPL_MEMORY); - ResolveRelocations(gLoadedModules, false, gUsedRPLs); - CallHook(gLoadedModules, WUMS_HOOK_RELOCATIONS_DONE); + + Modules::HooksManagement::CallHook(gLoadedModules, WUMS_HOOK_CLEAR_ALLOCATED_RPL_MEMORY); + Modules::RelocationUtils::ResolveRelocations(gLoadedModules, Modules::RelocationUtils::ExternalRPLLoadingStrategy::LOAD_EXTERNAL_RPLS, gUsedRPLs); + Modules::HooksManagement::CallHook(gLoadedModules, WUMS_HOOK_RELOCATIONS_DONE); } - SaveLoadedRPLsInGlobalInformation(&gModuleInformation, gUsedRPLs); + DEBUG_FUNCTION_LINE_VERBOSE("MEMGetTotalFreeSizeForExpHeap(gHeapHandle): %f KiB", MEMGetTotalFreeSizeForExpHeap(gHeapHandle) / 1024.0); + DEBUG_FUNCTION_LINE_VERBOSE("MEMGetAllocatableSizeForExpHeapEx(gHeapHandle,0x40): %f KiB", MEMGetAllocatableSizeForExpHeapEx(gHeapHandle, 0x40) / 1024.0); - CallHook(gLoadedModules, WUMS_HOOK_INIT_WUT_DEVOPTAB); - CallHook(gLoadedModules, WUMS_HOOK_INIT_WUT_SOCKETS); - CallHook(gLoadedModules, WUMS_HOOK_APPLICATION_STARTS); - CallHook(gLoadedModules, WUMS_HOOK_ALL_APPLICATION_STARTS_DONE); + gModuleInformation->setUsedRPLsList(gUsedRPLs); + + Modules::HooksManagement::CallHook(gLoadedModules, WUMS_HOOK_INIT_WUT_DEVOPTAB); + Modules::HooksManagement::CallHook(gLoadedModules, WUMS_HOOK_INIT_WUT_SOCKETS); + Modules::HooksManagement::CallHook(gLoadedModules, WUMS_HOOK_APPLICATION_STARTS); + Modules::HooksManagement::CallHook(gLoadedModules, WUMS_HOOK_ALL_APPLICATION_STARTS_DONE); deinitLogging(); fini_wut(); } - -void CallInitHooksForModule(const std::shared_ptr &curModule) { - CallHook(curModule, WUMS_HOOK_INIT_WUT_THREAD); - CallHook(curModule, WUMS_HOOK_INIT_WUT_MALLOC); - CallHook(curModule, WUMS_HOOK_INIT_WUT_NEWLIB); - CallHook(curModule, WUMS_HOOK_INIT_WUT_STDCPP); - CallHook(curModule, WUMS_HOOK_INIT_WUT_DEVOPTAB); - CallHook(curModule, WUMS_HOOK_INIT_WUT_SOCKETS); - CallHook(curModule, WUMS_HOOK_INIT_WRAPPER, !curModule->isSkipInitFini()); - CallHook(curModule, WUMS_HOOK_INIT); -} - -bool CheckModulesByDependencies(const std::vector> &loadedModules) { - std::set loaderModuleNames; - - for (auto const &curModule : loadedModules) { - DEBUG_FUNCTION_LINE_VERBOSE("Check if we can load %s", curModule->getExportName().c_str()); - for (auto &curRPL : curModule->getDependencies()) { - if (!curRPL.starts_with("homebrew")) { - continue; - } - if (curRPL == "homebrew_wupsbackend") { - OSFatal("Error: module depends on homebrew_wupsbackend, this is not supported"); - } - if (!loaderModuleNames.contains(curRPL)) { - DEBUG_FUNCTION_LINE_VERBOSE("%s requires %s which is not loaded yet", curModule->getExportName().c_str(), curRPL.c_str()); - return false; - } else { - DEBUG_FUNCTION_LINE_VERBOSE("Used %s, but it's already loaded", curRPL.c_str()); - } - } - loaderModuleNames.insert(curModule->getExportName()); - } - return true; -} - -std::vector> OrderModulesByDependencies(const std::vector> &loadedModules) { - std::vector> finalOrder; - std::set loadedModulesExportNames; - std::set loadedModulesEntrypoints; - - while (true) { - bool canBreak = true; - bool weDidSomething = false; - for (auto const &curModule : loadedModules) { - if (loadedModulesEntrypoints.contains(curModule->getEntrypoint())) { - // DEBUG_FUNCTION_LINE("%s [%08X] is already loaded" curModule->getExportName().c_str(), curModule->getEntrypoint()); - continue; - } - canBreak = false; - DEBUG_FUNCTION_LINE_VERBOSE("Check if we can load %s", curModule->getExportName().c_str()); - bool canLoad = true; - for (auto &curImportRPL : curModule->getDependencies()) { - if (!curImportRPL.starts_with("homebrew")) { - continue; - } - if (curImportRPL == "homebrew_wupsbackend") { - OSFatal("Error: module depends on homebrew_wupsbackend, this is not supported"); - } - if (!loadedModulesExportNames.contains(curImportRPL)) { - DEBUG_FUNCTION_LINE_VERBOSE("We can't load the module, because %s is not loaded yet", curImportRPL.c_str()); - canLoad = false; - break; - } - } - if (canLoad) { - weDidSomething = true; - DEBUG_FUNCTION_LINE_VERBOSE("We can load: %s", curModule->getExportName().c_str()); - finalOrder.push_back(curModule); - loadedModulesExportNames.insert(curModule->getExportName()); - loadedModulesEntrypoints.insert(curModule->getEntrypoint()); - } - } - if (canBreak) { - break; - } else if (!weDidSomething) { - OSFatal("Failed to resolve dependencies."); - } - } - return finalOrder; -} diff --git a/wumsloader/src/fs/DirList.cpp b/wumsloader/src/fs/DirList.cpp deleted file mode 100644 index 3198efb..0000000 --- a/wumsloader/src/fs/DirList.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/**************************************************************************** - * Copyright (C) 2010 - * by Dimok - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any - * damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any - * purpose, including commercial applications, and to alter it and - * redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you - * must not claim that you wrote the original software. If you use - * this software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and - * must not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - * - * DirList Class - * for WiiXplorer 2010 - ***************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -DirList::DirList() { - Flags = 0; - Filter = 0; - Depth = 0; -} - -DirList::DirList(const std::string &path, const char *filter, uint32_t flags, uint32_t maxDepth) { - this->LoadPath(path, filter, flags, maxDepth); - this->SortList(); -} - -DirList::~DirList() { - ClearList(); -} - -BOOL DirList::LoadPath(const std::string &folder, const char *filter, uint32_t flags, uint32_t maxDepth) { - if (folder.empty()) - return false; - - Flags = flags; - Filter = filter; - Depth = maxDepth; - - std::string folderpath(folder); - uint32_t length = folderpath.size(); - - //! clear path of double slashes - StringTools::RemoveDoubleSlashs(folderpath); - - //! remove last slash if exists - if (length > 0 && folderpath[length - 1] == '/') - folderpath.erase(length - 1); - - //! add root slash if missing - if (folderpath.find('/') == std::string::npos) { - folderpath += '/'; - } - - return InternalLoadPath(folderpath); -} - -BOOL DirList::InternalLoadPath(std::string &folderpath) { - if (folderpath.size() < 3) - return false; - - struct dirent *dirent = NULL; - DIR *dir = NULL; - - dir = opendir(folderpath.c_str()); - if (dir == NULL) - return false; - - while ((dirent = readdir(dir)) != 0) { - BOOL isDir = dirent->d_type & DT_DIR; - const char *filename = dirent->d_name; - - if (isDir) { - if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) - continue; - - if ((Flags & CheckSubfolders) && (Depth > 0)) { - int32_t length = folderpath.size(); - if (length > 2 && folderpath[length - 1] != '/') { - folderpath += '/'; - } - folderpath += filename; - - Depth--; - InternalLoadPath(folderpath); - folderpath.erase(length); - Depth++; - } - - if (!(Flags & Dirs)) - continue; - } else if (!(Flags & Files)) { - continue; - } - - if (Filter) { - char *fileext = strrchr(filename, '.'); - if (!fileext) - continue; - - if (StringTools::strtokcmp(fileext, Filter, ",") == 0) - AddEntrie(folderpath, filename, isDir); - } else { - AddEntrie(folderpath, filename, isDir); - } - } - closedir(dir); - - return true; -} - -void DirList::AddEntrie(const std::string &filepath, const char *filename, BOOL isDir) { - if (!filename) - return; - - int32_t pos = FileInfo.size(); - - FileInfo.resize(pos + 1); - - FileInfo[pos].FilePath = (char *) malloc(filepath.size() + strlen(filename) + 2); - if (!FileInfo[pos].FilePath) { - FileInfo.resize(pos); - return; - } - - sprintf(FileInfo[pos].FilePath, "%s/%s", filepath.c_str(), filename); - FileInfo[pos].isDir = isDir; -} - -void DirList::ClearList() { - for (uint32_t i = 0; i < FileInfo.size(); ++i) { - if (FileInfo[i].FilePath) { - free(FileInfo[i].FilePath); - FileInfo[i].FilePath = NULL; - } - } - - FileInfo.clear(); - std::vector().swap(FileInfo); -} - -const char *DirList::GetFilename(int32_t ind) const { - if (!valid(ind)) - return ""; - - return StringTools::FullpathToFilename(FileInfo[ind].FilePath); -} - -static BOOL SortCallback(const DirEntry &f1, const DirEntry &f2) { - if (f1.isDir && !(f2.isDir)) - return true; - if (!(f1.isDir) && f2.isDir) - return false; - - if (f1.FilePath && !f2.FilePath) - return true; - if (!f1.FilePath) - return false; - - if (strcasecmp(f1.FilePath, f2.FilePath) > 0) - return false; - - return true; -} - -void DirList::SortList() { - if (FileInfo.size() > 1) - std::sort(FileInfo.begin(), FileInfo.end(), SortCallback); -} - -void DirList::SortList(BOOL (*SortFunc)(const DirEntry &a, const DirEntry &b)) { - if (FileInfo.size() > 1) - std::sort(FileInfo.begin(), FileInfo.end(), SortFunc); -} - -uint64_t DirList::GetFilesize(int32_t index) const { - struct stat st; - const char *path = GetFilepath(index); - - if (!path || stat(path, &st) != 0) - return 0; - - return st.st_size; -} - -int32_t DirList::GetFileIndex(const char *filename) const { - if (!filename) - return -1; - - for (uint32_t i = 0; i < FileInfo.size(); ++i) { - if (strcasecmp(GetFilename(i), filename) == 0) - return i; - } - - return -1; -} diff --git a/wumsloader/src/fs/DirList.h b/wumsloader/src/fs/DirList.h deleted file mode 100644 index bd1cd90..0000000 --- a/wumsloader/src/fs/DirList.h +++ /dev/null @@ -1,122 +0,0 @@ -/**************************************************************************** - * Copyright (C) 2010 - * by Dimok - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any - * damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any - * purpose, including commercial applications, and to alter it and - * redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you - * must not claim that you wrote the original software. If you use - * this software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and - * must not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - * - * DirList Class - * for WiiXplorer 2010 - ***************************************************************************/ -#ifndef ___DIRLIST_H_ -#define ___DIRLIST_H_ - -#include -#include -#include - -typedef struct { - char *FilePath; - BOOL isDir; -} DirEntry; - -class DirList { -public: - //!Constructor - DirList(void); - - //!\param path Path from where to load the filelist of all files - //!\param filter A fileext that needs to be filtered - //!\param flags search/filter flags from the enum - DirList(const std::string &path, const char *filter = NULL, uint32_t flags = Files | Dirs, uint32_t maxDepth = 0xffffffff); - - //!Destructor - virtual ~DirList(); - - //! Load all the files from a directory - BOOL LoadPath(const std::string &path, const char *filter = NULL, uint32_t flags = Files | Dirs, uint32_t maxDepth = 0xffffffff); - - //! Get a filename of the list - //!\param list index - const char *GetFilename(int32_t index) const; - - //! Get the a filepath of the list - //!\param list index - const char *GetFilepath(int32_t index) const { - if (!valid(index)) - return ""; - else - return FileInfo[index].FilePath; - } - - //! Get the a filesize of the list - //!\param list index - uint64_t GetFilesize(int32_t index) const; - - //! Is index a dir or a file - //!\param list index - BOOL IsDir(int32_t index) const { - if (!valid(index)) - return false; - return FileInfo[index].isDir; - }; - - //! Get the filecount of the whole list - int32_t GetFilecount() const { - return FileInfo.size(); - }; - - //! Sort list by filepath - void SortList(); - - //! Custom sort command for custom sort functions definitions - void SortList(BOOL (*SortFunc)(const DirEntry &a, const DirEntry &b)); - - //! Get the index of the specified filename - int32_t GetFileIndex(const char *filename) const; - - //! Enum for search/filter flags - enum { - Files = 0x01, - Dirs = 0x02, - CheckSubfolders = 0x08, - }; - -protected: - // Internal parser - BOOL InternalLoadPath(std::string &path); - - //!Add a list entrie - void AddEntrie(const std::string &filepath, const char *filename, BOOL isDir); - - //! Clear the list - void ClearList(); - - //! Check if valid pos is requested - inline BOOL valid(uint32_t pos) const { - return (pos < FileInfo.size()); - }; - - uint32_t Flags; - uint32_t Depth; - const char *Filter; - std::vector FileInfo; -}; - -#endif diff --git a/wumsloader/src/fs/FileUtils.cpp b/wumsloader/src/fs/FileUtils.cpp deleted file mode 100644 index 5746b49..0000000 --- a/wumsloader/src/fs/FileUtils.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "utils/logger.h" -#include "utils/utils.h" -#include -#include -#include -#include -#include -#include - -int32_t LoadFileToMem(const std::string &filepath, uint8_t **inbuffer, uint32_t *size) { - //! always initialze input - *inbuffer = nullptr; - if (size) { - *size = 0; - } - - int32_t iFd = open(filepath.c_str(), O_RDONLY); - if (iFd < 0) { - return -1; - } - - uint32_t filesize = lseek(iFd, 0, SEEK_END); - lseek(iFd, 0, SEEK_SET); - - auto *buffer = (uint8_t *) MEMAllocFromDefaultHeapEx(ROUNDUP(filesize, 0x40), 0x40); - if (buffer == nullptr) { - close(iFd); - return -2; - } - - uint32_t blocksize = 0x20000; - uint32_t done = 0; - int32_t readBytes = 0; - - while (done < filesize) { - if (done + blocksize > filesize) { - blocksize = filesize - done; - } - readBytes = read(iFd, buffer + done, blocksize); - if (readBytes <= 0) - break; - done += readBytes; - } - - ::close(iFd); - - if (done != filesize) { - memset(buffer, 0, ROUNDUP(filesize, 0x40)); - free(buffer); - buffer = nullptr; - return -3; - } - - *inbuffer = buffer; - - //! sign is optional input - if (size) { - *size = filesize; - } - - return filesize; -} \ No newline at end of file diff --git a/wumsloader/src/fs/FileUtils.h b/wumsloader/src/fs/FileUtils.h deleted file mode 100644 index ac6b2cc..0000000 --- a/wumsloader/src/fs/FileUtils.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -int32_t LoadFileToMem(const std::string &filepath, uint8_t **inbuffer, uint32_t *size); \ No newline at end of file diff --git a/wumsloader/src/globals.cpp b/wumsloader/src/globals.cpp index a20e829..ae79321 100644 --- a/wumsloader/src/globals.cpp +++ b/wumsloader/src/globals.cpp @@ -1,13 +1,15 @@ #include "globals.h" -#include "module/ModuleData.h" + +#include "module/ModuleContainer.h" +#include "module/ModuleDataPersistence.h" MEMHeapHandle gHeapHandle __attribute__((section(".data"))) = nullptr; uint8_t gInitCalled __attribute__((section(".data"))) = 0; -module_information_t gModuleInformation __attribute__((section(".data"))); -std::vector> gLoadedModules __attribute__((section(".data"))); +std::unique_ptr gModuleInformation __attribute__((section(".data"))); +std::vector gLoadedModules __attribute__((section(".data"))); std::unique_ptr gModuleDataInfo __attribute__((section(".data"))); -std::map gUsedRPLs __attribute__((section(".data"))); +std::map> gUsedRPLs __attribute__((section(".data"))); std::vector gAllocatedAddresses __attribute__((section(".data"))); WUMSRPLAllocatorAllocFn gCustomRPLAllocatorAllocFn __attribute__((section(".data"))) = nullptr; diff --git a/wumsloader/src/globals.h b/wumsloader/src/globals.h index 70e5709..fbd2732 100644 --- a/wumsloader/src/globals.h +++ b/wumsloader/src/globals.h @@ -1,45 +1,30 @@ #pragma once +#include "module/ModuleContainer.h" +#include "utils/SegmentedTimer.h" + #include #include #include +#include #include #include -#include #include -class ModuleData; +namespace WUMSLoader::Modules { +class PersistedModuleData; +} extern uint8_t gInitCalled; extern MEMHeapHandle gHeapHandle; -extern module_information_t gModuleInformation; -extern std::vector> gLoadedModules; +extern std::unique_ptr gModuleInformation; +extern std::vector gLoadedModules; extern std::unique_ptr gModuleDataInfo; -extern std::map gUsedRPLs; +extern std::map> gUsedRPLs; extern std::vector gAllocatedAddresses; extern WUMSRPLAllocatorAllocFn gCustomRPLAllocatorAllocFn; -extern WUMSRPLAllocatorFreeFn gCustomRPLAllocatorFreeFn; - -#define MEMORY_REGION_START 0x00800000 -#define MEMORY_REGION_SIZE 0x00800000 - -#define RELOCATOR_SIZE 0x50000 // Maximum size of the wumsloader, needs to match the one defined in link.ld -#define ENVIRONMENT_PATH_LENGTH 0x100 // Length of the EnvironmentPath. -#define MEMORY_REGION_USABLE_MEM_REGION_END_LENGTH 0x04 // sizeof(uint32_t) - -#define MEMORY_REGION_ENVIRONMENT_STRING_ADRR (MEMORY_REGION_START + RELOCATOR_SIZE) -#define MEMORY_REGION_USABLE_MEM_REGION_END_VALUE_PTR ((uint32_t *) (MEMORY_REGION_ENVIRONMENT_STRING_ADRR + ENVIRONMENT_PATH_LENGTH)) -#define MEMORY_REGION_USABLE_MEM_REGION_END_VALUE (*MEMORY_REGION_USABLE_MEM_REGION_END_VALUE_PTR) - -// Technically we overwrite the CustomRPXLoader that is still loaded at 0x00800000... -// We can get away with it because the EnvironmentLoader exits instead of returning to the CustomRPXLoader. -#define MEMORY_REGION_USABLE_HEAP_START ((uint32_t) MEMORY_REGION_USABLE_MEM_REGION_END_VALUE_PTR + MEMORY_REGION_USABLE_MEM_REGION_END_LENGTH) -#define MEMORY_REGION_USABLE_HEAP_END MEMORY_REGION_USABLE_MEM_REGION_END_VALUE - -#define ENVRIONMENT_STRING ((char *) MEMORY_REGION_ENVIRONMENT_STRING_ADRR) - -#define WUMS_LOADER_SETUP_MAGIC_WORD 0x13371337 +extern WUMSRPLAllocatorFreeFn gCustomRPLAllocatorFreeFn; \ No newline at end of file diff --git a/wumsloader/src/link.ld b/wumsloader/src/link.ld index ad09c45..8cc3b5c 100644 --- a/wumsloader/src/link.ld +++ b/wumsloader/src/link.ld @@ -92,4 +92,4 @@ SECTIONS { } } -ASSERT((SIZEOF(.text) + SIZEOF(.data) + SIZEOF(.rodata) + SIZEOF(.eh_frame) + SIZEOF(.bss)) < 0x50000, "Memory overlapping with modules."); +ASSERT((SIZEOF(.text) + SIZEOF(.data) + SIZEOF(.rodata) + SIZEOF(.eh_frame) + SIZEOF(.bss)) < 0x67000, "Memory overlapping with modules. Update the link.ld and defines.h with new limits"); diff --git a/wumsloader/src/utils/ElfUtils.cpp b/wumsloader/src/module/ElfUtils.cpp similarity index 82% rename from wumsloader/src/utils/ElfUtils.cpp rename to wumsloader/src/module/ElfUtils.cpp index 9807db4..5f14e44 100644 --- a/wumsloader/src/utils/ElfUtils.cpp +++ b/wumsloader/src/module/ElfUtils.cpp @@ -1,12 +1,16 @@ -#include -#include - #include "ElfUtils.h" #include "utils/logger.h" +#include + +#include +#include + +namespace WUMSLoader::Modules::ElfUtils { + // See https://github.com/decaf-emu/decaf-emu/blob/43366a34e7b55ab9d19b2444aeb0ccd46ac77dea/src/libdecaf/src/cafe/loader/cafe_loader_reloc.cpp#L144 -bool ElfUtils::elfLinkOne(char type, size_t offset, int32_t addend, uint32_t destination, uint32_t symbol_addr, relocation_trampoline_entry_t *trampoline_data, uint32_t trampoline_data_length, - RelocationType reloc_type) { +bool elfLinkOne(char type, size_t offset, int32_t addend, uint32_t destination, uint32_t symbol_addr, + std::span trampolineData, RelocationType reloc_type) { if (type == R_PPC_NONE) { return true; } @@ -75,31 +79,29 @@ bool ElfUtils::elfLinkOne(char type, size_t offset, int32_t addend, uint32_t des // } auto distance = static_cast(value) - static_cast(target); if (distance > 0x1FFFFFC || distance < -0x1FFFFFC) { - if (trampoline_data == nullptr) { + if (trampolineData.empty()) { DEBUG_FUNCTION_LINE_ERR("***24-bit relative branch cannot hit target. Trampoline isn't provided"); DEBUG_FUNCTION_LINE_ERR("***value %08X - target %08X = distance %08X", value, target, distance); return false; } else { relocation_trampoline_entry_t *freeSlot = nullptr; - for (uint32_t i = 0; i < trampoline_data_length; i++) { + for (auto &cur : trampolineData) { // We want to override "old" relocations of imports - // Pending relocations have the status RELOC_TRAMP_IMPORT_IN_PROGRESS. // When all relocations are done successfully, they will be turned into RELOC_TRAMP_IMPORT_DONE // so they can be overridden/updated/reused on the next application launch. // - // Relocations that won't change will have the status RELOC_TRAMP_FIXED and are set to free when the module is unloaded. - if (trampoline_data[i].status == RELOC_TRAMP_FREE || - trampoline_data[i].status == RELOC_TRAMP_IMPORT_DONE) { - freeSlot = &(trampoline_data[i]); + // Relocations that won't change will have the status RELOC_TRAMP_FIXED and are set to free when the plugin is unloaded. + if (cur.status == RELOC_TRAMP_FREE) { + freeSlot = &cur; break; } } if (freeSlot == nullptr) { DEBUG_FUNCTION_LINE_ERR("***24-bit relative branch cannot hit target. Trampoline data list is full"); - DEBUG_FUNCTION_LINE_ERR("***value %08X - target %08X = distance %08X", value, target, target - (uint32_t) & (freeSlot->trampoline[0])); + DEBUG_FUNCTION_LINE_ERR("***value %08X - target %08X = distance %08X", value, target, target - reinterpret_cast(&(freeSlot->trampoline[0]))); return false; } - auto symbolValue = (uint32_t) & (freeSlot->trampoline[0]); + auto symbolValue = reinterpret_cast(&(freeSlot->trampoline[0])); auto newValue = symbolValue + addend; auto newDistance = static_cast(newValue) - static_cast(target); if (newDistance > 0x1FFFFFC || newDistance < -0x1FFFFFC) { @@ -116,8 +118,6 @@ bool ElfUtils::elfLinkOne(char type, size_t offset, int32_t addend, uint32_t des freeSlot->trampoline[1] = 0x616B0000 | (((uint32_t) value) & 0x0000ffff); // ori r11, r11, real_addr@l freeSlot->trampoline[2] = 0x7D6903A6; // mtctr r11 freeSlot->trampoline[3] = 0x4E800420; // bctr - DCFlushRange((void *) freeSlot->trampoline, sizeof(freeSlot->trampoline)); - ICInvalidateRange((unsigned char *) freeSlot->trampoline, sizeof(freeSlot->trampoline)); if (reloc_type == RELOC_TYPE_FIXED) { freeSlot->status = RELOC_TRAMP_FIXED; @@ -144,14 +144,14 @@ bool ElfUtils::elfLinkOne(char type, size_t offset, int32_t addend, uint32_t des return false; } - *(int32_t *) target = (*(int32_t *) target & 0xfc000003) | (distance & 0x03fffffc); + *reinterpret_cast(target) = (*reinterpret_cast(target) & 0xfc000003) | (distance & 0x03fffffc); break; } default: DEBUG_FUNCTION_LINE_ERR("***ERROR: Unsupported Relocation_Add Type (%08X):", type); return false; } - ICInvalidateRange(reinterpret_cast(target), 4); - DCFlushRange(reinterpret_cast(target), 4); + return true; } +} // namespace WUMSLoader::Modules::ElfUtils \ No newline at end of file diff --git a/wumsloader/src/module/ElfUtils.h b/wumsloader/src/module/ElfUtils.h new file mode 100644 index 0000000..7c067e6 --- /dev/null +++ b/wumsloader/src/module/ElfUtils.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include +#include + +namespace WUMSLoader::Modules::ElfUtils { + +static constexpr uint32_t R_PPC_NONE = 0; +static constexpr uint32_t R_PPC_ADDR32 = 1; +static constexpr uint32_t R_PPC_ADDR16_LO = 4; +static constexpr uint32_t R_PPC_ADDR16_HI = 5; +static constexpr uint32_t R_PPC_ADDR16_HA = 6; +static constexpr uint32_t R_PPC_REL24 = 10; +static constexpr uint32_t R_PPC_REL14 = 11; +static constexpr uint32_t R_PPC_DTPMOD32 = 68; +static constexpr uint32_t R_PPC_DTPREL32 = 78; +static constexpr uint32_t R_PPC_EMB_SDA21 = 109; +static constexpr uint32_t R_PPC_EMB_RELSDA = 116; +static constexpr uint32_t R_PPC_DIAB_SDA21_LO = 180; +static constexpr uint32_t R_PPC_DIAB_SDA21_HI = 181; +static constexpr uint32_t R_PPC_DIAB_SDA21_HA = 182; +static constexpr uint32_t R_PPC_DIAB_RELSDA_LO = 183; +static constexpr uint32_t R_PPC_DIAB_RELSDA_HI = 184; +static constexpr uint32_t R_PPC_DIAB_RELSDA_HA = 185; +static constexpr uint32_t R_PPC_GHS_REL16_HA = 251; +static constexpr uint32_t R_PPC_GHS_REL16_HI = 252; +static constexpr uint32_t R_PPC_GHS_REL16_LO = 253; + +// Masks for manipulating Power PC relocation targets +static constexpr uint32_t PPC_WORD32 = 0xFFFFFFFF; +static constexpr uint32_t PPC_WORD30 = 0xFFFFFFFC; +static constexpr uint32_t PPC_LOW24 = 0x03FFFFFC; +static constexpr uint32_t PPC_LOW14 = 0x0020FFFC; +static constexpr uint16_t PPC_HALF16 = 0xFFFF; + +bool elfLinkOne(char type, size_t offset, int32_t addend, uint32_t destination, uint32_t symbol_addr, std::span trampolineData, RelocationType reloc_type); + +} // namespace WUMSLoader::Modules::ElfUtils \ No newline at end of file diff --git a/wumsloader/src/module/ExportData.h b/wumsloader/src/module/ExportData.h index 1ad1d9f..30f5248 100644 --- a/wumsloader/src/module/ExportData.h +++ b/wumsloader/src/module/ExportData.h @@ -1,31 +1,34 @@ #pragma once +#include + #include -#include + +namespace WUMSLoader::Modules { class ExportData { public: - ExportData(wums_entry_type_t type, std::string name, const void *address) { - this->type = type; - this->name = std::move(name); - this->address = address; + ExportData(const wums_entry_type_t type, std::string name, const void *address) : mType(type), + mName(std::move(name)), + mAddress(address) {} + + [[nodiscard]] constexpr wums_entry_type_t getType() const { + return mType; } - [[nodiscard]] wums_entry_type_t getType() const { - return type; - } - - [[nodiscard]] const void *getAddress() const { - return address; + [[nodiscard]] constexpr const void *getAddress() const { + return mAddress; } [[nodiscard]] const std::string &getName() const { - return name; + return mName; } private: - wums_entry_type_t type; - std::string name; - const void *address; -}; \ No newline at end of file + wums_entry_type_t mType; + std::string mName; + const void *mAddress; +}; + +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/FunctionSymbolData.cpp b/wumsloader/src/module/FunctionSymbolData.cpp deleted file mode 100644 index 1692bf0..0000000 --- a/wumsloader/src/module/FunctionSymbolData.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "FunctionSymbolData.h" - -FunctionSymbolData::FunctionSymbolData(const FunctionSymbolData &o2) = default; - -FunctionSymbolData::FunctionSymbolData(std::string_view name, void *address, uint32_t size) : mName(name), - mAddress(address), - mSize(size) { -} - -bool FunctionSymbolData::operator<(const FunctionSymbolData &rhs) const { - return reinterpret_cast(mAddress) < reinterpret_cast(rhs.mAddress); -} - -FunctionSymbolData::~FunctionSymbolData() = default; - -[[nodiscard]] const std::string &FunctionSymbolData::getName() const { - return mName; -} - -[[nodiscard]] void *FunctionSymbolData::getAddress() const { - return mAddress; -} - -[[nodiscard]] uint32_t FunctionSymbolData::getSize() const { - return mSize; -} \ No newline at end of file diff --git a/wumsloader/src/module/FunctionSymbolData.h b/wumsloader/src/module/FunctionSymbolData.h index 6daa3d9..3e72782 100644 --- a/wumsloader/src/module/FunctionSymbolData.h +++ b/wumsloader/src/module/FunctionSymbolData.h @@ -22,25 +22,32 @@ #include +namespace WUMSLoader::Modules { + class FunctionSymbolData { - public: - FunctionSymbolData(const FunctionSymbolData &o2); + FunctionSymbolData(std::string name, void *address, uint32_t size) + : mName(std::move(name)), mAddress(address), mSize(size) {} - FunctionSymbolData(std::string_view name, void *address, uint32_t size); + bool operator<(const FunctionSymbolData &rhs) const { + return mAddress < rhs.mAddress; + } - bool operator<(const FunctionSymbolData &rhs) const; + [[nodiscard]] constexpr void *getAddress() const { + return mAddress; + } - virtual ~FunctionSymbolData(); + [[nodiscard]] constexpr uint32_t getSize() const { + return mSize; + } - [[nodiscard]] const std::string &getName() const; - - [[nodiscard]] void *getAddress() const; - - [[nodiscard]] uint32_t getSize() const; + [[nodiscard]] const std::string &getName() const { + return mName; + } private: std::string mName; void *mAddress; uint32_t mSize; -}; \ No newline at end of file +}; +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/HookData.cpp b/wumsloader/src/module/HookData.cpp deleted file mode 100644 index 8cc83e2..0000000 --- a/wumsloader/src/module/HookData.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "HookData.h" - -HookData::HookData(void *functionPointer, const wums_hook_type_t type) { - this->mFunctionPointer = functionPointer; - this->mType = type; -} - -HookData::~HookData() = default; - -[[nodiscard]] void *HookData::getFunctionPointer() const { - return mFunctionPointer; -} - -[[nodiscard]] wums_hook_type_t HookData::getType() const { - return mType; -} \ No newline at end of file diff --git a/wumsloader/src/module/HookData.h b/wumsloader/src/module/HookData.h index e590ced..4f6495b 100644 --- a/wumsloader/src/module/HookData.h +++ b/wumsloader/src/module/HookData.h @@ -19,18 +19,25 @@ #include +namespace WUMSLoader::Modules { class HookData { public: - HookData(void *functionPointer, wums_hook_type_t type); + constexpr HookData(void *functionPointer, wums_hook_type_t type) { + this->mFunctionPointer = functionPointer; + this->mType = type; + } - ~HookData(); + [[nodiscard]] constexpr void *getFunctionPointer() const { + return mFunctionPointer; + } - [[nodiscard]] void *getFunctionPointer() const; - - [[nodiscard]] wums_hook_type_t getType() const; + [[nodiscard]] constexpr wums_hook_type_t getType() const { + return mType; + } private: void *mFunctionPointer; wums_hook_type_t mType; }; +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/HooksManagement.cpp b/wumsloader/src/module/HooksManagement.cpp new file mode 100644 index 0000000..8b01fda --- /dev/null +++ b/wumsloader/src/module/HooksManagement.cpp @@ -0,0 +1,167 @@ +#include "HooksManagement.h" + +#include "ModuleContainer.h" +#include "ModuleDataPersistence.h" +#include "globals.h" +#include "utils/logger.h" + +#include + + +namespace WUMSLoader::Modules::HooksManagement { + +namespace { + +#ifdef DEBUG +constexpr std::string_view hookNameToStr(wums_hook_type_t hook) { + switch (hook) { + case WUMS_HOOK_INIT_WUT_MALLOC: + return "WUMS_HOOK_INIT_WUT_MALLOC"; + case WUMS_HOOK_FINI_WUT_MALLOC: + return "WUMS_HOOK_FINI_WUT_MALLOC"; + case WUMS_HOOK_INIT_WUT_NEWLIB: + return "WUMS_HOOK_INIT_WUT_NEWLIB"; + case WUMS_HOOK_FINI_WUT_NEWLIB: + return "WUMS_HOOK_FINI_WUT_NEWLIB"; + case WUMS_HOOK_INIT_WUT_STDCPP: + return "WUMS_HOOK_INIT_WUT_STDCPP"; + case WUMS_HOOK_FINI_WUT_STDCPP: + return "WUMS_HOOK_FINI_WUT_STDCPP"; + case WUMS_HOOK_INIT_WUT_DEVOPTAB: + return "WUMS_HOOK_INIT_WUT_DEVOPTAB"; + case WUMS_HOOK_FINI_WUT_DEVOPTAB: + return "WUMS_HOOK_FINI_WUT_DEVOPTAB"; + case WUMS_HOOK_INIT_WUT_SOCKETS: + return "WUMS_HOOK_INIT_WUT_SOCKETS"; + case WUMS_HOOK_FINI_WUT_SOCKETS: + return "WUMS_HOOK_FINI_WUT_SOCKETS"; + case WUMS_HOOK_INIT_WRAPPER: + return "WUMS_HOOK_INIT_WRAPPER"; + case WUMS_HOOK_FINI_WRAPPER: + return "WUMS_HOOK_FINI_WRAPPER"; + case WUMS_HOOK_INIT: + return "WUMS_HOOK_INIT"; + case WUMS_HOOK_APPLICATION_STARTS: + return "WUMS_HOOK_APPLICATION_STARTS"; + case WUMS_HOOK_APPLICATION_ENDS: + return "WUMS_HOOK_APPLICATION_ENDS"; + case WUMS_HOOK_RELOCATIONS_DONE: + return "WUMS_HOOK_RELOCATIONS_DONE"; + case WUMS_HOOK_APPLICATION_REQUESTS_EXIT: + return "WUMS_HOOK_APPLICATION_REQUESTS_EXIT"; + case WUMS_HOOK_DEINIT: + return "WUMS_HOOK_DEINIT"; + case WUMS_HOOK_ALL_APPLICATION_STARTS_DONE: + return "WUMS_HOOK_ALL_APPLICATION_STARTS_DONE"; + case WUMS_HOOK_ALL_APPLICATION_ENDS_DONE: + return "WUMS_HOOK_ALL_APPLICATION_ENDS_DONE"; + case WUMS_HOOK_ALL_APPLICATION_REQUESTS_EXIT_DONE: + return "WUMS_HOOK_ALL_APPLICATION_REQUESTS_EXIT_DONE"; + case WUMS_HOOK_GET_CUSTOM_RPL_ALLOCATOR: + return "WUMS_HOOK_GET_CUSTOM_RPL_ALLOCATOR"; + case WUMS_HOOK_CLEAR_ALLOCATED_RPL_MEMORY: + return "WUMS_HOOK_CLEAR_ALLOCATED_RPL_MEMORY"; + case WUMS_HOOK_INIT_WUT_THREAD: + return "WUMS_HOOK_INIT_WUT_THREAD"; + } + return ""; +} +#endif + +} // namespace + + +void CallHook(const std::vector &modules, wums_hook_type_t type, bool condition) { + if (condition) { + CallHook(modules, type); + } +} + +void CallHook(const std::vector &modules, wums_hook_type_t type) { + DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s [%d] for all modules", hookNameToStr(type).data(), type); + for (auto &curModule : modules) { + CallHook(curModule, type); + } +} + +void CallHook(const ModuleContainer &module, wums_hook_type_t type, bool condition) { + if (condition) { + CallHook(module, type); + } +} + +void CallHook(const ModuleContainer &module, wums_hook_type_t type) { + if (!module.isLinkedAndLoaded()) { + return; + } + PROFILE_FUNCTION(); +#ifdef DEBUG + bool foundHook = false; +#endif + for (auto &curHook : module.getLinkInformation().getHookDataList()) { + auto func_ptr = (uint32_t) curHook.getFunctionPointer(); + if (func_ptr == 0) { + DEBUG_FUNCTION_LINE_ERR("Module %s: hook ptr was NULL", module.getMetaInformation().getExportName().c_str()); + break; + } + + if (type == curHook.getType()) { +#ifdef DEBUG + foundHook = true; +#endif + + if ((type == WUMS_HOOK_APPLICATION_STARTS || + type == WUMS_HOOK_APPLICATION_ENDS || + type == WUMS_HOOK_INIT_WUT_MALLOC || + type == WUMS_HOOK_FINI_WUT_MALLOC || + type == WUMS_HOOK_INIT_WUT_NEWLIB || + type == WUMS_HOOK_FINI_WUT_NEWLIB || + type == WUMS_HOOK_INIT_WUT_STDCPP || + type == WUMS_HOOK_FINI_WUT_STDCPP || + type == WUMS_HOOK_INIT_WUT_DEVOPTAB || + type == WUMS_HOOK_FINI_WUT_DEVOPTAB || + type == WUMS_HOOK_INIT_WUT_SOCKETS || + type == WUMS_HOOK_FINI_WUT_SOCKETS || + type == WUMS_HOOK_INIT_WRAPPER || + type == WUMS_HOOK_FINI_WRAPPER || + type == WUMS_HOOK_DEINIT || + type == WUMS_HOOK_ALL_APPLICATION_STARTS_DONE || + type == WUMS_HOOK_ALL_APPLICATION_ENDS_DONE || + type == WUMS_HOOK_CLEAR_ALLOCATED_RPL_MEMORY || + type == WUMS_HOOK_APPLICATION_REQUESTS_EXIT || + type == WUMS_HOOK_ALL_APPLICATION_REQUESTS_EXIT_DONE || + type == WUMS_HOOK_INIT_WUT_THREAD)) { + DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s [%d] %d for %s: %p", hookNameToStr(type).data(), type, curHook.getType(), module.getMetaInformation().getExportName().c_str(), curHook.getFunctionPointer()); + ((void (*)())((uint32_t *) func_ptr))(); + break; + } else if (type == WUMS_HOOK_INIT || + type == WUMS_HOOK_RELOCATIONS_DONE) { + DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s [%d] %d for %s: %p", hookNameToStr(type).data(), type, curHook.getType(), module.getMetaInformation().getExportName().c_str(), curHook.getFunctionPointer()); + wums_app_init_args_t args; + args.module_information = gModuleInformation->getModuleInformation(); + ((void (*)(wums_app_init_args_t *))((uint32_t *) func_ptr))(&args); + } else if (type == WUMS_HOOK_GET_CUSTOM_RPL_ALLOCATOR) { + DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s [%d] %d for %s: %p", hookNameToStr(type).data(), type, curHook.getType(), module.getMetaInformation().getExportName().c_str(), curHook.getFunctionPointer()); + + const auto [allocFn, freeFn] = reinterpret_cast(reinterpret_cast(func_ptr))(); + gCustomRPLAllocatorAllocFn = allocFn; + gCustomRPLAllocatorFreeFn = freeFn; + + } else { + DEBUG_FUNCTION_LINE_ERR("#########################################"); + DEBUG_FUNCTION_LINE_ERR("#########HOOK NOT IMPLEMENTED %d#########", type); + DEBUG_FUNCTION_LINE_ERR("#########################################"); + } + break; + } + } +#ifdef DEBUG + if (foundHook && !MEMCheckExpHeap(MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM2), MEM_EXP_HEAP_CHECK_FLAGS_LOG_ERRORS)) { + DEBUG_FUNCTION_LINE_ERR("MEM2 default heap is corrupted while calling hook %s for module %s", hookNameToStr(type).data(), module.getMetaInformation().getExportName().c_str()); + OSFatal("WUMSLoader: MEM2 default heap is corrupted. \n Please restart the console."); + } +#endif +} + + +} // namespace WUMSLoader::Modules::HooksManagement \ No newline at end of file diff --git a/wumsloader/src/module/HooksManagement.h b/wumsloader/src/module/HooksManagement.h new file mode 100644 index 0000000..5833cc4 --- /dev/null +++ b/wumsloader/src/module/HooksManagement.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include + +namespace WUMSLoader::Modules { + +class ModuleContainer; + +namespace HooksManagement { + +void CallHook(const std::vector &modules, wums_hook_type_t type, bool condition); + +void CallHook(const std::vector &modules, wums_hook_type_t type); + +void CallHook(const ModuleContainer &module, wums_hook_type_t type, bool condition); + +void CallHook(const ModuleContainer &module, wums_hook_type_t type); + +} // namespace HooksManagement +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ImportRPLInformation.cpp b/wumsloader/src/module/ImportRPLInformation.cpp index b22fb38..db38e2f 100644 --- a/wumsloader/src/module/ImportRPLInformation.cpp +++ b/wumsloader/src/module/ImportRPLInformation.cpp @@ -1,7 +1,7 @@ #include "ImportRPLInformation.h" #include - -ImportRPLInformation::ImportRPLInformation(std::string_view name) { +namespace WUMSLoader::Modules { +ImportRPLInformation::ImportRPLInformation(const std::string_view name) { this->mName = name; } @@ -11,10 +11,18 @@ ImportRPLInformation::~ImportRPLInformation() = default; return mName; } -[[nodiscard]] std::string ImportRPLInformation::getRPLName() const { - return mName.substr(strlen(".dimport_")); +[[nodiscard]] std::string_view ImportRPLInformation::getRPLName() const { + static constexpr std::string_view prefix = "._import_"; + + if (mName.length() > prefix.length()) { + return std::string_view(mName).substr(prefix.length()); + } + + return mName; } [[nodiscard]] bool ImportRPLInformation::isData() const { - return mName.starts_with(".dimport_"); -} \ No newline at end of file + static constexpr std::string_view prefix = ".dimport_"; + return mName.starts_with(prefix); +} +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ImportRPLInformation.h b/wumsloader/src/module/ImportRPLInformation.h index 14afbf7..95fdfbe 100644 --- a/wumsloader/src/module/ImportRPLInformation.h +++ b/wumsloader/src/module/ImportRPLInformation.h @@ -20,6 +20,7 @@ #include #include +namespace WUMSLoader::Modules { class ImportRPLInformation { public: @@ -29,10 +30,11 @@ public: [[nodiscard]] const std::string &getName() const; - [[nodiscard]] std::string getRPLName() const; + [[nodiscard]] std::string_view getRPLName() const; [[nodiscard]] bool isData() const; private: std::string mName; }; +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ModuleAllocator.cpp b/wumsloader/src/module/ModuleAllocator.cpp new file mode 100644 index 0000000..0b3bd81 --- /dev/null +++ b/wumsloader/src/module/ModuleAllocator.cpp @@ -0,0 +1,60 @@ +#include "ModuleAllocator.h" + +#include "module/ModuleContainer.h" +#include "module/ModuleDataPersistence.h" +#include "utils/logger.h" + +#include +#include +#include + +namespace WUMSLoader::Modules { + +bool ModuleAllocator::isValid() const { + return alloc != nullptr && free != nullptr; +} + +ModuleAllocator ModuleAllocator::FromExpHeap(MEMHeapHandle handle) { + return ModuleAllocator{ + [handle](size_t size, int32_t align) { return MEMAllocFromExpHeapEx(handle, size, align); }, + [handle](void *ptr) { MEMFreeToExpHeap(handle, ptr); }}; +} + +std::optional ModuleAllocator::FromMappedMemory(const std::vector &modules) { + MEMAllocFromDefaultHeapExFn *allocExPtr = nullptr; + MEMFreeToDefaultHeapFn *freePtr = nullptr; + for (const auto &module : modules) { + if (!module.isLinkedAndLoaded()) { + continue; + } + if (module.getMetaInformation().getExportName() != "homebrew_memorymapping") { + continue; + } + + for (const auto &exportData : module.getLinkInformation().getExportDataList()) { + if (exportData.getType() != WUMS_DATA_EXPORT) { + continue; + } + if (exportData.getName() == "MEMAllocFromMappedMemoryEx") { + allocExPtr = (MEMAllocFromDefaultHeapExFn *) exportData.getAddress(); + } else if (exportData.getName() == "MEMFreeToMappedMemory") { + freePtr = (MEMFreeToDefaultHeapFn *) exportData.getAddress(); + } + } + break; + } + if (allocExPtr && freePtr) { + const auto allocExFunc = *allocExPtr; + const auto freeFunc = *freePtr; + return ModuleAllocator{ + [allocExFunc](size_t size, int32_t align) -> void * { + return allocExFunc(size, align); + }, + [freeFunc](void *ptr) { + freeFunc(ptr); + }}; + } + return std::nullopt; +} + +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ModuleAllocator.h b/wumsloader/src/module/ModuleAllocator.h new file mode 100644 index 0000000..19fe1bd --- /dev/null +++ b/wumsloader/src/module/ModuleAllocator.h @@ -0,0 +1,20 @@ +#pragma once + +#include "utils/HeapMemoryFixedSize.h" + +#include + +#include + +namespace WUMSLoader::Modules { +class ModuleContainer; +struct ModuleAllocator { + HeapMemoryFixedSizePool::AllocFunc alloc; + HeapMemoryFixedSizePool::FreeFunc free; + + [[nodiscard]] bool isValid() const; + + static ModuleAllocator FromExpHeap(MEMHeapHandle handle); + static std::optional FromMappedMemory(const std::vector &modules); +}; +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ModuleContainer.cpp b/wumsloader/src/module/ModuleContainer.cpp new file mode 100644 index 0000000..45847ec --- /dev/null +++ b/wumsloader/src/module/ModuleContainer.cpp @@ -0,0 +1,35 @@ +#include "ModuleContainer.h" + +namespace WUMSLoader::Modules { + +ModuleContainer::ModuleContainer(ModuleMetaInformation &&metaInformation, ModuleLinkInformation &&linkInformation) + : mEntrypoint(nullptr), + mMetaInformation(std::move(metaInformation)), + mLinkInformation(std::move(linkInformation)) { + // Abuse this as a stable handle that references itself and survives std::move + *mHandle = reinterpret_cast(mHandle.get()); +} + +const ModuleMetaInformation &ModuleContainer::getMetaInformation() const { + return this->mMetaInformation; +} + +const ModuleLinkInformation &ModuleContainer::getLinkInformation() const { + return this->mLinkInformation; +} + +ModuleLinkInformation &ModuleContainer::getLinkInformation() { + return this->mLinkInformation; +} + +uint32_t ModuleContainer::getHandle() const { + return *mHandle; +} +bool ModuleContainer::isLinkedAndLoaded() const { + return mLinkInformation.hasValidData(); +} +void ModuleContainer::setLinkInformation(ModuleLinkInformation &&linkInformation) { + this->mLinkInformation = std::move(linkInformation); +} + +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ModuleContainer.h b/wumsloader/src/module/ModuleContainer.h new file mode 100644 index 0000000..0bf8661 --- /dev/null +++ b/wumsloader/src/module/ModuleContainer.h @@ -0,0 +1,41 @@ +#pragma once + +#include "ModuleLinkInformation.h" +#include "ModuleMetaInformation.h" + +#include +#include + +namespace WUMSLoader::Modules { + +class ModuleContainer { +public: + ModuleContainer(ModuleMetaInformation &&metaInformation, ModuleLinkInformation &&linkInformation); + + ModuleContainer(const ModuleContainer &) = delete; + + ModuleContainer(ModuleContainer &&src) = default; + + ModuleContainer &operator=(ModuleContainer &&src) = default; + + [[nodiscard]] const ModuleMetaInformation &getMetaInformation() const; + + [[nodiscard]] const ModuleLinkInformation &getLinkInformation() const; + + [[nodiscard]] ModuleLinkInformation &getLinkInformation(); + + [[nodiscard]] uint32_t getHandle() const; + + [[nodiscard]] bool isLinkedAndLoaded() const; + + void setLinkInformation(ModuleLinkInformation &&linkInformation); + +private: + void *mEntrypoint; + ModuleMetaInformation mMetaInformation; + ModuleLinkInformation mLinkInformation; + + std::unique_ptr mHandle = std::make_unique(); +}; + +} // namespace WUMSLoader::Modules diff --git a/wumsloader/src/module/ModuleData.h b/wumsloader/src/module/ModuleData.h deleted file mode 100644 index 7f02b90..0000000 --- a/wumsloader/src/module/ModuleData.h +++ /dev/null @@ -1,197 +0,0 @@ -/**************************************************************************** - * Copyright (C) 2018-2021 Maschell - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - ****************************************************************************/ - -#pragma once - -#include "ExportData.h" -#include "FunctionSymbolData.h" -#include "HookData.h" -#include "RelocationData.h" -#include "SectionInfo.h" - -#include -#include -#include -#include -#include - -struct FunctionSymbolDataComparator { - bool operator()(const std::shared_ptr &lhs, - const std::shared_ptr &rhs) const { - return (uint32_t) lhs->getAddress() < (uint32_t) rhs->getAddress(); - } -}; - -class ModuleData { -public: - ModuleData() = default; - - ~ModuleData() = default; - - void setBSSLocation(uint32_t addr, uint32_t size) { - this->bssAddr = addr; - this->bssSize = size; - } - - void setSBSSLocation(uint32_t addr, uint32_t size) { - this->sbssAddr = addr; - this->sbssSize = size; - } - - void setEntrypoint(uint32_t addr) { - this->entrypoint = addr; - } - - void addRelocationData(std::unique_ptr relocation_data) { - addDependency(relocation_data->getImportRPLInformation().getRPLName()); - relocation_data_list.push_back(std::move(relocation_data)); - } - - [[nodiscard]] const std::vector> &getRelocationDataList() const { - return relocation_data_list; - } - - void addExportData(std::unique_ptr data) { - export_data_list.push_back(std::move(data)); - } - - [[nodiscard]] const std::vector> &getExportDataList() const { - return export_data_list; - } - - void addHookData(std::unique_ptr data) { - hook_data_list.push_back(std::move(data)); - } - - [[nodiscard]] const std::vector> &getHookDataList() const { - return hook_data_list; - } - - void addDependency(std::string module_name) { - dependency_list.insert(std::move(module_name)); - } - - [[nodiscard]] const std::set &getDependencies() const { - return dependency_list; - } - - void addSectionInfo(std::shared_ptr sectionInfo) { - section_info_list[sectionInfo->getName()] = std::move(sectionInfo); - } - - void addFunctionSymbolData(std::shared_ptr symbol_data) { - symbol_data_list.insert(std::move(symbol_data)); - } - - [[nodiscard]] const std::set, FunctionSymbolDataComparator> &getFunctionSymbolDataList() const { - return symbol_data_list; - } - - [[nodiscard]] const std::map> &getSectionInfoList() const { - return section_info_list; - } - - [[nodiscard]] std::optional> getSectionInfo(const std::string §ionName) const { - if (getSectionInfoList().count(sectionName) > 0) { - return section_info_list.at(sectionName); - } - return {}; - } - - [[nodiscard]] uint32_t getBSSAddress() const { - return bssAddr; - } - - [[nodiscard]] uint32_t getBSSSize() const { - return bssSize; - } - - [[nodiscard]] uint32_t getSBSSAddress() const { - return sbssAddr; - } - - [[nodiscard]] uint32_t getSBSSSize() const { - return sbssSize; - } - - [[nodiscard]] uint32_t getStartAddress() const { - return reinterpret_cast(dataPtr.get()); - } - - [[nodiscard]] uint32_t getEndAddress() const { - return reinterpret_cast(dataPtr.get()) + this->totalSize; - } - - [[nodiscard]] uint32_t getEntrypoint() const { - return entrypoint; - } - - void setExportName(std::string name) { - this->export_name = std::move(name); - } - - [[nodiscard]] const std::string &getExportName() const { - return this->export_name; - } - - [[nodiscard]] bool isInitBeforeRelocationDoneHook() const { - return this->initBeforeRelocationDoneHook; - } - - void setInitBeforeRelocationDoneHook(bool value) { - this->initBeforeRelocationDoneHook = value; - } - - [[nodiscard]] bool isSkipInitFini() const { - return this->skipInitFini; - } - - void setSkipInitFini(bool value) { - this->skipInitFini = value; - } - - void setDataPtr(std::unique_ptr ptr, uint32_t size) { - this->dataPtr = std::move(ptr); - this->totalSize = size; - } - - std::unique_ptr hookDataStruct; - std::unique_ptr exportDataStruct; - std::unique_ptr functionSymbolDataStruct; - std::unique_ptr rplDataStruct; - std::unique_ptr relocationDataStruct; - -private: - std::vector> relocation_data_list; - std::vector> export_data_list; - std::vector> hook_data_list; - std::set dependency_list; - std::set, FunctionSymbolDataComparator> symbol_data_list; - std::map> section_info_list; - - std::unique_ptr dataPtr; - std::string export_name; - - uint32_t bssAddr = 0; - uint32_t bssSize = 0; - uint32_t sbssAddr = 0; - uint32_t sbssSize = 0; - uint32_t entrypoint = 0; - uint32_t totalSize = 0; - bool initBeforeRelocationDoneHook = false; - bool skipInitFini = false; -}; diff --git a/wumsloader/src/module/ModuleDataFactory.cpp b/wumsloader/src/module/ModuleDataFactory.cpp deleted file mode 100644 index 6c17fc4..0000000 --- a/wumsloader/src/module/ModuleDataFactory.cpp +++ /dev/null @@ -1,498 +0,0 @@ -/**************************************************************************** - * Copyright (C) 2022 Maschell - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - ****************************************************************************/ - -#include "ModuleDataFactory.h" -#include "HookData.h" -#include "fs/FileUtils.h" -#include "utils/ElfUtils.h" -#include "utils/OnLeavingScope.h" -#include "utils/logger.h" -#include "utils/utils.h" -#include "utils/wiiu_zlib.hpp" -#include -#include -#include -#include -#include -#include - -using namespace ELFIO; - -std::optional> ModuleDataFactory::load(const std::string &path) { - elfio reader(new wiiu_zlib); - auto moduleData = make_shared_nothrow(); - if (!moduleData) { - DEBUG_FUNCTION_LINE_ERR("Failed to alloc module data"); - return {}; - } - - uint8_t *buffer = nullptr; - uint32_t fsize = 0; - if (LoadFileToMem(path, &buffer, &fsize) < 0) { - DEBUG_FUNCTION_LINE_ERR("Failed to load file"); - return {}; - } - - auto cleanupBuffer = onLeavingScope([buffer, fsize]() { - // Some games (e.g. Minecraft) expect the default heap to be empty. - // Make sure to clean up the memory after using it - memset(buffer, 0, ROUNDUP(fsize, 0x40)); - MEMFreeToDefaultHeap(buffer); - }); - - // Load ELF data - if (!reader.load(reinterpret_cast(buffer), fsize)) { - DEBUG_FUNCTION_LINE_ERR("Can't find or process %s", path.c_str()); - return {}; - } - uint32_t sec_num = reader.sections.size(); - - - bool checkedVersion = false; - for (uint32_t i = 0; i < sec_num; ++i) { - auto *psec = reader.sections[i]; - if (psec->get_name() == ".wums.meta") { - char *curEntry = (char *) psec->get_data(); - while ((uint32_t) curEntry < (uint32_t) psec->get_data() + psec->get_size()) { - if (*curEntry == '\0') { - curEntry++; - continue; - } - - auto firstFound = std::string(curEntry).find_first_of('='); - if (firstFound != std::string::npos) { - curEntry[firstFound] = '\0'; - std::string key(curEntry); - std::string value(curEntry + firstFound + 1); - - if (key == "export_name") { - moduleData->setExportName(value); - } else if (key == "skipInitFini") { - if (value == "true") { - DEBUG_FUNCTION_LINE("skipInitFini = %s", value.c_str()); - moduleData->setSkipInitFini(true); - } else { - moduleData->setSkipInitFini(false); - } - } else if (key == "initBeforeRelocationDoneHook") { - if (value == "true") { - DEBUG_FUNCTION_LINE("initBeforeRelocationDoneHook = %s", value.c_str()); - moduleData->setInitBeforeRelocationDoneHook(true); - } else { - moduleData->setInitBeforeRelocationDoneHook(false); - } - } else if (key == "wums" || key == "wum") { - checkedVersion = true; - if (value != "0.3.1" && value != "0.3.2" && value != "0.3.3" && value != "0.3.4" && value != "0.3.5") { - DEBUG_FUNCTION_LINE_WARN("Ignoring module - Unsupported WUMS version: %s.", value.c_str()); - return std::nullopt; - } - } - } - curEntry += strlen(curEntry) + 1; - } - break; - } - } - if (!checkedVersion) { - DEBUG_FUNCTION_LINE_ERR("Failed to check version. Ignoring module"); - return {}; - } - - auto destinations = make_unique_nothrow(sec_num); - if (!destinations) { - DEBUG_FUNCTION_LINE_ERR("Failed alloc memory for destinations array"); - return {}; - } - - uint32_t totalSize = 0; - - uint32_t text_size = 0; - uint32_t data_size = 0; - - for (uint32_t i = 0; i < sec_num; ++i) { - auto *psec = reader.sections[i]; - if (psec->get_type() == 0x80000002) { - continue; - } - - if ((psec->get_type() == SHT_PROGBITS || psec->get_type() == SHT_NOBITS) && (psec->get_flags() & SHF_ALLOC)) { - uint32_t sectionSize = psec->get_size(); - auto address = (uint32_t) psec->get_address(); - if ((address >= 0x02000000) && address < 0x10000000) { - text_size += sectionSize; - } else if ((address >= 0x10000000) && address < 0xC0000000) { - data_size += sectionSize; - } - if (psec->get_name().starts_with(".wums.")) { - data_size += sectionSize; - } - } - } - - auto data = make_unique_nothrow(text_size + data_size); - if (!data) { - DEBUG_FUNCTION_LINE_ERR("Failed to alloc memory for the .text section (%d bytes)", text_size); - return {}; - } - - DEBUG_FUNCTION_LINE("Allocated %d kb", (text_size + data_size) / 1024); - - void *text_data = data.get(); - void *data_data = (void *) ((uint32_t) data.get() + text_size); - auto baseOffset = (uint32_t) data.get(); - - uint32_t entrypoint = (uint32_t) text_data + (uint32_t) reader.get_entry() - 0x02000000; - - for (uint32_t i = 0; i < sec_num; ++i) { - section *psec = reader.sections[i]; - if (psec->get_type() == 0x80000002 || psec->get_name() == ".wut_load_bounds") { - continue; - } - - if ((psec->get_type() == SHT_PROGBITS || psec->get_type() == SHT_NOBITS) && (psec->get_flags() & SHF_ALLOC)) { - uint32_t sectionSize = psec->get_size(); - - totalSize += sectionSize; - - auto address = (uint32_t) psec->get_address(); - - destinations[psec->get_index()] = (uint8_t *) baseOffset; - - uint32_t destination = baseOffset + address; - if ((address >= 0x02000000) && address < 0x10000000) { - destination -= 0x02000000; - destinations[psec->get_index()] -= 0x02000000; - baseOffset += sectionSize; - } else if ((address >= 0x10000000) && address < 0xC0000000) { - destination -= 0x10000000; - destinations[psec->get_index()] -= 0x10000000; - } else if (address >= 0xC0000000) { - DEBUG_FUNCTION_LINE_ERR("Loading section from 0xC0000000 is NOT supported"); - return std::nullopt; - } else { - DEBUG_FUNCTION_LINE_ERR("Unhandled case"); - return std::nullopt; - } - - const char *p = reader.sections[i]->get_data(); - - if (destination + sectionSize > (uint32_t) data.get() + text_size + data_size) { - DEBUG_FUNCTION_LINE_ERR("Tried to overflow buffer. %08X > %08X", destination + sectionSize, (uint32_t) data.get() + text_size + data_size); - OSFatal("WUMSLoader: Tried to overflow buffer"); - } - - if (psec->get_type() == SHT_NOBITS) { - DEBUG_FUNCTION_LINE("memset section %s %08X to 0 (%d bytes)", psec->get_name().c_str(), destination, sectionSize); - memset((void *) destination, 0, sectionSize); - } else if (psec->get_type() == SHT_PROGBITS) { - DEBUG_FUNCTION_LINE("Copy section %s %p -> %08X (%d bytes)", psec->get_name().c_str(), p, destination, sectionSize); - memcpy((void *) destination, p, sectionSize); - } - - if (psec->get_name() == ".bss") { - moduleData->setBSSLocation(destination, sectionSize); - memset(reinterpret_cast(destination), 0, sectionSize); - - } else if (psec->get_name() == ".sbss") { - moduleData->setSBSSLocation(destination, sectionSize); - memset(reinterpret_cast(destination), 0, sectionSize); - } - auto sectionInfo = make_shared_nothrow(psec->get_name(), destination, sectionSize); - if (!sectionInfo) { - DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for section info"); - return {}; - } - moduleData->addSectionInfo(sectionInfo); - DEBUG_FUNCTION_LINE("Saved %s section info. Location: %08X size: %08X", psec->get_name().c_str(), destination, sectionSize); - - DCFlushRange((void *) destination, sectionSize); - ICInvalidateRange((void *) destination, sectionSize); - } - } - - for (uint32_t i = 0; i < sec_num; ++i) { - section *psec = reader.sections[i]; - if ((psec->get_type() == SHT_PROGBITS || psec->get_type() == SHT_NOBITS) && (psec->get_flags() & SHF_ALLOC)) { - DEBUG_FUNCTION_LINE("Linking (%d)... %s", i, psec->get_name().c_str()); - if (!linkSection(reader, psec->get_index(), (uint32_t) destinations[psec->get_index()], (uint32_t) text_data, (uint32_t) data_data, nullptr, 0)) { - DEBUG_FUNCTION_LINE_ERR("elfLink failed"); - return std::nullopt; - } - } - } - getImportRelocationData(moduleData, reader, destinations.get()); - - auto secInfo = moduleData->getSectionInfo(".wums.exports"); - if (secInfo && secInfo.value()->getSize() > 0) { - size_t entries_count = secInfo.value()->getSize() / sizeof(wums_entry_t); - auto *entries = (wums_entry_t *) secInfo.value()->getAddress(); - if (entries != nullptr) { - for (size_t j = 0; j < entries_count; j++) { - wums_entry_t *exp = &entries[j]; - DEBUG_FUNCTION_LINE("Saving export of type %d, name %s, target: %p", exp->type, exp->name, exp->address); - auto exportData = make_unique_nothrow(exp->type, exp->name, exp->address); - if (!exportData) { - DEBUG_FUNCTION_LINE_ERR("Failed to alloc ExportData"); - return {}; - } - moduleData->addExportData(std::move(exportData)); - } - } - } - - secInfo = moduleData->getSectionInfo(".wums.dependencies"); - if (secInfo && secInfo.value()->getSize() > 0) { - if (secInfo.value()->getAddress() != 0) { - char *curEntry = (char *) secInfo.value()->getAddress(); - while ((uint32_t) curEntry < (uint32_t) secInfo.value()->getAddress() + secInfo.value()->getSize()) { - if (*curEntry == '\0') { - curEntry++; - continue; - } - moduleData->addDependency(curEntry); - curEntry += strlen(curEntry) + 1; - } - } - } - - secInfo = moduleData->getSectionInfo(".wums.hooks"); - if (secInfo && secInfo.value()->getSize() > 0) { - size_t entries_count = secInfo.value()->getSize() / sizeof(wums_hook_t); - auto *hooks = (wums_hook_t *) secInfo.value()->getAddress(); - if (hooks != nullptr) { - for (size_t j = 0; j < entries_count; j++) { - wums_hook_t *hook = &hooks[j]; - DEBUG_FUNCTION_LINE("Saving hook of type %08X, target: %p", hook->type, hook->target); - auto hookData = make_unique_nothrow(const_cast(hook->target), hook->type); - if (!hookData) { - DEBUG_FUNCTION_LINE_ERR("Failed to alloc HookData"); - return {}; - } - moduleData->addHookData(std::move(hookData)); - } - } - } - - // Get the symbol for functions. - Elf_Half n = reader.sections.size(); - for (Elf_Half i = 0; i < n; ++i) { - section *sec = reader.sections[i]; - if (SHT_SYMTAB == sec->get_type()) { - symbol_section_accessor symbols(reader, sec); - auto sym_no = (uint32_t) symbols.get_symbols_num(); - if (sym_no > 0) { - for (Elf_Half j = 0; j < sym_no; ++j) { - std::string name; - Elf64_Addr value = 0; - Elf_Xword size = 0; - unsigned char bind = 0; - unsigned char type = 0; - Elf_Half section = 0; - unsigned char other = 0; - if (symbols.get_symbol(j, name, value, size, bind, type, section, other)) { - if (type == STT_FUNC) { // We only care about functions. - auto sectionVal = reader.sections[section]; - auto offsetVal = value - sectionVal->get_address(); - auto sectionOpt = moduleData->getSectionInfo(sectionVal->get_name()); - if (!sectionOpt.has_value()) { - continue; - } - auto finalAddress = offsetVal + sectionOpt.value()->getAddress(); - auto functionSymbolData = make_shared_nothrow(name, (void *) finalAddress, (uint32_t) size); - if (!functionSymbolData) { - DEBUG_FUNCTION_LINE_ERR("Failed to alloc FunctionSymbolData"); - return std::nullopt; - } else { - moduleData->addFunctionSymbolData(std::move(functionSymbolData)); - } - } - } - } - break; - } - } - } - - DCFlushRange((void *) text_data, text_size); - ICInvalidateRange((void *) text_data, text_size); - DCFlushRange((void *) data_data, data_size); - ICInvalidateRange((void *) data_data, data_size); - - if (totalSize > text_size + data_size) { - DEBUG_FUNCTION_LINE_ERR("We didn't allocate enough memory!!"); - return std::nullopt; - } - - DEBUG_FUNCTION_LINE("Saved entrypoint as %08X", entrypoint); - DEBUG_FUNCTION_LINE("Saved startAddress as %08X", (uint32_t) data.get()); - DEBUG_FUNCTION_LINE("Saved endAddress as %08X", (uint32_t) data.get() + totalSize); - - moduleData->setEntrypoint(entrypoint); - moduleData->setDataPtr(std::move(data), totalSize); - - DEBUG_FUNCTION_LINE("Loaded %s size: %d kilobytes", path.c_str(), totalSize / 1024); - - return moduleData; -} - -bool ModuleDataFactory::getImportRelocationData(std::shared_ptr &moduleData, ELFIO::elfio &reader, uint8_t **destinations) { - std::map> infoMap; - - uint32_t sec_num = reader.sections.size(); - - for (uint32_t i = 0; i < sec_num; ++i) { - auto *psec = reader.sections[i]; - if (psec->get_type() == 0x80000002) { - auto info = make_shared_nothrow(psec->get_name()); - if (!info) { - DEBUG_FUNCTION_LINE_ERR("Failed too allocate ImportRPLInformation"); - return false; - } - infoMap[i] = std::move(info); - } - } - - for (uint32_t i = 0; i < sec_num; ++i) { - section *psec = reader.sections[i]; - if (psec->get_type() == SHT_RELA || psec->get_type() == SHT_REL) { - relocation_section_accessor rel(reader, psec); - for (uint32_t j = 0; j < (uint32_t) rel.get_entries_num(); ++j) { - Elf_Word symbol = 0; - Elf64_Addr offset; - Elf_Word type; - Elf_Sxword addend; - std::string sym_name; - Elf64_Addr sym_value; - - if (!rel.get_entry(j, offset, symbol, type, addend)) { - DEBUG_FUNCTION_LINE_ERR("Failed to get relocation"); - return false; - } - symbol_section_accessor symbols(reader, reader.sections[(Elf_Half) psec->get_link()]); - - // Find the symbol - Elf_Xword size; - unsigned char bind; - unsigned char symbolType; - Elf_Half sym_section_index; - unsigned char other; - - if (!symbols.get_symbol(symbol, sym_name, sym_value, size, - bind, symbolType, sym_section_index, other)) { - DEBUG_FUNCTION_LINE_ERR("Failed to get symbol"); - return false; - } - - auto adjusted_sym_value = (uint32_t) sym_value; - if (adjusted_sym_value < 0xC0000000) { - continue; - } - - uint32_t section_index = psec->get_info(); - if (!infoMap.contains(sym_section_index)) { - DEBUG_FUNCTION_LINE_ERR("Relocation is referencing a unknown section. %d destination: %p sym_name %s", section_index, destinations[section_index], sym_name.c_str()); - OSFatal("Relocation is referencing a unknown section."); - } - - auto relocationData = make_unique_nothrow(type, - offset - 0x02000000, - addend, - (void *) (destinations[section_index] + 0x02000000), - sym_name, - infoMap[sym_section_index]); - - if (!relocationData) { - DEBUG_FUNCTION_LINE_ERR("Failed to alloc relocation data"); - return false; - } - - moduleData->addRelocationData(std::move(relocationData)); - } - } - } - return true; -} - -bool ModuleDataFactory::linkSection(elfio &reader, uint32_t section_index, uint32_t destination, uint32_t base_text, uint32_t base_data, relocation_trampoline_entry_t *trampoline_data, - uint32_t trampoline_data_length) { - uint32_t sec_num = reader.sections.size(); - - for (uint32_t i = 0; i < sec_num; ++i) { - section *psec = reader.sections[i]; - if (psec->get_info() == section_index) { - DEBUG_FUNCTION_LINE_VERBOSE("Found relocation section %s", psec->get_name().c_str()); - relocation_section_accessor rel(reader, psec); - for (uint32_t j = 0; j < (uint32_t) rel.get_entries_num(); ++j) { - Elf_Word symbol = 0; - Elf64_Addr offset; - Elf_Word type; - Elf_Sxword addend; - std::string sym_name; - Elf64_Addr sym_value; - - if (!rel.get_entry(j, offset, symbol, type, addend)) { - DEBUG_FUNCTION_LINE_ERR("Failed to get relocation"); - return false; - } - symbol_section_accessor symbols(reader, reader.sections[(Elf_Half) psec->get_link()]); - - // Find the symbol - Elf_Xword size; - unsigned char bind; - unsigned char symbolType; - Elf_Half sym_section_index; - unsigned char other; - - if (!symbols.get_symbol(symbol, sym_name, sym_value, size, - bind, symbolType, sym_section_index, other)) { - DEBUG_FUNCTION_LINE_ERR("Failed to get symbol"); - return false; - } - - auto adjusted_sym_value = (uint32_t) sym_value; - if ((adjusted_sym_value >= 0x02000000) && adjusted_sym_value < 0x10000000) { - adjusted_sym_value -= 0x02000000; - adjusted_sym_value += base_text; - } else if ((adjusted_sym_value >= 0x10000000) && adjusted_sym_value < 0xC0000000) { - adjusted_sym_value -= 0x10000000; - adjusted_sym_value += base_data; - } else if (adjusted_sym_value >= 0xC0000000) { - // Skip imports - continue; - } else if (adjusted_sym_value == 0x0) { - // - } else { - DEBUG_FUNCTION_LINE_ERR("Unhandled case %08X", adjusted_sym_value); - return false; - } - - if (sym_section_index == SHN_ABS) { - // - } else if (sym_section_index > SHN_LORESERVE) { - DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED: %04X", sym_section_index); - return false; - } - if (!ElfUtils::elfLinkOne(type, offset, addend, destination, adjusted_sym_value, trampoline_data, trampoline_data_length, RELOC_TYPE_FIXED)) { - DEBUG_FUNCTION_LINE_ERR("Link failed"); - return false; - } - } - } - } - return true; -} diff --git a/wumsloader/src/module/ModuleDataPersistence.cpp b/wumsloader/src/module/ModuleDataPersistence.cpp index 8a3f96a..7624fbb 100644 --- a/wumsloader/src/module/ModuleDataPersistence.cpp +++ b/wumsloader/src/module/ModuleDataPersistence.cpp @@ -1,213 +1,224 @@ #include "ModuleDataPersistence.h" + +#include "ImportRPLInformation.h" +#include "ModuleContainer.h" #include "globals.h" + #include "utils/logger.h" #include "utils/utils.h" + +#include #include -bool ModuleDataPersistence::saveModuleData(module_information_t *moduleInformation, const std::vector> &moduleList) { - auto module_data_list = make_unique_nothrow(moduleList.size()); - if (!module_data_list) { - DEBUG_FUNCTION_LINE_ERR("Failed to alloc memory to persist module data"); - return false; +#include + +namespace WUMSLoader::Modules { + +module_information_t *PersistedModuleData::getModuleInformation() { + return &mRootInfo; +} +void PersistedModuleData::setUsedRPLsList(const std::map> &usedRPls) { + PROFILE_FUNCTION(); + // free previous allocations. + if (mRootInfo.acquired_rpls) { + free(mRootInfo.acquired_rpls); + mRootInfo.acquired_rpls = nullptr; + } + mRootInfo.number_acquired_rpls = usedRPls.size(); + + if (usedRPls.empty()) { + mRootInfo.acquired_rpls = nullptr; + return; } - moduleInformation->modules = module_data_list.get(); - + mRootInfo.acquired_rpls = static_cast(malloc(usedRPls.size() * sizeof(uint32_t))); + if (!mRootInfo.acquired_rpls) { + DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for mRootInfo.acquired_rpls"); + OSFatal("Failed to allocate memory"); + } uint32_t i = 0; - for (auto &module : moduleList) { - auto &module_data = module_data_list[i]; - module_data = {}; - if (!saveModuleData(module_data, module)) { - module->relocationDataStruct.reset(); - module->hookDataStruct.reset(); - module->exportDataStruct.reset(); - module->functionSymbolDataStruct.reset(); - module->rplDataStruct.reset(); - DEBUG_FUNCTION_LINE_ERR("Failed to persist data for module. No memory?"); - OSFatal("Failed to persist data for module. No memory?"); - continue; - } else { - moduleInformation->number_modules++; - i++; - } + for (auto &rpl : usedRPls) { + mRootInfo.acquired_rpls[i] = reinterpret_cast(rpl.second); + ++i; + } +} +void PersistedModuleData::build(const std::vector &moduleList) { + PROFILE_FUNCTION(); + clearModuleInfo(); + if (moduleList.empty()) { + return; } - gModuleDataInfo = std::move(module_data_list); + // Pre-allocate the exact number of modules needed + mModules.resize(moduleList.size()); + + for (size_t i = 0; i < moduleList.size(); ++i) { + buildModuleData(moduleList[i], &mModules[i]); + } + + // Wire up the root info AFTER the vector is fully populated + // to ensure the .data() pointer doesn't invalidate. + mRootInfo.modules = mModules.data(); + mRootInfo.number_modules = mModules.size(); + OSMemoryBarrier(); - - return true; } -bool ModuleDataPersistence::saveModuleData(module_information_single_t &module_data, const std::shared_ptr &module) { - if (!saveRelocationDataForModule(module_data, module)) { - DEBUG_FUNCTION_LINE_ERR("Failed to store relocation data of module"); - return false; - } - - if (!saveHookDataForModule(module_data, module)) { - DEBUG_FUNCTION_LINE_ERR("Failed to store hook data of module"); - return false; - } - - if (!saveExportDataForModule(module_data, module)) { - DEBUG_FUNCTION_LINE_ERR("Failed to store export data of module"); - return false; - } - - if (!saveFunctionSymbolDataForModule(module_data, module)) { - DEBUG_FUNCTION_LINE_ERR("Failed to store function symbol data of module"); - return false; - } - - module_data.module_export_name = (char *) module->getExportName().c_str(); - - module_data.bssAddr = module->getBSSAddress(); - module_data.bssSize = module->getBSSSize(); - module_data.sbssAddr = module->getSBSSAddress(); - module_data.sbssSize = module->getSBSSSize(); - module_data.startAddress = module->getStartAddress(); - module_data.endAddress = module->getEndAddress(); - module_data.entrypoint = module->getEntrypoint(); - module_data.skipInitFini = module->isSkipInitFini(); - module_data.initBeforeRelocationDoneHook = module->isInitBeforeRelocationDoneHook(); - return true; +std::unique_ptr PersistedModuleData::create() { + auto persistedData = std::unique_ptr(new PersistedModuleData()); + persistedData->clear(); + return persistedData; } -bool ModuleDataPersistence::saveFunctionSymbolDataForModule(module_information_single_t &module_data, const std::shared_ptr &module) { - uint32_t entryCount = module->getFunctionSymbolDataList().size(); - auto function_symbol_data = make_unique_nothrow(entryCount); - if (!function_symbol_data) { - DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for the function symbol data."); - return false; - } - uint32_t i = 0; - for (auto &curFuncSym : module->getFunctionSymbolDataList()) { - if (i >= entryCount) { - DEBUG_FUNCTION_LINE_ERR("We tried to write more entries than we have space for."); - OSFatal("We tried to write more entries than we have space for."); - } - function_symbol_data[i].address = curFuncSym->getAddress(); - function_symbol_data[i].name = (char *) curFuncSym->getName().c_str(); - function_symbol_data[i].size = curFuncSym->getSize(); - i++; - } - module_data.function_symbol_entries = function_symbol_data.get(); - module_data.number_function_symbols = i; - - module->functionSymbolDataStruct = std::move(function_symbol_data); - - return true; +void PersistedModuleData::clearModuleInfo() { + mRootInfo.number_modules = 0; + mRootInfo.modules = nullptr; + mModules.clear(); + mHookDataList.clear(); + mExportDataList.clear(); + mFunctionSymbolDataList.clear(); + mRelocationDataList.clear(); + mRplDataList.clear(); } -bool ModuleDataPersistence::saveExportDataForModule(module_information_single_t &module_data, const std::shared_ptr &module) { - auto export_data = make_unique_nothrow(module->getExportDataList().size()); - if (!export_data) { - DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for the export data."); - return false; +void PersistedModuleData::clearRPLInfo() { + mRootInfo.number_acquired_rpls = 0; + if (mRootInfo.acquired_rpls) { + free(mRootInfo.acquired_rpls); } - - uint32_t exportCount = 0; - for (auto const &export_ : module->getExportDataList()) { - if (exportCount >= module->getExportDataList().size()) { - DEBUG_FUNCTION_LINE_ERR("We tried to write more entries than we have space for."); - OSFatal("We tried to write more entries than we have space for."); - } - auto *curExport = &export_data[exportCount++]; - curExport->type = export_->getType(); - curExport->name = export_->getName().c_str(); - curExport->address = (uint32_t) export_->getAddress(); - } - module_data.export_entries = export_data.get(); - module_data.number_export_entries = exportCount; - - module->exportDataStruct = std::move(export_data); - - return true; + mRootInfo.acquired_rpls = nullptr; } -bool ModuleDataPersistence::saveHookDataForModule(module_information_single_t &module_data, const std::shared_ptr &module) { - auto hook_data = make_unique_nothrow(module->getHookDataList().size()); - if (!hook_data) { - DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for the hook data."); - return false; - } - - uint32_t hookCount = 0; - for (auto const &hook : module->getHookDataList()) { - if (hookCount >= module->getHookDataList().size()) { - DEBUG_FUNCTION_LINE_ERR("We tried to write more entries than we have space for."); - OSFatal("We tried to write more entries than we have space for."); - } - auto *curHook = &hook_data[hookCount++]; - curHook->type = hook->getType(); - curHook->target = (uint32_t) hook->getFunctionPointer(); - } - module_data.hook_entries = hook_data.get(); - module_data.number_hook_entries = hookCount; - - module->hookDataStruct = std::move(hook_data); - - return true; +void PersistedModuleData::clear() { + mRootInfo.version = MODULE_INFORMATION_VERSION; + clearModuleInfo(); + clearRPLInfo(); } -bool ModuleDataPersistence::saveRelocationDataForModule(module_information_single_t &module_data, const std::shared_ptr &module) { - auto relocation_data = make_unique_nothrow(module->getRelocationDataList().size()); - if (!relocation_data) { - DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for the relocation data."); - return false; +void PersistedModuleData::buildModuleData(const ModuleContainer &module, module_information_single_t *module_data) { + if (!module_data) { + return; } + *module_data = {}; // Zero out - // Determine how many dyn_linking_import_t entries we need. - std::set rplInfoCountSet; - for (auto const &reloc : module->getRelocationDataList()) { - rplInfoCountSet.insert(reloc->getImportRPLInformation().getName()); - } + buildRelocationData(module, module_data); + buildHookData(module, module_data); + buildExportData(module, module_data); + buildFunctionSymbolData(module, module_data); - uint32_t rplInfoTotalCount = rplInfoCountSet.size(); - rplInfoCountSet.clear(); + const auto &metaInfo = module.getMetaInformation(); + const auto &linkInfo = module.getLinkInformation(); - auto rpl_data = make_unique_nothrow(rplInfoTotalCount); - if (!rpl_data) { - DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for the RPLInfos."); - return false; - } - - uint32_t relocationCount = 0; - uint32_t rplInfoCount = 0; - std::map rplInfoMap; - for (auto const &reloc : module->getRelocationDataList()) { - if (relocationCount >= module->getRelocationDataList().size()) { - DEBUG_FUNCTION_LINE_ERR("We tried to write more entries than we have space for."); - OSFatal("We tried to write more entries than we have space for."); - } - auto *curReloc = &relocation_data[relocationCount++]; - curReloc->destination = const_cast(reloc->getDestination()); - curReloc->offset = reloc->getOffset(); - curReloc->addend = reloc->getAddend(); - curReloc->type = reloc->getType(); - curReloc->functionName = reloc->getName().c_str(); - auto &rplInfo = reloc->getImportRPLInformation(); - - auto rplIt = rplInfoMap.find(rplInfo.getName()); - if (rplIt != rplInfoMap.end()) { - curReloc->importEntry = rplIt->second; - } else { - if (rplInfoCount >= rplInfoTotalCount) { - DEBUG_FUNCTION_LINE_ERR("We tried to write more entries than we have space for."); - OSFatal("We tried to write more entries than we have space for."); - } - auto *rplInfoPtr = &rpl_data[rplInfoCount++]; - rplInfoPtr->isData = rplInfo.isData(); - rplInfoPtr->importName = rplInfo.getName().c_str(); - rplInfoMap[rplInfo.getName()] = rplInfoPtr; - curReloc->importEntry = rplInfoPtr; - } - } - module_data.linking_entries = relocation_data.get(); - module_data.number_linking_entries = relocationCount; - - module->relocationDataStruct = std::move(relocation_data); - module->rplDataStruct = std::move(rpl_data); - - return true; + module_data->module_export_name = const_cast(metaInfo.getExportName().c_str()); + module_data->bssAddr = 0; + module_data->bssSize = 0; + module_data->sbssAddr = 0; + module_data->sbssSize = 0; + module_data->startAddress = linkInfo.getStartAddress(); + module_data->endAddress = linkInfo.getEndAddress(); + module_data->entrypoint = reinterpret_cast(linkInfo.getEntrypoint()); + module_data->skipInitFini = metaInfo.isSkipInitFini(); + module_data->initBeforeRelocationDoneHook = metaInfo.isInitBeforeRelocationDoneHook(); } + +void PersistedModuleData::buildFunctionSymbolData(const ModuleContainer &module, module_information_single_t *module_data) { + const auto &symList = module.getLinkInformation().getFunctionSymbolDataList(); + if (symList.empty()) return; + + std::vector data; + data.reserve(symList.size()); + + for (const auto &sym : symList) { + data.push_back({const_cast(sym.getName().c_str()), sym.getAddress(), sym.getSize()}); + } + std::ranges::sort(data, [](const auto &a, const auto &b) { + return a.address < b.address; + }); + + mFunctionSymbolDataList.push_back(std::move(data)); + module_data->function_symbol_entries = mFunctionSymbolDataList.back().data(); + module_data->number_function_symbols = mFunctionSymbolDataList.back().size(); +} + +void PersistedModuleData::buildExportData(const ModuleContainer &module, module_information_single_t *module_data) { + const auto &exportList = module.getLinkInformation().getExportDataList(); + if (exportList.empty()) return; + + std::vector data; + data.reserve(exportList.size()); + + for (const auto &exp : exportList) { + data.push_back({static_cast(exp.getType()), exp.getName().c_str(), reinterpret_cast(exp.getAddress())}); + } + + mExportDataList.push_back(std::move(data)); + module_data->export_entries = mExportDataList.back().data(); + module_data->number_export_entries = mExportDataList.back().size(); +} + +void PersistedModuleData::buildHookData(const ModuleContainer &module, module_information_single_t *module_data) { + const auto &hookList = module.getLinkInformation().getHookDataList(); + if (hookList.empty()) return; + + std::vector data; + data.reserve(hookList.size()); + + for (const auto &hook : hookList) { + data.push_back({.type = static_cast(hook.getType()), + .target = reinterpret_cast(hook.getFunctionPointer())}); + } + + mHookDataList.push_back(std::move(data)); + module_data->hook_entries = mHookDataList.back().data(); + module_data->number_hook_entries = mHookDataList.back().size(); +} + +void PersistedModuleData::buildRelocationData(const ModuleContainer &module, module_information_single_t *module_data) { + const auto &relocList = module.getLinkInformation().getRelocationDataList(); + if (relocList.empty()) return; + + // we need string_view and ImportRPLInformation* here to use the persistent string inside the ModuleContainer + std::map uniqueRpls; + for (const auto &reloc : relocList) { + const auto &rplInfo = reloc.getImportRPLInformation(); + uniqueRpls.try_emplace(rplInfo.getName(), &rplInfo); + } + + std::vector rplData; + rplData.reserve(uniqueRpls.size()); + + // Map string to INDEX + std::map rplIndexMap; + + for (const auto &[name, info] : uniqueRpls) { + rplData.push_back({.importName = name.data(), + .isData = info->isData()}); + rplIndexMap[name] = rplData.size() - 1; + } + + mRplDataList.push_back(std::move(rplData)); + + // Build the Relocation data array + std::vector relocData; + relocData.reserve(relocList.size()); + + for (const auto &reloc : relocList) { + const size_t targetIndex = rplIndexMap[reloc.getImportRPLInformation().getName()]; + relocData.push_back({ + .functionName = reloc.getName().c_str(), + .importEntry = &mRplDataList.back()[targetIndex], + .destination = const_cast(reloc.getDestination()), + .type = reloc.getType(), + .offset = reloc.getOffset(), + .addend = reloc.getAddend(), + }); + } + + mRelocationDataList.push_back(std::move(relocData)); + + // Only do this after the push_back to make sure .data() is valid! + module_data->linking_entries = mRelocationDataList.back().data(); + module_data->number_linking_entries = mRelocationDataList.back().size(); +} + +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ModuleDataPersistence.h b/wumsloader/src/module/ModuleDataPersistence.h index 1b7260b..0cd777b 100644 --- a/wumsloader/src/module/ModuleDataPersistence.h +++ b/wumsloader/src/module/ModuleDataPersistence.h @@ -1,15 +1,48 @@ #pragma once -#include "ModuleData.h" -#include -#include +#include -class ModuleDataPersistence { +#include + +#include +#include +#include +#include + +namespace WUMSLoader::Modules { +class ModuleContainer; +class PersistedModuleData { public: - static bool saveModuleData(module_information_t *moduleInformation, const std::vector> &moduleList); - static bool saveModuleData(module_information_single_t &module_data, const std::shared_ptr &module); - static bool saveRelocationDataForModule(module_information_single_t &module_data, const std::shared_ptr &module); - static bool saveExportDataForModule(module_information_single_t &module_data, const std::shared_ptr &module); - static bool saveHookDataForModule(module_information_single_t &module_data, const std::shared_ptr &module); - static bool saveFunctionSymbolDataForModule(module_information_single_t &module_data, const std::shared_ptr &module); + [[nodiscard]] module_information_t *getModuleInformation(); + + void setUsedRPLsList(const std::map> &usedRPls); + + void build(const std::vector &moduleList); + + static std::unique_ptr create(); + +private: + PersistedModuleData() = default; + + void clear(); + void clearModuleInfo(); + void clearRPLInfo(); + + // Internal builders + void buildModuleData(const ModuleContainer &module, module_information_single_t *module_data); + void buildRelocationData(const ModuleContainer &module, module_information_single_t *module_data); + void buildExportData(const ModuleContainer &module, module_information_single_t *module_data); + void buildHookData(const ModuleContainer &module, module_information_single_t *module_data); + void buildFunctionSymbolData(const ModuleContainer &module, module_information_single_t *module_data); + + module_information_t mRootInfo = {}; + + // Pure standard library memory management! + std::vector mModules; + std::vector> mHookDataList; + std::vector> mExportDataList; + std::vector> mFunctionSymbolDataList; + std::vector> mRelocationDataList; + std::vector> mRplDataList; }; +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ModuleLinkInformation.cpp b/wumsloader/src/module/ModuleLinkInformation.cpp new file mode 100644 index 0000000..ffc35b7 --- /dev/null +++ b/wumsloader/src/module/ModuleLinkInformation.cpp @@ -0,0 +1,129 @@ +#include "ModuleLinkInformation.h" + +#include "utils/logger.h" + +#include + +namespace WUMSLoader::Modules { + +ModuleLinkInformation::ModuleLinkInformation() : mEntrypoint(nullptr) { +} + +ModuleLinkInformation::ModuleLinkInformation(ModuleLinkInformation &&src) : mDependencies(std::move(src.mDependencies)), + mHookDataList(std::move(src.mHookDataList)), + mRelocationDataList(std::move(src.mRelocationDataList)), + mExportDataList(std::move(src.mExportDataList)), + mSymbolDataList(std::move(src.mSymbolDataList)), + mSectionInfoList(std::move(src.mSectionInfoList)), + mEntrypoint(src.mEntrypoint), + mModuleMemoryPool(std::move(src.mModuleMemoryPool)) { + + src.mEntrypoint = nullptr; +} + +ModuleLinkInformation &ModuleLinkInformation::operator=(ModuleLinkInformation &&src) noexcept { + if (this != &src) { + this->mDependencies = std::move(src.mDependencies); + this->mHookDataList = std::move(src.mHookDataList); + this->mExportDataList = std::move(src.mExportDataList); + this->mRelocationDataList = std::move(src.mRelocationDataList); + this->mSymbolDataList = std::move(src.mSymbolDataList); + this->mSectionInfoList = std::move(src.mSectionInfoList); + this->mModuleMemoryPool = std::move(src.mModuleMemoryPool); + this->mEntrypoint = src.mEntrypoint; + src.mEntrypoint = nullptr; + } + return *this; +} + +void ModuleLinkInformation::addHookData(HookData hookData) { + mHookDataList.push_back(std::move(hookData)); +} + +const std::vector &ModuleLinkInformation::getHookDataList() const { + return mHookDataList; +} + +void ModuleLinkInformation::addExportData(ExportData exportData) { + mExportDataList.push_back(std::move(exportData)); +} + +const std::vector &ModuleLinkInformation::getExportDataList() const { + return mExportDataList; +} + +void ModuleLinkInformation::addRelocationData(RelocationData relocationData) { + mRelocationDataList.push_back(std::move(relocationData)); +} + +const std::vector &ModuleLinkInformation::getRelocationDataList() const { + return mRelocationDataList; +} + +void ModuleLinkInformation::addFunctionSymbolData(FunctionSymbolData symbol_data) { + mSymbolDataList.push_back(std::move(symbol_data)); +} + +void ModuleLinkInformation::addSectionInfo(SectionInfo sectionInfo) { + std::string name = sectionInfo.getName(); + mSectionInfoList.emplace(std::move(name), std::move(sectionInfo)); +} + +void ModuleLinkInformation::addDependency(std::string_view dependency) { + mDependencies.emplace(dependency); +} + +void ModuleLinkInformation::setEntrypoint(void *val) { + mEntrypoint = val; +} + +const std::map &ModuleLinkInformation::getSectionInfoList() const { + return mSectionInfoList; +} + +std::optional ModuleLinkInformation::getSectionInfo(const std::string §ionName) const { + if (const auto it = mSectionInfoList.find(sectionName); it != mSectionInfoList.end()) { + return it->second; + } + return std::nullopt; +} + +const std::vector &ModuleLinkInformation::getFunctionSymbolDataList() const { + return mSymbolDataList; +} + +void *ModuleLinkInformation::getEntrypoint() const { + return mEntrypoint; +} + +uint32_t ModuleLinkInformation::getStartAddress() const { + return reinterpret_cast(mModuleMemoryPool.dataView().data()); +} + +uint32_t ModuleLinkInformation::getEndAddress() const { + return reinterpret_cast(mModuleMemoryPool.dataView().data()) + mModuleMemoryPool.dataView().size(); +} +const std::set &ModuleLinkInformation::getDependencyList() const { + return mDependencies; +} + +std::span ModuleLinkInformation::getTrampData() const { + if (mModuleMemoryPool && mModuleMemoryPool.numberOfSegments() < MEMORY_POOL_INDEX_TRAMP + 1) { + DEBUG_FUNCTION_LINE_ERR("Failed to return trampoline data. Memory is either not valid or has not enough segments"); + return {}; + } + const auto &entry = mModuleMemoryPool[MEMORY_POOL_INDEX_TRAMP]; // 1 is tramp data + return std::span(static_cast(entry.data()), entry.size() / sizeof(relocation_trampoline_entry_t)); +} + +bool ModuleLinkInformation::hasValidData() const { + return mModuleMemoryPool && mModuleMemoryPool.numberOfSegments() >= 3; +} + +void ModuleLinkInformation::flushCache() const { + if (!mModuleMemoryPool) return; + DCFlushRange((void *) mModuleMemoryPool.dataView().data(), mModuleMemoryPool.dataView().size_bytes()); + ICInvalidateRange((void *) mModuleMemoryPool.dataView().data(), mModuleMemoryPool.dataView().size_bytes()); +} + +} // namespace WUMSLoader::Modules diff --git a/wumsloader/src/module/ModuleLinkInformation.h b/wumsloader/src/module/ModuleLinkInformation.h new file mode 100644 index 0000000..e5e459b --- /dev/null +++ b/wumsloader/src/module/ModuleLinkInformation.h @@ -0,0 +1,87 @@ +#pragma once + +#include "ExportData.h" +#include "FunctionSymbolData.h" +#include "HookData.h" +#include "RelocationData.h" +#include "SectionInfo.h" + +#include "utils/HeapMemoryFixedSize.h" + +#include +#include +#include +#include + +namespace WUMSLoader::Modules { + +class ModuleLinkInformation { + static constexpr uint32_t MEMORY_POOL_INDEX_TEXT = 0; + static constexpr uint32_t MEMORY_POOL_INDEX_TRAMP = 1; + static constexpr uint32_t MEMORY_POOL_INDEX_DATA = 2; + +public: + ModuleLinkInformation(const ModuleLinkInformation &) = delete; + + ModuleLinkInformation(ModuleLinkInformation &&src) noexcept; + + ModuleLinkInformation &operator=(ModuleLinkInformation &&src) noexcept; + + [[nodiscard]] const std::vector &getExportDataList() const; + + [[nodiscard]] const std::vector &getHookDataList() const; + + [[nodiscard]] const std::vector &getRelocationDataList() const; + + [[nodiscard]] const std::map &getSectionInfoList() const; + + [[nodiscard]] std::optional getSectionInfo(const std::string §ionName) const; + + [[nodiscard]] const std::vector &getFunctionSymbolDataList() const; + + [[nodiscard]] void *getEntrypoint() const; + + [[nodiscard]] uint32_t getStartAddress() const; + + [[nodiscard]] uint32_t getEndAddress() const; + + [[nodiscard]] const std::set &getDependencyList() const; + + [[nodiscard]] std::span getTrampData() const; + + [[nodiscard]] bool hasValidData() const; + + void flushCache() const; + +private: + ModuleLinkInformation(); + + void addHookData(HookData hookData); + + void addExportData(ExportData exportData); + + void addRelocationData(RelocationData relocationData); + + void addFunctionSymbolData(FunctionSymbolData symbolData); + + void addSectionInfo(SectionInfo sectionInfo); + + void addDependency(std::string_view dependency); + + void setEntrypoint(void *val); + + std::set mDependencies; + std::vector mHookDataList; + std::vector mRelocationDataList; + std::vector mExportDataList; + + std::vector mSymbolDataList; + std::map mSectionInfoList; + + void *mEntrypoint; + + HeapMemoryFixedSizePool mModuleMemoryPool; + + friend class ModuleLinkInformationFactory; +}; +} // namespace WUMSLoader::Modules diff --git a/wumsloader/src/module/ModuleLinkInformationFactory.cpp b/wumsloader/src/module/ModuleLinkInformationFactory.cpp new file mode 100644 index 0000000..da81275 --- /dev/null +++ b/wumsloader/src/module/ModuleLinkInformationFactory.cpp @@ -0,0 +1,557 @@ +/**************************************************************************** + * Copyright (C) 2022 Maschell + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ + +#include "ModuleLinkInformationFactory.h" + +#include "ExportData.h" +#include "FunctionSymbolData.h" +#include "HookData.h" +#include "ImportRPLInformation.h" +#include "ModuleAllocator.h" +#include "ModuleLinkInformation.h" +#include "SectionInfo.h" + +#include "ElfUtils.h" +#include "ModuleMetaInformation.h" +#include "globals.h" +#include "utils/OnLeavingScope.h" +#include "utils/logger.h" +#include "utils/utils.h" + +#include "../utils/LoadedFile.h" +#include "elfio/elfio.hpp" +#include "utils/wiiu_zlib.hpp" + +#include + +#include + +#include +#include +#include + +namespace WUMSLoader::Modules { + +using namespace ELFIO; + +namespace { +bool predictRequiredTrampolineCount(const elfio &reader, uint32_t &outTrampCount) { + for (const auto &parentSec : reader.sections) { + if ((parentSec->get_type() == SHT_PROGBITS || parentSec->get_type() == SHT_NOBITS) && (parentSec->get_flags() & SHF_ALLOC)) { + for (const auto &psec : reader.sections) { + if ((psec->get_type() == SHT_REL || psec->get_type() == SHT_RELA) && + psec->get_info() == parentSec->get_index()) { + + const relocation_section_accessor rel(reader, psec.get()); + const symbol_section_accessor symbols(reader, reader.sections[static_cast(psec->get_link())]); + + Elf_Word symbol = 0; + Elf64_Addr offset; + Elf_Word type; + Elf_Sxword addend; + std::string sym_name; + Elf64_Addr sym_value; + Elf_Xword size; + unsigned char bind; + unsigned char symbolType; + Elf_Half sym_section_index; + unsigned char other; + + for (uint32_t j = 0; j < static_cast(rel.get_entries_num()); ++j) { + if (!rel.get_entry(j, offset, symbol, type, addend)) { + DEBUG_FUNCTION_LINE_ERR("Failed to get relocation"); + return false; + } + + if (!symbols.get_symbol(symbol, sym_name, sym_value, size, + bind, symbolType, sym_section_index, other)) { + DEBUG_FUNCTION_LINE_ERR("Failed to get symbol"); + return false; + } + + if (sym_section_index == SHN_ABS) { + // + } else if (sym_section_index > SHN_LORESERVE) { + DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED: %04X", sym_section_index); + return false; + } + + if (type == ElfUtils::R_PPC_REL24) { + const auto target = static_cast(offset); + const auto value = static_cast(sym_value + addend); + const auto newDistance = value - target; + + constexpr auto maxJumpDistance = 0x1FFFFFC; + constexpr auto maxUsedJumpDistance = maxJumpDistance - 0x10000; + + if (newDistance > maxUsedJumpDistance || newDistance < -maxUsedJumpDistance) { + outTrampCount++; + } + } + } + } + } + } + } + return true; +} + +bool linkSection(const elfio &reader, uint32_t section_index, uint32_t destination, uint32_t base_text, uint32_t base_data, std::span trampolineData) { + for (const auto &psec : reader.sections) { + if (psec->get_info() == section_index) { + DEBUG_FUNCTION_LINE_VERBOSE("Found relocation section %s", psec->get_name().c_str()); + relocation_section_accessor rel(reader, psec.get()); + symbol_section_accessor symbols(reader, reader.sections[(Elf_Half) psec->get_link()]); + for (uint32_t j = 0; j < (uint32_t) rel.get_entries_num(); ++j) { + Elf_Word symbol = 0; + Elf64_Addr offset; + Elf_Word type; + Elf_Sxword addend; + std::string sym_name; + Elf64_Addr sym_value; + + if (!rel.get_entry(j, offset, symbol, type, addend)) { + DEBUG_FUNCTION_LINE_ERR("Failed to get relocation"); + return false; + } + + // Find the symbol + Elf_Xword size; + unsigned char bind; + unsigned char symbolType; + Elf_Half sym_section_index; + unsigned char other; + + if (!symbols.get_symbol(symbol, sym_name, sym_value, size, + bind, symbolType, sym_section_index, other)) { + DEBUG_FUNCTION_LINE_ERR("Failed to get symbol"); + return false; + } + + auto adjusted_sym_value = (uint32_t) sym_value; + if ((adjusted_sym_value >= 0x02000000) && adjusted_sym_value < 0x10000000) { + adjusted_sym_value -= 0x02000000; + adjusted_sym_value += base_text; + } else if ((adjusted_sym_value >= 0x10000000) && adjusted_sym_value < 0xC0000000) { + adjusted_sym_value -= 0x10000000; + adjusted_sym_value += base_data; + } else if (adjusted_sym_value >= 0xC0000000) { + //DEBUG_FUNCTION_LINE("Skip imports"); + // Skip imports + continue; + } else if (adjusted_sym_value == 0x0) { + // + } else { + DEBUG_FUNCTION_LINE_ERR("Unhandled case %08X", adjusted_sym_value); + return false; + } + + auto adjusted_offset = (uint32_t) offset; + if ((offset >= 0x02000000) && offset < 0x10000000) { + adjusted_offset -= 0x02000000; + } else if ((offset >= 0x10000000) && offset < 0xC0000000) { + adjusted_offset -= 0x10000000; + } else if (offset >= 0xC0000000) { + adjusted_offset -= 0xC0000000; + } + + if (sym_section_index == SHN_ABS) { + // + } else if (sym_section_index > SHN_LORESERVE) { + DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED: %04X", sym_section_index); + return false; + } + + if (!ElfUtils::elfLinkOne(type, adjusted_offset, addend, destination, adjusted_sym_value, trampolineData, RELOC_TYPE_FIXED)) { + DEBUG_FUNCTION_LINE_ERR("Link failed"); + return false; + } + } + + DEBUG_FUNCTION_LINE_VERBOSE("done"); + return true; + } + } + return true; +} + +} // namespace + +std::optional ModuleLinkInformationFactory::loadAndLink(const std::span buffer, const ModuleAllocator &moduleAllocator, ModuleLinkErrors &error) { + PROFILE_FUNCTION(); + + error = ModuleLinkErrors::MODULE_LINK_ERROR_NONE; + + // Load ELF data + elfio reader(new wiiu_zlib); + if (!reader.load(reinterpret_cast(buffer.data()), buffer.size())) { + DEBUG_FUNCTION_LINE_ERR("Failed to parse WMS"); + error = ModuleLinkErrors::MODULE_LINK_ERROR_ELFIO_PARSE_FAILED; + return {}; + } + uint32_t sec_num = reader.sections.size(); + ModuleLinkInformation moduleInfo; + + auto destinationsData = make_unique_nothrow(sec_num); + if (!destinationsData) { + DEBUG_FUNCTION_LINE_ERR("Failed alloc memory for destinations array"); + error = ModuleLinkErrors::MODULE_LINK_ERROR_MALLOC_FAILED; + return std::nullopt; + } + std::span destinations(destinationsData.get(), sec_num); + + uint32_t totalSize = 0; + + uint32_t textSize = 0; + uint32_t dataSize = 0; + + for (uint32_t i = 0; i < sec_num; ++i) { + section *psec = reader.sections[i]; + if (psec->get_type() == 0x80000002) { + continue; + } + + if ((psec->get_type() == SHT_PROGBITS || psec->get_type() == SHT_NOBITS) && (psec->get_flags() & SHF_ALLOC)) { + uint32_t sectionSize = psec->get_size(); + auto address = (uint32_t) psec->get_address(); + if ((address >= 0x02000000) && address < 0x10000000) { + textSize += sectionSize + psec->get_addr_align(); + } else if ((address >= 0x10000000) && address < 0xC0000000) { + dataSize += sectionSize + psec->get_addr_align(); + } else if (psec->get_name().starts_with(".wums.")) { + dataSize += sectionSize + psec->get_addr_align(); + } + } + } + + uint32_t expectedTramps = 0; + if (!predictRequiredTrampolineCount(reader, expectedTramps)) { + DEBUG_FUNCTION_LINE_WARN("Failed to predict required trampoline count"); + } + + // Add 20 tramp slots by default to any plugin to be safe. + // This alone should already be more than enough from my current observations + expectedTramps += 20; + + size_t trampDataSize = (expectedTramps) * sizeof(relocation_trampoline_entry_t); + + // We have to have the .text section and trampolines as close as possible in memory + // Lets create a shared memory pool for both of them + HeapMemoryFixedSizePool moduleMemoryPool(moduleAllocator.alloc, moduleAllocator.free, {textSize, trampDataSize, dataSize}); + if (!moduleMemoryPool || moduleMemoryPool.numberOfSegments() < 3) { + DEBUG_FUNCTION_LINE_ERR("Failed to alloc memory for the .text section (%d bytes) and tramp data (%d bytes)", textSize, trampDataSize); + error = ModuleLinkErrors::MODULE_LINK_ERROR_MALLOC_FAILED; + return std::nullopt; + } + // Segment 0 is the .text data + const auto &text_data = moduleMemoryPool[ModuleLinkInformation::MEMORY_POOL_INDEX_TEXT]; + // Segment 1 the tramp data + const auto &tramp_data = moduleMemoryPool[ModuleLinkInformation::MEMORY_POOL_INDEX_TRAMP]; + memset(tramp_data.data(), 0, trampDataSize); + std::span trampolineList(static_cast(tramp_data.data()), expectedTramps); + // Segment 2 the data data + const auto &data_data = moduleMemoryPool[ModuleLinkInformation::MEMORY_POOL_INDEX_DATA]; + + uint32_t entrypoint = reinterpret_cast(text_data.data()) + static_cast(reader.get_entry()) - 0x02000000; + + for (const auto &psec : reader.sections) { + if (psec->get_type() == 0x80000002 || psec->get_name() == ".wut_load_bounds") { + continue; + } + + if ((psec->get_type() == SHT_PROGBITS || psec->get_type() == SHT_NOBITS) && (psec->get_flags() & SHF_ALLOC)) { + uint32_t sectionSize = psec->get_size(); + auto address = static_cast(psec->get_address()); + + uint32_t destination = address; + if ((address >= 0x02000000) && address < 0x10000000) { + destination += reinterpret_cast(text_data.data()); + destination -= 0x02000000; + destinations[psec->get_index()] = static_cast(text_data.data()); + + if (destination + sectionSize > reinterpret_cast(text_data.data()) + textSize) { + DEBUG_FUNCTION_LINE_ERR("Tried to overflow .text buffer. %08X > %08X", destination + sectionSize, reinterpret_cast(text_data.data()) + text_data.size()); + error = ModuleLinkErrors::MODULE_LINK_ERROR_OVERFLOW; + return std::nullopt; + } else if (destination < reinterpret_cast(text_data.data())) { + DEBUG_FUNCTION_LINE_ERR("Tried to underflow .text buffer. %08X < %08X", destination, reinterpret_cast(text_data.data())); + error = ModuleLinkErrors::MODULE_LINK_ERROR_UNDERFLOW; + return std::nullopt; + } + } else if ((address >= 0x10000000) && address < 0xC0000000) { + destination += reinterpret_cast(data_data.data()); + destination -= 0x10000000; + destinations[psec->get_index()] = static_cast(data_data.data()); + + if (destination + sectionSize > reinterpret_cast(data_data.data()) + data_data.size()) { + DEBUG_FUNCTION_LINE_ERR("Tried to overflow .data buffer. %08X > %08X", destination + sectionSize, reinterpret_cast(data_data.data()) + data_data.size()); + error = ModuleLinkErrors::MODULE_LINK_ERROR_OVERFLOW; + return std::nullopt; + } else if (destination < reinterpret_cast(data_data.data())) { + DEBUG_FUNCTION_LINE_ERR("Tried to underflow .data buffer. %08X < %08X", destination, reinterpret_cast(text_data.data())); + error = ModuleLinkErrors::MODULE_LINK_ERROR_UNDERFLOW; + return std::nullopt; + } + } else if (address >= 0xC0000000) { + DEBUG_FUNCTION_LINE_ERR("Loading section from 0xC0000000 is NOT supported"); + error = ModuleLinkErrors::MODULE_LINK_ERROR_UNSUPPORTED_SECTION; + return std::nullopt; + } else { + DEBUG_FUNCTION_LINE_ERR("Unhandled case"); + error = ModuleLinkErrors::MODULE_LINK_ERROR_UNKNOWN; + return std::nullopt; + } + + const char *p = psec->get_data(); + + uint32_t address_align = psec->get_addr_align(); + if ((destination & (address_align - 1)) != 0) { + DEBUG_FUNCTION_LINE_WARN("Address not aligned: %08X %08X", destination, address_align); + error = ModuleLinkErrors::MODULE_LINK_ERROR_ADDRESS_UNALIGNED; + return std::nullopt; + } + + if (psec->get_type() == SHT_NOBITS) { + DEBUG_FUNCTION_LINE_VERBOSE("memset section %s %08X to 0 (%d bytes)", psec->get_name().c_str(), destination, sectionSize); + memset((void *) destination, 0, sectionSize); + } else if (psec->get_type() == SHT_PROGBITS) { + DEBUG_FUNCTION_LINE_VERBOSE("Copy section %s %p -> %08X (%d bytes)", psec->get_name().c_str(), p, destination, sectionSize); + memcpy((void *) destination, p, sectionSize); + } + moduleInfo.addSectionInfo(SectionInfo(psec->get_name(), destination, sectionSize)); + DEBUG_FUNCTION_LINE_VERBOSE("Saved %s section info. Location: %08X size: %08X", psec->get_name().c_str(), destination, sectionSize); + + totalSize += sectionSize; + } + } + + for (uint32_t i = 0; i < sec_num; ++i) { + section *psec = reader.sections[i]; + if (psec && (psec->get_type() == SHT_PROGBITS || psec->get_type() == SHT_NOBITS) && (psec->get_flags() & SHF_ALLOC)) { + DEBUG_FUNCTION_LINE_VERBOSE("Linking (%d)... %s at %p", i, psec->get_name().c_str(), destinations[psec->get_index()]); + + if (!linkSection(reader, psec->get_index(), reinterpret_cast(destinations[psec->get_index()]), reinterpret_cast(text_data.data()), reinterpret_cast(data_data.data()), trampolineList)) { + DEBUG_FUNCTION_LINE_ERR("linkSection failed"); + error = ModuleLinkErrors::MODULE_LINK_ERROR_LINKING_FAILED; + return std::nullopt; + } + } + } + + if (!addImportRelocationData(moduleInfo, reader, destinations)) { + DEBUG_FUNCTION_LINE_ERR("addImportRelocationData failed"); + error = ModuleLinkErrors::MODULE_LINK_ERROR_PARSE_RELOCATION_DATA_FAILED; + return std::nullopt; + } + + auto secInfo = moduleInfo.getSectionInfo(".wums.exports"); + if (secInfo && secInfo.value().getSize() > 0) { + size_t entries_count = secInfo.value().getSize() / sizeof(wums_entry_t); + auto *entries = reinterpret_cast(secInfo.value().getAddress()); + if (entries != nullptr) { + for (size_t j = 0; j < entries_count; j++) { + wums_entry_t *exp = &entries[j]; + DEBUG_FUNCTION_LINE("Saving export of type %d, name %s, target: %p", exp->type, exp->name, exp->address); + moduleInfo.addExportData(ExportData(exp->type, exp->name, exp->address)); + } + } + } + + secInfo = moduleInfo.getSectionInfo(".wums.dependencies"); + if (secInfo && secInfo.value().getSize() > 0) { + if (secInfo.value().getAddress() != 0) { + const char *curEntry = reinterpret_cast(secInfo.value().getAddress()); + size_t remainingSize = secInfo.value().getSize(); + + while (remainingSize > 0) { + if (*curEntry == '\0') { + curEntry++; + remainingSize--; + continue; + } + // Use strnlen to guarantee we never read past remainingSize + size_t len = strnlen(curEntry, remainingSize); + moduleInfo.addDependency(std::string(curEntry, len)); + + curEntry += len; + remainingSize -= len; + + // Advance past the null terminator if we aren't at the end + if (remainingSize > 0) { + curEntry++; + remainingSize--; + } + } + } + } + + secInfo = moduleInfo.getSectionInfo(".wums.hooks"); + if (secInfo && secInfo.value().getSize() > 0) { + size_t entries_count = secInfo.value().getSize() / sizeof(wums_hook_t); + auto *hooks = reinterpret_cast(secInfo.value().getAddress()); + if (hooks != nullptr) { + for (size_t j = 0; j < entries_count; j++) { + wums_hook_t *hook = &hooks[j]; + DEBUG_FUNCTION_LINE("Saving hook of type %08X, target: %p", hook->type, hook->target); + moduleInfo.addHookData(HookData(const_cast(hook->target), hook->type)); + } + } + } + + // Get the symbol for functions. + for (const auto &sec : reader.sections) { + if (SHT_SYMTAB == sec->get_type()) { + symbol_section_accessor symbols(reader, sec.get()); + auto sym_no = (uint32_t) symbols.get_symbols_num(); + if (sym_no > 0) { + for (Elf_Half j = 0; j < sym_no; ++j) { + std::string name; + Elf64_Addr value = 0; + Elf_Xword size = 0; + unsigned char bind = 0; + unsigned char type = 0; + Elf_Half section = 0; + unsigned char other = 0; + if (symbols.get_symbol(j, name, value, size, bind, type, section, other)) { + if (type == STT_FUNC) { // We only care about functions. + auto sectionVal = reader.sections[section]; + auto offsetVal = value - sectionVal->get_address(); + auto sectionInfo = moduleInfo.getSectionInfo(sectionVal->get_name()); + if (!sectionInfo) { + continue; + } + + auto finalAddress = offsetVal + sectionInfo->getAddress(); + moduleInfo.addFunctionSymbolData(FunctionSymbolData(name, reinterpret_cast(finalAddress), static_cast(size))); + } + } + } + break; + } + } + } + + if (totalSize > textSize + dataSize) { + DEBUG_FUNCTION_LINE_ERR("We didn't allocate enough memory!!"); + error = ModuleLinkErrors::MODULE_LINK_ERROR_OVERFLOW; + return std::nullopt; + } + + DEBUG_FUNCTION_LINE("Saved entrypoint as %08X", entrypoint); + DEBUG_FUNCTION_LINE("Saved startAddress as %08X", (uint32_t) moduleMemoryPool.dataView().data()); + DEBUG_FUNCTION_LINE("Saved endAddress as %08X", (uint32_t) moduleMemoryPool.dataView().data() + moduleMemoryPool.dataView().size()); + + moduleInfo.mEntrypoint = reinterpret_cast(entrypoint); + moduleInfo.mModuleMemoryPool = std::move(moduleMemoryPool); + + DEBUG_FUNCTION_LINE("Loaded module: %d kilobytes", totalSize / 1024); + + moduleInfo.flushCache(); + + return moduleInfo; +} + +bool ModuleLinkInformationFactory::addImportRelocationData(ModuleLinkInformation &moduleInfo, const elfio &reader, const std::span destinations) { + uint32_t sec_num = reader.sections.size(); + + std::vector> infoMap(sec_num); + + for (uint32_t i = 0; i < sec_num; ++i) { + auto *psec = reader.sections[i]; + if (psec->get_type() == 0x80000002) { + auto info = make_shared_nothrow(psec->get_name()); + if (!info) { + DEBUG_FUNCTION_LINE_ERR("Failed to allocate ImportRPLInformation"); + return false; + } + infoMap[i] = std::move(info); + } + } + + for (const auto &psec : reader.sections) { + if (psec->get_type() == SHT_RELA || psec->get_type() == SHT_REL) { + DEBUG_FUNCTION_LINE_VERBOSE("Found relocation section %s", psec->get_name().c_str()); + relocation_section_accessor rel(reader, psec.get()); + + symbol_section_accessor symbols(reader, reader.sections[static_cast(psec->get_link())]); + + Elf_Word symbol = 0; + Elf64_Addr offset; + Elf_Word type; + Elf_Sxword addend; + std::string sym_name; + Elf64_Addr sym_value; + Elf_Xword size; + unsigned char bind; + unsigned char symbolType; + Elf_Half sym_section_index; + unsigned char other; + + for (uint32_t j = 0; j < static_cast(rel.get_entries_num()); ++j) { + if (!rel.get_entry(j, offset, symbol, type, addend)) { + DEBUG_FUNCTION_LINE_ERR("Failed to get relocation"); + return false; + } + + if (!symbols.get_symbol(symbol, sym_name, sym_value, size, + bind, symbolType, sym_section_index, other)) { + DEBUG_FUNCTION_LINE_ERR("Failed to get symbol"); + return false; + } + + auto adjusted_sym_value = static_cast(sym_value); + if (adjusted_sym_value < 0xC0000000) { + continue; + } + + uint32_t section_index = psec->get_info(); + + if (sym_section_index >= infoMap.size() || !infoMap[sym_section_index]) { + DEBUG_FUNCTION_LINE_ERR("Relocation referencing unknown/unloaded section. sym_name %s", sym_name.c_str()); + return false; + } + + if (section_index >= destinations.size() || destinations[section_index] == nullptr) { + DEBUG_FUNCTION_LINE_ERR("Target section was not loaded for relocation"); + return false; + } + + // Safely adjust the offset based on which segment the relocation lives in + auto adjusted_offset = static_cast(offset); + if (adjusted_offset >= 0x02000000 && adjusted_offset < 0x10000000) { + adjusted_offset -= 0x02000000; // .text segment + } else if (adjusted_offset >= 0x10000000 && adjusted_offset < 0xC0000000) { + DEBUG_FUNCTION_LINE_WARN("adjust offset for .data relocation of module , this has not tested/seen yet"); + adjusted_offset -= 0x10000000; // .data segment + } else { + DEBUG_FUNCTION_LINE_ERR("Relocation offset %08X is out of expected bounds", adjusted_offset); + return false; + } + + moduleInfo.addRelocationData(RelocationData(type, + adjusted_offset, + addend, + reinterpret_cast(destinations[section_index]), + sym_name, + infoMap[sym_section_index])); + } + } + } + return true; +} + +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ModuleLinkInformationFactory.h b/wumsloader/src/module/ModuleLinkInformationFactory.h new file mode 100644 index 0000000..a61c81b --- /dev/null +++ b/wumsloader/src/module/ModuleLinkInformationFactory.h @@ -0,0 +1,83 @@ +/**************************************************************************** + * Copyright (C) 2022 Maschell + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ + +#pragma once + +#include +#include +#include + +namespace ELFIO { +class elfio; +} +namespace WUMSLoader::Modules { +class ModuleMetaInformation; +struct ModuleAllocator; +class ModuleLinkInformation; +class ModuleContainer; + +enum class ModuleLinkErrors { + MODULE_LINK_ERROR_NONE, + MODULE_LINK_ERROR_UNKNOWN, + MODULE_LINK_ERROR_ELFIO_PARSE_FAILED, + MODULE_LINK_ERROR_IO_ERROR, + MODULE_LINK_ERROR_MALLOC_FAILED, + MODULE_LINK_ERROR_OVERFLOW, + MODULE_LINK_ERROR_UNDERFLOW, + MODULE_LINK_ERROR_UNSUPPORTED_SECTION, + MODULE_LINK_ERROR_ADDRESS_UNALIGNED, + MODULE_LINK_ERROR_LINKING_FAILED, + MODULE_LINK_ERROR_PARSE_RELOCATION_DATA_FAILED, +}; + +constexpr std::string_view ModuleLinkErrorsToString(const ModuleLinkErrors state) { + switch (state) { + case ModuleLinkErrors::MODULE_LINK_ERROR_NONE: + return "MODULE_LINK_ERROR_NONE"; + case ModuleLinkErrors::MODULE_LINK_ERROR_UNKNOWN: + return "MODULE_LINK_ERROR_UNKNOWN"; + case ModuleLinkErrors::MODULE_LINK_ERROR_ELFIO_PARSE_FAILED: + return "MODULE_LINK_ERROR_ELFIO_PARSE_FAILED"; + case ModuleLinkErrors::MODULE_LINK_ERROR_IO_ERROR: + return "MODULE_LINK_ERROR_IO_ERROR"; + case ModuleLinkErrors::MODULE_LINK_ERROR_MALLOC_FAILED: + return "MODULE_LINK_ERROR_MALLOC_FAILED"; + case ModuleLinkErrors::MODULE_LINK_ERROR_OVERFLOW: + return "MODULE_LINK_ERROR_OVERFLOW"; + case ModuleLinkErrors::MODULE_LINK_ERROR_UNDERFLOW: + return "MODULE_LINK_ERROR_UNDERFLOW"; + case ModuleLinkErrors::MODULE_LINK_ERROR_UNSUPPORTED_SECTION: + return "MODULE_LINK_ERROR_UNSUPPORTED_SECTION"; + case ModuleLinkErrors::MODULE_LINK_ERROR_ADDRESS_UNALIGNED: + return "MODULE_LINK_ERROR_ADDRESS_UNALIGNED"; + case ModuleLinkErrors::MODULE_LINK_ERROR_LINKING_FAILED: + return "MODULE_LINK_ERROR_LINKING_FAILED"; + case ModuleLinkErrors::MODULE_LINK_ERROR_PARSE_RELOCATION_DATA_FAILED: + return "MODULE_LINK_ERROR_PARSE_RELOCATION_DATA_FAILED"; + } + return "MODULE_LINK_ERROR_UNKNOWN"; +} + +class ModuleLinkInformationFactory { +public: + static std::optional loadAndLink(std::span buffer, const ModuleAllocator &moduleAllocator, ModuleLinkErrors &error); + +private: + static bool addImportRelocationData(ModuleLinkInformation &moduleInfo, const ELFIO::elfio &reader, std::span destinations); +}; + +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ModuleManagement.cpp b/wumsloader/src/module/ModuleManagement.cpp new file mode 100644 index 0000000..b969bed --- /dev/null +++ b/wumsloader/src/module/ModuleManagement.cpp @@ -0,0 +1,343 @@ +#include "ModuleManagement.h" + +#include "HooksManagement.h" +#include "ModuleAllocator.h" +#include "ModuleContainer.h" +#include "ModuleDataPersistence.h" +#include "ModuleLinkInformationFactory.h" +#include "ModuleMetaInformation.h" +#include "ModuleMetaInformationFactory.h" +#include "RelocationUtils.h" +#include "globals.h" +#include "utils/LoadedFile.h" +#include "utils/StringTools.h" + +#include "utils/logger.h" + +#include +#include +#include + +namespace WUMSLoader::Modules { + +namespace { + +struct ModuleInfoWithLoadedFileWrapper { + ModuleMetaInformation metaInfo; + Utils::LoadedFile file; +}; + +std::vector getModuleFilePaths(const std::string &basePath) { + PROFILE_FUNCTION(); + std::vector result; + struct dirent *dp; + DIR *dfd; + + if (basePath.empty()) { + DEBUG_FUNCTION_LINE_ERR("Failed to scan module dir: Path was empty"); + return result; + } + + if ((dfd = opendir(basePath.c_str())) == nullptr) { + DEBUG_FUNCTION_LINE_ERR("Couldn't open dir %s", basePath.c_str()); + return result; + } + + while ((dp = readdir(dfd)) != nullptr) { + if (dp->d_type == DT_DIR) { + continue; + } + + std::string_view fileName(dp->d_name); + if (fileName.starts_with('.') || fileName.starts_with('_') || !fileName.ends_with(".wms")) { + DEBUG_FUNCTION_LINE_WARN("Skip file %s/%s", basePath.c_str(), dp->d_name); + continue; + } + + result.push_back(basePath + "/" + dp->d_name); + } + closedir(dfd); + return result; +} + +bool CheckModulesByDependencies(const std::vector &modules) { + PROFILE_FUNCTION(); + std::set> loaderModuleNames; + + for (const auto &curModule : modules) { + const auto &exportName = curModule.metaInfo.getExportName(); + + DEBUG_FUNCTION_LINE_VERBOSE("Check if we can load %s", exportName.c_str()); + + for (const auto &curRPL : curModule.metaInfo.getDependencyList()) { + if (!curRPL.starts_with("homebrew")) { + continue; + } + if (curRPL == "homebrew_wupsbackend") { + OSFatal("Error: module depends on homebrew_wupsbackend, this is not supported"); + } + if (!loaderModuleNames.contains(curRPL)) { + DEBUG_FUNCTION_LINE_VERBOSE("%s requires %s which is not loaded yet", exportName.c_str(), curRPL.c_str()); + return false; + } else { + DEBUG_FUNCTION_LINE_VERBOSE("Used %s, but it's already loaded", curRPL.c_str()); + } + } + loaderModuleNames.insert(exportName); + } + return true; +} + +std::vector OrderModulesMetaByDependencies(std::vector &&modulesMetaInfo) { + PROFILE_FUNCTION(); + std::vector> pendingModules; + pendingModules.reserve(modulesMetaInfo.size()); + for (auto &mod : modulesMetaInfo) { + pendingModules.push_back(std::ref(mod)); + } + + std::vector> orderedRefs; + orderedRefs.reserve(modulesMetaInfo.size()); + + std::set> loadedModulesExportNames; + + while (!pendingModules.empty()) { + const size_t previousSize = pendingModules.size(); + + std::erase_if(pendingModules, [&](auto &ref) { + auto &metaInfo = ref.get(); + + const bool hasUnresolvedDeps = std::ranges::any_of( + metaInfo.metaInfo.getDependencyList(), + [&](const auto &curImportRPL) { + if (!curImportRPL.starts_with("homebrew")) { + return false; + } + + if (curImportRPL == "homebrew_wupsbackend") { + OSFatal("Error: module depends on homebrew_wupsbackend, this is not supported"); + } + + if (!loadedModulesExportNames.contains(curImportRPL)) { + DEBUG_FUNCTION_LINE_VERBOSE("We can't load the module, because %s is not loaded yet", curImportRPL.c_str()); + return true; + } + return false; + }); + + if (!hasUnresolvedDeps) { + orderedRefs.push_back(ref); + loadedModulesExportNames.insert(metaInfo.metaInfo.getExportName()); + return true; + } + + return false; + }); + + if (pendingModules.size() == previousSize) { + OSFatal("Failed to resolve dependencies."); + } + } + + std::vector finalOrder; + finalOrder.reserve(orderedRefs.size()); + + for (auto &ref : orderedRefs) { + finalOrder.push_back(std::move(ref.get())); + } + + return finalOrder; +} + +void CallInitHooksForModule(const ModuleContainer &curModule) { + HooksManagement::CallHook(curModule, WUMS_HOOK_INIT_WUT_THREAD); + HooksManagement::CallHook(curModule, WUMS_HOOK_INIT_WUT_MALLOC); + HooksManagement::CallHook(curModule, WUMS_HOOK_INIT_WUT_NEWLIB); + HooksManagement::CallHook(curModule, WUMS_HOOK_INIT_WUT_STDCPP); + HooksManagement::CallHook(curModule, WUMS_HOOK_INIT_WUT_DEVOPTAB); + HooksManagement::CallHook(curModule, WUMS_HOOK_INIT_WUT_SOCKETS); + HooksManagement::CallHook(curModule, WUMS_HOOK_INIT_WRAPPER, !curModule.getMetaInformation().isSkipInitFini()); + HooksManagement::CallHook(curModule, WUMS_HOOK_INIT); +} + +std::vector loadModuleMetaInformationForPath(std::vector &&loadedFiles) { + PROFILE_FUNCTION(); + + std::vector preloadedModules; + + for (auto &loadedFile : loadedFiles) { + ModuleParseErrors error = MODULE_PARSE_ERROR_NONE; + if (auto metaInfoOpt = ModuleMetaInformationFactory::loadModuleMetaInfo(std::span(loadedFile.data(), loadedFile.size()), error)) { + preloadedModules.push_back({std::move(*metaInfoOpt), std::move(loadedFile)}); + } else { + auto errMsg = string_format("Failed to load module meta information"); + if (error == MODULE_PARSE_ERROR_INCOMPATIBLE_VERSION) { + errMsg += ". Incompatible version."; + } + DEBUG_FUNCTION_LINE_ERR("%s", errMsg.c_str()); + OSFatal(errMsg.c_str()); + } + } + return preloadedModules; +} + +std::vector loadModulesIntoMemory(const std::string &basePath) { + PROFILE_FUNCTION(); + std::vector result; + for (const auto &modulePath : getModuleFilePaths(basePath)) { + auto fileOpt = Utils::LoadFileToMem(modulePath); + if (!fileOpt) { + DEBUG_FUNCTION_LINE_ERR("Failed to load file"); + return {}; + } + result.emplace_back(std::move(*fileOpt)); + } + return result; +} + +std::pair, std::vector> getModulesInCorrectOrderAndSplit(std::vector &&metaInfosWithLoadedFiles) { + PROFILE_FUNCTION(); + // Order modules list by dependencies. + metaInfosWithLoadedFiles = OrderModulesMetaByDependencies(std::move(metaInfosWithLoadedFiles)); + + // Make sure the plugin backend module is at the very end as module might depend on all other modules + const auto backendIt = std::ranges::find_if(metaInfosWithLoadedFiles, [](const auto &cur) { + return std::string_view(cur.metaInfo.getExportName()) == "homebrew_wupsbackend"; + }); + + if (backendIt != metaInfosWithLoadedFiles.end()) { + std::ranges::rotate(backendIt, backendIt + 1, metaInfosWithLoadedFiles.end()); + } + + // Make sure the "basemodule" is the first of the "regular" modules. + auto regularModulesMetaInfoRange = std::ranges::stable_partition(metaInfosWithLoadedFiles, [](const auto &cur) { + return cur.metaInfo.isInitBeforeRelocationDoneHook(); + }); + // Ensure homebrew_basemodule is the first of the regular modules (if it exists) + const auto baseModuleIt = std::ranges::find_if(regularModulesMetaInfoRange, [](const auto &cur) { + return std::string_view(cur.metaInfo.getExportName()) == "homebrew_basemodule"; + }); + + if (baseModuleIt != regularModulesMetaInfoRange.end()) { + // Rotate the base module to the start of the regular modules subrange + std::ranges::rotate(regularModulesMetaInfoRange.begin(), baseModuleIt, baseModuleIt + 1); + } + + // Final sanity check that the order is still meeting dependency requirements + if (!CheckModulesByDependencies(metaInfosWithLoadedFiles)) { + OSFatal("Failed to order Aroma Modules by depenencies"); + } + + // split into "early and "regular" modules + std::vector earlyModules; + std::vector regularModules; + + // Use the iterator returned from stable_partition to know exactly where the split is + const auto boundaryIt = regularModulesMetaInfoRange.begin(); + + // Move elements to avoid expensive copying + std::move(metaInfosWithLoadedFiles.begin(), boundaryIt, std::back_inserter(earlyModules)); + std::move(boundaryIt, metaInfosWithLoadedFiles.end(), std::back_inserter(regularModules)); + + return {std::move(earlyModules), std::move(regularModules)}; +} + + +} // namespace + +std::vector ModuleManagement::loadAndLinkModules(const std::string &basePath, std::map> &usedRPls, MEMHeapHandle defaultHeapHandle, PersistedModuleData &persistedData) { + PROFILE_FUNCTION(); + auto loadedModules = loadModulesIntoMemory(basePath); + auto moduleMetaInfos = loadModuleMetaInformationForPath(std::move(loadedModules)); + + // Sort modules to satisfy dependencies; split into two groups + auto [earlyModulesMetaInfoWithLoadedFiles, regularModuleMetaInfoWithLoadedFiles] = getModulesInCorrectOrderAndSplit(std::move(moduleMetaInfos)); + + std::vector moduleContainers; + moduleContainers.reserve(earlyModulesMetaInfoWithLoadedFiles.size() + regularModuleMetaInfoWithLoadedFiles.size()); + +#ifdef VERBOSE_DEBUG + DEBUG_FUNCTION_LINE_VERBOSE("Final order of early modules"); + for (auto &[metaInfo, file] : earlyModulesMetaInfoWithLoadedFiles) { + DEBUG_FUNCTION_LINE_VERBOSE("%s", metaInfo.getExportName().c_str()); + } + DEBUG_FUNCTION_LINE_VERBOSE("Final order of regular modules"); + for (auto &[metaInfo, file] : regularModuleMetaInfoWithLoadedFiles) { + DEBUG_FUNCTION_LINE_VERBOSE("%s", metaInfo.getExportName().c_str()); + } +#endif + + // ======================================================================== + // PHASE 1: Load and link "early" modules. + // These will get loaded into this smaller heap living between 0x00800000 and 0x01000000 + // ======================================================================== + ModuleAllocator defaultAllocator = ModuleAllocator::FromExpHeap(defaultHeapHandle); + for (auto &[metaInfo, file] : earlyModulesMetaInfoWithLoadedFiles) { + auto error = ModuleLinkErrors::MODULE_LINK_ERROR_NONE; + if (auto linkInfo = ModuleLinkInformationFactory::loadAndLink(std::span(file.data(), file.size()), defaultAllocator, error)) { + moduleContainers.emplace_back(std::move(metaInfo), std::move(*linkInfo)); + } else { + auto errMsg = string_format("Failed to load/link module %s. %s", metaInfo.getExportName().c_str(), ModuleLinkErrorsToString(error)); + DEBUG_FUNCTION_LINE_ERR("%s", errMsg.c_str()); + OSFatal(errMsg.c_str()); + } + } + + DEBUG_FUNCTION_LINE_VERBOSE("Resolve relocations without loading additional rpls"); + RelocationUtils::ResolveRelocations(moduleContainers, RelocationUtils::ExternalRPLLoadingStrategy::IGNORE_EXTERNAL_RPLS, usedRPls); + + // Build struct passed into INIT Hook + persistedData.build(moduleContainers); + + for (auto &curModule : moduleContainers) { + CallInitHooksForModule(curModule); + } + + // This sets the custom rpl allocator so we properly load external RPL now via ExternalRPLLoadingStrategy::LOAD_EXTERNAL_RPLS + HooksManagement::CallHook(moduleContainers, WUMS_HOOK_GET_CUSTOM_RPL_ALLOCATOR); + + // ======================================================================== + // PHASE 2: LOAD REGULAR MODULES. First get a module allocator which uses the mapped memory + // then load/link "regular" modules + // ======================================================================== + auto mappedAllocatorOpt = ModuleAllocator::FromMappedMemory(moduleContainers); + if (!mappedAllocatorOpt) { + DEBUG_FUNCTION_LINE_WARN("Failed to get memory module allocator, fallback to default one"); + mappedAllocatorOpt = defaultAllocator; + } + + for (auto &[metaInfo, file] : regularModuleMetaInfoWithLoadedFiles) { + ModuleLinkErrors error = ModuleLinkErrors::MODULE_LINK_ERROR_NONE; + if (auto linkInfo = ModuleLinkInformationFactory::loadAndLink(std::span(file.data(), file.size()), defaultAllocator, error)) { + moduleContainers.emplace_back(std::move(metaInfo), std::move(*linkInfo)); + } else { + auto errMsg = string_format("Failed to load/link module %s. %s", metaInfo.getExportName().c_str(), ModuleLinkErrorsToString(error)); + DEBUG_FUNCTION_LINE_ERR("%s", errMsg.c_str()); + OSFatal(errMsg.c_str()); + } + } + + // Resolve relocations for regular modules as well, this time we can actually load external RPLs + RelocationUtils::ResolveRelocations(moduleContainers, RelocationUtils::ExternalRPLLoadingStrategy::LOAD_EXTERNAL_RPLS, usedRPls); + + // rebuild struct passed into INIT Hook. The pointer passed to early module is still valid! (just hope they didn't make a copy) + persistedData.build(moduleContainers); + + DEBUG_FUNCTION_LINE_VERBOSE("Call Relocations done hook to replace memory alloc/free functions"); + HooksManagement::CallHook(moduleContainers, WUMS_HOOK_RELOCATIONS_DONE); + + DEBUG_FUNCTION_LINE("Call init hooks for regular modules"); + for (auto &moduleContainer : moduleContainers) { + if (moduleContainer.getMetaInformation().isInitBeforeRelocationDoneHook()) { + // These have already been initialized + continue; + } + CallInitHooksForModule(moduleContainer); + } + + DEBUG_FUNCTION_LINE("%d modules loaded successfully", moduleContainers.size()); + + return moduleContainers; +} + +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ModuleManagement.h b/wumsloader/src/module/ModuleManagement.h new file mode 100644 index 0000000..4890e46 --- /dev/null +++ b/wumsloader/src/module/ModuleManagement.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace WUMSLoader::Modules { +class PersistedModuleData; +class ModuleContainer; + +class ModuleManagement { +public: + static std::vector loadAndLinkModules(const std::string &basePath, std::map> &usedRPls, MEMHeapHandle defaultHeapHandle, PersistedModuleData &persistedData); +}; +} // namespace WUMSLoader::Modules diff --git a/wumsloader/src/module/ModuleMetaInformation.cpp b/wumsloader/src/module/ModuleMetaInformation.cpp new file mode 100644 index 0000000..8bcf90f --- /dev/null +++ b/wumsloader/src/module/ModuleMetaInformation.cpp @@ -0,0 +1,59 @@ +#include "ModuleMetaInformation.h" +#include "WUMSVersion.h" + +#include +#include + +#include + +namespace WUMSLoader::Modules { + +const std::string &ModuleMetaInformation::getExportName() const { + return mExportName; +} + +const WUMSVersion &ModuleMetaInformation::getWUMSVersion() const { + return mWUMSVersion; +} + +bool ModuleMetaInformation::isInitBeforeRelocationDoneHook() const { + return this->mInitBeforeRelocationDoneHook; +} + +bool ModuleMetaInformation::isSkipInitFini() const { + return this->mSkipInitFini; +} + +void ModuleMetaInformation::setExportName(std::string exportName) { + mExportName = std::move(exportName); +} + +void ModuleMetaInformation::setWUMSVersion(const uint16_t major, const uint16_t minor, const uint16_t revision) { + mWUMSVersion = WUMSVersion(major, minor, revision); +} + +void ModuleMetaInformation::setWUMSVersion(const WUMSVersion &wumsVersion) { + mWUMSVersion = wumsVersion; +} + +void ModuleMetaInformation::setInitBeforeRelocationDoneHook(const bool value) { + this->mInitBeforeRelocationDoneHook = value; +} + +void ModuleMetaInformation::setSkipInitFini(const bool value) { + this->mSkipInitFini = value; +} + +void ModuleMetaInformation::setSize(size_t size) { + mSize = size; +} + +void ModuleMetaInformation::addDependency(std::string dependency) { + mDependencyList.emplace(std::move(dependency)); +} + +const std::set &ModuleMetaInformation::getDependencyList() const { + return mDependencyList; +} + +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ModuleMetaInformation.h b/wumsloader/src/module/ModuleMetaInformation.h new file mode 100644 index 0000000..63076de --- /dev/null +++ b/wumsloader/src/module/ModuleMetaInformation.h @@ -0,0 +1,40 @@ +#pragma once + +#include "WUMSVersion.h" + +#include +#include + +namespace WUMSLoader::Modules { + +class ModuleMetaInformation { +public: + [[nodiscard]] const std::string &getExportName() const; + [[nodiscard]] const WUMSVersion &getWUMSVersion() const; + [[nodiscard]] bool isInitBeforeRelocationDoneHook() const; + [[nodiscard]] bool isSkipInitFini() const; + [[nodiscard]] const std::set &getDependencyList() const; + +private: + friend class ModuleMetaInformationFactory; + friend class ModuleLinkInformationFactory; + friend class ModuleManagement; + ModuleMetaInformation() = default; + + void setExportName(std::string exportName); + void addDependency(std::string dependency); + void setWUMSVersion(uint16_t major, uint16_t minor, uint16_t revision); + void setWUMSVersion(const WUMSVersion &wumsVersion); + void setInitBeforeRelocationDoneHook(bool value); + void setSkipInitFini(bool value); + void setSize(size_t size); + + std::string mExportName; + std::set mDependencyList; + WUMSVersion mWUMSVersion = WUMSVersion(0, 0, 0); + bool mInitBeforeRelocationDoneHook = false; + bool mSkipInitFini = false; + size_t mSize = 0; +}; + +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ModuleMetaInformationFactory.cpp b/wumsloader/src/module/ModuleMetaInformationFactory.cpp new file mode 100644 index 0000000..b484552 --- /dev/null +++ b/wumsloader/src/module/ModuleMetaInformationFactory.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** + * Copyright (C) 2018-2020 Maschell + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ + +#include "ModuleMetaInformationFactory.h" + +#include "ModuleMetaInformation.h" +#include "elfio/elfio.hpp" +#include "utils/logger.h" +#include "utils/wiiu_zlib.hpp" + +#include +#include + +namespace WUMSLoader::Modules { + +std::optional ModuleMetaInformationFactory::loadModuleMetaInfo(const std::span buffer, ModuleParseErrors &error) { + if (buffer.empty()) { + error = MODULE_PARSE_ERROR_BUFFER_EMPTY; + DEBUG_FUNCTION_LINE_ERR("Buffer is empty"); + return {}; + } + ELFIO::elfio reader(new wiiu_zlib); + + if (!reader.load(reinterpret_cast(buffer.data()), buffer.size())) { + error = MODULE_PARSE_ERROR_ELFIO_PARSE_FAILED; + DEBUG_FUNCTION_LINE_ERR("Can't find or process ELF file"); + return {}; + } + + size_t moduleSize = 0; + ModuleMetaInformation moduleInfo; + const uint32_t sec_num = reader.sections.size(); + + bool checkedVersion = false; + bool hasMetaSection = false; + + for (uint32_t i = 0; i < sec_num; ++i) { + ELFIO::section *psec = reader.sections[i]; + + // Calculate total size: + if ((psec->get_type() == ELFIO::SHT_PROGBITS || psec->get_type() == ELFIO::SHT_NOBITS) && (psec->get_flags() & ELFIO::SHF_ALLOC)) { + uint32_t sectionSize = psec->get_size(); + const auto address = static_cast(psec->get_address()); + if (address >= 0x02000000 && address < 0xC0000000) { + moduleSize += sectionSize; + } + } + + if (psec->get_name() == ".wums.meta") { + hasMetaSection = true; + const char *curEntry = psec->get_data(); + size_t remainingSize = psec->get_size(); + + while (remainingSize > 0) { + if (*curEntry == '\0') { + curEntry++; + remainingSize--; + continue; + } + + const size_t entryLen = strnlen(curEntry, remainingSize); + std::string_view entryView(curEntry, entryLen); + auto splitPos = entryView.find('='); + + if (splitPos != std::string_view::npos) { + std::string_view key = entryView.substr(0, splitPos); + std::string_view value = entryView.substr(splitPos + 1); + + if (key == "export_name") { + moduleInfo.setExportName(std::string(value)); + } else if (key == "skipInitFini") { + const bool flag = (value == "true"); + DEBUG_FUNCTION_LINE("skipInitFini = %s", flag ? "true" : "false"); + moduleInfo.setSkipInitFini(flag); + } else if (key == "initBeforeRelocationDoneHook") { + const bool flag = (value == "true"); + DEBUG_FUNCTION_LINE("initBeforeRelocationDoneHook = %s", flag ? "true" : "false"); + moduleInfo.setInitBeforeRelocationDoneHook(flag); + } else if (key == "wums" || key == "wum") { + checkedVersion = true; + if (value == "0.3.1") { + moduleInfo.setWUMSVersion(0, 3, 1); + } else if (value == "0.3.2") { + moduleInfo.setWUMSVersion(0, 3, 2); + } else if (value == "0.3.3") { + moduleInfo.setWUMSVersion(0, 3, 3); + } else if (value == "0.3.4") { + moduleInfo.setWUMSVersion(0, 3, 4); + } else if (value == "0.3.5") { + moduleInfo.setWUMSVersion(0, 3, 5); + } else { + error = MODULE_PARSE_ERROR_INCOMPATIBLE_VERSION; + DEBUG_FUNCTION_LINE_WARN("Ignoring module - Unsupported WUMS version: %.*s.", static_cast(value.length()), value.data()); + return {}; + } + } + } + + curEntry += entryLen; + remainingSize -= entryLen; + + if (remainingSize > 0) { + curEntry++; + remainingSize--; + } + } + } else if (psec->get_name() == ".wums.dependencies") { + const char *curEntry = psec->get_data(); + size_t remainingSize = psec->get_size(); + + if (curEntry != nullptr && remainingSize > 0) { + while (remainingSize > 0) { + if (*curEntry == '\0') { + curEntry++; + remainingSize--; + continue; + } + size_t len = strnlen(curEntry, remainingSize); + moduleInfo.addDependency(std::string(curEntry, len)); + + curEntry += len; + remainingSize -= len; + + if (remainingSize > 0) { + curEntry++; + remainingSize--; + } + } + } + } + } + + if (!hasMetaSection) { + DEBUG_FUNCTION_LINE_ERR("File has no \".wums.meta\" section"); + error = MODULE_PARSE_ERROR_NO_MODULE; + return {}; + } + + if (!checkedVersion) { + DEBUG_FUNCTION_LINE_ERR("File has no version information in \".wums.meta\" section"); + error = MODULE_PARSE_ERROR_INCOMPATIBLE_VERSION; + return {}; + } + + moduleInfo.setSize(moduleSize); + error = MODULE_PARSE_ERROR_NONE; + + return moduleInfo; +} + +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/ModuleDataFactory.h b/wumsloader/src/module/ModuleMetaInformationFactory.h similarity index 50% rename from wumsloader/src/module/ModuleDataFactory.h rename to wumsloader/src/module/ModuleMetaInformationFactory.h index 2728bc1..c5d3acc 100644 --- a/wumsloader/src/module/ModuleDataFactory.h +++ b/wumsloader/src/module/ModuleMetaInformationFactory.h @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright (C) 2022 Maschell + * Copyright (C) 2019-2026 Maschell * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,25 +17,28 @@ #pragma once -#include "ModuleData.h" -#include "elfio/elfio.hpp" -#include -#include -#include -#include -#include +#include +#include +#include -class ModuleDataFactory { -public: - static std::optional> load(const std::string &path); +#include - static bool linkSection(ELFIO::elfio &reader, - uint32_t section_index, - uint32_t destination, - uint32_t base_text, - uint32_t base_data, - relocation_trampoline_entry_t *trampoline_data, - uint32_t trampoline_data_length); +namespace WUMSLoader::Modules { - static bool getImportRelocationData(std::shared_ptr &moduleData, ELFIO::elfio &reader, uint8_t **destinations); +class ModuleMetaInformation; + +enum ModuleParseErrors { + MODULE_PARSE_ERROR_NONE, + MODULE_PARSE_ERROR_UNKNOWN, + MODULE_PARSE_ERROR_NO_MODULE, + MODULE_PARSE_ERROR_INCOMPATIBLE_VERSION, + MODULE_PARSE_ERROR_BUFFER_EMPTY, + MODULE_PARSE_ERROR_ELFIO_PARSE_FAILED, + MODULE_PARSE_ERROR_IO_ERROR, }; + +class ModuleMetaInformationFactory { +public: + static std::optional loadModuleMetaInfo(std::span buffer, ModuleParseErrors &error); +}; +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/RelocationData.cpp b/wumsloader/src/module/RelocationData.cpp index b51b026..4f86a12 100644 --- a/wumsloader/src/module/RelocationData.cpp +++ b/wumsloader/src/module/RelocationData.cpp @@ -1,5 +1,9 @@ #include "RelocationData.h" +#include "ImportRPLInformation.h" +#include + +namespace WUMSLoader::Modules { RelocationData::RelocationData(const char type, const size_t offset, const int32_t addend, @@ -39,4 +43,5 @@ RelocationData::~RelocationData() = default; [[nodiscard]] const ImportRPLInformation &RelocationData::getImportRPLInformation() const { return *mRPLInfo; -} \ No newline at end of file +} +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/RelocationData.h b/wumsloader/src/module/RelocationData.h index 8b45db7..284f490 100644 --- a/wumsloader/src/module/RelocationData.h +++ b/wumsloader/src/module/RelocationData.h @@ -17,13 +17,14 @@ #pragma once -#include "ImportRPLInformation.h" #include #include #include +namespace WUMSLoader::Modules { +class ImportRPLInformation; class RelocationData { public: @@ -31,6 +32,9 @@ public: RelocationData(const RelocationData &o2); + RelocationData(RelocationData &&) noexcept = default; + RelocationData &operator=(RelocationData &&) noexcept = default; + virtual ~RelocationData(); [[nodiscard]] char getType() const; @@ -53,3 +57,4 @@ private: std::string mName; std::shared_ptr mRPLInfo; }; +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/RelocationUtils.cpp b/wumsloader/src/module/RelocationUtils.cpp new file mode 100644 index 0000000..1a32636 --- /dev/null +++ b/wumsloader/src/module/RelocationUtils.cpp @@ -0,0 +1,203 @@ +#include "RelocationUtils.h" + +#include "ElfUtils.h" +#include "ImportRPLInformation.h" +#include "globals.h" +#include "module/ModuleContainer.h" +#include "module/RelocationData.h" +#include "utils/OnLeavingScope.h" +#include "utils/StringTools.h" +#include "utils/logger.h" +#include "utils/memory.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace WUMSLoader::Modules::RelocationUtils { + +namespace { +OSDynLoad_Error CustomDynLoadAlloc(int32_t size, int32_t align, void **outAddr) { + if (!outAddr) { + return OS_DYNLOAD_INVALID_ALLOCATOR_PTR; + } + + if (align < 4) { + align = 4; + } + + if (!(*outAddr = memalign(align, size))) { + return OS_DYNLOAD_OUT_OF_MEMORY; + } + + // keep track of allocated memory to clean it up in case the RPLs won't get unloaded properly + gAllocatedAddresses.push_back(*outAddr); + + return OS_DYNLOAD_OK; +} + +void CustomDynLoadFree(void *addr) { + free(addr); + + // Remove from list + if (const auto it = std::ranges::find(gAllocatedAddresses, addr); it != gAllocatedAddresses.end()) { + gAllocatedAddresses.erase(it); + } +} + +bool doRelocation(const std::vector &relocData, + const std::map> &moduleMap, + const ExternalRPLLoadingStrategy rplLoadingStrategy, + std::span trampData, + std::map> &usedRPls) { + + for (const auto &curReloc : relocData) { + const auto &functionName = curReloc.getName(); + const std::string_view rplName = curReloc.getImportRPLInformation().getRPLName(); + uint32_t functionAddress = 0; + + if (auto it = moduleMap.find(rplName); it != moduleMap.end()) { + const auto *module = it->second; + bool found = false; + for (const auto &exportData : module->getLinkInformation().getExportDataList()) { + if (functionName == exportData.getName()) { + functionAddress = reinterpret_cast(exportData.getAddress()); + found = true; + break; + } + } + if (!found) { + DEBUG_FUNCTION_LINE_ERR("Failed to find export %.*s of module: %.*s", + static_cast(functionName.length()), functionName.data(), + static_cast(rplName.length()), rplName.data()); + return false; + } + } + + if (functionAddress == 0) { + if (functionName == "MEMAllocFromDefaultHeap") { + functionAddress = reinterpret_cast(&MEMAlloc); + } else if (functionName == "MEMAllocFromDefaultHeapEx") { + functionAddress = reinterpret_cast(&MEMAllocEx); + } else if (functionName == "MEMFreeToDefaultHeap") { + functionAddress = reinterpret_cast(&MEMFree); + } + } + + if (functionAddress == 0) { + const int32_t isData = curReloc.getImportRPLInformation().isData(); + OSDynLoad_Module rplHandle = nullptr; + + if (auto rplIt = usedRPls.find(rplName); rplIt == usedRPls.end()) { + OSDynLoad_Module tmp = nullptr; + if (OSDynLoad_IsModuleLoaded(rplName.data(), &tmp) != OS_DYNLOAD_OK || tmp == nullptr) { + if (rplLoadingStrategy == ExternalRPLLoadingStrategy::IGNORE_EXTERNAL_RPLS) { + DEBUG_FUNCTION_LINE_ERR("Relocation requires .rpl which isn't loaded and loading is not allowed"); + return false; + } + } + + if (OSDynLoad_Acquire(rplName.data(), &rplHandle) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("Failed to acquire %.*s", (int) rplName.length(), rplName.data()); + return false; + } + usedRPls.emplace(std::string(rplName), rplHandle); + } else { + rplHandle = rplIt->second; + } + + const auto res = OSDynLoad_FindExport(rplHandle, static_cast(isData), + functionName.c_str(), reinterpret_cast(&functionAddress)); + + if (res != OS_DYNLOAD_OK || functionAddress == 0) { + DEBUG_FUNCTION_LINE_ERR("Failed to find export %.*s of %.*s", + static_cast(functionName.length()), functionName.c_str(), + static_cast(rplName.length()), rplName.data()); + return false; + } + } + + if (!ElfUtils::elfLinkOne(curReloc.getType(), curReloc.getOffset(), curReloc.getAddend(), + reinterpret_cast(curReloc.getDestination()), + functionAddress, trampData, RELOC_TYPE_IMPORT)) { + return false; + } + } + return true; +} + +} // namespace + + +bool ResolveRelocations(const std::vector &loadedModules, const ExternalRPLLoadingStrategy rplLoadingStrategy, std::map> &usedRPls) { + PROFILE_FUNCTION(); + bool wasSuccessful = true; + + OSDynLoadAllocFn prevDynLoadAlloc = nullptr; + OSDynLoadFreeFn prevDynLoadFree = nullptr; + + if (rplLoadingStrategy == ExternalRPLLoadingStrategy::LOAD_EXTERNAL_RPLS) { + OSDynLoad_GetAllocator(&prevDynLoadAlloc, &prevDynLoadFree); + if (gCustomRPLAllocatorAllocFn != nullptr && gCustomRPLAllocatorFreeFn != nullptr) { + OSDynLoad_SetAllocator(reinterpret_cast(gCustomRPLAllocatorAllocFn), gCustomRPLAllocatorFreeFn); + } else { + OSDynLoad_SetAllocator(CustomDynLoadAlloc, CustomDynLoadFree); + } + } + auto restoreOSDynLoadAllocator = onLeavingScope([rplLoadingStrategy, prevDynLoadAlloc, prevDynLoadFree]() { + if (rplLoadingStrategy == ExternalRPLLoadingStrategy::LOAD_EXTERNAL_RPLS) { + OSDynLoad_SetAllocator(prevDynLoadAlloc, prevDynLoadFree); + } + }); + + std::map> moduleMap; + for (const auto &mod : loadedModules) { + if (mod.isLinkedAndLoaded()) { + moduleMap[mod.getMetaInformation().getExportName()] = &mod; + } + } + + for (auto &curModule : loadedModules) { + if (!curModule.isLinkedAndLoaded()) { + DEBUG_FUNCTION_LINE_VERBOSE("Skip doing relocations for %s as it's not linked properly", curModule.getMetaInformation().getExportName().c_str()); + continue; + } + const auto &trampData = curModule.getLinkInformation().getTrampData(); + for (auto &cur : trampData) { + if (cur.status == RELOC_TRAMP_IMPORT_DONE) { + cur.status = RELOC_TRAMP_FREE; + } + } + + DEBUG_FUNCTION_LINE("Let's do the relocations for %s", curModule.getMetaInformation().getExportName().c_str()); + + const auto &relocData = curModule.getLinkInformation().getRelocationDataList(); + if (!doRelocation(relocData, + moduleMap, + rplLoadingStrategy, + trampData, + usedRPls)) { + wasSuccessful = false; + + const auto errMsg = string_format("Failed to do Relocations for %s", curModule.getMetaInformation().getExportName().c_str()); + DEBUG_FUNCTION_LINE_ERR("%s", errMsg.c_str()); + OSFatal(errMsg.c_str()); + } + curModule.getLinkInformation().flushCache(); + } + + return wasSuccessful; +} + + +} // namespace WUMSLoader::Modules::RelocationUtils \ No newline at end of file diff --git a/wumsloader/src/module/RelocationUtils.h b/wumsloader/src/module/RelocationUtils.h new file mode 100644 index 0000000..c277e6a --- /dev/null +++ b/wumsloader/src/module/RelocationUtils.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include +#include +#include + +#include + +namespace WUMSLoader::Modules { +class ModuleContainer; +class RelocationData; + +namespace RelocationUtils { + +enum class ExternalRPLLoadingStrategy { + LOAD_EXTERNAL_RPLS, + IGNORE_EXTERNAL_RPLS +}; + +bool ResolveRelocations(const std::vector &loadedModules, + ExternalRPLLoadingStrategy rplLoadingStrategy, + std::map> &usedRPls); + +} // namespace RelocationUtils +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/SectionInfo.cpp b/wumsloader/src/module/SectionInfo.cpp index f109220..7d8e8c6 100644 --- a/wumsloader/src/module/SectionInfo.cpp +++ b/wumsloader/src/module/SectionInfo.cpp @@ -1,5 +1,6 @@ #include "SectionInfo.h" +namespace WUMSLoader::Modules { SectionInfo::SectionInfo(std::string name, const uint32_t address, const uint32_t sectionSize) : mName(std::move(name)), @@ -21,4 +22,5 @@ SectionInfo::SectionInfo(std::string name, [[nodiscard]] uint32_t SectionInfo::isInSection(uint32_t addr) const { return addr >= mAddress && addr < mAddress + mSectionSize; -} \ No newline at end of file +} +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/SectionInfo.h b/wumsloader/src/module/SectionInfo.h index 13fae4f..cb53dc3 100644 --- a/wumsloader/src/module/SectionInfo.h +++ b/wumsloader/src/module/SectionInfo.h @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright (C) 2019 Maschell +* Copyright (C) 2019 Maschell * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ #include +namespace WUMSLoader::Modules { class SectionInfo { public: @@ -39,3 +40,4 @@ private: uint32_t mAddress = {}; uint32_t mSectionSize = {}; }; +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/WUMSVersion.cpp b/wumsloader/src/module/WUMSVersion.cpp new file mode 100644 index 0000000..91b2aae --- /dev/null +++ b/wumsloader/src/module/WUMSVersion.cpp @@ -0,0 +1,44 @@ +#include "WUMSVersion.h" +#include "utils/StringTools.h" + +namespace WUMSLoader::Modules { + +WUMSVersion::WUMSVersion(const int major, const int minor, const int revision) + : mMajor(major), mMinor(minor), mRevision(revision) { +} + +std::optional WUMSVersion::createFromString(const std::string &versionStr) { + char *end; + errno = 0; // Initialize errno before calling strtol + + const auto major = strtol(versionStr.c_str(), &end, 10); + if (errno || *end != '.') { + return std::nullopt; + } + + + const auto minor = strtol(end + 1, &end, 10); + if (errno || *end != '.') { + return std::nullopt; + } + + const auto revision = strtol(end + 1, &end, 10); + if (errno || *end != '\0') { + return std::nullopt; + } + + return WUMSVersion(static_cast(major), static_cast(minor), static_cast(revision)); +} + +std::strong_ordering WUMSVersion::operator<=>(const WUMSVersion &other) const { + if (const auto cmp = mMajor <=> other.mMajor; cmp != std::strong_ordering::equal) return cmp; + if (const auto cmp = mMinor <=> other.mMinor; cmp != std::strong_ordering::equal) return cmp; + return mRevision <=> other.mRevision; +} + +[[nodiscard]] std::string WUMSVersion::toString() const { + return string_format("%d.%d.%d", mMajor, + mMinor, + mRevision); +} +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/module/WUMSVersion.h b/wumsloader/src/module/WUMSVersion.h new file mode 100644 index 0000000..5de38ef --- /dev/null +++ b/wumsloader/src/module/WUMSVersion.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include + +namespace WUMSLoader::Modules { + +class WUMSVersion { +public: + WUMSVersion(int major, int minor, int revision); + + static std::optional createFromString(const std::string &versionStr); + + std::strong_ordering operator<=>(const WUMSVersion &other) const; + + [[nodiscard]] std::string toString() const; + +private: + uint32_t mMajor; + uint32_t mMinor; + uint32_t mRevision; +}; + +} // namespace WUMSLoader::Modules \ No newline at end of file diff --git a/wumsloader/src/utils/ElfUtils.h b/wumsloader/src/utils/ElfUtils.h deleted file mode 100644 index 9d196f3..0000000 --- a/wumsloader/src/utils/ElfUtils.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define R_PPC_NONE 0 -#define R_PPC_ADDR32 1 -#define R_PPC_ADDR16_LO 4 -#define R_PPC_ADDR16_HI 5 -#define R_PPC_ADDR16_HA 6 -#define R_PPC_REL24 10 -#define R_PPC_REL14 11 -#define R_PPC_DTPMOD32 68 -#define R_PPC_DTPREL32 78 -#define R_PPC_EMB_SDA21 109 -#define R_PPC_EMB_RELSDA 116 -#define R_PPC_DIAB_SDA21_LO 180 -#define R_PPC_DIAB_SDA21_HI 181 -#define R_PPC_DIAB_SDA21_HA 182 -#define R_PPC_DIAB_RELSDA_LO 183 -#define R_PPC_DIAB_RELSDA_HI 184 -#define R_PPC_DIAB_RELSDA_HA 185 -#define R_PPC_GHS_REL16_HA 251 -#define R_PPC_GHS_REL16_HI 252 -#define R_PPC_GHS_REL16_LO 253 - -// Masks for manipulating Power PC relocation targets -#define PPC_WORD32 0xFFFFFFFF -#define PPC_WORD30 0xFFFFFFFC -#define PPC_LOW24 0x03FFFFFC -#define PPC_LOW14 0x0020FFFC -#define PPC_HALF16 0xFFFF - -#ifdef __cplusplus -} -#endif - -class ElfUtils { - -public: - static bool elfLinkOne(char type, size_t offset, int32_t addend, uint32_t destination, uint32_t symbol_addr, relocation_trampoline_entry_t *trampoline_data, uint32_t trampoline_data_length, - RelocationType reloc_type); -}; diff --git a/wumsloader/src/utils/HeapMemoryFixedSize.cpp b/wumsloader/src/utils/HeapMemoryFixedSize.cpp new file mode 100644 index 0000000..1ea328a --- /dev/null +++ b/wumsloader/src/utils/HeapMemoryFixedSize.cpp @@ -0,0 +1,92 @@ +#include "HeapMemoryFixedSize.h" + +#include "logger.h" +#include "utils.h" + +#include + +HeapMemoryFixedSizePool::MemorySegmentInfo::MemorySegmentInfo(void *data, const size_t size) : mData(data), + mSize(size) {} + +void *HeapMemoryFixedSizePool::MemorySegmentInfo::data() const { + return mData; +} + +size_t HeapMemoryFixedSizePool::MemorySegmentInfo::size() const { + return mSize; +} + +HeapMemoryFixedSizePool::HeapMemoryFixedSizePool() = default; + +HeapMemoryFixedSizePool::HeapMemoryFixedSizePool(const AllocFunc &allocFunc, FreeFunc freeFunc, const std::initializer_list segmentSizes) + : HeapMemoryFixedSizePool(allocFunc, std::move(freeFunc), std::span(segmentSizes.begin(), segmentSizes.size())) { +} + +HeapMemoryFixedSizePool::HeapMemoryFixedSizePool(const AllocFunc &allocFunc, FreeFunc freeFunc, const std::span segmentSizes) { + assert(!segmentSizes.empty()); + assert(allocFunc != nullptr); + assert(freeFunc != nullptr); + + size_t totalSize = 0; + for (const auto size : segmentSizes) { + totalSize += size + 0x40; // add 0x40 bytes overhead for each entry to ensure padding to 0x40 + } + + // Call the injected allocator! + void *raw_mem = allocFunc(totalSize, 0x40); + + if (!raw_mem) { + return; + } + + mData = std::unique_ptr( + static_cast(raw_mem), + CustomDeleter{std::move(freeFunc)}); + + mTotalSize = (mData ? totalSize : 0); + if (mData) { + auto address = reinterpret_cast(mData.get()); + for (const auto size : segmentSizes) { + address = ROUNDUP(static_cast(address), 0x40); + assert(address >= reinterpret_cast(mData.get()) && address < reinterpret_cast(mData.get()) + totalSize); + mSegmentInfos.emplace_back(reinterpret_cast(address), size); + address += size; + } + } +} + +HeapMemoryFixedSizePool::HeapMemoryFixedSizePool(HeapMemoryFixedSizePool &&other) noexcept + : mData(std::move(other.mData)), mTotalSize(other.mTotalSize), mSegmentInfos(std::move(other.mSegmentInfos)) { + other.mTotalSize = 0; +} + +HeapMemoryFixedSizePool &HeapMemoryFixedSizePool::operator=(HeapMemoryFixedSizePool &&other) noexcept { + if (this != &other) { + mData = std::move(other.mData); + mTotalSize = other.mTotalSize; + mSegmentInfos = std::move(other.mSegmentInfos); + other.mTotalSize = 0; + other.mSegmentInfos.clear(); + } + return *this; +} + +uint32_t HeapMemoryFixedSizePool::numberOfSegments() const { + return mSegmentInfos.size(); +} + +HeapMemoryFixedSizePool::operator bool() const { + return mData != nullptr && mTotalSize > 0; +} + +HeapMemoryFixedSizePool::MemorySegmentInfo HeapMemoryFixedSizePool::operator[](const size_t idx) const { + if (idx >= mSegmentInfos.size()) { + DEBUG_FUNCTION_LINE_ERR("Out of bounce access (tried to access index %d; size is %d", idx, mSegmentInfos.size()); + return {nullptr, 0}; + } + return mSegmentInfos[idx]; +} + +std::span HeapMemoryFixedSizePool::dataView() const { + return {mData.get(), mTotalSize}; +} \ No newline at end of file diff --git a/wumsloader/src/utils/HeapMemoryFixedSize.h b/wumsloader/src/utils/HeapMemoryFixedSize.h new file mode 100644 index 0000000..1578c31 --- /dev/null +++ b/wumsloader/src/utils/HeapMemoryFixedSize.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include +#include +#include + +class HeapMemoryFixedSizePool { +public: + // Define our custom allocator and deleter signatures + using AllocFunc = std::function; + using FreeFunc = std::function; + + class MemorySegmentInfo { + public: + MemorySegmentInfo(void *data, size_t size); + + [[nodiscard]] void *data() const; + [[nodiscard]] size_t size() const; + + private: + void *mData; + size_t mSize; + }; + + HeapMemoryFixedSizePool(); + + // Updated constructors to take the custom functions + HeapMemoryFixedSizePool(const AllocFunc &allocFunc, FreeFunc freeFunc, std::initializer_list segmentSizes); + HeapMemoryFixedSizePool(const AllocFunc &allocFunc, FreeFunc freeFunc, std::span segmentSizes); + + // Delete the copy constructor and copy assignment operator + HeapMemoryFixedSizePool(const HeapMemoryFixedSizePool &) = delete; + HeapMemoryFixedSizePool &operator=(const HeapMemoryFixedSizePool &) = delete; + + HeapMemoryFixedSizePool(HeapMemoryFixedSizePool &&other) noexcept; + + HeapMemoryFixedSizePool &operator=(HeapMemoryFixedSizePool &&other) noexcept; + + [[nodiscard]] uint32_t numberOfSegments() const; + + explicit operator bool() const; + + MemorySegmentInfo operator[](size_t idx) const; + + [[nodiscard]] std::span dataView() const; + +private: + struct CustomDeleter { + FreeFunc freeFunc; + + void operator()(uint8_t *ptr) const { + if (ptr != nullptr && freeFunc) { + freeFunc(ptr); + } + } + }; + + // Initialize with a null pointer and an empty CustomDeleter + std::unique_ptr mData{nullptr, CustomDeleter{nullptr}}; + std::size_t mTotalSize{}; + std::vector mSegmentInfos; +}; \ No newline at end of file diff --git a/wumsloader/src/utils/LoadedFile.cpp b/wumsloader/src/utils/LoadedFile.cpp new file mode 100644 index 0000000..bf67115 --- /dev/null +++ b/wumsloader/src/utils/LoadedFile.cpp @@ -0,0 +1,122 @@ +#include "LoadedFile.h" + +#include "globals.h" +#include "utils/logger.h" +#include "utils/utils.h" + +#include + +#include +#include +#include + +#include +#include + +namespace WUMSLoader::Utils { + +namespace { +class ScopedFileDescriptor { +public: + explicit ScopedFileDescriptor(const int fd) : mFD(fd) {} + ~ScopedFileDescriptor() { + if (mFD >= 0) { + ::close(mFD); + } + } + + ScopedFileDescriptor(const ScopedFileDescriptor &) = delete; + ScopedFileDescriptor &operator=(const ScopedFileDescriptor &) = delete; + + [[nodiscard]] bool isValid() const { + return mFD >= 0; + } + [[nodiscard]] int get() const { + return mFD; + } + +private: + int mFD = -1; +}; +} // namespace + +LoadedFile::LoadedFile(uint8_t *buffer, uint32_t size) : mBuffer(buffer), mSize(size) { +} + +LoadedFile::~LoadedFile() { + if (mBuffer) { + memset(mBuffer, 0, ROUNDUP(static_cast(mSize), 0x40)); + MEMFreeToDefaultHeap(mBuffer); + } +} + +LoadedFile::LoadedFile(LoadedFile &&other) noexcept : mBuffer(std::exchange(other.mBuffer, nullptr)), + mSize(std::exchange(other.mSize, 0)) {} + +LoadedFile &LoadedFile::operator=(LoadedFile &&other) noexcept { + if (this != &other) { + if (mBuffer) { + memset(mBuffer, 0, ROUNDUP(static_cast(mSize), 0x40)); + MEMFreeToDefaultHeap(mBuffer); + } + mBuffer = std::exchange(other.mBuffer, nullptr); + mSize = std::exchange(other.mSize, 0); + } + return *this; +} + +uint8_t *LoadedFile::data() { + return mBuffer; +} + +const uint8_t *LoadedFile::data() const { + return mBuffer; +} + +uint32_t LoadedFile::size() const { + return mSize; +} + +std::optional LoadFileToMem(const std::string &filepath) { + PROFILE_FUNCTION(); + const ScopedFileDescriptor fd(open(filepath.c_str(), O_RDONLY)); + if (!fd.isValid()) { + return std::nullopt; + } + + const off_t rawSize = lseek(fd.get(), 0, SEEK_END); + if (rawSize <= 0) { + return std::nullopt; + } + + const auto filesize = static_cast(rawSize); + lseek(fd.get(), 0, SEEK_SET); + + auto *buffer = static_cast(MEMAllocFromDefaultHeapEx(ROUNDUP(static_cast(filesize), 0x40), 0x40)); + if (buffer == nullptr) { + return std::nullopt; + } + + LoadedFile file(buffer, filesize); + + uint32_t done = 0; + while (done < filesize) { + constexpr uint32_t blockSize = 0x20000; + uint32_t bytesToRead = std::min(blockSize, filesize - done); + + const ssize_t readBytes = read(fd.get(), file.data() + done, bytesToRead); + + if (readBytes <= 0) { + break; + } + done += static_cast(readBytes); + } + + if (done != filesize) { + return std::nullopt; + } + + return file; +} + +} // namespace WUMSLoader::Utils \ No newline at end of file diff --git a/wumsloader/src/utils/LoadedFile.h b/wumsloader/src/utils/LoadedFile.h new file mode 100644 index 0000000..c67c281 --- /dev/null +++ b/wumsloader/src/utils/LoadedFile.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#include + +namespace WUMSLoader::Utils { + +class LoadedFile { +public: + LoadedFile(uint8_t *buffer, uint32_t size); + ~LoadedFile(); + + LoadedFile(const LoadedFile &) = delete; + LoadedFile &operator=(const LoadedFile &) = delete; + + LoadedFile(LoadedFile &&other) noexcept; + LoadedFile &operator=(LoadedFile &&other) noexcept; + + [[nodiscard]] uint8_t *data(); // Added non-const version + [[nodiscard]] const uint8_t *data() const; // Added const version + [[nodiscard]] uint32_t size() const; + +private: + uint8_t *mBuffer = nullptr; + uint32_t mSize = 0; +}; + +[[nodiscard]] std::optional LoadFileToMem(const std::string &filepath); + +} // namespace WUMSLoader::Utils \ No newline at end of file diff --git a/wumsloader/src/utils/OnLeavingScope.h b/wumsloader/src/utils/OnLeavingScope.h index e92e6ca..d1cee9f 100644 --- a/wumsloader/src/utils/OnLeavingScope.h +++ b/wumsloader/src/utils/OnLeavingScope.h @@ -106,8 +106,8 @@ private: * from the object passed as the function argument. */ template -OnLeavingScope::type> onLeavingScope(Func &&f) { - return OnLeavingScope::type>(std::forward(f)); +OnLeavingScope> onLeavingScope(Func &&f) { + return OnLeavingScope>(std::forward(f)); } #endif // CRASCIT_ONLEAVINGSCOPE_H diff --git a/wumsloader/src/utils/RelocationUtils.cpp b/wumsloader/src/utils/RelocationUtils.cpp deleted file mode 100644 index 16f0934..0000000 --- a/wumsloader/src/utils/RelocationUtils.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include "RelocationUtils.h" -#include "ElfUtils.h" -#include "globals.h" -#include "logger.h" -#include "memory.h" -#include -#include -#include -#include -#include - -static OSDynLoad_Error CustomDynLoadAlloc(int32_t size, int32_t align, void **outAddr) { - if (!outAddr) { - return OS_DYNLOAD_INVALID_ALLOCATOR_PTR; - } - - if (align >= 0 && align < 4) { - align = 4; - } else if (align < 0 && align > -4) { - align = -4; - } - - if (!(*outAddr = memalign(align, size))) { - return OS_DYNLOAD_OUT_OF_MEMORY; - } - - // keep track of allocated memory to clean it up in case the RPLs won't get unloaded properly - gAllocatedAddresses.push_back(*outAddr); - - return OS_DYNLOAD_OK; -} - -static void CustomDynLoadFree(void *addr) { - free(addr); - - // Remove from list - if (const auto it = std::ranges::find(gAllocatedAddresses, addr); it != gAllocatedAddresses.end()) { - gAllocatedAddresses.erase(it); - } -} - -bool ResolveRelocations(const std::vector> &loadedModules, const bool skipUnloadedRpl, std::map &usedRPls) { - bool wasSuccessful = true; - - - OSDynLoadAllocFn prevDynLoadAlloc = nullptr; - OSDynLoadFreeFn prevDynLoadFree = nullptr; - - if (!skipUnloadedRpl) { - OSDynLoad_GetAllocator(&prevDynLoadAlloc, &prevDynLoadFree); - if (gCustomRPLAllocatorAllocFn != nullptr && gCustomRPLAllocatorFreeFn != nullptr) { - OSDynLoad_SetAllocator(reinterpret_cast(gCustomRPLAllocatorAllocFn), gCustomRPLAllocatorFreeFn); - } else { - OSDynLoad_SetAllocator(CustomDynLoadAlloc, CustomDynLoadFree); - } - } - - for (auto &curModule : loadedModules) { - DEBUG_FUNCTION_LINE("Let's do the relocations for %s", curModule->getExportName().c_str()); - - auto &relocData = curModule->getRelocationDataList(); - - if (!doRelocation(gLoadedModules, relocData, nullptr, 0, usedRPls, skipUnloadedRpl)) { - wasSuccessful = false; - DEBUG_FUNCTION_LINE_ERR("Failed to do Relocations for %s", curModule->getExportName().c_str()); - OSFatal("Failed to do Relocations"); - } - } - if (!skipUnloadedRpl) { - OSDynLoad_SetAllocator(prevDynLoadAlloc, prevDynLoadFree); - } - - DCFlushRange((void *) MEMORY_REGION_START, MEMORY_REGION_SIZE); - ICInvalidateRange((void *) MEMORY_REGION_START, MEMORY_REGION_SIZE); - return wasSuccessful; -} - - -bool doRelocation(const std::vector> &moduleList, - const std::vector> &relocData, - relocation_trampoline_entry_t *tramp_data, - const uint32_t tramp_length, - std::map &usedRPls, - const bool skipUnloadedRpl) { - for (auto const &curReloc : relocData) { - auto &functionName = curReloc->getName(); - std::string rplName = curReloc->getImportRPLInformation().getRPLName(); - uint32_t functionAddress = 0; - - for (auto &module : moduleList) { - if (rplName == module->getExportName()) { - auto found = false; - for (auto &exportData : module->getExportDataList()) { - if (functionName == exportData->getName()) { - functionAddress = (uint32_t) exportData->getAddress(); - found = true; - } - } - if (!found) { - DEBUG_FUNCTION_LINE_ERR("Failed to find export %s of module: %s", functionName.c_str(), rplName.c_str()); - OSFatal("Failed to find export of module."); - } - } - } - - if (functionName == "MEMAllocFromDefaultHeap") { - functionAddress = reinterpret_cast(&MEMAlloc); - } else if (functionName == "MEMAllocFromDefaultHeapEx") { - functionAddress = reinterpret_cast(&MEMAllocEx); - } else if (functionName == "MEMFreeToDefaultHeap") { - functionAddress = reinterpret_cast(&MEMFree); - } - - if (functionAddress == 0) { - int32_t isData = curReloc->getImportRPLInformation().isData(); - OSDynLoad_Module rplHandle = nullptr; - - if (!usedRPls.contains(rplName)) { - OSDynLoad_Module tmp = nullptr; - if (OSDynLoad_IsModuleLoaded(rplName.c_str(), &tmp) != OS_DYNLOAD_OK || tmp == nullptr) { - if (skipUnloadedRpl) { - DEBUG_FUNCTION_LINE_VERBOSE("Skip acquire of %s", rplName.c_str()); - continue; - } - } - - // 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 (See: AromaBaseModule) - usedRPls[rplName] = rplHandle; - } else { - //DEBUG_FUNCTION_LINE_VERBOSE("Use from usedRPLs cache! %s", rplName.c_str()); - } - rplHandle = usedRPls[rplName]; - - OSDynLoad_FindExport(rplHandle, (OSDynLoad_ExportType) isData, functionName.c_str(), (void **) &functionAddress); - if (functionAddress == 0) { - DEBUG_FUNCTION_LINE_ERR("Failed to find export %s of %s", functionName.c_str(), rplName.c_str()); - OSFatal("Failed to find export"); - return false; - } - } - - if (!ElfUtils::elfLinkOne(curReloc->getType(), curReloc->getOffset(), curReloc->getAddend(), (uint32_t) curReloc->getDestination(), functionAddress, tramp_data, tramp_length, - RELOC_TYPE_IMPORT)) { - DEBUG_FUNCTION_LINE_ERR("Relocation failed"); - return false; - } - } - if (tramp_data != nullptr) { - DCFlushRange(tramp_data, tramp_length * sizeof(relocation_trampoline_entry_t)); - ICInvalidateRange(tramp_data, tramp_length * sizeof(relocation_trampoline_entry_t)); - } - return true; -} \ No newline at end of file diff --git a/wumsloader/src/utils/RelocationUtils.h b/wumsloader/src/utils/RelocationUtils.h deleted file mode 100644 index 1237f33..0000000 --- a/wumsloader/src/utils/RelocationUtils.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include "module/ModuleData.h" -#include -#include -#include - -bool ResolveRelocations(const std::vector> &loadedModules, - bool skipUnloadedRpl, - std::map &usedRPls); - -bool doRelocation(const std::vector> &moduleList, - const std::vector> &relocData, - relocation_trampoline_entry_t *tramp_data, - uint32_t tramp_length, - std::map &usedRPls, - bool skipUnloadedRpl); \ No newline at end of file diff --git a/wumsloader/src/utils/SegmentedTimer.h b/wumsloader/src/utils/SegmentedTimer.h new file mode 100644 index 0000000..d62573d --- /dev/null +++ b/wumsloader/src/utils/SegmentedTimer.h @@ -0,0 +1,134 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace WUMSLoader::Utils { +class SegmentedTimer { +public: + inline static SegmentedTimer *globalTimer = nullptr; + + void start_segment(const std::string &segment_name) { + // Only start if it isn't already running + if (!mActiveSegments.contains(segment_name)) { + mActiveSegments.insert(segment_name); + mSegmentStartTimes[segment_name] = std::chrono::high_resolution_clock::now(); + } + } + + void stop_segment(const std::string &segment_name) { + // Only stop if it's currently tracked as running + if (mActiveSegments.contains(segment_name)) { + auto now = std::chrono::high_resolution_clock::now(); + mSegmentTotals[segment_name] += std::chrono::duration_cast(now - mSegmentStartTimes[segment_name]); + mActiveSegments.erase(segment_name); + } + } + + SegmentedTimer(std::string name = "Timer") + : name_(std::move(name)), + mStartTime(std::chrono::high_resolution_clock::now()), + mAccumulatedActiveTime(0) { + } + + ~SegmentedTimer() { + auto now = std::chrono::high_resolution_clock::now(); + + // Finalize main timers and any marks still running + mAccumulatedActiveTime += std::chrono::duration_cast(now - mStartTime); + + for (const auto &mark_name : mActiveSegments) { + mSegmentTotals[mark_name] += std::chrono::duration_cast(now - mSegmentStartTimes[mark_name]); + } + + // Print Main Timer + long long active_us = mAccumulatedActiveTime.count(); + OSReport("\n=== [%s] Profiler Results ===\n", name_.c_str()); + OSReport("Total Active: %.3f ms\n", active_us / 1000.0); + + // Print all Repurposed Segments (Sorted Longest to Shortest) + if (!mSegmentTotals.empty()) { + OSReport("--- Segments (Sorted by Time) ---\n"); + + // Copy unordered map contents to a vector for sorting + std::vector> sorted_segments( + mSegmentTotals.begin(), mSegmentTotals.end()); + + // Sort descending based on duration (second element in the pair) + std::sort(sorted_segments.begin(), sorted_segments.end(), + [](const auto &a, const auto &b) { + return a.second > b.second; + }); + + for (const auto &pair : sorted_segments) { + long long seg_us = pair.second.count(); + OSReport(" -> %8.3f ms : [%s]\n", seg_us / 1000.0, pair.first.c_str()); + } + } + OSReport("=================================\n\n"); + globalTimer = nullptr; + } + + // --- Segment API --- + + +private: + std::string name_; + + // Core Time points & Accumulators + std::chrono::time_point mStartTime; + std::chrono::microseconds mAccumulatedActiveTime; + + // Segment Tracking Maps + std::unordered_set mActiveSegments; + std::unordered_map> mSegmentStartTimes; + std::unordered_map mSegmentTotals; +}; + +class ScopedTimer { +public: + ScopedTimer(const char *name) : mName(name) { + if (SegmentedTimer::globalTimer) { + SegmentedTimer::globalTimer->start_segment(mName); + } + } + + ~ScopedTimer() { + if (SegmentedTimer::globalTimer) { + SegmentedTimer::globalTimer->stop_segment(mName); + } + } + +private: + const char *mName; +}; + + +} // namespace WUMSLoader::Utils + +#ifdef WUMS_ENABLE_PROFILING + +// Token pasting macros to generate unique local variable names based on the line number +#define PROFILE_CONCAT_INNER(a, b) a##b +#define PROFILE_CONCAT(a, b) PROFILE_CONCAT_INNER(a, b) + +// Drops a timer for the entire function scope using the short function name +#define PROFILE_FUNCTION() \ + WUMSLoader::Utils::ScopedTimer PROFILE_CONCAT(_scoped_timer_, __LINE__) { __func__ } + +// Drops a timer for a specific block or loop with a custom string +#define PROFILE_BLOCK(name) \ + WUMSLoader::Utils::ScopedTimer PROFILE_CONCAT(_scoped_timer_, __LINE__) { name } + +#else + +// When profiling is disabled, these macros expand to absolutely nothing. +// The compiler will ignore them completely (zero overhead). +#define PROFILE_FUNCTION() (void) 0 +#define PROFILE_BLOCK(name) (void) 0 + +#endif \ No newline at end of file diff --git a/wumsloader/src/utils/StringTools.cpp b/wumsloader/src/utils/StringTools.cpp deleted file mode 100644 index 3fa643a..0000000 --- a/wumsloader/src/utils/StringTools.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2010 -* by Dimok -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any -* damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any -* purpose, including commercial applications, and to alter it and -* redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you -* must not claim that you wrote the original software. If you use -* this software in a product, an acknowledgment in the product -* documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and -* must not be misrepresented as being the original software. -* -* 3. This notice may not be removed or altered from any source -* distribution. -* -* for WiiXplorer 2010 -***************************************************************************/ - -#include -#include -#include -#include -#include - -int32_t StringTools::strtokcmp(const char *string, const char *compare, const char *separator) { - if (!string || !compare) - return -1; - - char TokCopy[512]; - strncpy(TokCopy, compare, sizeof(TokCopy)); - TokCopy[511] = '\0'; - - char *strTok = strtok(TokCopy, separator); - - while (strTok != nullptr) { - if (strcasecmp(string, strTok) == 0) { - return 0; - } - strTok = strtok(nullptr, separator); - } - - return -1; -} diff --git a/wumsloader/src/utils/StringTools.h b/wumsloader/src/utils/StringTools.h index c64de2c..53dfbae 100644 --- a/wumsloader/src/utils/StringTools.h +++ b/wumsloader/src/utils/StringTools.h @@ -1,37 +1,8 @@ -/*************************************************************************** - * Copyright (C) 2010 - * by Dimok - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any - * damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any - * purpose, including commercial applications, and to alter it and - * redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you - * must not claim that you wrote the original software. If you use - * this software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and - * must not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - * - * for WiiXplorer 2010 - ***************************************************************************/ -#pragma once - #include "logger.h" #include "utils.h" #include #include #include -#include -#include template std::string string_format(const std::string &format, Args... args) { @@ -46,40 +17,3 @@ std::string string_format(const std::string &format, Args... args) { std::snprintf(buf.get(), size, format.c_str(), args...); return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside } - - -class StringTools { -public: - static int32_t strtokcmp(const char *string, const char *compare, const char *separator); - - - static const char *FullpathToFilename(const char *path) { - if (!path) - return path; - - const char *ptr = path; - const char *Filename = ptr; - - while (*ptr != '\0') { - if (ptr[0] == '/' && ptr[1] != '\0') - Filename = ptr + 1; - - ++ptr; - } - - return Filename; - } - - static void RemoveDoubleSlashs(std::string &str) { - uint32_t length = str.size(); - - //! clear path of double slashes - for (uint32_t i = 1; i < length; ++i) { - if (str[i - 1] == '/' && str[i] == '/') { - str.erase(i, 1); - i--; - length--; - } - } - } -}; \ No newline at end of file diff --git a/wumsloader/src/utils/hooks.cpp b/wumsloader/src/utils/hooks.cpp deleted file mode 100644 index 6828e67..0000000 --- a/wumsloader/src/utils/hooks.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include "hooks.h" -#include "globals.h" -#include "module/ModuleData.h" -#include "utils/logger.h" -#include -#include -#include -#include - -#ifdef DEBUG -static const char **hook_names = (const char *[]){ - "WUMS_HOOK_INIT_WUT_MALLOC", - "WUMS_HOOK_FINI_WUT_MALLOC", - "WUMS_HOOK_INIT_WUT_NEWLIB", - "WUMS_HOOK_FINI_WUT_NEWLIB", - "WUMS_HOOK_INIT_WUT_STDCPP", - "WUMS_HOOK_FINI_WUT_STDCPP", - "WUMS_HOOK_INIT_WUT_DEVOPTAB", - "WUMS_HOOK_FINI_WUT_DEVOPTAB", - "WUMS_HOOK_INIT_WUT_SOCKETS", - "WUMS_HOOK_FINI_WUT_SOCKETS", - - "WUMS_HOOK_INIT_WRAPPER", - "WUMS_HOOK_FINI_WRAPPER", - - "WUMS_HOOK_INIT", - "WUMS_HOOK_APPLICATION_STARTS", - "WUMS_HOOK_APPLICATION_ENDS", - "WUMS_HOOK_RELOCATIONS_DONE", - "WUMS_HOOK_APPLICATION_REQUESTS_EXIT", - - "WUMS_HOOK_DEINIT", - - "WUMS_HOOK_ALL_APPLICATION_STARTS_DONE", - "WUMS_HOOK_ALL_APPLICATION_ENDS_DONE", - "WUMS_HOOK_ALL_APPLICATION_REQUESTS_EXIT_DONE", - - "WUMS_HOOK_GET_CUSTOM_RPL_ALLOCATOR", - "WUMS_HOOK_CLEAR_ALLOCATED_RPL_MEMORY", - "WUMS_HOOK_CLEAR_ALLOCATED_RPL_MEMORY", - "WUMS_HOOK_INIT_WUT_THREAD"}; -#endif - -void CallHook(const std::vector> &modules, wums_hook_type_t type, bool condition) { - if (condition) { - CallHook(modules, type); - } -} - -void CallHook(const std::vector> &modules, wums_hook_type_t type) { - DEBUG_FUNCTION_LINE("Calling hook of type %s [%d] for all modules", hook_names[type], type); - for (auto &curModule : modules) { - CallHook(curModule, type); - } -} - -void CallHook(const std::shared_ptr &module, wums_hook_type_t type, bool condition) { - if (condition) { - CallHook(module, type); - } -} - -void CallHook(const std::shared_ptr &module, wums_hook_type_t type) { -#ifdef DEBUG - bool foundHook = false; -#endif - for (auto &curHook : module->getHookDataList()) { - auto func_ptr = (uint32_t) curHook->getFunctionPointer(); - if (func_ptr == 0) { - DEBUG_FUNCTION_LINE_ERR("Module %s: hook ptr was NULL", module->getExportName().c_str()); - break; - } - - if (type == curHook->getType()) { -#ifdef DEBUG - foundHook = true; -#endif - if ((type == WUMS_HOOK_APPLICATION_STARTS || - type == WUMS_HOOK_APPLICATION_ENDS || - type == WUMS_HOOK_INIT_WUT_MALLOC || - type == WUMS_HOOK_FINI_WUT_MALLOC || - type == WUMS_HOOK_INIT_WUT_NEWLIB || - type == WUMS_HOOK_FINI_WUT_NEWLIB || - type == WUMS_HOOK_INIT_WUT_STDCPP || - type == WUMS_HOOK_FINI_WUT_STDCPP || - type == WUMS_HOOK_INIT_WUT_DEVOPTAB || - type == WUMS_HOOK_FINI_WUT_DEVOPTAB || - type == WUMS_HOOK_INIT_WUT_SOCKETS || - type == WUMS_HOOK_FINI_WUT_SOCKETS || - type == WUMS_HOOK_FINI_WUT_SOCKETS || - type == WUMS_HOOK_INIT_WRAPPER || - type == WUMS_HOOK_FINI_WRAPPER || - type == WUMS_HOOK_DEINIT || - type == WUMS_HOOK_ALL_APPLICATION_STARTS_DONE || - type == WUMS_HOOK_ALL_APPLICATION_ENDS_DONE || - type == WUMS_HOOK_CLEAR_ALLOCATED_RPL_MEMORY || - type == WUMS_HOOK_ALL_APPLICATION_REQUESTS_EXIT_DONE || - type == WUMS_HOOK_INIT_WUT_THREAD)) { - DEBUG_FUNCTION_LINE("Calling hook of type %s [%d] %d for %s: %p", hook_names[type], type, curHook->getType(), module->getExportName().c_str(), curHook->getTarget()); - ((void (*)())((uint32_t *) func_ptr))(); - break; - } else if (type == WUMS_HOOK_INIT || - type == WUMS_HOOK_RELOCATIONS_DONE) { - DEBUG_FUNCTION_LINE("Calling hook of type %s [%d] %d for %s: %p", hook_names[type], type, curHook->getType(), module->getExportName().c_str(), curHook->getTarget()); - wums_app_init_args_t args; - args.module_information = &gModuleInformation; - ((void (*)(wums_app_init_args_t *))((uint32_t *) func_ptr))(&args); - } else if (type == WUMS_HOOK_GET_CUSTOM_RPL_ALLOCATOR) { - DEBUG_FUNCTION_LINE("Calling hook of type %s [%d] %d for %s: %p", hook_names[type], type, curHook->getType(), module->getExportName().c_str(), curHook->getTarget()); - - const auto [allocFn, freeFn] = reinterpret_cast(reinterpret_cast(func_ptr))(); - gCustomRPLAllocatorAllocFn = allocFn; - gCustomRPLAllocatorFreeFn = freeFn; - - } else { - DEBUG_FUNCTION_LINE_ERR("#########################################"); - DEBUG_FUNCTION_LINE_ERR("#########HOOK NOT IMPLEMENTED %d#########", type); - DEBUG_FUNCTION_LINE_ERR("#########################################"); - } - break; - } - } -#ifdef DEBUG - if (foundHook && !MEMCheckExpHeap(MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM2), MEM_EXP_HEAP_CHECK_FLAGS_LOG_ERRORS)) { - DEBUG_FUNCTION_LINE_ERR("MEM2 default heap is corrupted while calling hook %s for module %s", hook_names[type], module->getExportName().c_str()); - OSFatal("WUMSLoader: MEM2 default heap is corrupted. \n Please restart the console."); - } -#endif -} \ No newline at end of file diff --git a/wumsloader/src/utils/hooks.h b/wumsloader/src/utils/hooks.h deleted file mode 100644 index 7685af1..0000000 --- a/wumsloader/src/utils/hooks.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "module/ModuleData.h" -#include -#include -#include - -void CallHook(const std::vector> &modules, wums_hook_type_t type, bool condition); - -void CallHook(const std::vector> &modules, wums_hook_type_t type); - -void CallHook(const std::shared_ptr &module, wums_hook_type_t type, bool condition); - -void CallHook(const std::shared_ptr &module, wums_hook_type_t type); diff --git a/wumsloader/src/utils/imports.h b/wumsloader/src/utils/imports.h index 4f36a1b..3d7192e 100644 --- a/wumsloader/src/utils/imports.h +++ b/wumsloader/src/utils/imports.h @@ -57,6 +57,8 @@ IMPORT(OSFastMutex_Unlock); IMPORT(OSInitSpinLock); IMPORT(OSGetAlarmUserData); IMPORT(OSRestoreInterrupts); +IMPORT(OSGetTime); +IMPORT(OSGetSystemTime); IMPORT(OSDisableInterrupts); IMPORT(FSTimeToCalendarTime); diff --git a/wumsloader/src/utils/logger.h b/wumsloader/src/utils/logger.h index 8880106..ef67468 100644 --- a/wumsloader/src/utils/logger.h +++ b/wumsloader/src/utils/logger.h @@ -8,11 +8,24 @@ extern "C" { #endif -#define LOG_APP_TYPE "M" -#define LOG_APP_NAME "WUMSLoader" +#define LOG_APP_TYPE "M" +#define LOG_APP_NAME "WUMSLoader" -#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) -#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) +#ifdef __cplusplus +constexpr const char *ConstexprFileName(const char *path) { + const char *file = path; + for (const char *p = path; *p; ++p) { + if (*p == '/' || *p == '\\') { + file = p + 1; + } + } + return file; +} +#define __FILENAME__ ConstexprFileName(__FILE__) +#else +#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) +#endif #define LOG(LOG_FUNC, FMT, ARGS...) LOG_EX(LOG_FUNC, "", "", FMT, ##ARGS) @@ -37,6 +50,7 @@ extern "C" { #define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS) #define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX(WHBLogPrintf, "##WARN ## ", "", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX(OSReport, "##INFO ## ", "\n", FMT, ##ARGS) #else diff --git a/wumsloader/src/utils/memory.cpp b/wumsloader/src/utils/memory.cpp index 2c935fc..898d9b1 100644 --- a/wumsloader/src/utils/memory.cpp +++ b/wumsloader/src/utils/memory.cpp @@ -51,8 +51,6 @@ void *MemoryAllocEx(uint32_t size, uint32_t align) { void MemoryFree(void *ptr) { if (ptr) { MEMFreeToExpHeap(gHeapHandle, ptr); - } else { - OSFatal("Failed to free"); } } diff --git a/wumsloader/src/utils/utils.h b/wumsloader/src/utils/utils.h index 11704d7..c08e94f 100644 --- a/wumsloader/src/utils/utils.h +++ b/wumsloader/src/utils/utils.h @@ -1,9 +1,17 @@ #pragma once + #include #include -#define ROUNDDOWN(val, align) ((val) & ~(align - 1)) -#define ROUNDUP(val, align) ROUNDDOWN(((val) + (align - 1)), align) +template +[[nodiscard]] constexpr T ROUNDDOWN(T val, U align) { + return val & ~(static_cast(align) - 1); +} + +template +[[nodiscard]] constexpr T ROUNDUP(T val, U align) { + return ROUNDDOWN(val + static_cast(align - 1), align); +} template std::unique_ptr make_unique_nothrow(Args &&...args) noexcept(noexcept(T(std::forward(args)...))) {