Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions source/PatchedFunctionData.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "PatchedFunctionData.h"
#include "utils/KernelFindExport.h"
#include "utils/globals.h"
#include "utils/utils.h"
#include <coreinit/mcp.h>
#include <coreinit/title.h>
Expand Down Expand Up @@ -102,16 +103,14 @@ bool PatchedFunctionData::allocateDataForJumps() {
}
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);
this->jumpData = (uint32_t *) MEMAllocFromExpHeapEx(this->heapHandle, this->jumpDataSize * sizeof(uint32_t), 0x20);

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);

this->jumpToOriginal = (uint32_t *) MEMAllocFromExpHeapEx(this->heapHandle, 0x5 * sizeof(uint32_t), 0x20);
if (!this->jumpToOriginal) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc jump data");
return false;
Expand Down Expand Up @@ -244,10 +243,11 @@ void PatchedFunctionData::generateJumpToOriginal() {
this->jumpToOriginal[1] = 0x48000002 | (jumpToAddress & 0x01FFFFFC);
}

DCFlushRange((void *) this->jumpToOriginal, sizeof(uint32_t) * 5);
ICInvalidateRange((void *) this->jumpToOriginal, sizeof(uint32_t) * 5);
DCFlushRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);
ICInvalidateRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);

*(this->realCallFunctionAddressPtr) = (uint32_t) this->jumpToOriginal;

OSMemoryBarrier();
}

Expand Down Expand Up @@ -311,8 +311,8 @@ void PatchedFunctionData::generateReplacementJump() {

this->replaceWithInstruction = 0x48000002 | ((uint32_t) this->jumpData & 0x01FFFFFC);

DCFlushRange((void *) this->jumpData, sizeof(uint32_t) * 15);
ICInvalidateRange((void *) this->jumpData, sizeof(uint32_t) * 15);
DCFlushRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);
ICInvalidateRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);
}

DCFlushRange((void *) &replaceWithInstruction, 4);
Expand All @@ -330,6 +330,9 @@ PatchedFunctionData::~PatchedFunctionData() {
MEMFreeToExpHeap(this->heapHandle, this->jumpData);
this->jumpData = nullptr;
}

DCFlushRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);
ICInvalidateRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);
}

