mirror of
https://github.com/wiiu-env/EnvironmentLoader.git
synced 2025-01-14 14:09:11 +01:00
Allow Aroma to init early
This commit is contained in:
parent
2054d97e62
commit
35b8c0eeb7
294
source/main.cpp
294
source/main.cpp
@ -30,19 +30,18 @@
|
||||
#include "kernel.h"
|
||||
#include "module/ModuleDataFactory.h"
|
||||
#include "utils/DrawUtils.h"
|
||||
#include "utils/FileUtils.h"
|
||||
#include "utils/InputUtils.h"
|
||||
#include "utils/OnLeavingScope.h"
|
||||
#include "utils/PairUtils.h"
|
||||
#include "utils/utils.h"
|
||||
#include "utils/wiiu_zlib.hpp"
|
||||
#include "version.h"
|
||||
|
||||
#define ENVIRONMENT_LOADER_VERSION "v0.2.0"
|
||||
|
||||
// clang-format off
|
||||
#define MEMORY_REGION_START 0x00A00000
|
||||
#define MEMORY_REGION_SIZE 0x00600000
|
||||
#define MEMORY_REGION_END (MEMORY_REGION_START + MEMORY_REGION_SIZE)
|
||||
|
||||
#define AUTOBOOT_CONFIG_PATH "fs:/vol/external01/wiiu/environments/default.cfg"
|
||||
// clang-format on
|
||||
#define MEMORY_REGION_START 0x00800000
|
||||
#define AUTOBOOT_CONFIG_PATH "fs:/vol/external01/wiiu/environments/default.cfg"
|
||||
|
||||
bool CheckRunning() {
|
||||
switch (ProcUIProcessMessages(true)) {
|
||||
@ -95,6 +94,7 @@ bool writeFileContent(const std::string &path, const std::string &content) {
|
||||
|
||||
extern "C" void __fini();
|
||||
extern "C" void __init_wut_malloc();
|
||||
void LoadAndRunModule(std::string_view filepath, std::string_view environment_path);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// We need to call __init_wut_malloc somewhere so wut_malloc will be used for the memory allocation.
|
||||
@ -113,28 +113,21 @@ int main(int argc, char **argv) {
|
||||
|
||||
DEBUG_FUNCTION_LINE("Hello from EnvironmentLoader!");
|
||||
|
||||
char environmentPath[0x100];
|
||||
memset(environmentPath, 0, sizeof(environmentPath));
|
||||
|
||||
auto handle = IOS_Open("/dev/mcp", IOS_OPEN_READ);
|
||||
char environmentPathFromIOSU[0x100] = {};
|
||||
auto handle = IOS_Open("/dev/mcp", IOS_OPEN_READ);
|
||||
if (handle >= 0) {
|
||||
int in = 0xF9; // IPC_CUSTOM_COPY_ENVIRONMENT_PATH
|
||||
if (IOS_Ioctl(handle, 100, &in, sizeof(in), environmentPath, sizeof(environmentPath)) == IOS_ERROR_OK) {
|
||||
DEBUG_FUNCTION_LINE("Boot into %s", environmentPath);
|
||||
if (IOS_Ioctl(handle, 100, &in, sizeof(in), environmentPathFromIOSU, sizeof(environmentPathFromIOSU)) == IOS_ERROR_OK) {
|
||||
DEBUG_FUNCTION_LINE("Boot into %s", environmentPathFromIOSU);
|
||||
}
|
||||
|
||||
IOS_Close(handle);
|
||||
}
|
||||
|
||||
// We substract 0x100 to be safe.
|
||||
uint32_t textSectionStart = textStart() - 0x100;
|
||||
|
||||
auto gModuleData = (module_information_t *) (textSectionStart - sizeof(module_information_t));
|
||||
|
||||
bool noEnvironmentsFound = false;
|
||||
|
||||
std::string environment_path = std::string(environmentPath);
|
||||
if (strncmp(environmentPath, "fs:/vol/external01/wiiu/environments/", strlen("fs:/vol/external01/wiiu/environments/")) != 0) {
|
||||
std::string environmentPath = std::string(environmentPathFromIOSU);
|
||||
if (!environmentPath.starts_with("fs:/vol/external01/wiiu/environments/")) { // If the environment path in IOSU is empty or unexpected, read config
|
||||
DirList environmentDirs("fs:/vol/external01/wiiu/environments/", nullptr, DirList::Dirs, 1);
|
||||
|
||||
std::map<std::string, std::string> environmentPaths;
|
||||
@ -151,9 +144,9 @@ int main(int argc, char **argv) {
|
||||
for (auto const &[key, val] : environmentPaths) {
|
||||
if (res.value() == key) {
|
||||
DEBUG_FUNCTION_LINE("Found environment %s from config at index %d", res.value().c_str(), i);
|
||||
autobootIndex = i;
|
||||
environment_path = val;
|
||||
forceMenu = false;
|
||||
autobootIndex = i;
|
||||
environmentPath = val;
|
||||
forceMenu = false;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
@ -168,11 +161,11 @@ int main(int argc, char **argv) {
|
||||
|
||||
if (forceMenu || ((input.trigger | input.hold) & VPAD_BUTTON_X) == VPAD_BUTTON_X) {
|
||||
DEBUG_FUNCTION_LINE_VERBOSE("Open menu!");
|
||||
environment_path = EnvironmentSelectionScreen(environmentPaths, autobootIndex);
|
||||
environmentPath = EnvironmentSelectionScreen(environmentPaths, autobootIndex);
|
||||
if (environmentPaths.empty()) {
|
||||
noEnvironmentsFound = true;
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_VERBOSE("Selected %s", environment_path.c_str());
|
||||
DEBUG_FUNCTION_LINE_VERBOSE("Selected %s", environmentPath.c_str());
|
||||
}
|
||||
}
|
||||
InputUtils::DeInit();
|
||||
@ -180,10 +173,9 @@ int main(int argc, char **argv) {
|
||||
RevertMainHook();
|
||||
|
||||
if (!noEnvironmentsFound) {
|
||||
DirList setupModules(environment_path + "/modules/setup", ".rpx", DirList::Files, 1);
|
||||
DirList setupModules(environmentPath + "/modules/setup", ".rpx", DirList::Files, 1);
|
||||
setupModules.SortList();
|
||||
|
||||
std::map<std::string, OSDynLoad_Module> usedRPls;
|
||||
for (int i = 0; i < setupModules.GetFilecount(); i++) {
|
||||
//! skip hidden linux and mac files
|
||||
if (setupModules.GetFilename(i)[0] == '.' || setupModules.GetFilename(i)[0] == '_') {
|
||||
@ -191,48 +183,7 @@ int main(int argc, char **argv) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Some module may unmount the sd card on exit.
|
||||
FSAInit();
|
||||
auto client = FSAAddClient(nullptr);
|
||||
if (client) {
|
||||
FSAMount(client, "/dev/sdcard01", "/vol/external01", static_cast<FSAMountFlags>(0), nullptr, 0);
|
||||
FSADelClient(client);
|
||||
}
|
||||
|
||||
uint32_t destination_address_end = ((uint32_t) gModuleData) & 0xFFFF0000;
|
||||
memset((void *) gModuleData, 0, sizeof(module_information_t));
|
||||
DEBUG_FUNCTION_LINE("Trying to run %s.", setupModules.GetFilepath(i), destination_address_end, ((uint32_t) gModuleData) - MEMORY_REGION_START);
|
||||
auto moduleData = ModuleDataFactory::load(setupModules.GetFilepath(i), destination_address_end, ((uint32_t) gModuleData) - MEMORY_REGION_START, gModuleData->trampolines,
|
||||
DYN_LINK_TRAMPOLIN_LIST_LENGTH);
|
||||
if (!moduleData) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to load %s", setupModules.GetFilepath(i));
|
||||
OSFatal("EnvironmentLoader: Failed to load module");
|
||||
continue;
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("Loaded module data");
|
||||
if (!ElfUtils::doRelocation(moduleData.value()->getRelocationDataList(), gModuleData->trampolines, DYN_LINK_TRAMPOLIN_LIST_LENGTH, usedRPls)) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Relocations failed");
|
||||
OSFatal("EnvironmentLoader: Relocations failed");
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE("Relocation done");
|
||||
}
|
||||
|
||||
DCFlushRange((void *) moduleData.value()->getStartAddress(), moduleData.value()->getEndAddress() - moduleData.value()->getStartAddress());
|
||||
ICInvalidateRange((void *) moduleData.value()->getStartAddress(), moduleData.value()->getEndAddress() - moduleData.value()->getStartAddress());
|
||||
|
||||
DEBUG_FUNCTION_LINE("Calling entrypoint @%08X", moduleData.value()->getEntrypoint());
|
||||
char *arr[1];
|
||||
arr[0] = (char *) environment_path.c_str();
|
||||
// clang-format off
|
||||
((int(*)(int, char **)) moduleData.value()->getEntrypoint())(1, arr);
|
||||
// clang-format on
|
||||
DEBUG_FUNCTION_LINE("Back from module");
|
||||
|
||||
for (auto &rpl : usedRPls) {
|
||||
DEBUG_FUNCTION_LINE_VERBOSE("Release %s", rpl.first.c_str());
|
||||
OSDynLoad_Release(rpl.second);
|
||||
}
|
||||
usedRPls.clear();
|
||||
LoadAndRunModule(setupModules.GetFilepath(i), environmentPath);
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -267,6 +218,211 @@ int main(int argc, char **argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::optional<HeapWrapper> GetHeapFromMappedMemory(uint32_t heapSize) {
|
||||
void *(*MEMAllocFromDefaultHeapExForThreads)(uint32_t size, int align) = nullptr;
|
||||
void (*MEMFreeToDefaultHeapForThreads)(void *ptr) = nullptr;
|
||||
|
||||
// Let's try to get the memalign and free functions from the memorymapping module.
|
||||
OSDynLoad_Module module;
|
||||
if (OSDynLoad_Acquire("homebrew_memorymapping", &module) != OS_DYNLOAD_OK) {
|
||||
DEBUG_FUNCTION_LINE("Failed to acquire homebrew_memorymapping.");
|
||||
return {};
|
||||
}
|
||||
/* Memory allocation functions */
|
||||
uint32_t *allocPtr = nullptr, *freePtr = nullptr;
|
||||
if (OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_DATA, "MEMAllocFromMappedMemoryEx", reinterpret_cast<void **>(&allocPtr)) != OS_DYNLOAD_OK) {
|
||||
DEBUG_FUNCTION_LINE("OSDynLoad_FindExport for MEMAllocFromDefaultHeapEx failed");
|
||||
return {};
|
||||
}
|
||||
if (OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_DATA, "MEMFreeToMappedMemory", reinterpret_cast<void **>(&freePtr)) != OS_DYNLOAD_OK) {
|
||||
DEBUG_FUNCTION_LINE("OSDynLoad_FindExport for MEMFreeToDefaultHeap failed");
|
||||
return {};
|
||||
}
|
||||
|
||||
MEMAllocFromDefaultHeapExForThreads = (void *(*) (uint32_t, int) ) * allocPtr;
|
||||
MEMFreeToDefaultHeapForThreads = (void (*)(void *)) * freePtr;
|
||||
|
||||
if (!MEMAllocFromDefaultHeapExForThreads || !MEMFreeToDefaultHeapForThreads) {
|
||||
DEBUG_FUNCTION_LINE_ERR("MEMAllocFromDefaultHeapExForThreads or MEMFreeToDefaultHeapForThreads is null");
|
||||
// the mapped memory is not available (yet)
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t size = heapSize;
|
||||
auto ptr = MEMAllocFromDefaultHeapExForThreads(size, 0x4);
|
||||
if (!ptr) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to alloc memory: %d bytes", size);
|
||||
return {};
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("Let's create a memory wrapper for 0x%08X, size: %d", ptr, size);
|
||||
return HeapWrapper(MemoryWrapper(ptr, size, MEMFreeToDefaultHeapForThreads));
|
||||
}
|
||||
|
||||
std::optional<HeapWrapper> GetHeapForModule(uint32_t heapSize) {
|
||||
// If Aroma is already loaded, we can't use the region between MEMORY_REGION_START and MEMORY_REGION_END anymore because Aroma is using.
|
||||
// So instead we check before loading a module if aromas memory mapping module is already usable. If yes, we use this to load the module instead
|
||||
if (auto heapWrapper = GetHeapFromMappedMemory(heapSize)) {
|
||||
return heapWrapper;
|
||||
}
|
||||
|
||||
// If Aroma is not already loaded, we use the existing 0x00800000 - 0x01000000 memory region. This is where aroma is loaded to. Note: this region may be only mapped to the main core.
|
||||
// The environment loader is loaded to the end of 0x00800000 - 0x01000000 memory region. With this helper we know the start of the .text section
|
||||
uint32_t textSectionStart = textStart() - 0x100;
|
||||
|
||||
auto endOfUsableMemory = textSectionStart;
|
||||
uint32_t startAddress = ((uint32_t) endOfUsableMemory - heapSize) & 0xFFFF0000;
|
||||
uint32_t size = endOfUsableMemory - startAddress;
|
||||
|
||||
if (startAddress < MEMORY_REGION_START) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Not enough static memory");
|
||||
return {};
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("Let's create a memory wrapper for 0x%08X, size: %d", ptr, size);
|
||||
auto res = HeapWrapper(MemoryWrapper((void *) startAddress, size, /* we don't need to free this memory*/ nullptr));
|
||||
if ((uint32_t) res.GetHeapHandle() != startAddress) {
|
||||
OSFatal("EnvironmentLoader: Unexpected address");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void SetupKernelModule() {
|
||||
void *(*KernelSetupDefaultSyscalls)() = nullptr;
|
||||
|
||||
OSDynLoad_Module module;
|
||||
if (OSDynLoad_Acquire("homebrew_kernel", &module) != OS_DYNLOAD_OK) {
|
||||
DEBUG_FUNCTION_LINE("Failed to acquire homebrew_kernel.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_FUNC, "KernelSetupDefaultSyscalls", reinterpret_cast<void **>(&KernelSetupDefaultSyscalls)) != OS_DYNLOAD_OK) {
|
||||
DEBUG_FUNCTION_LINE("OSDynLoad_FindExport for KernelSetupDefaultSyscalls failed");
|
||||
OSFatal("EnvironmentLoader: KernelModule is missing the export\n"
|
||||
"\"KernelSetupDefaultSyscalls\"... Please update Aroma!\n"
|
||||
"\n"
|
||||
"See https://wiiu.hacks.guide/ for more information.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!KernelSetupDefaultSyscalls) {
|
||||
DEBUG_FUNCTION_LINE_WARN("KernelSetupDefaultSyscalls is null");
|
||||
OSFatal("EnvironmentLoader: KernelModule is missing the export\n"
|
||||
"\"KernelSetupDefaultSyscalls\"... Please update Aroma!\n"
|
||||
"\n"
|
||||
"See https://wiiu.hacks.guide/ for more information.");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("Call KernelSetupDefaultSyscalls");
|
||||
KernelSetupDefaultSyscalls();
|
||||
OSDynLoad_Release(module);
|
||||
}
|
||||
|
||||
void LoadAndRunModule(std::string_view filepath, std::string_view environment_path) {
|
||||
// Some module may unmount the sd card on exit.
|
||||
FSAInit();
|
||||
auto client = FSAAddClient(nullptr);
|
||||
if (client) {
|
||||
FSAMount(client, "/dev/sdcard01", "/vol/external01", static_cast<FSAMountFlags>(0), nullptr, 0);
|
||||
FSADelClient(client);
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to add FSA client");
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("Trying to load %s into memory", filepath.data());
|
||||
uint8_t *buffer = nullptr;
|
||||
uint32_t fsize = 0;
|
||||
if (LoadFileToMem(filepath.data(), &buffer, &fsize) < 0) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to load file");
|
||||
OSFatal("EnvironmentLoader: Failed to load file to memory");
|
||||
return;
|
||||
}
|
||||
|
||||
auto cleanupBuffer = onLeavingScope([buffer]() { free(buffer); });
|
||||
|
||||
ELFIO::elfio reader(new wiiu_zlib);
|
||||
// Load ELF data
|
||||
if (!reader.load(reinterpret_cast<const char *>(buffer), fsize)) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Can't parse .wms from buffer.");
|
||||
OSFatal("Can't parse .wms from buffer.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t moduleSize = ModuleDataFactory::GetSizeOfModule(reader);
|
||||
DEBUG_FUNCTION_LINE_VERBOSE("Module has size: %d", moduleSize);
|
||||
|
||||
uint32_t requiredHeapSize = moduleSize + sizeof(module_information_t) + 0x10000; // add another 0x10000 to be safe
|
||||
DEBUG_FUNCTION_LINE_VERBOSE("Allocate %d bytes for heap (%.2f KiB)", requiredHeapSize, requiredHeapSize / 1024.0f);
|
||||
|
||||
if (auto heapWrapperOpt = GetHeapForModule(requiredHeapSize); heapWrapperOpt.has_value()) {
|
||||
// Frees automatically, must not survive the heapWrapper.
|
||||
auto moduleInfoOpt = heapWrapperOpt->Alloc(sizeof(module_information_t), 0x4);
|
||||
if (!moduleInfoOpt) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to alloc module information");
|
||||
OSFatal("EnvironmentLoader: Failed to alloc module information");
|
||||
return;
|
||||
}
|
||||
|
||||
auto moduleInfo = std::move(*moduleInfoOpt);
|
||||
auto moduleInfoPtr = (module_information_t *) moduleInfo.data();
|
||||
|
||||
// Frees automatically, must not survive the heapWrapper.
|
||||
auto moduleData = ModuleDataFactory::load(reader, *heapWrapperOpt, moduleInfoPtr->trampolines, sizeof(moduleInfoPtr->trampolines) / sizeof(moduleInfoPtr->trampolines[0]));
|
||||
if (!moduleData) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to load %s", filepath);
|
||||
OSFatal("EnvironmentLoader: Failed to load module");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("Loaded module data");
|
||||
std::map<std::string, OSDynLoad_Module> usedRPls;
|
||||
if (!ElfUtils::doRelocation(moduleData.value()->getRelocationDataList(), moduleInfoPtr->trampolines, sizeof(moduleInfoPtr->trampolines) / sizeof(moduleInfoPtr->trampolines[0]), usedRPls)) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Relocations failed");
|
||||
OSFatal("EnvironmentLoader: Relocations failed");
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE("Relocation done");
|
||||
}
|
||||
|
||||
DCFlushRange((void *) moduleData.value()->getStartAddress(), moduleData.value()->getEndAddress() - moduleData.value()->getStartAddress());
|
||||
ICInvalidateRange((void *) moduleData.value()->getStartAddress(), moduleData.value()->getEndAddress() - moduleData.value()->getStartAddress());
|
||||
|
||||
char *arr[4];
|
||||
arr[0] = (char *) environment_path.data();
|
||||
arr[1] = (char *) "EnvironmentLoader"; //
|
||||
arr[2] = (char *) 0x02; // Version
|
||||
/*
|
||||
* This is a hacky work around to tell Aromas Module Loader which memory region it can use safely. After using it, it's expected to expose new memory region via the
|
||||
* custom rpl "homebrew_mappedmemory" (See: GetHeapFromMappedMemory). The returned memory is expected to be RWX for user and kernel.
|
||||
* Once a custom memory allocator is provided, usable_mem_start and usable_mem_end are set to 0.
|
||||
*/
|
||||
auto usable_mem_end = (uint32_t) heapWrapperOpt->GetHeapHandle();
|
||||
if (heapWrapperOpt->IsAllocated()) { // Check if you use memory which is actually allocated. This means we can't give it to the module.
|
||||
DEBUG_FUNCTION_LINE("Don't give the module a usable memory region because it will be loaded on a custom memory region.");
|
||||
usable_mem_end = 0;
|
||||
}
|
||||
arr[3] = (char *) usable_mem_end; // End of usable memory
|
||||
|
||||
DEBUG_FUNCTION_LINE("Calling entrypoint @%08X with: \"%s\", \"%s\", %08X, %08X", moduleData.value()->getEntrypoint(), arr[0], arr[1], arr[2], arr[3]);
|
||||
// clang-format off
|
||||
((int(*)(int, char **)) moduleData.value()->getEntrypoint())(sizeof(arr)/ sizeof(arr[0]), arr);
|
||||
// clang-format on
|
||||
DEBUG_FUNCTION_LINE("Back from module");
|
||||
|
||||
for (auto &rpl : usedRPls) {
|
||||
DEBUG_FUNCTION_LINE_VERBOSE("Release %s", rpl.first.c_str());
|
||||
OSDynLoad_Release(rpl.second);
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to create heap");
|
||||
OSFatal("EnvironmentLoader: Failed to create heap");
|
||||
}
|
||||
|
||||
// module may override the syscalls used by the Aroma KernelModule. This (tries to) re-init(s) the KernelModule after a setup module has been run.
|
||||
SetupKernelModule();
|
||||
}
|
||||
|
||||
std::string EnvironmentSelectionScreen(const std::map<std::string, std::string> &payloads, int32_t autobootIndex) {
|
||||
OSScreenInit();
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "RelocationData.h"
|
||||
#include "utils/MemoryUtils.h"
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
@ -61,9 +62,14 @@ public:
|
||||
return endAddress;
|
||||
}
|
||||
|
||||
void setMemory(ExpHeapMemory &&memory) {
|
||||
mMemory = std::move(memory);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<RelocationData>> relocation_data_list;
|
||||
uint32_t entrypoint = 0;
|
||||
uint32_t startAddress = 0;
|
||||
uint32_t endAddress = 0;
|
||||
ExpHeapMemory mMemory;
|
||||
};
|
||||
|
@ -24,41 +24,10 @@
|
||||
#include <coreinit/cache.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
std::optional<std::unique_ptr<ModuleData>>
|
||||
ModuleDataFactory::load(const std::string &path, uint32_t destination_address_end, uint32_t maximum_size, relocation_trampoline_entry_t *trampoline_data, uint32_t trampoline_data_length) {
|
||||
ELFIO::elfio reader(new wiiu_zlib);
|
||||
auto moduleData = make_unique_nothrow<ModuleData>();
|
||||
if (!moduleData) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to allocate ModuleData");
|
||||
return {};
|
||||
}
|
||||
|
||||
uint8_t *buffer = nullptr;
|
||||
uint32_t fsize = 0;
|
||||
if (LoadFileToMem(path.c_str(), &buffer, &fsize) < 0) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to load file");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Load ELF data
|
||||
if (!reader.load(reinterpret_cast<char *>(buffer), fsize)) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Can't find or process %s", path.c_str());
|
||||
free(buffer);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto cleanupBuffer = onLeavingScope([buffer]() { free(buffer); });
|
||||
|
||||
uint32_t sec_num = reader.sections.size();
|
||||
|
||||
auto destinations = make_unique_nothrow<uint8_t *[]>(sec_num);
|
||||
if (!destinations) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed alloc memory for destinations array");
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t ModuleDataFactory::GetSizeOfModule(const ELFIO::elfio &reader) {
|
||||
uint32_t sec_num = reader.sections.size();
|
||||
uint32_t sizeOfModule = 0;
|
||||
for (uint32_t i = 0; i < sec_num; ++i) {
|
||||
ELFIO::section *psec = reader.sections[i];
|
||||
@ -70,13 +39,36 @@ ModuleDataFactory::load(const std::string &path, uint32_t destination_address_en
|
||||
sizeOfModule += psec->get_size() + 1;
|
||||
}
|
||||
}
|
||||
return sizeOfModule;
|
||||
}
|
||||
|
||||
if (sizeOfModule > maximum_size) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Module is too big.");
|
||||
std::optional<std::unique_ptr<ModuleData>>
|
||||
ModuleDataFactory::load(const ELFIO::elfio &reader, const HeapWrapper &heapWrapper, relocation_trampoline_entry_t *trampoline_data, uint32_t trampoline_data_length) {
|
||||
auto moduleData = make_unique_nothrow<ModuleData>();
|
||||
if (!moduleData) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to allocate ModuleData");
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t baseOffset = (destination_address_end - sizeOfModule) & 0xFFFFFF00;
|
||||
uint32_t sec_num = reader.sections.size();
|
||||
|
||||
auto destinations = make_unique_nothrow<uint8_t *[]>(sec_num);
|
||||
if (!destinations) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed alloc memory for destinations array");
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t sizeOfModule = GetSizeOfModule(reader);
|
||||
|
||||
auto moduleDataOpt = heapWrapper.Alloc(sizeOfModule, 0x100);
|
||||
if (!moduleData) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for module");
|
||||
return {};
|
||||
}
|
||||
ExpHeapMemory moduleMemory = std::move(*moduleDataOpt);
|
||||
|
||||
uint32_t dataPtr = (uint32_t) ((void *) moduleMemory);
|
||||
uint32_t baseOffset = dataPtr;
|
||||
uint32_t startAddress = baseOffset;
|
||||
|
||||
uint32_t offset_text = baseOffset;
|
||||
@ -97,7 +89,7 @@ ModuleDataFactory::load(const std::string &path, uint32_t destination_address_en
|
||||
uint32_t sectionSize = psec->get_size();
|
||||
|
||||
totalSize += sectionSize;
|
||||
if (totalSize > maximum_size) {
|
||||
if (totalSize > sizeOfModule) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Couldn't load setup module because it's too big.");
|
||||
return {};
|
||||
}
|
||||
@ -125,8 +117,12 @@ ModuleDataFactory::load(const std::string &path, uint32_t destination_address_en
|
||||
|
||||
const char *p = reader.sections[i]->get_data();
|
||||
|
||||
if (destination + sectionSize > (uint32_t) destination_address_end) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Tried to overflow buffer. %08X > %08X", destination + sectionSize, destination_address_end);
|
||||
if (destination < dataPtr) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Tried to underflow buffer. %08X < %08X", destination, dataPtr);
|
||||
OSFatal("EnvironmentLoader: Tried to underflow buffer");
|
||||
}
|
||||
if (destination + sectionSize > (uint32_t) dataPtr + sizeOfModule) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Tried to overflow buffer. %08X > %08X", destination + sectionSize, dataPtr + sizeOfModule);
|
||||
OSFatal("EnvironmentLoader: Tried to overflow buffer");
|
||||
}
|
||||
|
||||
@ -165,18 +161,20 @@ ModuleDataFactory::load(const std::string &path, uint32_t destination_address_en
|
||||
}
|
||||
getImportRelocationData(moduleData, reader, destinations.get());
|
||||
|
||||
DCFlushRange((void *) baseOffset, totalSize);
|
||||
ICInvalidateRange((void *) baseOffset, totalSize);
|
||||
DCFlushRange((void *) dataPtr, totalSize);
|
||||
ICInvalidateRange((void *) dataPtr, totalSize);
|
||||
|
||||
moduleData->setStartAddress(startAddress);
|
||||
moduleData->setEndAddress(endAddress);
|
||||
moduleData->setEntrypoint(entrypoint);
|
||||
moduleData->setMemory(std::move(moduleMemory));
|
||||
|
||||
DEBUG_FUNCTION_LINE("Saved entrypoint as %08X", entrypoint);
|
||||
|
||||
return moduleData;
|
||||
}
|
||||
|
||||
bool ModuleDataFactory::getImportRelocationData(std::unique_ptr<ModuleData> &moduleData, ELFIO::elfio &reader, uint8_t **destinations) {
|
||||
bool ModuleDataFactory::getImportRelocationData(std::unique_ptr<ModuleData> &moduleData, const ELFIO::elfio &reader, uint8_t **destinations) {
|
||||
std::map<uint32_t, std::shared_ptr<ImportRPLInformation>> infoMap;
|
||||
|
||||
uint32_t sec_num = reader.sections.size();
|
||||
@ -254,7 +252,7 @@ bool ModuleDataFactory::getImportRelocationData(std::unique_ptr<ModuleData> &mod
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModuleDataFactory::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,
|
||||
bool ModuleDataFactory::linkSection(const 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) {
|
||||
uint32_t sec_num = reader.sections.size();
|
||||
|
||||
|
@ -20,17 +20,20 @@
|
||||
#include "../common/relocation_defines.h"
|
||||
#include "ModuleData.h"
|
||||
#include "elfio/elfio.hpp"
|
||||
#include "utils/MemoryUtils.h"
|
||||
#include "utils/utils.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ModuleDataFactory {
|
||||
public:
|
||||
static std::optional<std::unique_ptr<ModuleData>>
|
||||
load(const std::string &path, uint32_t destination_address_end, uint32_t maximum_size, relocation_trampoline_entry_t *trampoline_data, uint32_t trampoline_data_length);
|
||||
static uint32_t GetSizeOfModule(const ELFIO::elfio &reader);
|
||||
|
||||
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,
|
||||
static std::optional<std::unique_ptr<ModuleData>> load(const ELFIO::elfio &reader, const HeapWrapper &heapWrapper, relocation_trampoline_entry_t *trampoline_data, uint32_t trampoline_data_length);
|
||||
|
||||
static bool linkSection(const 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);
|
||||
|
||||
static bool getImportRelocationData(std::unique_ptr<ModuleData> &moduleData, ELFIO::elfio &reader, uint8_t **destinations);
|
||||
static bool getImportRelocationData(std::unique_ptr<ModuleData> &moduleData, const ELFIO::elfio &reader, uint8_t **destinations);
|
||||
};
|
||||
|
200
source/utils/MemoryUtils.h
Normal file
200
source/utils/MemoryUtils.h
Normal file
@ -0,0 +1,200 @@
|
||||
#pragma once
|
||||
#include "logger.h"
|
||||
#include <coreinit/memexpheap.h>
|
||||
#include <coreinit/memheap.h>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
|
||||
typedef void (*FreeMemoryFn)(void *);
|
||||
|
||||
class MemoryWrapper {
|
||||
public:
|
||||
MemoryWrapper(void *ptr, uint32_t size, FreeMemoryFn freeFn) : mPtr(ptr), mSize(size), mFreeFn(freeFn) {
|
||||
}
|
||||
~MemoryWrapper() {
|
||||
if (mPtr && mFreeFn) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Free memory wrapper for %08X %d", mPtr, mSize);
|
||||
memset(mPtr, 0, mSize);
|
||||
mFreeFn(mPtr);
|
||||
}
|
||||
}
|
||||
|
||||
MemoryWrapper(const MemoryWrapper &) = delete;
|
||||
MemoryWrapper &operator=(const MemoryWrapper &) = delete;
|
||||
|
||||
MemoryWrapper(MemoryWrapper &&other) noexcept
|
||||
: mPtr(other.mPtr), mSize(other.mSize), mFreeFn(other.mFreeFn) {
|
||||
other.mPtr = {};
|
||||
other.mSize = {};
|
||||
other.mFreeFn = {};
|
||||
}
|
||||
|
||||
MemoryWrapper &operator=(MemoryWrapper &&other) noexcept {
|
||||
if (this != &other) {
|
||||
mPtr = other.mPtr;
|
||||
mSize = other.mSize;
|
||||
mFreeFn = other.mFreeFn;
|
||||
other.mPtr = {};
|
||||
other.mSize = 0;
|
||||
other.mFreeFn = {};
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] void *data() const {
|
||||
return mPtr;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint32_t size() const {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsAllocated() const {
|
||||
return mFreeFn && mPtr && mSize > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void *mPtr = {};
|
||||
uint32_t mSize = 0;
|
||||
FreeMemoryFn mFreeFn = {};
|
||||
};
|
||||
|
||||
|
||||
class ExpHeapMemory {
|
||||
|
||||
public:
|
||||
ExpHeapMemory(MEMHeapHandle heapHandle, void *data, uint32_t size) : mHeapHandle(heapHandle),
|
||||
mData(data),
|
||||
mSize(size) {
|
||||
}
|
||||
ExpHeapMemory() = default;
|
||||
|
||||
~ExpHeapMemory() {
|
||||
if (mData) {
|
||||
MEMFreeToExpHeap(mHeapHandle, mData);
|
||||
}
|
||||
mData = nullptr;
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
// Delete the copy constructor and copy assignment operator
|
||||
ExpHeapMemory(const ExpHeapMemory &) = delete;
|
||||
ExpHeapMemory &operator=(const ExpHeapMemory &) = delete;
|
||||
|
||||
ExpHeapMemory(ExpHeapMemory &&other) noexcept
|
||||
: mHeapHandle(other.mHeapHandle), mData(other.mData), mSize(other.mSize) {
|
||||
other.mHeapHandle = {};
|
||||
other.mData = {};
|
||||
other.mSize = 0;
|
||||
}
|
||||
|
||||
ExpHeapMemory &operator=(ExpHeapMemory &&other) noexcept {
|
||||
if (this != &other) {
|
||||
mHeapHandle = other.mHeapHandle;
|
||||
mData = other.mData;
|
||||
mSize = other.mSize;
|
||||
other.mHeapHandle = {};
|
||||
other.mData = {};
|
||||
other.mSize = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return mData != nullptr;
|
||||
}
|
||||
|
||||
explicit operator void *() const {
|
||||
// Return the desired void* value
|
||||
return mData;
|
||||
}
|
||||
|
||||
[[nodiscard]] void *data() const {
|
||||
return mData;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t size() const {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
static std::optional<ExpHeapMemory> Alloc(MEMHeapHandle heapHandle, uint32_t size, int32_t alignment) {
|
||||
auto *ptr = MEMAllocFromExpHeapEx(heapHandle, size, alignment);
|
||||
if (!ptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return ExpHeapMemory(heapHandle, ptr, size);
|
||||
}
|
||||
|
||||
private:
|
||||
MEMHeapHandle mHeapHandle{};
|
||||
void *mData = nullptr;
|
||||
uint32_t mSize{};
|
||||
};
|
||||
|
||||
class HeapWrapper {
|
||||
public:
|
||||
explicit HeapWrapper(MemoryWrapper &&memory) : mMemory(std::move(memory)) {
|
||||
mHeapHandle = MEMCreateExpHeapEx(mMemory.data(), mMemory.size(), MEM_HEAP_FLAG_USE_LOCK);
|
||||
if (mHeapHandle) {
|
||||
mSize = mMemory.size();
|
||||
mPtr = mMemory.data();
|
||||
}
|
||||
}
|
||||
|
||||
~HeapWrapper() {
|
||||
if (mHeapHandle) {
|
||||
MEMDestroyExpHeap(mHeapHandle);
|
||||
}
|
||||
if (mPtr) {
|
||||
memset(mPtr, 0, mSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the copy constructor and copy assignment operator
|
||||
HeapWrapper(const HeapWrapper &) = delete;
|
||||
HeapWrapper &operator=(const HeapWrapper &) = delete;
|
||||
|
||||
HeapWrapper(HeapWrapper &&other) noexcept
|
||||
: mMemory(std::move(other.mMemory)), mHeapHandle(other.mHeapHandle), mPtr(other.mPtr), mSize(other.mSize) {
|
||||
other.mHeapHandle = {};
|
||||
other.mPtr = {};
|
||||
other.mSize = 0;
|
||||
}
|
||||
|
||||
HeapWrapper &operator=(HeapWrapper &&other) noexcept {
|
||||
if (this != &other) {
|
||||
mMemory = std::move(other.mMemory);
|
||||
mHeapHandle = other.mHeapHandle;
|
||||
mPtr = other.mPtr;
|
||||
mSize = other.mSize;
|
||||
other.mHeapHandle = {};
|
||||
other.mPtr = {};
|
||||
other.mSize = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] MEMHeapHandle GetHeapHandle() const {
|
||||
return mHeapHandle;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint32_t GetHeapSize() const {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsAllocated() const {
|
||||
return mMemory.IsAllocated();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<ExpHeapMemory> Alloc(uint32_t size, int align) const {
|
||||
return ExpHeapMemory::Alloc(mHeapHandle, size, align);
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryWrapper mMemory;
|
||||
MEMHeapHandle mHeapHandle = {};
|
||||
void *mPtr = {};
|
||||
uint32_t mSize = 0;
|
||||
};
|
@ -34,6 +34,7 @@ extern "C" {
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX(WHBLogPrintf, "##WARN ## ", "", FMT, ##ARGS)
|
||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS)
|
||||
|
||||
#else
|
||||
@ -44,6 +45,7 @@ extern "C" {
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX(OSReport, "##WARN ## ", "\n", FMT, ##ARGS)
|
||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user