This commit is contained in:
Maschell 2022-07-26 19:29:41 +02:00
parent 5a3bae8616
commit 60a0679699
7 changed files with 232 additions and 75 deletions

22
main.py
View File

@ -116,6 +116,7 @@ COMMAND_TOGGLE_BREAKPOINT = 8
COMMAND_POKE_REGISTERS = 9
COMMAND_RECEIVE_MESSAGES = 10
COMMAND_SEND_MESSAGE = 11
COMMAND_DISASM = 12
class Debugger:
def __init__(self):
@ -157,6 +158,12 @@ class Debugger:
self.sendall(struct.pack(">II", addr, num))
data = self.recvall(num)
return data
def disasm(self, addr, num):
self.sendbyte(COMMAND_DISASM)
self.sendall(struct.pack(">II", addr, num))
length = struct.unpack(">I", self.recvall(4))[0]
return self.recvall(length).decode("ascii")
def write(self, addr, data):
self.sendbyte(COMMAND_WRITE)
@ -503,18 +510,11 @@ class DisassemblyWidget(QTextEdit):
self.updateText()
self.updateHighlight()
def updateText(self):
if debugger.connected:
blob = debugger.read(self.base, 0x60)
else:
blob = b"\x00" * 0x60
def updateText(self):
text = ""
for i in range(24):
address = self.base + i * 4
value = struct.unpack_from(">I", blob, i * 4)[0]
instr = disassemble.disassemble(value, address)
text += "%08X: %08X %s\n" %(address, value, instr)
if debugger.connected:
text = debugger.disasm(self.base, 0x60)
self.setPlainText(text)
def updateHighlight(self):

View File

