diff --git a/Dockerfile b/Dockerfile index b269abd..79419a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM wiiuenv/devkitppc:20221228 COPY --from=wiiuenv/libkernel:20220904 /artifacts $DEVKITPRO -COPY --from=wiiuenv/libfunctionpatcher:20230106 /artifacts $DEVKITPRO +COPY --from=wiiuenv/libfunctionpatcher:20230107 /artifacts $DEVKITPRO COPY --from=wiiuenv/wiiumodulesystem:20230106 /artifacts $DEVKITPRO WORKDIR project diff --git a/source/PatchedFunctionData.cpp b/source/PatchedFunctionData.cpp index c787a66..72cbb7b 100644 --- a/source/PatchedFunctionData.cpp +++ b/source/PatchedFunctionData.cpp @@ -1,9 +1,69 @@ #include "PatchedFunctionData.h" +#include "utils/KernelFindExport.h" #include "utils/utils.h" +#include +#include +#include -std::optional> PatchedFunctionData::make_shared(std::shared_ptr functionAddressProvider, - function_replacement_data_t *replacementData, - MEMHeapHandle heapHandle) { +std::optional> PatchedFunctionData::make_shared_v3(std::shared_ptr functionAddressProvider, + function_replacement_data_v3_t *replacementData, + MEMHeapHandle heapHandle) { + if (!replacementData) { + return {}; + } + + auto ptr = make_shared_nothrow(std::move(functionAddressProvider)); + if (!ptr) { + DEBUG_FUNCTION_LINE_ERR("Failed to alloc PatchedFunctionData"); + return {}; + } + + ptr->isPatched = false; + ptr->heapHandle = heapHandle; + ptr->replacementFunctionAddress = replacementData->replaceAddr; + ptr->realCallFunctionAddressPtr = replacementData->replaceCall; + ptr->targetProcess = replacementData->targetProcess; + ptr->type = replacementData->type; + + switch (replacementData->type) { + case FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_NAME: + case FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_ADDRESS: { + ptr->library = {}; + for (uint32_t i = 0; i < replacementData->ReplaceInRPX.targetTitleIdsCount; i++) { + ptr->titleIds.insert(replacementData->ReplaceInRPX.targetTitleIds[i]); + } + ptr->titleVersionMin = replacementData->ReplaceInRPX.versionMin; + ptr->titleVersionMax = replacementData->ReplaceInRPX.versionMax; + ptr->executableName = replacementData->ReplaceInRPX.executableName; + if (replacementData->type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_ADDRESS) { + ptr->textOffset = replacementData->ReplaceInRPX.textOffset; + } else if (replacementData->type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_NAME) { + ptr->functionName = replacementData->ReplaceInRPX.functionName; + } + break; + } + case FUNCTION_PATCHER_REPLACE_BY_LIB_OR_ADDRESS: { + ptr->library = replacementData->ReplaceInRPL.library; + if (replacementData->ReplaceInRPL.library != LIBRARY_OTHER) { + ptr->functionName = replacementData->ReplaceInRPL.function_name; + } else { + ptr->realEffectiveFunctionAddress = replacementData->virtualAddr; + ptr->realPhysicalFunctionAddress = replacementData->physicalAddr; + } + break; + } + } + + if (!ptr->allocateDataForJumps()) { + return {}; + } + + return ptr; +} + +std::optional> PatchedFunctionData::make_shared_v2(std::shared_ptr functionAddressProvider, + function_replacement_data_v2_t *replacementData, + MEMHeapHandle heapHandle) { if (!replacementData) { return {}; } @@ -13,6 +73,7 @@ std::optional> PatchedFunctionData::make_sh return {}; } + ptr->type = FUNCTION_PATCHER_REPLACE_BY_LIB_OR_ADDRESS; ptr->isPatched = false; ptr->heapHandle = heapHandle; ptr->library = replacementData->library; @@ -27,47 +88,136 @@ std::optional> PatchedFunctionData::make_sh ptr->realPhysicalFunctionAddress = replacementData->physicalAddr; } - ptr->jumpToOriginal = (uint32_t *) MEMAllocFromExpHeapEx(ptr->heapHandle, 0x5 * sizeof(uint32_t), 4); - - if (!ptr->jumpToOriginal) { - DEBUG_FUNCTION_LINE_ERR("Failed to alloc jump data"); + if (!ptr->allocateDataForJumps()) { return {}; } - if (ptr->replacementFunctionAddress > 0x01FFFFFC || ptr->targetProcess != FP_TARGET_PROCESS_ALL) { - ptr->jumpDataSize = 15; // We could predict the actual size and save some memory, but at the moment we don't need it. - ptr->jumpData = (uint32_t *) MEMAllocFromExpHeapEx(ptr->heapHandle, ptr->jumpDataSize * sizeof(uint32_t), 4); - - if (!ptr->jumpData) { - DEBUG_FUNCTION_LINE_ERR("Failed to alloc jump data"); - return {}; - } - } - return ptr; } -bool PatchedFunctionData::updateFunctionAddresses() { - if (this->library == LIBRARY_OTHER) { + +bool PatchedFunctionData::allocateDataForJumps() { + if (this->jumpData != nullptr && this->jumpToOriginal != nullptr) { return true; } + if (this->replacementFunctionAddress > 0x01FFFFFC || this->targetProcess != FP_TARGET_PROCESS_ALL) { + this->jumpDataSize = 15; // We could predict the actual size and save some memory, but at the moment we don't need it. + this->jumpData = (uint32_t *) MEMAllocFromExpHeapEx(this->heapHandle, this->jumpDataSize * sizeof(uint32_t), 4); - if (!this->functionName) { - DEBUG_FUNCTION_LINE_ERR("Function name was empty. This should never happen."); - OSFatal("FunctionPatcherModule: function name was empty"); + if (!this->jumpData) { + DEBUG_FUNCTION_LINE_ERR("Failed to alloc jump data"); + return false; + } + } + + this->jumpToOriginal = (uint32_t *) MEMAllocFromExpHeapEx(this->heapHandle, 0x5 * sizeof(uint32_t), 4); + + if (!this->jumpToOriginal) { + DEBUG_FUNCTION_LINE_ERR("Failed to alloc jump data"); + return false; + } + return true; +} + +bool PatchedFunctionData::getAddressForExecutable(uint32_t *outAddress) const { + if (!outAddress) { return false; } - auto real_address = functionAddressProvider->getEffectiveAddressOfFunction(library, this->functionName->c_str()); - if (!real_address) { - DEBUG_FUNCTION_LINE("OSDynLoad_FindExport failed for %s, updating address not possible.", this->functionName->c_str()); + if (!executableName.has_value()) { return false; } + uint32_t result = 0; + if (type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_ADDRESS) { + int num_rpls = OSDynLoad_GetNumberOfRPLs(); + if (num_rpls == 0) { + DEBUG_FUNCTION_LINE_ERR("OSDynLoad_GetNumberOfRPLs failed. Missing patches?"); + OSFatal("OSDynLoad_GetNumberOfRPLs failed. This shouldn't happen. Missing patches?"); + return false; + } + + std::vector rpls; + rpls.resize(num_rpls); + + bool ret = OSDynLoad_GetRPLInfo(0, num_rpls, rpls.data()); + if (!ret) { + DEBUG_FUNCTION_LINE_ERR("OSDynLoad_GetRPLInfo failed. Missing patches?"); + OSFatal("OSDynLoad_GetNumberOfRPLs failed. This shouldn't happen. Missing patches?"); + return false; + } + bool found = false; + for (auto &rpl : rpls) { + if (std::string_view(rpl.name).ends_with(executableName.value())) { + result = rpl.textAddr + textOffset; + found = true; + break; + } + } + if (!found) { + if (executableName->ends_with(".rpx")) { + DEBUG_FUNCTION_LINE_ERR("Can't patch function. \"%s\" is not loaded.", executableName->c_str()); + } else { + DEBUG_FUNCTION_LINE_WARN("Can't patch function. \"%s\" is not loaded.", executableName->c_str()); + } + return false; + } + } else if (type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_NAME) { + if (!this->functionName) { + DEBUG_FUNCTION_LINE_ERR("Function name was empty. This should never happen."); + OSFatal("Function name was empty. This should never happen. Check logs for more information."); + return false; + } + result = KernelFindExport(executableName.value(), functionName.value()); + if (result == 0) { + DEBUG_FUNCTION_LINE_WARN("Failed to find function \"%s\" in \"%s\".", functionName->c_str(), executableName->c_str()); + return false; + } + } else { + DEBUG_FUNCTION_LINE_ERR("Unexpected function patching type. %d", type); + OSFatal("Unexpected function patching type."); + return false; + } + + *outAddress = result; + return true; +} + +bool PatchedFunctionData::updateFunctionAddresses() { + uint32_t real_address; + if (type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_NAME || type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_ADDRESS) { + if (!getAddressForExecutable(&real_address)) { + return false; + } + } else { + if (!this->library) { + DEBUG_FUNCTION_LINE_ERR("library name was empty. This should never happen."); + OSFatal("library was empty. This should never happen. Check logs for more information."); + return false; + } + if (this->library == LIBRARY_OTHER) { + // Use the provided physical/effective address! + return true; + } + + if (!this->functionName) { + DEBUG_FUNCTION_LINE_ERR("Function name was empty. This should never happen."); + OSFatal("Function name was empty. This should never happen. Check logs for more information."); + return false; + } + + real_address = functionAddressProvider->getEffectiveAddressOfFunction(library.value(), this->functionName->c_str()); + if (!real_address) { + DEBUG_FUNCTION_LINE("OSDynLoad_FindExport failed for %s, updating address not possible.", this->functionName->c_str()); + return false; + } + } + this->realEffectiveFunctionAddress = real_address; auto physicalFunctionAddress = (uint32_t) OSEffectiveToPhysical(real_address); if (!physicalFunctionAddress) { DEBUG_FUNCTION_LINE_ERR("Error. Something is wrong with the physical address"); + OSFatal("Error. Something is wrong with the physical address"); return false; } this->realPhysicalFunctionAddress = physicalFunctionAddress; @@ -181,3 +331,35 @@ PatchedFunctionData::~PatchedFunctionData() { this->jumpData = nullptr; } } + +bool PatchedFunctionData::shouldBePatched() const { + if (type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_NAME || type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_ADDRESS) { + uint64_t curTitleId = OSGetTitleID(); + if (!this->titleIds.contains(curTitleId)) { + DEBUG_FUNCTION_LINE_VERBOSE("Skip function patch. Patch is not for title %016llX", curTitleId); + return false; + } + auto mcpHandle = MCP_Open(); + MCPTitleListType titleInfo; + int32_t res = -1; + if ((curTitleId & 0x0000000F00000000) == 0) { + res = MCP_GetTitleInfo(mcpHandle, curTitleId | 0x0000000E00000000, &titleInfo); + } + if (res != 0) { + res = MCP_GetTitleInfo(mcpHandle, curTitleId, &titleInfo); + } + MCP_Close(mcpHandle); + if (res != 0) { + DEBUG_FUNCTION_LINE_WARN("Failed to get title version of %016llX.", curTitleId); + OSFatal("Failed to get title version. This should not happen.\n" + "Please report this with a crash log."); + return false; + } + MCP_Close(mcpHandle); + if (titleInfo.titleVersion < titleVersionMin || titleInfo.titleVersion > titleVersionMax) { + DEBUG_FUNCTION_LINE("Skipping function patch. Title version does not match: Expected >= %d && <= %d. Real version: %d", titleVersionMin, titleVersionMax, titleInfo.titleVersion); + return false; + } + } + return true; +} diff --git a/source/PatchedFunctionData.h b/source/PatchedFunctionData.h index a0e1bce..8bf3691 100644 --- a/source/PatchedFunctionData.h +++ b/source/PatchedFunctionData.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -22,9 +23,16 @@ public: explicit PatchedFunctionData(std::shared_ptr functionAddressProvider) : functionAddressProvider(std::move(functionAddressProvider)) { } - static std::optional> make_shared(std::shared_ptr functionAddressProvider, - function_replacement_data_t *replacementData, - MEMHeapHandle heapHandle); + static std::optional> make_shared_v2(std::shared_ptr functionAddressProvider, + function_replacement_data_v2_t *replacementData, + MEMHeapHandle heapHandle); + static std::optional> make_shared_v3(std::shared_ptr functionAddressProvider, + function_replacement_data_v3_t *replacementData, + MEMHeapHandle heapHandle); + + bool allocateDataForJumps(); + + bool getAddressForExecutable(uint32_t *outAddress) const; bool updateFunctionAddresses(); @@ -32,29 +40,38 @@ public: void generateReplacementJump(); + [[nodiscard]] bool shouldBePatched() const; + uint32_t getHandle() { return (uint32_t) this; } - uint32_t *jumpToOriginal{}; - uint32_t *jumpData{}; + uint32_t *jumpToOriginal = {}; + uint32_t *jumpData = {}; - uint32_t realEffectiveFunctionAddress{}; - uint32_t realPhysicalFunctionAddress{}; + uint32_t realEffectiveFunctionAddress = {}; + uint32_t realPhysicalFunctionAddress = {}; - uint32_t *realCallFunctionAddressPtr{}; + uint32_t *realCallFunctionAddressPtr = {}; - uint32_t replacementFunctionAddress{}; + uint32_t replacementFunctionAddress = {}; - uint32_t replacedInstruction{}; + uint32_t replacedInstruction = {}; - uint32_t replaceWithInstruction{}; - uint32_t jumpDataSize = 15; - MEMHeapHandle heapHandle = nullptr; + uint32_t replaceWithInstruction = {}; + uint32_t jumpDataSize = 15; + MEMHeapHandle heapHandle = nullptr; - bool isPatched{}; - function_replacement_library_type_t library{}; - FunctionPatcherTargetProcess targetProcess{}; - std::optional functionName = {}; - std::shared_ptr functionAddressProvider; + FunctionPatcherFunctionType type = {}; + std::set titleIds; + uint16_t titleVersionMin = 0; + uint16_t titleVersionMax = 0xFFFF; + std::optional executableName = {}; + uint32_t textOffset = 0; + + bool isPatched = {}; + std::optional library = {}; + FunctionPatcherTargetProcess targetProcess = {}; + std::optional functionName = {}; + std::shared_ptr functionAddressProvider = {}; }; diff --git a/source/export.cpp b/source/export.cpp index 983a3d1..a550363 100644 --- a/source/export.cpp +++ b/source/export.cpp @@ -6,17 +6,30 @@ #include #include +WUT_CHECK_OFFSET(function_replacement_data_v2_t, 0x00, VERSION); +WUT_CHECK_OFFSET(function_replacement_data_v3_t, 0x00, version); + FunctionPatcherStatus FPAddFunctionPatch(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle, bool *outHasBeenPatched) { if (function_data == nullptr) { DEBUG_FUNCTION_LINE_ERR("function_data was NULL"); return FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT; } - if (function_data->VERSION != FUNCTION_REPLACEMENT_DATA_STRUCT_VERSION) { + + if (function_data->version < 2 || function_data->version > 3) { DEBUG_FUNCTION_LINE_ERR("Failed to patch function. struct version mismatch"); return FUNCTION_PATCHER_RESULT_UNSUPPORTED_STRUCT_VERSION; } - auto functionDataOpt = PatchedFunctionData::make_shared(gFunctionAddressProvider, function_data, gJumpHeapHandle); + std::optional> functionDataOpt; + if (function_data->version == 2) { + functionDataOpt = PatchedFunctionData::make_shared_v2(gFunctionAddressProvider, (function_replacement_data_v2_t *) function_data, gJumpHeapHandle); + } else if (function_data->version == 3) { + functionDataOpt = PatchedFunctionData::make_shared_v3(gFunctionAddressProvider, (function_replacement_data_v3_t *) function_data, gJumpHeapHandle); + } else { + // Should never happen. + DEBUG_FUNCTION_LINE_ERR("Unknown function_replacement_data_t struct version"); + OSFatal("Unknown function patching struct version. Update FunctionPatcherModule/Aroma."); + } if (!functionDataOpt) { return FUNCTION_PATCHER_RESULT_UNKNOWN_ERROR; diff --git a/source/function_patcher.cpp b/source/function_patcher.cpp index ba3d30f..37438e5 100644 --- a/source/function_patcher.cpp +++ b/source/function_patcher.cpp @@ -36,6 +36,10 @@ bool PatchFunction(std::shared_ptr &patchedFunction) { return true; } + if (!patchedFunction->shouldBePatched()) { + return false; + } + // The addresses of a function might change every time with run another application. if (!patchedFunction->updateFunctionAddresses()) { return false; diff --git a/source/main.cpp b/source/main.cpp index 8b191af..c0b7d7e 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -6,13 +6,14 @@ #include "utils/utils.h" #include #include +#include #include #include #include WUMS_MODULE_EXPORT_NAME("homebrew_functionpatcher"); WUMS_MODULE_INIT_BEFORE_RELOCATION_DONE_HOOK(); -WUMS_DEPENDS_ON(homebrew_kernel); +WUMS_DEPENDS_ON(homebrew_kernel); void UpdateFunctionPointer() { // We need the real MEMAllocFromDefaultHeapEx/MEMFreeToDefaultHeap function pointer to force-allocate memory on the default heap. @@ -70,8 +71,44 @@ void CheckIfPatchedFunctionsAreStillInMemory() { } } +bool PatchInstruction(void *instr, uint32_t original, uint32_t replacement) { + uint32_t current = *(uint32_t *) instr; + if (current != original) { + return current == replacement; + } + + KernelCopyData(OSEffectiveToPhysical((uint32_t) instr), OSEffectiveToPhysical((uint32_t) &replacement), sizeof(replacement)); + + DCFlushRange(instr, 4); + ICInvalidateRange(instr, 4); + + current = *(uint32_t *) instr; + + return true; +} + +bool PatchDynLoadFunctions() { + uint32_t *patch1 = ((uint32_t *) &OSDynLoad_GetNumberOfRPLs) + 6; + uint32_t *patch2 = ((uint32_t *) &OSDynLoad_GetRPLInfo) + 22; + + if (!PatchInstruction(patch1, 0x41820038 /* beq +38 */, 0x60000000 /*nop*/)) { + return false; + } + if (!PatchInstruction(patch2, 0x41820100 /* beq +100 */, 0x60000000 /*nop*/)) { + return false; + } + + return true; +} + WUMS_INITIALIZE() { UpdateFunctionPointer(); + initLogging(); + + if (!PatchDynLoadFunctions()) { + DEBUG_FUNCTION_LINE_ERR("Failed to patch OSDynLoad_GetRPLInfo or OSDynLoad_GetNumberOfRPLs"); + OSFatal("Failed to patch OSDynLoad_GetRPLInfo or OSDynLoad_GetNumberOfRPLs"); + } memset(gJumpHeapData, 0, JUMP_HEAP_DATA_SIZE); gJumpHeapHandle = MEMCreateExpHeapEx((void *) (gJumpHeapData), JUMP_HEAP_DATA_SIZE, 1); @@ -101,7 +138,7 @@ void notify_callback(OSDynLoad_Module module, auto library = gFunctionAddressProvider->getTypeForHandle(module); if (library != LIBRARY_OTHER) { for (auto &cur : gPatchedFunctions) { - if (cur->library == library) { + if (cur->type == FUNCTION_PATCHER_REPLACE_BY_LIB_OR_ADDRESS && cur->library.has_value() && cur->library == library) { cur->isPatched = false; } } diff --git a/source/utils/KernelFindExport.cpp b/source/utils/KernelFindExport.cpp new file mode 100644 index 0000000..2ba71fb --- /dev/null +++ b/source/utils/KernelFindExport.cpp @@ -0,0 +1,94 @@ +#include "KernelFindExport.h" +#include +#include +#include +#include + +#define KernelGetLoadedRPL ((LOADED_RPL * (*) (uint32_t))(0xfff13524)) +#define KernelGetRAMPID ((int32_t(*)(uint32_t *))(0xfff10ea0)) +#define KernelSetRAMPID ((uint32_t(*)(uint32_t, uint32_t))(0xfff10cc0)) + +#define ELF_ST_TYPE(i) ((i) &0xf) + +/* + * Based on the findClosestSymbol implementation. See: + * https://github.com/decaf-emu/decaf-emu/blob/e8c9af3057a7d94f6e970406eb1ba1c37c87b4d1/src/libdecaf/src/cafe/kernel/cafe_kernel_loader.cpp#L251 + */ +uint32_t FindExportKernel(const char *rplName, const char *functionName) { + if (rplName == nullptr || functionName == nullptr) { + return 0; + } + uint32_t result = 0; + uint32_t currentRamPID; + auto err = KernelGetRAMPID(¤tRamPID); + if (err != -1) { + // Switch to loader address space view. + KernelSetRAMPID(err, 2); + for (auto rpl = KernelGetLoadedRPL(0); rpl != nullptr; rpl = rpl->nextLoadedRpl) { + if (std::string_view(rpl->moduleNameBuffer) != rplName) { + continue; + } + + uint32_t textSectionIndex = 0xFFFFFFFF; + for (auto i = 0u; i < rpl->elfHeader.shnum; ++i) { + auto sectionAddress = rpl->sectionAddressBuffer[i]; + if (!sectionAddress) { + continue; + } + auto sectionHeader = (ElfSectionHeader *) (((uint32_t) (rpl->sectionHeaderBuffer)) + rpl->elfHeader.shentsize * i); + + if (auto shstrndx = rpl->elfHeader.shstrndx) { + auto shStrSection = (char *) (rpl->sectionAddressBuffer[shstrndx]); + auto sectionName = (shStrSection + sectionHeader->name); + if (std::string_view(sectionName) == ".text") { + textSectionIndex = i; + } + } + } + for (auto i = 0u; i < rpl->elfHeader.shnum; ++i) { + auto sectionAddress = rpl->sectionAddressBuffer[i]; + if (!sectionAddress) { + continue; + } + auto sectionHeader = (ElfSectionHeader *) (((uint32_t) (rpl->sectionHeaderBuffer)) + rpl->elfHeader.shentsize * i); + + if (sectionHeader->type == SHT_SYMTAB) { + auto strTab = rpl->sectionAddressBuffer[sectionHeader->link]; + auto symTabEntSize = sectionHeader->entsize ? static_cast(sectionHeader->entsize) : sizeof(ElfSymbol); + auto numSymbols = sectionHeader->size / symTabEntSize; + bool found = false; + for (auto j = 0u; j < numSymbols; ++j) { + auto symbol = (ElfSymbol *) (sectionAddress + j * symTabEntSize); + if (symbol->shndx == textSectionIndex && ELF_ST_TYPE(symbol->info) == STT_FUNC) { + auto symbolName = (const char *) (strTab + symbol->name); + if (std::string_view(symbolName) == functionName) { + result = symbol->value; + found = true; + break; + } + } + } + if (found) { + break; + } + } + } + break; + } + // Switch back to "old" space address view + KernelSetRAMPID(err, currentRamPID); + } + return result; +} + +extern "C" uint32_t SC_0x51(const char *rplname, const char *functionName); + +uint32_t KernelFindExport(const std::string_view &rplName, const std::string_view &functionName) { + KernelPatchSyscall(0x51, (uint32_t) &FindExportKernel); + OSMemoryBarrier(); + if (rplName.ends_with(".rpx") || rplName.ends_with(".rpl")) { + auto pureRPLName = std::string(rplName).substr(0, rplName.length() - 4); + return SC_0x51(pureRPLName.c_str(), functionName.data()); + } + return SC_0x51(rplName.data(), functionName.data()); +} \ No newline at end of file diff --git a/source/utils/KernelFindExport.h b/source/utils/KernelFindExport.h new file mode 100644 index 0000000..bb6bd93 --- /dev/null +++ b/source/utils/KernelFindExport.h @@ -0,0 +1,151 @@ +#pragma once +#include +#include +#include + +/* + * https://github.com/decaf-emu/decaf-emu/blob/e8c9af3057a7d94f6e970406eb1ba1c37c87b4d1/src/libdecaf/src/cafe/loader/cafe_loader_rpl.h#L150 + */ +struct ElfHeader { + uint8_t magic[4]; // File identification. + uint8_t fileClass; // File class. + uint8_t encoding; // Data encoding. + uint8_t elfVersion; // File version. + uint8_t abi; // OS/ABI identification. + uint8_t abiVersion; // OS/ABI version. + uint8_t pad[7]; + + uint16_t type; // Type of file (ET_*) + uint16_t machine; // Required architecture for this file (EM_*) + uint32_t version; // Must be equal to 1 + uint32_t entry; // Address to jump to in order to start program + uint32_t phoff; // Program header table's file offset, in bytes + uint32_t shoff; // Section header table's file offset, in bytes + uint32_t flags; // Processor-specific flags + uint16_t ehsize; // Size of ELF header, in bytes + uint16_t phentsize; // Size of an entry in the program header table + uint16_t phnum; // Number of entries in the program header table + uint16_t shentsize; // Size of an entry in the section header table + uint16_t shnum; // Number of entries in the section header table + uint16_t shstrndx; // Sect hdr table index of sect name string table +}; +WUT_CHECK_OFFSET(ElfHeader, 0x00, magic); +WUT_CHECK_OFFSET(ElfHeader, 0x04, fileClass); +WUT_CHECK_OFFSET(ElfHeader, 0x05, encoding); +WUT_CHECK_OFFSET(ElfHeader, 0x06, elfVersion); +WUT_CHECK_OFFSET(ElfHeader, 0x07, abi); +WUT_CHECK_OFFSET(ElfHeader, 0x08, abiVersion); +WUT_CHECK_OFFSET(ElfHeader, 0x10, type); +WUT_CHECK_OFFSET(ElfHeader, 0x12, machine); +WUT_CHECK_OFFSET(ElfHeader, 0x14, version); +WUT_CHECK_OFFSET(ElfHeader, 0x18, entry); +WUT_CHECK_OFFSET(ElfHeader, 0x1C, phoff); +WUT_CHECK_OFFSET(ElfHeader, 0x20, shoff); +WUT_CHECK_OFFSET(ElfHeader, 0x24, flags); +WUT_CHECK_OFFSET(ElfHeader, 0x28, ehsize); +WUT_CHECK_OFFSET(ElfHeader, 0x2A, phentsize); +WUT_CHECK_OFFSET(ElfHeader, 0x2C, phnum); +WUT_CHECK_OFFSET(ElfHeader, 0x2E, shentsize); +WUT_CHECK_OFFSET(ElfHeader, 0x30, shnum); +WUT_CHECK_OFFSET(ElfHeader, 0x32, shstrndx); +WUT_CHECK_SIZE(ElfHeader, 0x34); + +/* + * https://github.com/decaf-emu/decaf-emu/blob/e8c9af3057a7d94f6e970406eb1ba1c37c87b4d1/src/libdecaf/src/cafe/loader/cafe_loader_rpl.h#L216 + */ +struct ElfSectionHeader { + //! Section name (index into string table) + uint32_t name; + + //! Section type (SHT_*) + uint32_t type; + + //! Section flags (SHF_*) + uint32_t flags; + + //! Address where section is to be loaded + uint32_t addr; + + //! File offset of section data, in bytes + uint32_t offset; + + //! Size of section, in bytes + uint32_t size; + + //! Section type-specific header table index link + uint32_t link; + + //! Section type-specific extra information + uint32_t info; + + //! Section address alignment + uint32_t addralign; + + //! Size of records contained within the section + uint32_t entsize; +}; +WUT_CHECK_OFFSET(ElfSectionHeader, 0x00, name); +WUT_CHECK_OFFSET(ElfSectionHeader, 0x04, type); +WUT_CHECK_OFFSET(ElfSectionHeader, 0x08, flags); +WUT_CHECK_OFFSET(ElfSectionHeader, 0x0C, addr); +WUT_CHECK_OFFSET(ElfSectionHeader, 0x10, offset); +WUT_CHECK_OFFSET(ElfSectionHeader, 0x14, size); +WUT_CHECK_OFFSET(ElfSectionHeader, 0x18, link); +WUT_CHECK_OFFSET(ElfSectionHeader, 0x1C, info); +WUT_CHECK_OFFSET(ElfSectionHeader, 0x20, addralign); +WUT_CHECK_OFFSET(ElfSectionHeader, 0x24, entsize); +WUT_CHECK_SIZE(ElfSectionHeader, 0x28); + + +/* + * https://github.com/decaf-emu/decaf-emu/blob/e8c9af3057a7d94f6e970406eb1ba1c37c87b4d1/src/libdecaf/src/cafe/loader/cafe_loader_rpl.h#L260 + */ +struct ElfSymbol { + //! Symbol name (index into string table) + uint32_t name; + + //! Value or address associated with the symbol + uint32_t value; + + //! Size of the symbol + uint32_t size; + + //! Symbol's type and binding attributes + uint8_t info; + + //! Must be zero; reserved + uint8_t other; + + //! Which section (header table index) it's defined in (SHN_*) + uint16_t shndx; +}; +WUT_CHECK_OFFSET(ElfSymbol, 0x00, name); +WUT_CHECK_OFFSET(ElfSymbol, 0x04, value); +WUT_CHECK_OFFSET(ElfSymbol, 0x08, size); +WUT_CHECK_OFFSET(ElfSymbol, 0x0C, info); +WUT_CHECK_OFFSET(ElfSymbol, 0x0D, other); +WUT_CHECK_OFFSET(ElfSymbol, 0x0E, shndx); +WUT_CHECK_SIZE(ElfSymbol, 0x10); + +/* + * https://github.com/decaf-emu/decaf-emu/blob/6feb1be1db3938e6da2d4a65fc0a7a8599fc8dd6/src/libdecaf/src/cafe/loader/cafe_loader_loaded_rpl.h#L26 + */ +struct LOADED_RPL { + WUT_UNKNOWN_BYTES(0x08); + char *moduleNameBuffer; + WUT_UNKNOWN_BYTES(0x10); + ElfHeader elfHeader; + void *sectionHeaderBuffer; + WUT_UNKNOWN_BYTES(0xA0); + uint32_t *sectionAddressBuffer; + WUT_UNKNOWN_BYTES(0x1C); + LOADED_RPL *nextLoadedRpl; +}; +WUT_CHECK_OFFSET(LOADED_RPL, 0x08, moduleNameBuffer); +WUT_CHECK_OFFSET(LOADED_RPL, 0x1C, elfHeader); +WUT_CHECK_OFFSET(LOADED_RPL, 0x50, sectionHeaderBuffer); +WUT_CHECK_OFFSET(LOADED_RPL, 0xF4, sectionAddressBuffer); +WUT_CHECK_OFFSET(LOADED_RPL, 0x114, nextLoadedRpl); +WUT_CHECK_SIZE(LOADED_RPL, 0x118); + +uint32_t KernelFindExport(const std::string_view &rplName, const std::string_view &functioName); \ No newline at end of file diff --git a/source/utils/syscall.s b/source/utils/syscall.s new file mode 100644 index 0000000..962d42e --- /dev/null +++ b/source/utils/syscall.s @@ -0,0 +1,5 @@ +.global SC_0x51 +SC_0x51: + li %r0, 0x5100 + sc +blr \ No newline at end of file