WUMSLoader/wumsloader/src/utils/RelocationUtils.cpp

152 lines
6.3 KiB
C++

#include "RelocationUtils.h"
#include "ElfUtils.h"
#include "globals.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
auto it = std::find(gAllocatedAddresses.begin(), gAllocatedAddresses.end(), addr);
if (it != gAllocatedAddresses.end()) {
gAllocatedAddresses.erase(it);
}
}
bool ResolveRelocations(std::vector<std::shared_ptr<ModuleData>> &loadedModules, bool skipMemoryMappingModule, std::map<std::string, OSDynLoad_Module> &usedRPls) {
bool wasSuccessful = true;
OSDynLoadAllocFn prevDynLoadAlloc = nullptr;
OSDynLoadFreeFn prevDynLoadFree = nullptr;
OSDynLoad_GetAllocator(&prevDynLoadAlloc, &prevDynLoadFree);
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();
// On first usage we can't redirect the alloc functions to our custom heap
// because threads can't run it on it. In order to patch the kernel
// to fully support our memory region, we have to run the MemoryMapping once with the default heap.
// Afterwards we can just rely on the custom heap.
bool skipAllocFunction = skipMemoryMappingModule && (std::string_view(curModule->getExportName()) == "homebrew_memorymapping");
DEBUG_FUNCTION_LINE_VERBOSE("Skip alloc replace? %d", skipAllocFunction);
if (!doRelocation(gLoadedModules, relocData, nullptr, 0, skipAllocFunction, usedRPls)) {
wasSuccessful = false;
DEBUG_FUNCTION_LINE_ERR("Failed to do Relocations for %s", curModule->getExportName().c_str());
OSFatal("Failed to do Relocations");
}
}
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,
uint32_t tramp_length,
bool skipAllocReplacement,
std::map<std::string, OSDynLoad_Module> &usedRPls) {
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 (!skipAllocReplacement) {
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)) {
DEBUG_FUNCTION_LINE_VERBOSE("Acquire %s", rplName.c_str());
// Always acquire to increase refcount and make sure it won't get unloaded while we're using it.
OSDynLoad_Error err = OSDynLoad_Acquire(rplName.c_str(), &rplHandle);
if (err != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE_ERR("Failed to acquire %s", rplName.c_str());
return false;
}
// Keep track RPLs we are using.
// They will be released on exit (See: AromaBaseModule)
usedRPls[rplName] = rplHandle;
} else {
DEBUG_FUNCTION_LINE_VERBOSE("Use from usedRPLs cache! %s", rplName.c_str());
}
rplHandle = usedRPls[rplName];
OSDynLoad_FindExport(rplHandle, 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;
}