WUMSLoader/relocator/src/entry.cpp

264 lines
12 KiB
C++
Raw Normal View History

#include "../../source/module/RelocationData.h"
2020-04-28 14:43:07 +02:00
#include "ElfUtils.h"
2022-02-04 21:44:03 +01:00
#include "ModuleDataPersistence.h"
#include "globals.h"
#include "hooks.h"
2022-02-04 21:44:03 +01:00
#include "utils/dynamic.h"
#include "utils/logger.h"
#include "utils/memory.h"
#include <algorithm>
#include <coreinit/cache.h>
#include <coreinit/dynload.h>
#include <coreinit/memexpheap.h>
#include <cstdint>
#include <map>
#include <string>
#include <vector>
2020-04-28 14:43:07 +02:00
MEMHeapHandle gHeapHandle __attribute__((section(".data"))) = nullptr;
uint8_t gFunctionsPatched __attribute__((section(".data"))) = 0;
2022-02-04 21:44:03 +01:00
uint8_t gInitCalled __attribute__((section(".data"))) = 0;
2020-05-29 19:27:30 +02:00
2021-04-17 11:53:51 +02:00
extern "C" void socket_lib_init();
2021-12-07 18:23:18 +01:00
std::vector<std::shared_ptr<ModuleDataMinimal>> OrderModulesByDependencies(const std::vector<std::shared_ptr<ModuleDataMinimal>> &loadedModules);
void CallInitHooksForModule(const std::shared_ptr<ModuleDataMinimal> &curModule);
2020-04-28 14:43:07 +02:00
extern "C" void doStart(int argc, char **argv);
// We need to wrap it to make sure the main function is called AFTER our code.
// The compiler tries to optimize this otherwise and calling the main function earlier
extern "C" int _start(int argc, char **argv) {
InitFunctionPointers();
static uint8_t ucSetupRequired = 1;
if (ucSetupRequired) {
2022-02-04 21:44:03 +01:00
gHeapHandle = MEMCreateExpHeapEx((void *) (MEMORY_REGION_USABLE_HEAP_START), MEMORY_REGION_USABLE_HEAP_END - MEMORY_REGION_USABLE_HEAP_START, 1);
ucSetupRequired = 0;
}
2020-04-28 14:43:07 +02:00
socket_lib_init();
log_init();
2020-05-17 19:05:51 +02:00
doStart(argc, argv);
2020-04-28 14:43:07 +02:00
2021-04-01 00:41:13 +02:00
DEBUG_FUNCTION_LINE_VERBOSE("Call real one\n");
2021-04-17 11:53:51 +02:00
log_deinit();
2021-09-23 18:38:29 +02:00
2022-02-04 21:44:03 +01:00
return ((int (*)(int, char **))(*(unsigned int *) 0x1005E040))(argc, argv);
2020-04-28 14:43:07 +02:00
}
2022-01-27 12:52:45 +01:00
bool doRelocation(std::vector<std::shared_ptr<RelocationData>> &relocData, relocation_trampoline_entry_t *tramp_data, uint32_t tramp_length, bool skipAllocReplacement) {
2020-05-17 19:05:51 +02:00
std::map<std::string, OSDynLoad_Module> moduleCache;
2022-02-04 21:44:03 +01:00
for (auto const &curReloc : relocData) {
2021-12-07 18:23:18 +01:00
std::string functionName = curReloc->getName();
2022-02-04 21:44:03 +01:00
std::string rplName = curReloc->getImportRPLInformation()->getName();
2020-04-28 14:43:07 +02:00
uint32_t functionAddress = 0;
for (uint32_t i = 0; i < MAXIMUM_MODULES; i++) {
if (rplName == gModuleData->module_data[i].module_export_name) {
export_data_t *exportEntries = gModuleData->module_data[i].export_entries;
for (uint32_t j = 0; j < EXPORT_ENTRY_LIST_LENGTH; j++) {
if (functionName == exportEntries[j].name) {
functionAddress = (uint32_t) exportEntries[j].address;
}
}
}
}
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);
}
}
2020-06-01 16:38:13 +02:00
if (functionAddress == 0) {
2022-02-04 21:44:03 +01:00
int32_t isData = curReloc->getImportRPLInformation()->isData();
OSDynLoad_Module rplHandle = nullptr;
if (moduleCache.count(rplName) == 0) {
OSDynLoad_Error err = OSDynLoad_IsModuleLoaded(rplName.c_str(), &rplHandle);
if (err != OS_DYNLOAD_OK || rplHandle == nullptr) {
2021-04-01 00:41:13 +02:00
DEBUG_FUNCTION_LINE_VERBOSE("%s is not yet loaded\n", rplName.c_str());
// only acquire if not already loaded.
err = OSDynLoad_Acquire(rplName.c_str(), &rplHandle);
if (err != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("Failed to acquire %s\n", rplName.c_str());
//return false;
}
}
moduleCache[rplName] = rplHandle;
}
rplHandle = moduleCache.at(rplName);
OSDynLoad_FindExport(rplHandle, isData, functionName.c_str(), (void **) &functionAddress);
if (functionAddress == 0) {
OSFatal_printf("Failed to find export %s of %s", functionName.c_str(), rplName.c_str());
return false;
}
2020-04-28 14:43:07 +02:00
}
2021-12-07 18:23:18 +01:00
if (!ElfUtils::elfLinkOne(curReloc->getType(), curReloc->getOffset(), curReloc->getAddend(), (uint32_t) curReloc->getDestination(), functionAddress, tramp_data, tramp_length,
RELOC_TYPE_IMPORT)) {
2020-04-28 14:43:07 +02:00
DEBUG_FUNCTION_LINE("Relocation failed\n");
return false;
}
}
2022-01-27 12:52:45 +01:00
DCFlushRange(tramp_data, tramp_length * sizeof(relocation_trampoline_entry_t));
ICInvalidateRange(tramp_data, tramp_length * sizeof(relocation_trampoline_entry_t));
2020-04-28 14:43:07 +02:00
return true;
}
2020-05-17 19:05:51 +02:00
2021-12-07 18:23:18 +01:00
bool ResolveRelocations(std::vector<std::shared_ptr<ModuleDataMinimal>> &loadedModules, bool skipMemoryMappingModule) {
2020-04-28 14:43:07 +02:00
bool wasSuccessful = true;
2022-02-04 21:44:03 +01:00
for (auto &curModule : loadedModules) {
2021-12-07 18:23:18 +01:00
DEBUG_FUNCTION_LINE_VERBOSE("Let's do the relocations for %s\n", curModule->getExportName().c_str());
2020-05-17 19:05:51 +02:00
if (wasSuccessful) {
2021-12-07 18:23:18 +01:00
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 FunctionPatcher and MemoryMapping
// once with the default heap. Afterwards we can just rely on the custom heap.
2021-12-07 18:23:18 +01:00
bool skipAllocFunction = skipMemoryMappingModule && (curModule->getExportName() == "homebrew_memorymapping" || curModule->getExportName() == "homebrew_functionpatcher");
2021-04-01 00:41:13 +02:00
DEBUG_FUNCTION_LINE_VERBOSE("Skip alloc replace? %d\n", skipAllocFunction);
2022-01-27 12:52:45 +01:00
if (!doRelocation(relocData, gModuleData->trampolines, DYN_LINK_TRAMPOLINE_LIST_LENGTH, skipAllocFunction)) {
2020-04-28 14:43:07 +02:00
DEBUG_FUNCTION_LINE("FAIL\n");
2022-02-04 21:44:03 +01:00
wasSuccessful = false;
2021-12-07 18:23:18 +01:00
curModule->relocationsDone = false;
2020-04-28 14:43:07 +02:00
}
2021-12-07 18:23:18 +01:00
curModule->relocationsDone = true;
2020-04-28 14:43:07 +02:00
}
}
DCFlushRange((void *) MEMORY_REGION_START, MEMORY_REGION_SIZE);
ICInvalidateRange((void *) MEMORY_REGION_START, MEMORY_REGION_SIZE);
2020-04-28 14:43:07 +02:00
return wasSuccessful;
}
extern "C" void doStart(int argc, char **argv) {
2020-05-17 19:05:51 +02:00
if (!gFunctionsPatched) {
gFunctionsPatched = 1;
}
2020-05-29 19:27:30 +02:00
DEBUG_FUNCTION_LINE("Loading module data\n");
auto loadedModulesUnordered = ModuleDataPersistence::loadModuleData(gModuleData);
2022-02-04 21:44:03 +01:00
auto loadedModules = OrderModulesByDependencies(loadedModulesUnordered);
2020-05-29 19:27:30 +02:00
bool applicationEndHookLoaded = false;
2022-02-04 21:44:03 +01:00
for (auto &curModule : loadedModules) {
2021-12-07 18:23:18 +01:00
if (curModule->getExportName() == "homebrew_applicationendshook") {
2021-04-01 00:41:13 +02:00
DEBUG_FUNCTION_LINE_VERBOSE("We have ApplicationEndsHook Module!\n");
applicationEndHookLoaded = true;
break;
}
}
// Make sure WUMS_HOOK_APPLICATION_ENDS and WUMS_HOOK_FINI_WUT are called
2022-02-04 21:44:03 +01:00
for (auto &curModule : loadedModules) {
for (auto &curHook : curModule->getHookDataList()) {
2021-12-07 18:23:18 +01:00
if (curHook->getType() == WUMS_HOOK_APPLICATION_ENDS || curHook->getType() == WUMS_HOOK_FINI_WUT_DEVOPTAB) {
if (!applicationEndHookLoaded) {
2021-12-07 18:23:18 +01:00
OSFatal_printf("%s requires module homebrew_applicationendshook", curModule->getExportName().c_str());
}
}
}
}
2021-04-01 00:41:13 +02:00
DEBUG_FUNCTION_LINE_VERBOSE("Number of modules %d\n", gModuleData->number_used_modules);
2020-05-29 19:27:30 +02:00
if (!gInitCalled) {
gInitCalled = 1;
2021-04-01 00:41:13 +02:00
DEBUG_FUNCTION_LINE_VERBOSE("Resolve relocations without replacing alloc functions\n");
ResolveRelocations(loadedModules, true);
2022-02-04 21:44:03 +01:00
for (auto &curModule : loadedModules) {
2021-12-07 18:23:18 +01:00
if (curModule->isInitBeforeRelocationDoneHook()) {
CallInitHooksForModule(curModule);
}
}
2020-05-29 19:27:30 +02:00
2022-01-23 22:17:48 +01:00
DEBUG_FUNCTION_LINE_VERBOSE("Call Relocations done hook\n");
CallHook(loadedModules, WUMS_HOOK_RELOCATIONS_DONE);
2022-02-04 21:44:03 +01:00
for (auto &curModule : loadedModules) {
2021-12-07 18:23:18 +01:00
if (!curModule->isInitBeforeRelocationDoneHook()) {
CallInitHooksForModule(curModule);
}
}
} else {
2021-04-01 00:41:13 +02:00
DEBUG_FUNCTION_LINE_VERBOSE("Resolve relocations and replace alloc functions\n");
ResolveRelocations(loadedModules, false);
CallHook(loadedModules, WUMS_HOOK_RELOCATIONS_DONE);
2020-04-28 14:43:07 +02:00
}
2021-12-07 18:23:18 +01:00
2021-09-17 16:22:54 +02:00
CallHook(loadedModules, WUMS_HOOK_INIT_WUT_DEVOPTAB);
CallHook(loadedModules, WUMS_HOOK_INIT_WUT_SOCKETS);
CallHook(loadedModules, WUMS_HOOK_APPLICATION_STARTS);
2020-04-28 14:43:07 +02:00
}
2020-05-29 19:27:30 +02:00
void CallInitHooksForModule(const std::shared_ptr<ModuleDataMinimal> &curModule) {
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);
2022-01-23 22:07:38 +01:00
CallHook(curModule, WUMS_HOOK_INIT_WRAPPER, !curModule->isSkipInitFini());
CallHook(curModule, WUMS_HOOK_INIT);
}
2021-12-07 18:23:18 +01:00
std::vector<std::shared_ptr<ModuleDataMinimal>> OrderModulesByDependencies(const std::vector<std::shared_ptr<ModuleDataMinimal>> &loadedModules) {
std::vector<std::shared_ptr<ModuleDataMinimal>> finalOrder;
2020-05-29 19:27:30 +02:00
std::vector<std::string> loadedModulesExportNames;
std::vector<uint32_t> loadedModulesEntrypoints;
while (true) {
2022-02-04 21:44:03 +01:00
bool canBreak = true;
2020-05-29 19:27:30 +02:00
bool weDidSomething = false;
2022-02-04 21:44:03 +01:00
for (auto const &curModule : loadedModules) {
2021-12-07 18:23:18 +01:00
if (std::find(loadedModulesEntrypoints.begin(), loadedModulesEntrypoints.end(), curModule->getEntrypoint()) != loadedModulesEntrypoints.end()) {
// DEBUG_FUNCTION_LINE("%s [%08X] is already loaded\n", curModule->getExportName().c_str(), curModule->getEntrypoint());
2020-05-29 19:27:30 +02:00
continue;
}
canBreak = false;
2021-12-07 18:23:18 +01:00
DEBUG_FUNCTION_LINE_VERBOSE("Check if we can load %s\n", curModule->getExportName().c_str());
2020-05-29 19:27:30 +02:00
std::vector<std::string> importsFromOtherModules;
2022-02-04 21:44:03 +01:00
for (const auto &curReloc : curModule->getRelocationDataList()) {
2021-12-07 18:23:18 +01:00
std::string curRPL = curReloc->getImportRPLInformation()->getName();
2020-05-29 19:27:30 +02:00
if (curRPL.rfind("homebrew", 0) == 0) {
if (std::find(importsFromOtherModules.begin(), importsFromOtherModules.end(), curRPL) != importsFromOtherModules.end()) {
// is already in vector
} else {
2021-12-07 18:23:18 +01:00
DEBUG_FUNCTION_LINE_VERBOSE("%s is importing from %s\n", curModule->getExportName().c_str(), curRPL.c_str());
2020-05-29 19:27:30 +02:00
importsFromOtherModules.push_back(curRPL);
}
}
}
bool canLoad = true;
2022-02-04 21:44:03 +01:00
for (auto &curImportRPL : importsFromOtherModules) {
2020-05-29 19:27:30 +02:00
if (std::find(loadedModulesExportNames.begin(), loadedModulesExportNames.end(), curImportRPL) != loadedModulesExportNames.end()) {
} else {
2021-04-01 00:41:13 +02:00
DEBUG_FUNCTION_LINE_VERBOSE("We can't load the module, because %s is not loaded yet\n", curImportRPL.c_str());
2020-05-29 19:27:30 +02:00
canLoad = false;
break;
}
}
if (canLoad) {
weDidSomething = true;
2021-12-07 18:23:18 +01:00
DEBUG_FUNCTION_LINE_VERBOSE("We can load: %s\n", curModule->getExportName().c_str());
finalOrder.push_back(curModule);
2021-12-07 18:23:18 +01:00
loadedModulesExportNames.push_back(curModule->getExportName());
loadedModulesEntrypoints.push_back(curModule->getEntrypoint());
2020-05-29 19:27:30 +02:00
}
}
if (canBreak) {
break;
} else if (!weDidSomething) {
OSFatal_printf("Failed to resolve dependencies.");
}
}
return finalOrder;
}