@ -7,20 +7,27 @@
#include <coreinit/debug.h>
#include <coreinit/dynload.h>
#include <coreinit/exception.h>
#include <coreinit/interrupts.h>
#include <coreinit/memory.h>
#include <coreinit/scheduler.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <gx2/context.h>
#include <malloc.h>
#include <sysapp/switch.h>
#include <vpad/input.h>
#include <stdarg.h>
#include <string>
Debugger *debugger;
bool initDebugState = false;
bool BreakPoint::isRange(uint32_t addr, uint32_t length) const {
return address >= addr && address <= addr + length - 1;
}
BreakPoint *BreakPointMgr::find(uint32_t addr, bool includeSpecial) {
BreakPoint *bp = breakpoints.find(addr);
if (!bp && includeSpecial) {
@ -280,7 +287,6 @@ bool ExceptionState::isBreakpoint() {
}
void ExceptionState::resume() {
DEBUG_FUNCTION_LINE("OSLoadContext");
OSLoadContext(&context);
}
@ -540,30 +546,26 @@ void Debugger::handleBreakPoint(ExceptionState *state) {
if (firstTrap) {
firstTrap = false;
Screen screen;
screen.init();
auto *screen = new Screen;
Screen::drawText(
0, 0, "Waiting for debugger connection.\n"
"Press the home button to continue without debugger.\n"
"You can still connect while the game is running.");
Screen::flip();
delete screen;
while (!connected) {
uint32_t buttons = GetInput(VPAD_BUTTON_HOME);
if (buttons) {
DEBUG_FUNCTION_LINE("Pressed home");
state->context.srr0 += 4;
state->resume();
}
}
}
DEBUG_FUNCTION_LINE("stepper.handleBreakPoint(state);");
stepper.handleBreakPoint(state);
if (!connected) {
DEBUG_FUNCTION_LINE("if (!connected) {");
handleFatalCrash(&state->context, state->type);
}
@ -671,10 +673,6 @@ void Debugger::cleanup() {
;
}
extern "C" void OSRestoreInterrupts(int state);
extern "C" void __OSLockScheduler(void *);
extern "C" void __OSUnlockScheduler(void *);
extern "C" int OSDisableInterrupts();
static const char **commandNames = (const char *[]){
"COMMAND_CLOSE",
@ -688,15 +686,49 @@ static const char **commandNames = (const char *[]){
"COMMAND_TOGGLE_BREAKPOINT",
"COMMAND_POKE_REGISTERS",
"COMMAND_RECEIVE_MESSAGES",
"COMMAND_SEND_MESSAGE"};
"COMMAND_SEND_MESSAGE",
"COMMAND_DISASM"};
#define LOG_DISASSEMBLY_SIZE (4096)
static char
sDisassemblyBuffer[LOG_DISASSEMBLY_SIZE];
static uint32_t
sDisassemblyLength = 0;
static void
disassemblyPrintCallback(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
sDisassemblyLength += vsprintf(sDisassemblyBuffer + sDisassemblyLength,
fmt, args);
sDisassemblyBuffer[sDisassemblyLength] = 0;
va_end(args);
}
void eraseAllSubStr(std::string & mainStr, const std::string & toErase)
{
size_t pos = std::string::npos;
// Search for the substring in string in a loop untill nothing is found
while ((pos = mainStr.find(toErase) )!= std::string::npos)
{
// If found then erase it from string
mainStr.erase(pos, toErase.length());
}
}
//#define OSIopShell_Command_Disassemble ((void (*)(void*, uint32_t))(0x101C400 + 0x173e0))
#define __os_printf ((void (*)(char*, ...))(0x101C400 + 0x012e88))
void Debugger::mainLoop(Client *client) {
DEBUG_FUNCTION_LINE("About to enter mainLoop while");
while (!stopRunning) {
uint8_t cmd;
if (!client->recvall(&cmd, 1)) return;
if (cmd <= 11) {
if (cmd <= 12 && cmd != 10) {
DEBUG_FUNCTION_LINE("Recieved command %s %d", commandNames[cmd], cmd);
}
if (cmd == COMMAND_CLOSE) {
@ -713,6 +745,50 @@ void Debugger::mainLoop(Client *client) {
return;
}
delete[] buffer;
} else if (cmd == COMMAND_DISASM) {
uint32_t addr, length;
if (!client->recvall(&addr, 4)) return;
if (!client->recvall(&length, 4)) return;
char *buffer = new char[0x40];
auto addrAsPtr = (uint32_t *) (addr & 0xfffffffc);
DisassemblePPCRange(reinterpret_cast<void *>(addr + 0x20), reinterpret_cast<void *>(addr + length), reinterpret_cast<DisassemblyPrintFn>(__os_printf),OSGetSymbolName,
static_cast<DisassemblePPCFlags>(0x121));
for (int i = 0; i < length / 4; i++) {
DisassemblePPCOpcode(&addrAsPtr[i],
buffer,
0x40,
OSGetSymbolName,
static_cast<DisassemblePPCFlags>(0x121));
disassemblyPrintCallback("0x%08x 0x%08x %s\n",&addrAsPtr[i],addrAsPtr[i],buffer);
}
delete[] buffer;
std::string shit(sDisassemblyBuffer);
eraseAllSubStr(shit, "(null)");
DEBUG_FUNCTION_LINE("done");
auto length_ = shit.length() + 1;
if (!client->sendall(&length_, 4)) {
sDisassemblyLength = 0;
return;
}
if (!client->sendall(shit.c_str(), length_)) {
sDisassemblyLength = 0;
return;
}
sDisassemblyLength = 0;
} else if (cmd == COMMAND_WRITE) {
uint32_t addr, length;
if (!client->recvall(&addr, 4)) return;
@ -733,15 +809,18 @@ void Debugger::mainLoop(Client *client) {
} else if (cmd == COMMAND_GET_MODULE_NAME) {
char name[0x40];
int length = 0x40;
OSDynLoad_GetModuleName(reinterpret_cast<OSDynLoad_Module>(-1), name, &length);
if(OSDynLoad_GetModuleName(reinterpret_cast<OSDynLoad_Module>(-1), name, &length) != OS_DYNLOAD_OK){
strncat(name, "ERROR", sizeof(name) -1);
}
length = strlen(name);
if (!client->sendall(&length, 4)) return;
if (!client->sendall(name, length)) return;
} else if (cmd == COMMAND_GET_MODULE_LIST) {
int num_rpls = OSDynLoad_GetNumberOfRPLs();
if (num_rpls == 0) {
return;
continue;
}
std::vector<OSDynLoad_NotifyData> rpls;
@ -749,7 +828,7 @@ void Debugger::mainLoop(Client *client) {
bool ret = OSDynLoad_GetRPLInfo(0, num_rpls, rpls.data());
if (!ret) {
return;
continue;
}
char buffer[0x1000]; //This should be enough
@ -784,6 +863,7 @@ void Debugger::mainLoop(Client *client) {
OSThread *current = ThreadList;
while (current) {
const char *name = OSGetThreadName(current);
OSReport("name %s", name);
uint32_t namelen = 0;
if (name) {
@ -795,7 +875,7 @@ void Debugger::mainLoop(Client *client) {
}
int priority = current->basePriority;
int type = *(uint32_t *) (current->__unk11);
int type = *((uint32_t *) current->__unk11);
if (type == 1) {
priority -= 0x20;
} else if (type == 2) {
@ -905,24 +985,20 @@ void Debugger::mainLoop(Client *client) {
void Debugger::threadFunc() {
DEBUG_FUNCTION_LINE("Hello from debugger thread :)!");
OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_DSI, dsiHandler);
OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_ISI, isiHandler);
OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_PROGRAM, programHandler);
DEBUG_FUNCTION_LINE("Callback init done.!");
prevDsiHandler = OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_DSI, dsiHandler);
prevIsiHandler = OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_ISI, isiHandler);
prevProgramHandler = OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_PROGRAM, programHandler);
Server server;
Client client;
DEBUG_FUNCTION_LINE("Set initialized = true");
initialized = true;
while (!stopRunning) {
if (!server.init(Socket::TCP)) continue;
if (!server.bind(1560)) continue;
if (!server.accept(&client)) continue;
DEBUG_FUNCTION_LINE("Accepted a connection");
connected = true;
mainLoop(&client);
DEBUG_FUNCTION_LINE("Lets do some cleanup");
cleanup();
connected = false;
client.close();
@ -931,55 +1007,55 @@ void Debugger::threadFunc() {
}
int Debugger::threadEntry(int argc, const char **argv) {
DEBUG_FUNCTION_LINE("threadEntry");
debugger->threadFunc();
return 0;
}
void Debugger::start() {
initialized = false;
connected = false;
firstTrap = true;
DEBUG_FUNCTION_LINE("OSInitMessageQueue");
OSInitMessageQueue(&eventQueue, eventMessages, MESSAGE_COUNT);
DEBUG_FUNCTION_LINE("init breakpoints");
breakpoints.init();
DEBUG_FUNCTION_LINE("init exceptions");
exceptions.init();
DEBUG_FUNCTION_LINE("init stepper");
stepper.init();
DEBUG_FUNCTION_LINE("Alloc thread");
serverThread = (OSThread *) memalign(0x20, sizeof(OSThread));
DEBUG_FUNCTION_LINE("Alloc stack");
serverStack = (char *) memalign(0x20, STACK_SIZE);
serverStack = (char *) memalign(0x20, STACK_SIZE);
DEBUG_FUNCTION_LINE("Create thread");
OSCreateThread(
serverThread, threadEntry, 0, 0,
serverStack + STACK_SIZE, STACK_SIZE,
0, 12);
DEBUG_FUNCTION_LINE("Set thread name");
0, OS_THREAD_ATTRIB_AFFINITY_CPU2 | OS_THREAD_ATTRIB_DETACHED);
OSSetThreadName(serverThread, "Debug Server");
DEBUG_FUNCTION_LINE("Resume thread");
OSResumeThread(serverThread);
while (!initialized) {
DEBUG_FUNCTION_LINE("Wait for thread init");
OSSleepTicks(OSMillisecondsToTicks(20));
}
initDebugState = true;
DCFlushRange(&initDebugState, sizeof(initDebugState));
DEBUG_FUNCTION_LINE("Thread init done! Exit start()");
}
Debugger::~Debugger() {
stopRunning = true;
DCFlushRange(&stopRunning, sizeof(stopRunning));
OSJoinThread(serverThread, nullptr);
free(serverStack);
serverStack = nullptr;
free(serverThread);
serverThread = nullptr;
initialized = false;
connected = false;
firstTrap = true;
// Restore exceptions.
OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_DSI, prevDsiHandler);
OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_ISI, prevIsiHandler);
OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_PROGRAM, prevProgramHandler);
}

View File

@ -7,6 +7,7 @@
#include <coreinit/mutex.h>
#include <coreinit/thread.h>
#include <coreinit/exception.h>
#include <cstdint>
#include <vector>
@ -233,7 +234,8 @@ private:
COMMAND_TOGGLE_BREAKPOINT,
COMMAND_POKE_REGISTERS,
COMMAND_RECEIVE_MESSAGES,
COMMAND_SEND_MESSAGE
COMMAND_SEND_MESSAGE,
COMMAND_DISASM
};
static int threadEntry(int argc, const char **argv);
@ -269,6 +271,11 @@ private:
bool initialized{};
bool connected{};
bool firstTrap{};
OSExceptionCallbackFn prevDsiHandler = nullptr;
OSExceptionCallbackFn prevIsiHandler = nullptr;
OSExceptionCallbackFn prevProgramHandler = nullptr;
};
extern "C" Debugger *debugger;
extern bool initDebugState;

View File

@ -1,28 +1,65 @@
#include "debugger.h"
#include "exceptions.h"
#include "logger.h"
#include <coreinit/cache.h>
#include <coreinit/debug.h>
#include <coreinit/dynload.h>
#include <coreinit/memorymap.h>
#include <kernel/kernel.h>
#include <wups.h>
OSThread **pThreadList;
WUPS_PLUGIN_NAME("Debugger");
WUPS_PLUGIN_DESCRIPTION("FTP Server");
WUPS_PLUGIN_DESCRIPTION("Wii U Debugger");
WUPS_PLUGIN_VERSION("0.1");
WUPS_PLUGIN_AUTHOR("Kinnay");
WUPS_PLUGIN_AUTHOR("Kinnay, Maschell");
WUPS_PLUGIN_LICENSE("GPL");
WUPS_USE_WUT_DEVOPTAB();
INITIALIZE_PLUGIN() {
InstallExceptionHandlers();
/* https://github.com/QuarkTheAwesome/CafeBinPatch/blob/main/src/runtime-patcher.cpp#L58 */
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));
//Only works on AROMA! WUPS 0.1's KernelCopyData is uncached, needs DCInvalidate here instead
DCFlushRange(instr, 4);
ICInvalidateRange(instr, 4);
current = *(uint32_t *) instr;
return true;
}
/* https://github.com/QuarkTheAwesome/CafeBinPatch/blob/main/src/runtime-patcher.cpp#L74 */
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;
}
INITIALIZE_PLUGIN() {
PatchDynLoadFunctions();
}
ON_APPLICATION_START() {
initLogging();
DEBUG_FUNCTION_LINE("Started Debugger plugin");
pThreadList = (OSThread **) 0x100567F8;
DEBUG_FUNCTION_LINE("Hello from Debugger plugin");
pThreadList = (OSThread **) 0x100567F8; // 100567f8
debugger = new Debugger();
DCFlushRange(&debugger, 4);
DCFlushRange(debugger, sizeof(Debugger));
DEBUG_FUNCTION_LINE("Created Debugger");
debugger->start();
DEBUG_FUNCTION_LINE("Started Debugger thread");
@ -34,3 +71,7 @@ ON_APPLICATION_REQUESTS_EXIT() {
DEBUG_FUNCTION_LINE("Deleted Debugger thread");
deinitLogging();
}
ON_APPLICATION_ENDS() {
initDebugState = false;
DCFlushRange(&initDebugState, sizeof(initDebugState));
}

View File

@ -1,17 +1,35 @@
#include "logger.h"
#include <coreinit/exception.h>
#include <cstdint>
#include <wups.h>
DECL_FUNCTION(int, OSSetExceptionCallback) {
return 0;
extern bool initDebugState;
DECL_FUNCTION(OSExceptionCallbackFn, OSSetExceptionCallback,
OSExceptionType exceptionType,
OSExceptionCallbackFn callback) {
if (initDebugState) {
return nullptr;
} else {
return real_OSSetExceptionCallback(exceptionType, callback);
}
}
DECL_FUNCTION(int, OSSetExceptionCallbackEx) {
return 0;
DECL_FUNCTION(OSExceptionCallbackFn, OSSetExceptionCallbackEx,
OSExceptionMode mode,
OSExceptionType exceptionType,
OSExceptionCallbackFn callback) {
if (initDebugState) {
return nullptr;
} else {
return real_OSSetExceptionCallbackEx(mode, exceptionType, callback);
}
}
DECL_FUNCTION(int, OSIsDebuggerInitialized) {
return true;
return initDebugState;
}
WUPS_MUST_REPLACE(OSSetExceptionCallback, WUPS_LOADER_LIBRARY_COREINIT, OSSetExceptionCallback);
WUPS_MUST_REPLACE(OSSetExceptionCallbackEx, WUPS_LOADER_LIBRARY_COREINIT, OSSetExceptionCallbackEx);
WUPS_MUST_REPLACE(OSIsDebuggerInitialized, WUPS_LOADER_LIBRARY_COREINIT, OSIsDebuggerInitialized);
// OSSetExceptionCallbackEx is just a branch to another function.
// Instead of "fixing" the FunctionPatcher, we use this hacky solution :)
WUPS_MUST_REPLACE_PHYSICAL_FOR_PROCESS(OSSetExceptionCallbackEx, (0x3201C400 + 0x286b0), (0x101C400 + 0x286b0), WUPS_FP_TARGET_PROCESS_GAME_AND_MENU);
WUPS_MUST_REPLACE_FOR_PROCESS(OSSetExceptionCallback, WUPS_LOADER_LIBRARY_COREINIT, OSSetExceptionCallback, WUPS_FP_TARGET_PROCESS_GAME_AND_MENU);
WUPS_MUST_REPLACE_FOR_PROCESS(OSIsDebuggerInitialized, WUPS_LOADER_LIBRARY_COREINIT, OSIsDebuggerInitialized, WUPS_FP_TARGET_PROCESS_GAME_AND_MENU);

View File

@ -1,14 +1,16 @@
#include "screen.h"
#include "logger.h"
#include <coreinit/debug.h>
#include <gx2/state.h>
#include <memory/mappedmemory.h>
Screen::Screen() : screenBuffer(nullptr) {}
Screen::Screen() : screenBuffer(nullptr) {
init();
}
Screen::~Screen() {
if (screenBuffer) {
MEMFreeToMappedMemory(screenBuffer);
screenBuffer = nullptr;
}
destroyBuffer();
}
void Screen::init() {
@ -17,6 +19,9 @@ void Screen::init() {
uint32_t bufferSize0 = OSScreenGetBufferSizeEx(SCREEN_TV);
uint32_t bufferSize1 = OSScreenGetBufferSizeEx(SCREEN_DRC);
screenBuffer = MEMAllocFromMappedMemoryForGX2Ex(bufferSize0 + bufferSize1, 0x100);
if (screenBuffer == nullptr) {
OSFatal("Failed to allocate screenbuffer");
}
OSScreenSetBufferEx(SCREEN_TV, screenBuffer);
OSScreenSetBufferEx(SCREEN_DRC, (char *) screenBuffer + bufferSize0);
@ -86,3 +91,11 @@ void Screen::flip() {
flip(SCREEN_TV);
flip(SCREEN_DRC);
}
void Screen::destroyBuffer() {
if (screenBuffer) {
MEMFreeToMappedMemory(screenBuffer);
screenBuffer = nullptr;
}
GX2Init(nullptr);
}

View File

@ -22,6 +22,8 @@ public:
static void drawText(OSScreenID screen, int x, int y, const char *text);
static void flip(OSScreenID screen);
void destroyBuffer();
private:
void *screenBuffer;