mirror of
https://github.com/wiiu-env/WUMSLoader.git
synced 2026-04-22 17:27:19 -05:00
Rewrite module loading to load some module into custom memory mapped heap.
This commit is contained in:
parent
b024464ca7
commit
a94111fa36
|
|
@ -42,7 +42,7 @@ IndentPPDirectives: None
|
|||
IndentWidth: 4
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MaxEmptyLinesToKeep: 2
|
||||
NamespaceIndentation: All
|
||||
NamespaceIndentation: None
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PointerAlignment: Right
|
||||
|
|
|
|||
|
|
@ -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 <coreinit/cache.h>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#include "../wumsloader/src/globals.h"
|
||||
#include "../wumsloader/src/defines.h"
|
||||
#include "kernel.h"
|
||||
#include "utils/logger.h"
|
||||
|
||||
#include <nn/act/client_cpp.h>
|
||||
#include <string>
|
||||
#include <sysapp/launch.h>
|
||||
|
||||
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<uint32_t *>(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();
|
||||
|
|
|
|||
2
wumsloader/.clangd
Normal file
2
wumsloader/.clangd
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
CompileFlags:
|
||||
Add: [-m32]
|
||||
35
wumsloader/src/defines.h
Normal file
35
wumsloader/src/defines.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
|
||||
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<uint8_t> getMemoryForHeap() {
|
||||
return std::span(reinterpret_cast<uint8_t *>(MEMORY_REGION_USABLE_HEAP_START), (*reinterpret_cast<uint32_t *>(MEMORY_REGION_USABLE_MEM_REGION_END_VALUE_PTR)) - MEMORY_REGION_USABLE_HEAP_START);
|
||||
}
|
||||
|
||||
inline std::span<char> getEnvironmentStringBuffer() {
|
||||
return std::span(
|
||||
reinterpret_cast<char *>(MEMORY_REGION_ENVIRONMENT_STRING_ADRR),
|
||||
MEMORY_REGION_ENVIRONMENT_PATH_LENGTH);
|
||||
}
|
||||
|
|
@ -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 <coreinit/debug.h>
|
||||
#include <coreinit/interrupts.h>
|
||||
#include <coreinit/kernel.h>
|
||||
#include <coreinit/memexpheap.h>
|
||||
#include <coreinit/thread.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
#include <list>
|
||||
|
||||
#define VERSION "v0.2.9"
|
||||
|
||||
void CallInitHooksForModule(const std::shared_ptr<ModuleData> &curModule);
|
||||
|
||||
bool CheckModulesByDependencies(const std::vector<std::shared_ptr<ModuleData>> &loadedModules);
|
||||
std::vector<std::shared_ptr<ModuleData>> OrderModulesByDependencies(const std::vector<std::shared_ptr<ModuleData>> &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<std::string, OSDynLoad_Module> &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<std::shared_ptr<ModuleData>> 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<ModuleData> &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<std::shared_ptr<ModuleData>> &loadedModules) {
|
||||
std::set<std::string> 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<std::shared_ptr<ModuleData>> OrderModulesByDependencies(const std::vector<std::shared_ptr<ModuleData>> &loadedModules) {
|
||||
std::vector<std::shared_ptr<ModuleData>> finalOrder;
|
||||
std::set<std::string> loadedModulesExportNames;
|
||||
std::set<uint32_t> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <algorithm>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <strings.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <fs/DirList.h>
|
||||
#include <utils/StringTools.h>
|
||||
|
||||
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<DirEntry>().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;
|
||||
}
|
||||
|
|
@ -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 <string>
|
||||
#include <vector>
|
||||
#include <wut_types.h>
|
||||
|
||||
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<DirEntry> FileInfo;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#include "utils/logger.h"
|
||||
#include "utils/utils.h"
|
||||
#include <coreinit/memdefaultheap.h>
|
||||
#include <cstdint>
|
||||
#include <fcntl.h>
|
||||
#include <malloc.h>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
int32_t LoadFileToMem(const std::string &filepath, uint8_t **inbuffer, uint32_t *size);
|
||||
|
|
@ -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<std::shared_ptr<ModuleData>> gLoadedModules __attribute__((section(".data")));
|
||||
std::unique_ptr<WUMSLoader::Modules::PersistedModuleData> gModuleInformation __attribute__((section(".data")));
|
||||
std::vector<WUMSLoader::Modules::ModuleContainer> gLoadedModules __attribute__((section(".data")));
|
||||
std::unique_ptr<module_information_single_t[]> gModuleDataInfo __attribute__((section(".data")));
|
||||
|
||||
std::map<std::string, OSDynLoad_Module> gUsedRPLs __attribute__((section(".data")));
|
||||
std::map<std::string, OSDynLoad_Module, std::less<>> gUsedRPLs __attribute__((section(".data")));
|
||||
std::vector<void *> gAllocatedAddresses __attribute__((section(".data")));
|
||||
|
||||
WUMSRPLAllocatorAllocFn gCustomRPLAllocatorAllocFn __attribute__((section(".data"))) = nullptr;
|
||||
|
|
|
|||
|
|
@ -1,45 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "module/ModuleContainer.h"
|
||||
#include "utils/SegmentedTimer.h"
|
||||
|
||||
#include <wums/hooks.h>
|
||||
|
||||
#include <coreinit/dynload.h>
|
||||
#include <coreinit/memheap.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class ModuleData;
|
||||
namespace WUMSLoader::Modules {
|
||||
class PersistedModuleData;
|
||||
}
|
||||
|
||||
extern uint8_t gInitCalled;
|
||||
extern MEMHeapHandle gHeapHandle;
|
||||
extern module_information_t gModuleInformation;
|
||||
extern std::vector<std::shared_ptr<ModuleData>> gLoadedModules;
|
||||
extern std::unique_ptr<WUMSLoader::Modules::PersistedModuleData> gModuleInformation;
|
||||
extern std::vector<WUMSLoader::Modules::ModuleContainer> gLoadedModules;
|
||||
extern std::unique_ptr<module_information_single_t[]> gModuleDataInfo;
|
||||
extern std::map<std::string, OSDynLoad_Module> gUsedRPLs;
|
||||
extern std::map<std::string, OSDynLoad_Module, std::less<>> gUsedRPLs;
|
||||
extern std::vector<void *> 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;
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
#include <coreinit/cache.h>
|
||||
#include <valarray>
|
||||
|
||||
#include "ElfUtils.h"
|
||||
#include "utils/logger.h"
|
||||
|
||||
#include <span>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
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<relocation_trampoline_entry_t> 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<int32_t>(value) - static_cast<int32_t>(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<uint32_t>(&(freeSlot->trampoline[0])));
|
||||
return false;
|
||||
}
|
||||
auto symbolValue = (uint32_t) & (freeSlot->trampoline[0]);
|
||||
auto symbolValue = reinterpret_cast<uint32_t>(&(freeSlot->trampoline[0]));
|
||||
auto newValue = symbolValue + addend;
|
||||
auto newDistance = static_cast<int32_t>(newValue) - static_cast<int32_t>(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<int32_t *>(target) = (*reinterpret_cast<int32_t *>(target) & 0xfc000003) | (distance & 0x03fffffc);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DEBUG_FUNCTION_LINE_ERR("***ERROR: Unsupported Relocation_Add Type (%08X):", type);
|
||||
return false;
|
||||
}
|
||||
ICInvalidateRange(reinterpret_cast<void *>(target), 4);
|
||||
DCFlushRange(reinterpret_cast<void *>(target), 4);
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace WUMSLoader::Modules::ElfUtils
|
||||
40
wumsloader/src/module/ElfUtils.h
Normal file
40
wumsloader/src/module/ElfUtils.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include <wums/defines/relocation_defines.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
|
||||
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<relocation_trampoline_entry_t> trampolineData, RelocationType reloc_type);
|
||||
|
||||
} // namespace WUMSLoader::Modules::ElfUtils
|
||||
|
|
@ -1,31 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include <wums/exports.h>
|
||||
|
||||
#include <string>
|
||||
#include <wums.h>
|
||||
|
||||
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;
|
||||
};
|
||||
wums_entry_type_t mType;
|
||||
std::string mName;
|
||||
const void *mAddress;
|
||||
};
|
||||
|
||||
} // namespace WUMSLoader::Modules
|
||||
|
|
@ -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<uint32_t>(mAddress) < reinterpret_cast<uint32_t>(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;
|
||||
}
|
||||
|
|
@ -22,25 +22,32 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
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;
|
||||
};
|
||||
};
|
||||
} // namespace WUMSLoader::Modules
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -19,18 +19,25 @@
|
|||
|
||||
#include <wums/hooks.h>
|
||||
|
||||
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
|
||||
167
wumsloader/src/module/HooksManagement.cpp
Normal file
167
wumsloader/src/module/HooksManagement.cpp
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
#include "HooksManagement.h"
|
||||
|
||||
#include "ModuleContainer.h"
|
||||
#include "ModuleDataPersistence.h"
|
||||
#include "globals.h"
|
||||
#include "utils/logger.h"
|
||||
|
||||
#include <coreinit/memexpheap.h>
|
||||
|
||||
|
||||
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<ModuleContainer> &modules, wums_hook_type_t type, bool condition) {
|
||||
if (condition) {
|
||||
CallHook(modules, type);
|
||||
}
|
||||
}
|
||||
|
||||
void CallHook(const std::vector<ModuleContainer> &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<wums_internal_custom_rpl_allocator_t (*)()>(reinterpret_cast<uint32_t *>(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
|
||||
22
wumsloader/src/module/HooksManagement.h
Normal file
22
wumsloader/src/module/HooksManagement.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <wums/hooks.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace WUMSLoader::Modules {
|
||||
|
||||
class ModuleContainer;
|
||||
|
||||
namespace HooksManagement {
|
||||
|
||||
void CallHook(const std::vector<ModuleContainer> &modules, wums_hook_type_t type, bool condition);
|
||||
|
||||
void CallHook(const std::vector<ModuleContainer> &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
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#include "ImportRPLInformation.h"
|
||||
#include <cstring>
|
||||
|
||||
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_");
|
||||
}
|
||||
static constexpr std::string_view prefix = ".dimport_";
|
||||
return mName.starts_with(prefix);
|
||||
}
|
||||
} // namespace WUMSLoader::Modules
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
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
|
||||
60
wumsloader/src/module/ModuleAllocator.cpp
Normal file
60
wumsloader/src/module/ModuleAllocator.cpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#include "ModuleAllocator.h"
|
||||
|
||||
#include "module/ModuleContainer.h"
|
||||
#include "module/ModuleDataPersistence.h"
|
||||
#include "utils/logger.h"
|
||||
|
||||
#include <coreinit/memdefaultheap.h>
|
||||
#include <coreinit/memexpheap.h>
|
||||
#include <optional>
|
||||
|
||||
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> ModuleAllocator::FromMappedMemory(const std::vector<ModuleContainer> &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
|
||||
20
wumsloader/src/module/ModuleAllocator.h
Normal file
20
wumsloader/src/module/ModuleAllocator.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "utils/HeapMemoryFixedSize.h"
|
||||
|
||||
#include <coreinit/memheap.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
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<ModuleAllocator> FromMappedMemory(const std::vector<ModuleContainer> &modules);
|
||||
};
|
||||
} // namespace WUMSLoader::Modules
|
||||
35
wumsloader/src/module/ModuleContainer.cpp
Normal file
35
wumsloader/src/module/ModuleContainer.cpp
Normal file
|
|
@ -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<uint32_t>(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
|
||||
41
wumsloader/src/module/ModuleContainer.h
Normal file
41
wumsloader/src/module/ModuleContainer.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include "ModuleLinkInformation.h"
|
||||
#include "ModuleMetaInformation.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
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<uint32_t> mHandle = std::make_unique<uint32_t>();
|
||||
};
|
||||
|
||||
} // namespace WUMSLoader::Modules
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ExportData.h"
|
||||
#include "FunctionSymbolData.h"
|
||||
#include "HookData.h"
|
||||
#include "RelocationData.h"
|
||||
#include "SectionInfo.h"
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct FunctionSymbolDataComparator {
|
||||
bool operator()(const std::shared_ptr<FunctionSymbolData> &lhs,
|
||||
const std::shared_ptr<FunctionSymbolData> &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<RelocationData> relocation_data) {
|
||||
addDependency(relocation_data->getImportRPLInformation().getRPLName());
|
||||
relocation_data_list.push_back(std::move(relocation_data));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::unique_ptr<RelocationData>> &getRelocationDataList() const {
|
||||
return relocation_data_list;
|
||||
}
|
||||
|
||||
void addExportData(std::unique_ptr<ExportData> data) {
|
||||
export_data_list.push_back(std::move(data));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::unique_ptr<ExportData>> &getExportDataList() const {
|
||||
return export_data_list;
|
||||
}
|
||||
|
||||
void addHookData(std::unique_ptr<HookData> data) {
|
||||
hook_data_list.push_back(std::move(data));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::unique_ptr<HookData>> &getHookDataList() const {
|
||||
return hook_data_list;
|
||||
}
|
||||
|
||||
void addDependency(std::string module_name) {
|
||||
dependency_list.insert(std::move(module_name));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::set<std::string> &getDependencies() const {
|
||||
return dependency_list;
|
||||
}
|
||||
|
||||
void addSectionInfo(std::shared_ptr<SectionInfo> sectionInfo) {
|
||||
section_info_list[sectionInfo->getName()] = std::move(sectionInfo);
|
||||
}
|
||||
|
||||
void addFunctionSymbolData(std::shared_ptr<FunctionSymbolData> symbol_data) {
|
||||
symbol_data_list.insert(std::move(symbol_data));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::set<std::shared_ptr<FunctionSymbolData>, FunctionSymbolDataComparator> &getFunctionSymbolDataList() const {
|
||||
return symbol_data_list;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::map<std::string_view, std::shared_ptr<SectionInfo>> &getSectionInfoList() const {
|
||||
return section_info_list;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<std::shared_ptr<SectionInfo>> 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<uint32_t>(dataPtr.get());
|
||||
}
|
||||
|
||||
[[nodiscard]] uint32_t getEndAddress() const {
|
||||
return reinterpret_cast<uint32_t>(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<uint8_t[]> ptr, uint32_t size) {
|
||||
this->dataPtr = std::move(ptr);
|
||||
this->totalSize = size;
|
||||
}
|
||||
|
||||
std::unique_ptr<hook_data_t[]> hookDataStruct;
|
||||
std::unique_ptr<export_data_t[]> exportDataStruct;
|
||||
std::unique_ptr<module_function_symbol_data_t[]> functionSymbolDataStruct;
|
||||
std::unique_ptr<dyn_linking_import_t[]> rplDataStruct;
|
||||
std::unique_ptr<dyn_linking_relocation_entry_t[]> relocationDataStruct;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<RelocationData>> relocation_data_list;
|
||||
std::vector<std::unique_ptr<ExportData>> export_data_list;
|
||||
std::vector<std::unique_ptr<HookData>> hook_data_list;
|
||||
std::set<std::string> dependency_list;
|
||||
std::set<std::shared_ptr<FunctionSymbolData>, FunctionSymbolDataComparator> symbol_data_list;
|
||||
std::map<std::string_view, std::shared_ptr<SectionInfo>> section_info_list;
|
||||
|
||||
std::unique_ptr<uint8_t[]> 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;
|
||||
};
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
****************************************************************************/
|
||||
|
||||
#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 <coreinit/cache.h>
|
||||
#include <coreinit/memdefaultheap.h>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <wums.h>
|
||||
|
||||
using namespace ELFIO;
|
||||
|
||||
std::optional<std::shared_ptr<ModuleData>> ModuleDataFactory::load(const std::string &path) {
|
||||
elfio reader(new wiiu_zlib);
|
||||
auto moduleData = make_shared_nothrow<ModuleData>();
|
||||
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<char *>(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<uint8_t *[]>(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<uint8_t[]>(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<void *>(destination), 0, sectionSize);
|
||||
|
||||
} else if (psec->get_name() == ".sbss") {
|
||||
moduleData->setSBSSLocation(destination, sectionSize);
|
||||
memset(reinterpret_cast<void *>(destination), 0, sectionSize);
|
||||
}
|
||||
auto sectionInfo = make_shared_nothrow<SectionInfo>(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<ExportData>(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<HookData>(const_cast<void *>(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<FunctionSymbolData>(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> &moduleData, ELFIO::elfio &reader, uint8_t **destinations) {
|
||||
std::map<uint32_t, std::shared_ptr<ImportRPLInformation>> 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<ImportRPLInformation>(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<RelocationData>(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;
|
||||
}
|
||||
|
|
@ -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 <algorithm>
|
||||
#include <coreinit/cache.h>
|
||||
|
||||
bool ModuleDataPersistence::saveModuleData(module_information_t *moduleInformation, const std::vector<std::shared_ptr<ModuleData>> &moduleList) {
|
||||
auto module_data_list = make_unique_nothrow<module_information_single_t[]>(moduleList.size());
|
||||
if (!module_data_list) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to alloc memory to persist module data");
|
||||
return false;
|
||||
#include <map>
|
||||
|
||||
namespace WUMSLoader::Modules {
|
||||
|
||||
module_information_t *PersistedModuleData::getModuleInformation() {
|
||||
return &mRootInfo;
|
||||
}
|
||||
void PersistedModuleData::setUsedRPLsList(const std::map<std::string, OSDynLoad_Module, std::less<>> &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<uint32_t *>(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<uint32_t>(rpl.second);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
void PersistedModuleData::build(const std::vector<ModuleContainer> &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<ModuleData> &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> PersistedModuleData::create() {
|
||||
auto persistedData = std::unique_ptr<PersistedModuleData>(new PersistedModuleData());
|
||||
persistedData->clear();
|
||||
return persistedData;
|
||||
}
|
||||
|
||||
bool ModuleDataPersistence::saveFunctionSymbolDataForModule(module_information_single_t &module_data, const std::shared_ptr<ModuleData> &module) {
|
||||
uint32_t entryCount = module->getFunctionSymbolDataList().size();
|
||||
auto function_symbol_data = make_unique_nothrow<module_function_symbol_data_t[]>(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<ModuleData> &module) {
|
||||
auto export_data = make_unique_nothrow<export_data_t[]>(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<ModuleData> &module) {
|
||||
auto hook_data = make_unique_nothrow<hook_data_t[]>(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<ModuleData> &module) {
|
||||
auto relocation_data = make_unique_nothrow<dyn_linking_relocation_entry_t[]>(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<std::string_view> 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<dyn_linking_import_t[]>(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<std::string_view, dyn_linking_import_t *> 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<void *>(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<char *>(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<uint32_t>(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<module_function_symbol_data_t> data;
|
||||
data.reserve(symList.size());
|
||||
|
||||
for (const auto &sym : symList) {
|
||||
data.push_back({const_cast<char *>(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<export_data_t> data;
|
||||
data.reserve(exportList.size());
|
||||
|
||||
for (const auto &exp : exportList) {
|
||||
data.push_back({static_cast<uint32_t>(exp.getType()), exp.getName().c_str(), reinterpret_cast<uint32_t>(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<hook_data_t> data;
|
||||
data.reserve(hookList.size());
|
||||
|
||||
for (const auto &hook : hookList) {
|
||||
data.push_back({.type = static_cast<uint32_t>(hook.getType()),
|
||||
.target = reinterpret_cast<uint32_t>(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<std::string_view, const ImportRPLInformation *> uniqueRpls;
|
||||
for (const auto &reloc : relocList) {
|
||||
const auto &rplInfo = reloc.getImportRPLInformation();
|
||||
uniqueRpls.try_emplace(rplInfo.getName(), &rplInfo);
|
||||
}
|
||||
|
||||
std::vector<dyn_linking_import_t> rplData;
|
||||
rplData.reserve(uniqueRpls.size());
|
||||
|
||||
// Map string to INDEX
|
||||
std::map<std::string_view, size_t> 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<dyn_linking_relocation_entry_t> 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<void *>(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
|
||||
|
|
@ -1,15 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include "ModuleData.h"
|
||||
#include <coreinit/memheap.h>
|
||||
#include <wums.h>
|
||||
#include <wums/defines/module_defines.h>
|
||||
|
||||
class ModuleDataPersistence {
|
||||
#include <coreinit/dynload.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace WUMSLoader::Modules {
|
||||
class ModuleContainer;
|
||||
class PersistedModuleData {
|
||||
public:
|
||||
static bool saveModuleData(module_information_t *moduleInformation, const std::vector<std::shared_ptr<ModuleData>> &moduleList);
|
||||
static bool saveModuleData(module_information_single_t &module_data, const std::shared_ptr<ModuleData> &module);
|
||||
static bool saveRelocationDataForModule(module_information_single_t &module_data, const std::shared_ptr<ModuleData> &module);
|
||||
static bool saveExportDataForModule(module_information_single_t &module_data, const std::shared_ptr<ModuleData> &module);
|
||||
static bool saveHookDataForModule(module_information_single_t &module_data, const std::shared_ptr<ModuleData> &module);
|
||||
static bool saveFunctionSymbolDataForModule(module_information_single_t &module_data, const std::shared_ptr<ModuleData> &module);
|
||||
[[nodiscard]] module_information_t *getModuleInformation();
|
||||
|
||||
void setUsedRPLsList(const std::map<std::string, OSDynLoad_Module, std::less<>> &usedRPls);
|
||||
|
||||
void build(const std::vector<ModuleContainer> &moduleList);
|
||||
|
||||
static std::unique_ptr<PersistedModuleData> 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<module_information_single_t> mModules;
|
||||
std::vector<std::vector<hook_data_t>> mHookDataList;
|
||||
std::vector<std::vector<export_data_t>> mExportDataList;
|
||||
std::vector<std::vector<module_function_symbol_data_t>> mFunctionSymbolDataList;
|
||||
std::vector<std::vector<dyn_linking_relocation_entry_t>> mRelocationDataList;
|
||||
std::vector<std::vector<dyn_linking_import_t>> mRplDataList;
|
||||
};
|
||||
} // namespace WUMSLoader::Modules
|
||||
129
wumsloader/src/module/ModuleLinkInformation.cpp
Normal file
129
wumsloader/src/module/ModuleLinkInformation.cpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#include "ModuleLinkInformation.h"
|
||||
|
||||
#include "utils/logger.h"
|
||||
|
||||
#include <coreinit/cache.h>
|
||||
|
||||
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<HookData> &ModuleLinkInformation::getHookDataList() const {
|
||||
return mHookDataList;
|
||||
}
|
||||
|
||||
void ModuleLinkInformation::addExportData(ExportData exportData) {
|
||||
mExportDataList.push_back(std::move(exportData));
|
||||
}
|
||||
|
||||
const std::vector<ExportData> &ModuleLinkInformation::getExportDataList() const {
|
||||
return mExportDataList;
|
||||
}
|
||||
|
||||
void ModuleLinkInformation::addRelocationData(RelocationData relocationData) {
|
||||
mRelocationDataList.push_back(std::move(relocationData));
|
||||
}
|
||||
|
||||
const std::vector<RelocationData> &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<std::string, SectionInfo> &ModuleLinkInformation::getSectionInfoList() const {
|
||||
return mSectionInfoList;
|
||||
}
|
||||
|
||||
std::optional<SectionInfo> 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<FunctionSymbolData> &ModuleLinkInformation::getFunctionSymbolDataList() const {
|
||||
return mSymbolDataList;
|
||||
}
|
||||
|
||||
void *ModuleLinkInformation::getEntrypoint() const {
|
||||
return mEntrypoint;
|
||||
}
|
||||
|
||||
uint32_t ModuleLinkInformation::getStartAddress() const {
|
||||
return reinterpret_cast<uint32_t>(mModuleMemoryPool.dataView().data());
|
||||
}
|
||||
|
||||
uint32_t ModuleLinkInformation::getEndAddress() const {
|
||||
return reinterpret_cast<uint32_t>(mModuleMemoryPool.dataView().data()) + mModuleMemoryPool.dataView().size();
|
||||
}
|
||||
const std::set<std::string> &ModuleLinkInformation::getDependencyList() const {
|
||||
return mDependencies;
|
||||
}
|
||||
|
||||
std::span<relocation_trampoline_entry_t> 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<relocation_trampoline_entry_t *>(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
|
||||
87
wumsloader/src/module/ModuleLinkInformation.h
Normal file
87
wumsloader/src/module/ModuleLinkInformation.h
Normal file
|
|
@ -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 <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
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<ExportData> &getExportDataList() const;
|
||||
|
||||
[[nodiscard]] const std::vector<HookData> &getHookDataList() const;
|
||||
|
||||
[[nodiscard]] const std::vector<RelocationData> &getRelocationDataList() const;
|
||||
|
||||
[[nodiscard]] const std::map<std::string, SectionInfo> &getSectionInfoList() const;
|
||||
|
||||
[[nodiscard]] std::optional<SectionInfo> getSectionInfo(const std::string §ionName) const;
|
||||
|
||||
[[nodiscard]] const std::vector<FunctionSymbolData> &getFunctionSymbolDataList() const;
|
||||
|
||||
[[nodiscard]] void *getEntrypoint() const;
|
||||
|
||||
[[nodiscard]] uint32_t getStartAddress() const;
|
||||
|
||||
[[nodiscard]] uint32_t getEndAddress() const;
|
||||
|
||||
[[nodiscard]] const std::set<std::string> &getDependencyList() const;
|
||||
|
||||
[[nodiscard]] std::span<relocation_trampoline_entry_t> 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<std::string> mDependencies;
|
||||
std::vector<HookData> mHookDataList;
|
||||
std::vector<RelocationData> mRelocationDataList;
|
||||
std::vector<ExportData> mExportDataList;
|
||||
|
||||
std::vector<FunctionSymbolData> mSymbolDataList;
|
||||
std::map<std::string, SectionInfo> mSectionInfoList;
|
||||
|
||||
void *mEntrypoint;
|
||||
|
||||
HeapMemoryFixedSizePool mModuleMemoryPool;
|
||||
|
||||
friend class ModuleLinkInformationFactory;
|
||||
};
|
||||
} // namespace WUMSLoader::Modules
|
||||
557
wumsloader/src/module/ModuleLinkInformationFactory.cpp
Normal file
557
wumsloader/src/module/ModuleLinkInformationFactory.cpp
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
****************************************************************************/
|
||||
|
||||
#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 <wums/exports.h>
|
||||
|
||||
#include <coreinit/memdefaultheap.h>
|
||||
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <wums/hooks.h>
|
||||
|
||||
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<Elf_Half>(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<uint32_t>(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<int32_t>(offset);
|
||||
const auto value = static_cast<int32_t>(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<relocation_trampoline_entry_t> 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<ModuleLinkInformation> ModuleLinkInformationFactory::loadAndLink(const std::span<uint8_t> 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<const char *>(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<uint8_t *[]>(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<relocation_trampoline_entry_t *>(tramp_data.data()), expectedTramps);
|
||||
// Segment 2 the data data
|
||||
const auto &data_data = moduleMemoryPool[ModuleLinkInformation::MEMORY_POOL_INDEX_DATA];
|
||||
|
||||
uint32_t entrypoint = reinterpret_cast<uint32_t>(text_data.data()) + static_cast<uint32_t>(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<uint32_t>(psec->get_address());
|
||||
|
||||
uint32_t destination = address;
|
||||
if ((address >= 0x02000000) && address < 0x10000000) {
|
||||
destination += reinterpret_cast<uint32_t>(text_data.data());
|
||||
destination -= 0x02000000;
|
||||
destinations[psec->get_index()] = static_cast<uint8_t *>(text_data.data());
|
||||
|
||||
if (destination + sectionSize > reinterpret_cast<uint32_t>(text_data.data()) + textSize) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Tried to overflow .text buffer. %08X > %08X", destination + sectionSize, reinterpret_cast<uint32_t>(text_data.data()) + text_data.size());
|
||||
error = ModuleLinkErrors::MODULE_LINK_ERROR_OVERFLOW;
|
||||
return std::nullopt;
|
||||
} else if (destination < reinterpret_cast<uint32_t>(text_data.data())) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Tried to underflow .text buffer. %08X < %08X", destination, reinterpret_cast<uint32_t>(text_data.data()));
|
||||
error = ModuleLinkErrors::MODULE_LINK_ERROR_UNDERFLOW;
|
||||
return std::nullopt;
|
||||
}
|
||||
} else if ((address >= 0x10000000) && address < 0xC0000000) {
|
||||
destination += reinterpret_cast<uint32_t>(data_data.data());
|
||||
destination -= 0x10000000;
|
||||
destinations[psec->get_index()] = static_cast<uint8_t *>(data_data.data());
|
||||
|
||||
if (destination + sectionSize > reinterpret_cast<uint32_t>(data_data.data()) + data_data.size()) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Tried to overflow .data buffer. %08X > %08X", destination + sectionSize, reinterpret_cast<uint32_t>(data_data.data()) + data_data.size());
|
||||
error = ModuleLinkErrors::MODULE_LINK_ERROR_OVERFLOW;
|
||||
return std::nullopt;
|
||||
} else if (destination < reinterpret_cast<uint32_t>(data_data.data())) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Tried to underflow .data buffer. %08X < %08X", destination, reinterpret_cast<uint32_t>(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<uint32_t>(destinations[psec->get_index()]), reinterpret_cast<uint32_t>(text_data.data()), reinterpret_cast<uint32_t>(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<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);
|
||||
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<const char *>(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<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);
|
||||
moduleInfo.addHookData(HookData(const_cast<void *>(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<void *>(finalAddress), static_cast<uint32_t>(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<void *>(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<uint8_t *> destinations) {
|
||||
uint32_t sec_num = reader.sections.size();
|
||||
|
||||
std::vector<std::shared_ptr<ImportRPLInformation>> 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<ImportRPLInformation>(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<Elf_Half>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<void *>(destinations[section_index]),
|
||||
sym_name,
|
||||
infoMap[sym_section_index]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace WUMSLoader::Modules
|
||||
83
wumsloader/src/module/ModuleLinkInformationFactory.h
Normal file
83
wumsloader/src/module/ModuleLinkInformationFactory.h
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
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<ModuleLinkInformation> loadAndLink(std::span<uint8_t> buffer, const ModuleAllocator &moduleAllocator, ModuleLinkErrors &error);
|
||||
|
||||
private:
|
||||
static bool addImportRelocationData(ModuleLinkInformation &moduleInfo, const ELFIO::elfio &reader, std::span<uint8_t *> destinations);
|
||||
};
|
||||
|
||||
} // namespace WUMSLoader::Modules
|
||||
343
wumsloader/src/module/ModuleManagement.cpp
Normal file
343
wumsloader/src/module/ModuleManagement.cpp
Normal file
|
|
@ -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 <algorithm>
|
||||
#include <sys/dirent.h>
|
||||
#include <vector>
|
||||
|
||||
namespace WUMSLoader::Modules {
|
||||
|
||||
namespace {
|
||||
|
||||
struct ModuleInfoWithLoadedFileWrapper {
|
||||
ModuleMetaInformation metaInfo;
|
||||
Utils::LoadedFile file;
|
||||
};
|
||||
|
||||
std::vector<std::string> getModuleFilePaths(const std::string &basePath) {
|
||||
PROFILE_FUNCTION();
|
||||
std::vector<std::string> 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<ModuleInfoWithLoadedFileWrapper> &modules) {
|
||||
PROFILE_FUNCTION();
|
||||
std::set<std::string, std::less<>> 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<ModuleInfoWithLoadedFileWrapper> OrderModulesMetaByDependencies(std::vector<ModuleInfoWithLoadedFileWrapper> &&modulesMetaInfo) {
|
||||
PROFILE_FUNCTION();
|
||||
std::vector<std::reference_wrapper<ModuleInfoWithLoadedFileWrapper>> pendingModules;
|
||||
pendingModules.reserve(modulesMetaInfo.size());
|
||||
for (auto &mod : modulesMetaInfo) {
|
||||
pendingModules.push_back(std::ref(mod));
|
||||
}
|
||||
|
||||
std::vector<std::reference_wrapper<ModuleInfoWithLoadedFileWrapper>> orderedRefs;
|
||||
orderedRefs.reserve(modulesMetaInfo.size());
|
||||
|
||||
std::set<std::string, std::less<>> 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<ModuleInfoWithLoadedFileWrapper> 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<ModuleInfoWithLoadedFileWrapper> loadModuleMetaInformationForPath(std::vector<Utils::LoadedFile> &&loadedFiles) {
|
||||
PROFILE_FUNCTION();
|
||||
|
||||
std::vector<ModuleInfoWithLoadedFileWrapper> 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<Utils::LoadedFile> loadModulesIntoMemory(const std::string &basePath) {
|
||||
PROFILE_FUNCTION();
|
||||
std::vector<Utils::LoadedFile> 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<ModuleInfoWithLoadedFileWrapper>, std::vector<ModuleInfoWithLoadedFileWrapper>> getModulesInCorrectOrderAndSplit(std::vector<ModuleInfoWithLoadedFileWrapper> &&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<ModuleInfoWithLoadedFileWrapper> earlyModules;
|
||||
std::vector<ModuleInfoWithLoadedFileWrapper> 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<ModuleContainer> ModuleManagement::loadAndLinkModules(const std::string &basePath, std::map<std::string, OSDynLoad_Module, std::less<>> &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<ModuleContainer> 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
|
||||
18
wumsloader/src/module/ModuleManagement.h
Normal file
18
wumsloader/src/module/ModuleManagement.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <coreinit/dynload.h>
|
||||
#include <coreinit/memheap.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace WUMSLoader::Modules {
|
||||
class PersistedModuleData;
|
||||
class ModuleContainer;
|
||||
|
||||
class ModuleManagement {
|
||||
public:
|
||||
static std::vector<ModuleContainer> loadAndLinkModules(const std::string &basePath, std::map<std::string, OSDynLoad_Module, std::less<>> &usedRPls, MEMHeapHandle defaultHeapHandle, PersistedModuleData &persistedData);
|
||||
};
|
||||
} // namespace WUMSLoader::Modules
|
||||
59
wumsloader/src/module/ModuleMetaInformation.cpp
Normal file
59
wumsloader/src/module/ModuleMetaInformation.cpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#include "ModuleMetaInformation.h"
|
||||
#include "WUMSVersion.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
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<std::string> &ModuleMetaInformation::getDependencyList() const {
|
||||
return mDependencyList;
|
||||
}
|
||||
|
||||
} // namespace WUMSLoader::Modules
|
||||
40
wumsloader/src/module/ModuleMetaInformation.h
Normal file
40
wumsloader/src/module/ModuleMetaInformation.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include "WUMSVersion.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
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<std::string> &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<std::string> mDependencyList;
|
||||
WUMSVersion mWUMSVersion = WUMSVersion(0, 0, 0);
|
||||
bool mInitBeforeRelocationDoneHook = false;
|
||||
bool mSkipInitFini = false;
|
||||
size_t mSize = 0;
|
||||
};
|
||||
|
||||
} // namespace WUMSLoader::Modules
|
||||
165
wumsloader/src/module/ModuleMetaInformationFactory.cpp
Normal file
165
wumsloader/src/module/ModuleMetaInformationFactory.cpp
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
****************************************************************************/
|
||||
|
||||
#include "ModuleMetaInformationFactory.h"
|
||||
|
||||
#include "ModuleMetaInformation.h"
|
||||
#include "elfio/elfio.hpp"
|
||||
#include "utils/logger.h"
|
||||
#include "utils/wiiu_zlib.hpp"
|
||||
|
||||
#include <coreinit/memdefaultheap.h>
|
||||
#include <optional>
|
||||
|
||||
namespace WUMSLoader::Modules {
|
||||
|
||||
std::optional<ModuleMetaInformation> ModuleMetaInformationFactory::loadModuleMetaInfo(const std::span<const uint8_t> 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<const char *>(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<uint32_t>(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<int>(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
|
||||
|
|
@ -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 <coreinit/memheap.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <wums.h>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
class ModuleDataFactory {
|
||||
public:
|
||||
static std::optional<std::shared_ptr<ModuleData>> load(const std::string &path);
|
||||
#include <cstdint>
|
||||
|
||||
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> &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<ModuleMetaInformation> loadModuleMetaInfo(std::span<const uint8_t> buffer, ModuleParseErrors &error);
|
||||
};
|
||||
} // namespace WUMSLoader::Modules
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
#include "RelocationData.h"
|
||||
#include "ImportRPLInformation.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
} // namespace WUMSLoader::Modules
|
||||
|
|
@ -17,13 +17,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "ImportRPLInformation.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
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<ImportRPLInformation> mRPLInfo;
|
||||
};
|
||||
} // namespace WUMSLoader::Modules
|
||||
203
wumsloader/src/module/RelocationUtils.cpp
Normal file
203
wumsloader/src/module/RelocationUtils.cpp
Normal file
|
|
@ -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 <wums/defines/relocation_defines.h>
|
||||
|
||||
#include <coreinit/debug.h>
|
||||
#include <coreinit/dynload.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include <malloc.h>
|
||||
|
||||
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<RelocationData> &relocData,
|
||||
const std::map<std::string, const ModuleContainer *, std::less<>> &moduleMap,
|
||||
const ExternalRPLLoadingStrategy rplLoadingStrategy,
|
||||
std::span<relocation_trampoline_entry_t> trampData,
|
||||
std::map<std::string, OSDynLoad_Module, std::less<>> &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<uint32_t>(exportData.getAddress());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to find export %.*s of module: %.*s",
|
||||
static_cast<int>(functionName.length()), functionName.data(),
|
||||
static_cast<int>(rplName.length()), rplName.data());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (functionAddress == 0) {
|
||||
if (functionName == "MEMAllocFromDefaultHeap") {
|
||||
functionAddress = reinterpret_cast<uint32_t>(&MEMAlloc);
|
||||
} else if (functionName == "MEMAllocFromDefaultHeapEx") {
|
||||
functionAddress = reinterpret_cast<uint32_t>(&MEMAllocEx);
|
||||
} else if (functionName == "MEMFreeToDefaultHeap") {
|
||||
functionAddress = reinterpret_cast<uint32_t>(&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<OSDynLoad_ExportType>(isData),
|
||||
functionName.c_str(), reinterpret_cast<void **>(&functionAddress));
|
||||
|
||||
if (res != OS_DYNLOAD_OK || functionAddress == 0) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to find export %.*s of %.*s",
|
||||
static_cast<int>(functionName.length()), functionName.c_str(),
|
||||
static_cast<int>(rplName.length()), rplName.data());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ElfUtils::elfLinkOne(curReloc.getType(), curReloc.getOffset(), curReloc.getAddend(),
|
||||
reinterpret_cast<uint32_t>(curReloc.getDestination()),
|
||||
functionAddress, trampData, RELOC_TYPE_IMPORT)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
bool ResolveRelocations(const std::vector<ModuleContainer> &loadedModules, const ExternalRPLLoadingStrategy rplLoadingStrategy, std::map<std::string, OSDynLoad_Module, std::less<>> &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<OSDynLoadAllocFn>(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<std::string, const ModuleContainer *, std::less<>> 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
|
||||
27
wumsloader/src/module/RelocationUtils.h
Normal file
27
wumsloader/src/module/RelocationUtils.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <coreinit/dynload.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace WUMSLoader::Modules {
|
||||
class ModuleContainer;
|
||||
class RelocationData;
|
||||
|
||||
namespace RelocationUtils {
|
||||
|
||||
enum class ExternalRPLLoadingStrategy {
|
||||
LOAD_EXTERNAL_RPLS,
|
||||
IGNORE_EXTERNAL_RPLS
|
||||
};
|
||||
|
||||
bool ResolveRelocations(const std::vector<ModuleContainer> &loadedModules,
|
||||
ExternalRPLLoadingStrategy rplLoadingStrategy,
|
||||
std::map<std::string, OSDynLoad_Module, std::less<>> &usedRPls);
|
||||
|
||||
} // namespace RelocationUtils
|
||||
} // namespace WUMSLoader::Modules
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
} // namespace WUMSLoader::Modules
|
||||
|
|
@ -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 <cstdint>
|
||||
|
||||
namespace WUMSLoader::Modules {
|
||||
class SectionInfo {
|
||||
|
||||
public:
|
||||
|
|
@ -39,3 +40,4 @@ private:
|
|||
uint32_t mAddress = {};
|
||||
uint32_t mSectionSize = {};
|
||||
};
|
||||
} // namespace WUMSLoader::Modules
|
||||
44
wumsloader/src/module/WUMSVersion.cpp
Normal file
44
wumsloader/src/module/WUMSVersion.cpp
Normal file
|
|
@ -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> 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<int>(major), static_cast<int>(minor), static_cast<int>(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
|
||||
26
wumsloader/src/module/WUMSVersion.h
Normal file
26
wumsloader/src/module/WUMSVersion.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace WUMSLoader::Modules {
|
||||
|
||||
class WUMSVersion {
|
||||
public:
|
||||
WUMSVersion(int major, int minor, int revision);
|
||||
|
||||
static std::optional<WUMSVersion> 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
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <wums.h>
|
||||
|
||||
#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);
|
||||
};
|
||||
92
wumsloader/src/utils/HeapMemoryFixedSize.cpp
Normal file
92
wumsloader/src/utils/HeapMemoryFixedSize.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#include "HeapMemoryFixedSize.h"
|
||||
|
||||
#include "logger.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
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<size_t> segmentSizes)
|
||||
: HeapMemoryFixedSizePool(allocFunc, std::move(freeFunc), std::span(segmentSizes.begin(), segmentSizes.size())) {
|
||||
}
|
||||
|
||||
HeapMemoryFixedSizePool::HeapMemoryFixedSizePool(const AllocFunc &allocFunc, FreeFunc freeFunc, const std::span<const std::size_t> 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<uint8_t[], CustomDeleter>(
|
||||
static_cast<uint8_t *>(raw_mem),
|
||||
CustomDeleter{std::move(freeFunc)});
|
||||
|
||||
mTotalSize = (mData ? totalSize : 0);
|
||||
if (mData) {
|
||||
auto address = reinterpret_cast<uint32_t>(mData.get());
|
||||
for (const auto size : segmentSizes) {
|
||||
address = ROUNDUP(static_cast<int>(address), 0x40);
|
||||
assert(address >= reinterpret_cast<uint32_t>(mData.get()) && address < reinterpret_cast<uint32_t>(mData.get()) + totalSize);
|
||||
mSegmentInfos.emplace_back(reinterpret_cast<void *>(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<const uint8_t> HeapMemoryFixedSizePool::dataView() const {
|
||||
return {mData.get(), mTotalSize};
|
||||
}
|
||||
64
wumsloader/src/utils/HeapMemoryFixedSize.h
Normal file
64
wumsloader/src/utils/HeapMemoryFixedSize.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
class HeapMemoryFixedSizePool {
|
||||
public:
|
||||
// Define our custom allocator and deleter signatures
|
||||
using AllocFunc = std::function<void *(size_t size, int32_t alignment)>;
|
||||
using FreeFunc = std::function<void(void *ptr)>;
|
||||
|
||||
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<size_t> segmentSizes);
|
||||
HeapMemoryFixedSizePool(const AllocFunc &allocFunc, FreeFunc freeFunc, std::span<const std::size_t> 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<const uint8_t> 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<uint8_t[], CustomDeleter> mData{nullptr, CustomDeleter{nullptr}};
|
||||
std::size_t mTotalSize{};
|
||||
std::vector<MemorySegmentInfo> mSegmentInfos;
|
||||
};
|
||||
122
wumsloader/src/utils/LoadedFile.cpp
Normal file
122
wumsloader/src/utils/LoadedFile.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
#include "LoadedFile.h"
|
||||
|
||||
#include "globals.h"
|
||||
#include "utils/logger.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
#include <coreinit/memdefaultheap.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <malloc.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
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<int>(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<int>(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<LoadedFile> 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<uint32_t>(rawSize);
|
||||
lseek(fd.get(), 0, SEEK_SET);
|
||||
|
||||
auto *buffer = static_cast<uint8_t *>(MEMAllocFromDefaultHeapEx(ROUNDUP(static_cast<int>(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<uint32_t>(readBytes);
|
||||
}
|
||||
|
||||
if (done != filesize) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
} // namespace WUMSLoader::Utils
|
||||
32
wumsloader/src/utils/LoadedFile.h
Normal file
32
wumsloader/src/utils/LoadedFile.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
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<LoadedFile> LoadFileToMem(const std::string &filepath);
|
||||
|
||||
} // namespace WUMSLoader::Utils
|
||||
|
|
@ -106,8 +106,8 @@ private:
|
|||
* from the object passed as the function argument.
|
||||
*/
|
||||
template<typename Func>
|
||||
OnLeavingScope<typename std::decay<Func>::type> onLeavingScope(Func &&f) {
|
||||
return OnLeavingScope<typename std::decay<Func>::type>(std::forward<Func>(f));
|
||||
OnLeavingScope<std::decay_t<Func>> onLeavingScope(Func &&f) {
|
||||
return OnLeavingScope<std::decay_t<Func>>(std::forward<Func>(f));
|
||||
}
|
||||
|
||||
#endif // CRASCIT_ONLEAVINGSCOPE_H
|
||||
|
|
|
|||
|
|
@ -1,160 +0,0 @@
|
|||
#include "RelocationUtils.h"
|
||||
#include "ElfUtils.h"
|
||||
#include "globals.h"
|
||||
#include "logger.h"
|
||||
#include "memory.h"
|
||||
#include <algorithm>
|
||||
#include <coreinit/cache.h>
|
||||
#include <iterator>
|
||||
#include <malloc.h>
|
||||
#include <vector>
|
||||
|
||||
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<std::shared_ptr<ModuleData>> &loadedModules, const bool skipUnloadedRpl, std::map<std::string, OSDynLoad_Module> &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<OSDynLoadAllocFn>(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<std::shared_ptr<ModuleData>> &moduleList,
|
||||
const std::vector<std::unique_ptr<RelocationData>> &relocData,
|
||||
relocation_trampoline_entry_t *tramp_data,
|
||||
const uint32_t tramp_length,
|
||||
std::map<std::string, OSDynLoad_Module> &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<uint32_t>(&MEMAlloc);
|
||||
} else if (functionName == "MEMAllocFromDefaultHeapEx") {
|
||||
functionAddress = reinterpret_cast<uint32_t>(&MEMAllocEx);
|
||||
} else if (functionName == "MEMFreeToDefaultHeap") {
|
||||
functionAddress = reinterpret_cast<uint32_t>(&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;
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
#pragma once
|
||||
#include "module/ModuleData.h"
|
||||
#include <coreinit/dynload.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
bool ResolveRelocations(const std::vector<std::shared_ptr<ModuleData>> &loadedModules,
|
||||
bool skipUnloadedRpl,
|
||||
std::map<std::string, OSDynLoad_Module> &usedRPls);
|
||||
|
||||
bool doRelocation(const std::vector<std::shared_ptr<ModuleData>> &moduleList,
|
||||
const std::vector<std::unique_ptr<RelocationData>> &relocData,
|
||||
relocation_trampoline_entry_t *tramp_data,
|
||||
uint32_t tramp_length,
|
||||
std::map<std::string, OSDynLoad_Module> &usedRPls,
|
||||
bool skipUnloadedRpl);
|
||||
134
wumsloader/src/utils/SegmentedTimer.h
Normal file
134
wumsloader/src/utils/SegmentedTimer.h
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <coreinit/debug.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
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<std::chrono::microseconds>(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<std::chrono::microseconds>(now - mStartTime);
|
||||
|
||||
for (const auto &mark_name : mActiveSegments) {
|
||||
mSegmentTotals[mark_name] += std::chrono::duration_cast<std::chrono::microseconds>(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<std::pair<std::string, std::chrono::microseconds>> 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<std::chrono::high_resolution_clock> mStartTime;
|
||||
std::chrono::microseconds mAccumulatedActiveTime;
|
||||
|
||||
// Segment Tracking Maps
|
||||
std::unordered_set<std::string> mActiveSegments;
|
||||
std::unordered_map<std::string, std::chrono::time_point<std::chrono::high_resolution_clock>> mSegmentStartTimes;
|
||||
std::unordered_map<std::string, std::chrono::microseconds> 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
|
||||
|
|
@ -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 <cstring>
|
||||
#include <string>
|
||||
#include <strings.h>
|
||||
#include <utils/StringTools.h>
|
||||
#include <wut_types.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -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 <coreinit/debug.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <wut_types.h>
|
||||
|
||||
template<typename... Args>
|
||||
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--;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
#include "hooks.h"
|
||||
#include "globals.h"
|
||||
#include "module/ModuleData.h"
|
||||
#include "utils/logger.h"
|
||||
#include <coreinit/memexpheap.h>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <wums.h>
|
||||
|
||||
#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<std::shared_ptr<ModuleData>> &modules, wums_hook_type_t type, bool condition) {
|
||||
if (condition) {
|
||||
CallHook(modules, type);
|
||||
}
|
||||
}
|
||||
|
||||
void CallHook(const std::vector<std::shared_ptr<ModuleData>> &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<ModuleData> &module, wums_hook_type_t type, bool condition) {
|
||||
if (condition) {
|
||||
CallHook(module, type);
|
||||
}
|
||||
}
|
||||
|
||||
void CallHook(const std::shared_ptr<ModuleData> &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<wums_internal_custom_rpl_allocator_t (*)()>(reinterpret_cast<uint32_t *>(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
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "module/ModuleData.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <wums/hooks.h>
|
||||
|
||||
void CallHook(const std::vector<std::shared_ptr<ModuleData>> &modules, wums_hook_type_t type, bool condition);
|
||||
|
||||
void CallHook(const std::vector<std::shared_ptr<ModuleData>> &modules, wums_hook_type_t type);
|
||||
|
||||
void CallHook(const std::shared_ptr<ModuleData> &module, wums_hook_type_t type, bool condition);
|
||||
|
||||
void CallHook(const std::shared_ptr<ModuleData> &module, wums_hook_type_t type);
|
||||
|
|
@ -57,6 +57,8 @@ IMPORT(OSFastMutex_Unlock);
|
|||
IMPORT(OSInitSpinLock);
|
||||
IMPORT(OSGetAlarmUserData);
|
||||
IMPORT(OSRestoreInterrupts);
|
||||
IMPORT(OSGetTime);
|
||||
IMPORT(OSGetSystemTime);
|
||||
IMPORT(OSDisableInterrupts);
|
||||
|
||||
IMPORT(FSTimeToCalendarTime);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#define ROUNDDOWN(val, align) ((val) & ~(align - 1))
|
||||
#define ROUNDUP(val, align) ROUNDDOWN(((val) + (align - 1)), align)
|
||||
template<typename T, typename U>
|
||||
[[nodiscard]] constexpr T ROUNDDOWN(T val, U align) {
|
||||
return val & ~(static_cast<T>(align) - 1);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
[[nodiscard]] constexpr T ROUNDUP(T val, U align) {
|
||||
return ROUNDDOWN(val + static_cast<T>(align - 1), align);
|
||||
}
|
||||
|
||||
template<class T, class... Args>
|
||||
std::unique_ptr<T> make_unique_nothrow(Args &&...args) noexcept(noexcept(T(std::forward<Args>(args)...))) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user