bool PatchedFunctionData::shouldBePatched() const {
Expand Down
32 changes: 16 additions & 16 deletions source/export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,21 @@ FunctionPatcherStatus FPAddFunctionPatch(function_replacement_data_t *function_d
return FUNCTION_PATCHER_RESULT_UNKNOWN_ERROR;
}

auto &functionData = functionDataOpt.value();

// PatchFunction calls OSFatal on fatal errors.
// If this function returns false the target function was not patched
// Usually this means the target RPL is not (yet) loaded.
auto patchResult = PatchFunction(functionData);
if (outHasBeenPatched) {
*outHasBeenPatched = patchResult;
}

if (outHandle) {
*outHandle = functionData->getHandle();
}

{
std::lock_guard lock(gPatchedFunctionsMutex);
auto &functionData = functionDataOpt.value();
// PatchFunction calls OSFatal on fatal errors.
// If this function returns false the target function was not patched
// Usually this means the target RPL is not (yet) loaded.
auto patchResult = PatchFunction(functionData);
if (outHasBeenPatched) {
*outHasBeenPatched = patchResult;
}

if (outHandle) {
*outHandle = functionData->getHandle();
}

gPatchedFunctions.push_back(std::move(functionData));

OSMemoryBarrier();
Expand Down Expand Up @@ -96,7 +95,8 @@ FunctionPatcherStatus FPRemoveFunctionPatch(PatchedFunctionHandle handle) {
return FUNCTION_PATCHER_RESULT_PATCH_NOT_FOUND;
}

if (toBeRemoved->isPatched) {
bool functionWasPatched = toBeRemoved->isPatched;
if (functionWasPatched) {
// Restore function patches that were done after the patch we actually want to restore.
for (auto &cur : std::ranges::reverse_view(toBeTempRestored)) {
RestoreFunction(cur);
Expand All @@ -108,7 +108,7 @@ FunctionPatcherStatus FPRemoveFunctionPatch(PatchedFunctionHandle handle) {

gPatchedFunctions.erase(gPatchedFunctions.begin() + erasePosition);

if (toBeRemoved->isPatched) {
if (functionWasPatched) {
// Apply the other patches again
for (auto &cur : toBeTempRestored) {
PatchFunction(cur);
Expand Down
12 changes: 12 additions & 0 deletions source/function_patcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ bool PatchFunction(std::shared_ptr<PatchedFunctionData> &patchedFunction) {
return false;
}

DCFlushRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);
ICInvalidateRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);

if (patchedFunction->functionName) {
DEBUG_FUNCTION_LINE("Patching function %s...", patchedFunction->functionName->c_str());
} else {
Expand All @@ -85,6 +88,9 @@ bool PatchFunction(std::shared_ptr<PatchedFunctionData> &patchedFunction) {
// Write this->replaceWithInstruction to the first instruction of the function we want to replace.
CThread::runOnAllCores(writeDataAndFlushIC, patchedFunction.get());

DCFlushRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);
ICInvalidateRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);

// Set patch status
patchedFunction->isPatched = true;

Expand All @@ -101,6 +107,9 @@ bool RestoreFunction(std::shared_ptr<PatchedFunctionData> &patchedFunction) {
return false;
}

DCFlushRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);
ICInvalidateRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);

auto targetAddrPhys = (uint32_t) patchedFunction->realPhysicalFunctionAddress;

if (patchedFunction->library != LIBRARY_OTHER) {
Expand Down Expand Up @@ -137,6 +146,9 @@ bool RestoreFunction(std::shared_ptr<PatchedFunctionData> &patchedFunction) {
ICInvalidateRange((void *) patchedFunction->realEffectiveFunctionAddress, 4);
DCFlushRange((void *) patchedFunction->realEffectiveFunctionAddress, 4);

DCFlushRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);
ICInvalidateRange(gJumpHeapData, JUMP_HEAP_DATA_SIZE);

patchedFunction->isPatched = false;
return true;
}
4 changes: 3 additions & 1 deletion source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ WUMS_INITIALIZE() {
}

memset(gJumpHeapData, 0, JUMP_HEAP_DATA_SIZE);
gJumpHeapHandle = MEMCreateExpHeapEx((void *) (gJumpHeapData), JUMP_HEAP_DATA_SIZE, 1);
gJumpHeapHandle = MEMCreateExpHeapEx((void *) (gJumpHeapData), JUMP_HEAP_DATA_SIZE, MEM_HEAP_FLAG_USE_LOCK);
if (gJumpHeapHandle == nullptr) {
DEBUG_FUNCTION_LINE_ERR("Failed to create heap for jump data");
OSFatal("FunctionPatcherModule: Failed to create heap for jump data");
Expand Down Expand Up @@ -177,6 +177,8 @@ WUMS_APPLICATION_STARTS() {
OSMemoryBarrier();
OSDynLoad_AddNotifyCallback(notify_callback, nullptr);
}

CheckMemExpHeapJumpData();
}

WUMS_APPLICATION_REQUESTS_EXIT() {
Expand Down
4 changes: 2 additions & 2 deletions source/utils/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
#include <mutex>
#include <vector>

#define MODULE_VERSION "v0.2.4"
#define MODULE_VERSION "v0.2.5"
#define MODULE_VERSION_FULL MODULE_VERSION MODULE_VERSION_EXTRA

#define JUMP_HEAP_DATA_SIZE (32 * 1024)
#define JUMP_HEAP_DATA_SIZE (128 * 1024)
extern char gJumpHeapData[];
extern MEMHeapHandle gJumpHeapHandle;

Expand Down
97 changes: 96 additions & 1 deletion source/utils/utils.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
#include "CThread.h"
#include "globals.h"
#include "logger.h"


#include <coreinit/cache.h>
#include <coreinit/core.h>
#include <coreinit/memexpheap.h>
#include <coreinit/memorymap.h>
#include <kernel/kernel.h>

Expand All @@ -25,4 +32,92 @@ bool ReadFromPhysicalAddress(uint32_t srcPhys, uint32_t *out) {
DCFlushRange((void *) &currentInstruction, 4);
*out = currentInstruction;
return true;
}
}

bool CheckMemExpHeapBlock(MEMExpHeap *heap, MEMExpHeapBlockList *block, uint32_t tag, const char *listName, uint32_t &totalSizeOut) {
MEMExpHeapBlock *prevBlock = nullptr;
for (auto *cur = block->head; cur != nullptr; cur = cur->next) {
if (cur->prev != prevBlock) {
DEBUG_FUNCTION_LINE_ERR("[Exp Heap Check] \"%s\" prev is invalid. expected %p actual %p", listName, prevBlock, cur->prev);

return false;
}
if (cur < heap->header.dataStart || cur > heap->header.dataEnd || ((uint32_t) cur + sizeof(MEMExpHeapBlock) + cur->blockSize) > (uint32_t) heap->header.dataEnd) {
DEBUG_FUNCTION_LINE_ERR("[Exp Heap Check] Block is not inside heap. block: %p size %d; heap start %p heap end %p", cur, sizeof(MEMExpHeapBlock) + cur->blockSize, heap->header.dataStart, heap->header.dataEnd);

return false;
}
if (cur->tag != tag) {
DEBUG_FUNCTION_LINE_ERR("[%p][%d][Exp Heap Check] Invalid block tag expected %04X, actual %04X", &cur->tag, OSGetCoreId(), tag, cur->tag);

return false;
}

totalSizeOut = totalSizeOut + cur->blockSize + (cur->attribs >> 8 & 0x7fffff) + sizeof(MEMExpHeapBlock);
prevBlock = cur;
}
if (prevBlock != block->tail) {
DEBUG_FUNCTION_LINE_ERR("[Exp Heap Check] \"%s\" tail is unexpected! expected %p, actual %p", listName, heap->usedList.tail, prevBlock);

return false;
}
return true;
}

bool CheckMemExpHeapCore(MEMExpHeap *heap) {
uint32_t totalSize = 0;
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
if (!CheckMemExpHeapBlock(heap, &heap->usedList, 0x5544, "used", totalSize)) {
return false;
}

#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
if (!CheckMemExpHeapBlock(heap, &heap->freeList, 0x4652, "free", totalSize)) {
return false;
}

if (totalSize != (uint32_t) heap->header.dataEnd - (uint32_t) heap->header.dataStart) {
DEBUG_FUNCTION_LINE_ERR("[Exp Heap Check] heap size is unexpected! expected %08X, actual %08X", (uint32_t) heap->header.dataEnd - (uint32_t) heap->header.dataStart, totalSize);
return false;
}
return true;
}


bool CheckMemExpHeap(MEMExpHeap *heap) {

OSMemoryBarrier();
if (heap->header.tag != MEM_EXPANDED_HEAP_TAG) {
DEBUG_FUNCTION_LINE_ERR("[Exp Heap Check] Invalid heap handle. - %08X", heap->header.tag);
return false;
}

if (heap->header.flags & MEM_HEAP_FLAG_USE_LOCK) {
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
OSUninterruptibleSpinLock_Acquire(&(heap->header).lock);
}

auto result = CheckMemExpHeapCore(heap);

if (heap->header.flags & MEM_HEAP_FLAG_USE_LOCK) {
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
OSUninterruptibleSpinLock_Release(&(heap->header).lock);
}

return result;
}


static void CheckMemExpHeapJumpDataCallback(CThread *, void *) {
if (gJumpHeapHandle != nullptr) {
if (!CheckMemExpHeap(reinterpret_cast<MEMExpHeap *>(gJumpHeapHandle))) {
OSFatal("FunctionPatcherModule: Corrupted heap");
} else {
DEBUG_FUNCTION_LINE_VERBOSE("JumpData heap has no curruption. Checked on core %d", OSGetCoreId());
}
}
}

void CheckMemExpHeapJumpData() {
CThread::runOnAllCores(CheckMemExpHeapJumpDataCallback, nullptr, 0, 16, 0x1000);
}
2 changes: 2 additions & 0 deletions source/utils/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ std::shared_ptr<T> make_shared_nothrow(Args &&...args) noexcept(noexcept(T(std::
}

bool ReadFromPhysicalAddress(uint32_t srcPhys, uint32_t *out);

void CheckMemExpHeapJumpData();