mirror of
https://github.com/wiiu-env/gdbstub_plugin.git
synced 2024-09-29 21:08:36 +02:00
946 lines
21 KiB
C++
946 lines
21 KiB
C++
|
|
#include "cafe/coreinit.h"
|
|
#include "cafe/nsysnet.h"
|
|
#include "cafe/vpad.h"
|
|
#include "kernel.h"
|
|
|
|
#include "debugger.h"
|
|
#include "exceptions.h"
|
|
#include "screen.h"
|
|
#include "input.h"
|
|
|
|
#include <cstring>
|
|
|
|
|
|
bool BreakPoint::isRange(uint32_t addr, uint32_t length) {
|
|
return address >= addr && address <= addr + length - 1;
|
|
}
|
|
|
|
|
|
BreakPoint *BreakPointMgr::find(uint32_t addr, bool includeSpecial) {
|
|
BreakPoint *bp = breakpoints.find(addr);
|
|
if (!bp && includeSpecial) {
|
|
bp = special.find(addr);
|
|
}
|
|
return bp;
|
|
}
|
|
|
|
BreakPoint *BreakPointMgr::findRange(uint32_t addr, uint32_t length, int *index, bool includeSpecial) {
|
|
BreakPoint *bp = breakpoints.findRange(addr, length, index);
|
|
if (bp) {
|
|
return bp;
|
|
}
|
|
|
|
if (includeSpecial) {
|
|
int temp = *index - breakpoints.size();
|
|
bp = special.findRange(addr, length, &temp);
|
|
*index = temp + breakpoints.size();
|
|
return bp;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
SpecialBreakPoint *BreakPointMgr::findSpecial(uint32_t addr, OSThread *thread) {
|
|
int index = 0;
|
|
SpecialBreakPoint *bp = special.findRange(addr, 4, &index);
|
|
while (bp) {
|
|
if (bp->thread == thread) {
|
|
return bp;
|
|
}
|
|
bp = special.findRange(addr, 4, &index);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool BreakPointMgr::isCustom(uint32_t addr) {
|
|
return find(addr, false);
|
|
}
|
|
|
|
bool BreakPointMgr::isSpecial(uint32_t addr) {
|
|
return find(addr, true) && !find(addr, false);
|
|
}
|
|
|
|
bool BreakPointMgr::isSoftware(uint32_t addr) {
|
|
uint32_t instr = getInstr(addr);
|
|
uint32_t opcode = instr >> 26;
|
|
if (opcode == 3) { //twi
|
|
return true;
|
|
}
|
|
else if (opcode == 31) {
|
|
return (instr & 0x7FF) == 8; //tw
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void BreakPointMgr::disable(BreakPoint *bp) {
|
|
uint32_t address = bp->address;
|
|
if (bp->address) {
|
|
bp->address = 0;
|
|
if (!find(address, true)) {
|
|
KernelWriteU32(address, bp->instruction);
|
|
}
|
|
bp->instruction = 0;
|
|
}
|
|
}
|
|
|
|
void BreakPointMgr::enable(BreakPoint *bp, uint32_t addr) {
|
|
BreakPoint *other = find(addr, true);
|
|
if (other) {
|
|
bp->instruction = other->instruction;
|
|
}
|
|
else {
|
|
bp->instruction = *(uint32_t *)addr;
|
|
KernelWriteU32(addr, TRAP);
|
|
}
|
|
bp->address = addr;
|
|
}
|
|
|
|
void BreakPointMgr::init() {
|
|
OSInitMutex(&mutex);
|
|
}
|
|
|
|
void BreakPointMgr::lock() {
|
|
OSLockMutex(&mutex);
|
|
}
|
|
|
|
void BreakPointMgr::unlock() {
|
|
OSUnlockMutex(&mutex);
|
|
}
|
|
|
|
void BreakPointMgr::cleanup() {
|
|
lock();
|
|
breakpoints.cleanup();
|
|
special.cleanup();
|
|
unlock();
|
|
}
|
|
|
|
void BreakPointMgr::read(void *buffer, uint32_t addr, uint32_t length) {
|
|
lock();
|
|
|
|
memcpy(buffer, (void *)addr, length);
|
|
|
|
int index = 0;
|
|
BreakPoint *bp = findRange(addr, length, &index, true);
|
|
while (bp) {
|
|
uint32_t offset = bp->address - addr;
|
|
char *bufptr = (char *)buffer + offset;
|
|
if (bp->address > addr + length - 4) {
|
|
uint32_t value = bp->instruction;
|
|
for (int i = 0; i < length - offset; i++) {
|
|
bufptr[i] = value >> 24;
|
|
value <<= 8;
|
|
}
|
|
}
|
|
else {
|
|
*(uint32_t *)bufptr = bp->instruction;
|
|
}
|
|
bp = findRange(addr, length, &index, true);
|
|
}
|
|
unlock();
|
|
}
|
|
|
|
void BreakPointMgr::write(const void *buffer, uint32_t addr, uint32_t length) {
|
|
lock();
|
|
|
|
length = length & ~3;
|
|
|
|
int index = 0;
|
|
if (!findRange(addr, length, &index, true)) {
|
|
KernelWrite(addr, buffer, length);
|
|
}
|
|
else {
|
|
for (uint32_t i = 0; i < length; i += 4) {
|
|
uint32_t value = *(uint32_t *)((char *)buffer + i);
|
|
|
|
int index = 0;
|
|
BreakPoint *bp = findRange(addr + i, 4, &index, true);
|
|
if (!bp) {
|
|
KernelWriteU32(addr + i, value);
|
|
}
|
|
else {
|
|
while (bp) {
|
|
bp->instruction = value;
|
|
bp = findRange(addr + i, 4, &index, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unlock();
|
|
}
|
|
|
|
void BreakPointMgr::toggle(uint32_t addr) {
|
|
lock();
|
|
|
|
BreakPoint *bp = find(addr, false);
|
|
if (bp) {
|
|
disable(bp);
|
|
}
|
|
else {
|
|
BreakPoint *bp = breakpoints.alloc();
|
|
bp->isSpecial = false;
|
|
enable(bp, addr);
|
|
}
|
|
|
|
unlock();
|
|
}
|
|
|
|
uint32_t BreakPointMgr::getInstr(uint32_t addr) {
|
|
lock();
|
|
uint32_t instruction;
|
|
BreakPoint *bp = find(addr, true);
|
|
if (bp) {
|
|
instruction = bp->instruction;
|
|
}
|
|
else {
|
|
instruction = *(uint32_t *)addr;
|
|
}
|
|
unlock();
|
|
return instruction;
|
|
}
|
|
|
|
void BreakPointMgr::clearSpecial(OSThread *thread) {
|
|
lock();
|
|
for (int i = 0; i < special.size(); i++) {
|
|
SpecialBreakPoint *bp = special[i];
|
|
if (bp->thread == thread) {
|
|
disable(bp);
|
|
}
|
|
}
|
|
unlock();
|
|
}
|
|
|
|
void BreakPointMgr::predictStep(ExceptionState *state, bool stepOver) {
|
|
lock();
|
|
|
|
uint32_t address = state->context.srr0;
|
|
uint32_t instruction = getInstr(address);
|
|
|
|
uint32_t target1 = address + 4;
|
|
uint32_t target2 = 0;
|
|
|
|
uint8_t opcode = instruction >> 26;
|
|
if (opcode == 18) { //Branch
|
|
bool AA = instruction & 2;
|
|
bool LK = instruction & 1;
|
|
|
|
uint32_t LI = instruction & 0x3FFFFFC;
|
|
if (LI & 0x2000000) LI -= 0x4000000;
|
|
|
|
if (!LK || !stepOver) {
|
|
if (AA) target1 = LI;
|
|
else {
|
|
target1 = address + LI;
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (opcode == 16) { //Conditional branch
|
|
bool AA = instruction & 2;
|
|
bool LK = instruction & 1;
|
|
|
|
uint32_t BD = instruction & 0xFFFC;
|
|
if (BD & 0x8000) BD -= 0x10000;
|
|
|
|
if (!LK || !stepOver) {
|
|
if (AA) target2 = BD;
|
|
else {
|
|
target2 = address + BD;
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (opcode == 19) { //Conditional branch to lr/ctr
|
|
uint16_t XO = (instruction >> 1) & 0x3FF;
|
|
bool LK = instruction & 1;
|
|
|
|
if (!LK || !stepOver) {
|
|
if (XO == 16) target2 = state->context.lr;
|
|
else if (XO == 528) target2 = state->context.ctr;
|
|
}
|
|
}
|
|
|
|
SpecialBreakPoint *bp = special.alloc();
|
|
bp->isSpecial = true;
|
|
bp->thread = state->thread;
|
|
enable(bp, target1);
|
|
|
|
if (target2) {
|
|
SpecialBreakPoint *bp = special.alloc();
|
|
bp->isSpecial = true;
|
|
bp->thread = state->thread;
|
|
enable(bp, target2);
|
|
}
|
|
|
|
unlock();
|
|
}
|
|
|
|
|
|
bool ExceptionState::isBreakpoint() {
|
|
return type == PROGRAM && context.srr1 & 0x20000;
|
|
}
|
|
|
|
void ExceptionState::resume() {
|
|
OSLoadContext(&context);
|
|
}
|
|
|
|
|
|
void ExceptionMgr::init() {
|
|
OSInitMutex(&mutex);
|
|
}
|
|
|
|
void ExceptionMgr::lock() {
|
|
OSLockMutex(&mutex);
|
|
}
|
|
|
|
void ExceptionMgr::unlock() {
|
|
OSUnlockMutex(&mutex);
|
|
}
|
|
|
|
void ExceptionMgr::cleanup() {
|
|
OSMessage message;
|
|
message.message = Debugger::STEP_CONTINUE;
|
|
|
|
lock();
|
|
|
|
for (int i = 0; i < list.size(); i++) {
|
|
ExceptionState *state = list[i];
|
|
if (state->isPaused) {
|
|
OSSendMessage(&state->queue, &message, OS_MESSAGE_FLAGS_NONE);
|
|
}
|
|
}
|
|
|
|
unlock();
|
|
}
|
|
|
|
ExceptionState *ExceptionMgr::find(OSThread *thread) {
|
|
lock();
|
|
for (int i = 0; i < list.size(); i++) {
|
|
ExceptionState *state = list[i];
|
|
if (state->thread == thread) {
|
|
unlock();
|
|
return state;
|
|
}
|
|
}
|
|
unlock();
|
|
return nullptr;
|
|
}
|
|
|
|
ExceptionState *ExceptionMgr::findOrCreate(OSThread *thread) {
|
|
lock();
|
|
ExceptionState *state = find(thread);
|
|
if (!state) {
|
|
state = new ExceptionState();
|
|
state->thread = thread;
|
|
state->isPaused = false;
|
|
OSInitMessageQueue(&state->queue, &state->message, 1);
|
|
list.push_back(state);
|
|
}
|
|
unlock();
|
|
return state;
|
|
}
|
|
|
|
|
|
uint32_t StepMgr::buffer[96];
|
|
|
|
void StepMgr::init() {
|
|
OSInitMutex(&mutex);
|
|
usedMask = 0;
|
|
}
|
|
|
|
void StepMgr::lock() {
|
|
OSLockMutex(&mutex);
|
|
}
|
|
|
|
void StepMgr::unlock() {
|
|
OSUnlockMutex(&mutex);
|
|
}
|
|
|
|
uint32_t *StepMgr::alloc() {
|
|
lock();
|
|
int index = 0;
|
|
uint32_t mask = usedMask;
|
|
while (mask & 1) {
|
|
mask >>= 1;
|
|
index++;
|
|
}
|
|
usedMask |= 1 << index;
|
|
unlock();
|
|
|
|
if (index == 32) {
|
|
OSFatal("Displaced step allocation failed");
|
|
}
|
|
return &buffer[index * 3];
|
|
}
|
|
|
|
void StepMgr::free(int index) {
|
|
lock();
|
|
usedMask &= ~(1 << index);
|
|
unlock();
|
|
}
|
|
|
|
void StepMgr::branchConditional(ExceptionState *state, uint32_t instruction, uint32_t target, bool checkCtr) {
|
|
Bits<5> BO = (instruction >> 21) & 0x1F;
|
|
Bits<32> CR = state->context.cr;
|
|
|
|
uint32_t BI = (instruction >> 16) & 0x1F;
|
|
bool LK = instruction & 1;
|
|
|
|
if (LK) {
|
|
state->context.lr = state->context.srr0 + 4;
|
|
}
|
|
|
|
bool ctr_ok = true;
|
|
if (checkCtr) {
|
|
if (!BO[2]) {
|
|
state->context.ctr--;
|
|
}
|
|
ctr_ok = BO[2] || ((state->context.ctr != 0) ^ BO[3]);
|
|
}
|
|
|
|
bool cond_ok = BO[0] || (CR[BI] == BO[1]);
|
|
if (ctr_ok && cond_ok) {
|
|
state->context.srr0 = target;
|
|
}
|
|
}
|
|
|
|
void StepMgr::singleStep(ExceptionState *state, uint32_t instruction) {
|
|
uint8_t opcode = instruction >> 26;
|
|
|
|
if (opcode == 18) { //Branch
|
|
bool AA = instruction & 2;
|
|
bool LK = instruction & 1;
|
|
|
|
uint32_t LI = instruction & 0x3FFFFFC;
|
|
if (LI & 0x2000000) LI -= 0x4000000;
|
|
|
|
if (LK) {
|
|
state->context.lr = state->context.srr0 + 4;
|
|
}
|
|
|
|
if (AA) state->context.srr0 = LI;
|
|
else {
|
|
state->context.srr0 += LI;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (opcode == 16) { //Conditional branch
|
|
bool AA = instruction & 2;
|
|
|
|
uint32_t BD = instruction & 0xFFFC;
|
|
if (BD & 0x8000) BD -= 0x10000;
|
|
|
|
uint32_t target;
|
|
if (AA) target = BD;
|
|
else {
|
|
target = state->context.srr0 + BD;
|
|
}
|
|
|
|
branchConditional(state, instruction, target, true);
|
|
return;
|
|
}
|
|
|
|
if (opcode == 19) { //Conditional branch to lr/ctr
|
|
uint16_t XO = (instruction >> 1) & 0x3FF;
|
|
|
|
uint32_t target;
|
|
if (XO == 16) {
|
|
branchConditional(state, instruction, state->context.lr, true);
|
|
return;
|
|
}
|
|
else if (XO == 528) {
|
|
branchConditional(state, instruction, state->context.ctr, false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
uint32_t *ptr = alloc();
|
|
ptr[0] = instruction;
|
|
ptr[1] = TRAP;
|
|
ptr[2] = state->context.srr0;
|
|
DCFlushRange(ptr, 12);
|
|
ICInvalidateRange(ptr, 12);
|
|
state->context.srr0 = (uint32_t)ptr;
|
|
}
|
|
|
|
void StepMgr::handleBreakPoint(ExceptionState *state) {
|
|
uint32_t start = (uint32_t)buffer;
|
|
uint32_t end = (uint32_t)buffer + sizeof(buffer);
|
|
|
|
uint32_t addr = state->context.srr0;
|
|
if (addr >= start && addr < end) {
|
|
int offset = addr - start;
|
|
if (offset % 12 != 4) {
|
|
char buffer[100];
|
|
snprintf(buffer, 100, "Unexpected srr0 after displaced step: %08X", addr);
|
|
OSFatal(buffer);
|
|
}
|
|
|
|
int index = offset / 12;
|
|
state->context.srr0 = buffer[index * 3 + 2] + 4;
|
|
free(index);
|
|
|
|
state->resume();
|
|
}
|
|
}
|
|
|
|
void StepMgr::adjustAddress(ExceptionState *state) {
|
|
uint32_t start = (uint32_t)buffer;
|
|
uint32_t end = (uint32_t)buffer + sizeof(buffer);
|
|
|
|
uint32_t addr = state->context.srr0;
|
|
if (addr >= start && addr < end) {
|
|
int index = (addr - start) / 12;
|
|
state->context.srr0 = buffer[index * 3 + 2];
|
|
}
|
|
}
|
|
|
|
|
|
bool Debugger::checkDataRead(uint32_t addr, uint32_t length) {
|
|
uint32_t memStart, memSize;
|
|
OSGetMemBound(MEM2, &memStart, &memSize);
|
|
|
|
uint32_t memEnd = memStart + memSize;
|
|
|
|
return addr >= 0x10000000 && addr + length <= memEnd;
|
|
}
|
|
|
|
Debugger::StepCommand Debugger::notifyBreak(ExceptionState *state) {
|
|
OSMessage message;
|
|
message.message = ExceptionState::PROGRAM;
|
|
message.args[0] = (uint32_t)&state->context;
|
|
message.args[1] = sizeof(OSContext);
|
|
message.args[2] = (uint32_t)state->thread;
|
|
OSSendMessage(&eventQueue, &message, OS_MESSAGE_FLAGS_BLOCKING);
|
|
|
|
state->isPaused = true;
|
|
OSReceiveMessage(&state->queue, &message, OS_MESSAGE_FLAGS_BLOCKING);
|
|
state->isPaused = false;
|
|
return (StepCommand)message.message;
|
|
}
|
|
|
|
void Debugger::resumeBreakPoint(ExceptionState *state) {
|
|
uint32_t address = state->context.srr0;
|
|
if (breakpoints.isSoftware(address)) {
|
|
state->context.srr0 += 4;
|
|
state->resume();
|
|
}
|
|
|
|
uint32_t instruction = breakpoints.getInstr(state->context.srr0);
|
|
stepper.singleStep(state, instruction);
|
|
state->resume();
|
|
}
|
|
|
|
void Debugger::processBreakPoint(ExceptionState *state) {
|
|
StepCommand command = notifyBreak(state);
|
|
if (command == STEP_INTO || command == STEP_OVER) {
|
|
breakpoints.predictStep(state, command == STEP_OVER);
|
|
}
|
|
}
|
|
|
|
void Debugger::handleBreakPoint(ExceptionState *state) {
|
|
if (firstTrap) {
|
|
firstTrap = false;
|
|
|
|
Screen screen;
|
|
screen.init();
|
|
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();
|
|
|
|
while (!connected) {
|
|
uint32_t buttons = GetInput(VPAD_BUTTON_HOME);
|
|
if (buttons) {
|
|
state->context.srr0 += 4;
|
|
state->resume();
|
|
}
|
|
}
|
|
}
|
|
|
|
stepper.handleBreakPoint(state);
|
|
|
|
if (!connected) {
|
|
handleFatalCrash(&state->context, state->type);
|
|
}
|
|
|
|
uint32_t addr = state->context.srr0;
|
|
bool trap;
|
|
|
|
breakpoints.lock();
|
|
trap = breakpoints.isCustom(addr) || breakpoints.isSoftware(addr) ||
|
|
breakpoints.findSpecial(addr, state->thread);
|
|
breakpoints.unlock();
|
|
|
|
breakpoints.clearSpecial(state->thread);
|
|
if (trap) {
|
|
processBreakPoint(state);
|
|
}
|
|
resumeBreakPoint(state);
|
|
}
|
|
|
|
void Debugger::handleCrash(ExceptionState *state) {
|
|
stepper.adjustAddress(state);
|
|
if (connected) {
|
|
OSMessage message;
|
|
message.message = state->type;
|
|
message.args[0] = (uint32_t)&state->context;
|
|
message.args[1] = sizeof(OSContext);
|
|
message.args[2] = (uint32_t)state->thread;
|
|
OSSendMessage(&eventQueue, &message, OS_MESSAGE_FLAGS_BLOCKING);
|
|
|
|
while (true) {
|
|
OSReceiveMessage(&state->queue, &message, OS_MESSAGE_FLAGS_BLOCKING);
|
|
}
|
|
}
|
|
else {
|
|
handleFatalCrash(&state->context, state->type);
|
|
}
|
|
}
|
|
|
|
void Debugger::handleFatalCrash(OSContext *context, ExceptionState::Type type) {
|
|
const char *name;
|
|
if (type == ExceptionState::DSI) name = "A DSI";
|
|
else if (type == ExceptionState::ISI) name = "An ISI";
|
|
else {
|
|
name = "A program";
|
|
}
|
|
DumpContext(context, name);
|
|
}
|
|
|
|
void Debugger::handleException(OSContext *context, ExceptionState::Type type) {
|
|
OSThread *thread = OSGetCurrentThread();
|
|
|
|
if (thread == serverThread) {
|
|
handleFatalCrash(context, type);
|
|
}
|
|
|
|
ExceptionState *state = exceptions.findOrCreate(thread);
|
|
memcpy(&state->context, context, sizeof(OSContext));
|
|
state->type = type;
|
|
|
|
delete context;
|
|
|
|
if (state->isBreakpoint()) {
|
|
handleBreakPoint(state);
|
|
}
|
|
else {
|
|
handleCrash(state);
|
|
}
|
|
}
|
|
|
|
void Debugger::exceptionHandler(OSContext *context, ExceptionState::Type type) {
|
|
debugger->handleException(context, type);
|
|
}
|
|
|
|
bool Debugger::dsiHandler(OSContext *context) {
|
|
OSContext *info = new OSContext();
|
|
memcpy(info, context, sizeof(OSContext));
|
|
context->srr0 = (uint32_t)exceptionHandler;
|
|
context->gpr[3] = (uint32_t)info;
|
|
context->gpr[4] = ExceptionState::DSI;
|
|
return true;
|
|
}
|
|
|
|
bool Debugger::isiHandler(OSContext *context) {
|
|
OSContext *info = new OSContext();
|
|
memcpy(info, context, sizeof(OSContext));
|
|
context->srr0 = (uint32_t)exceptionHandler;
|
|
context->gpr[3] = (uint32_t)info;
|
|
context->gpr[4] = ExceptionState::ISI;
|
|
return true;
|
|
}
|
|
|
|
bool Debugger::programHandler(OSContext *context) {
|
|
OSContext *info = new OSContext();
|
|
memcpy(info, context, sizeof(OSContext));
|
|
context->srr0 = (uint32_t)exceptionHandler;
|
|
context->gpr[3] = (uint32_t)info;
|
|
context->gpr[4] = ExceptionState::PROGRAM;
|
|
return true;
|
|
}
|
|
|
|
void Debugger::cleanup() {
|
|
breakpoints.cleanup();
|
|
exceptions.cleanup();
|
|
|
|
OSMessage message;
|
|
while (OSReceiveMessage(&eventQueue, &message, OS_MESSAGE_FLAGS_NONE));
|
|
}
|
|
|
|
void Debugger::mainLoop(Client *client) {
|
|
while (true) {
|
|
uint8_t cmd;
|
|
if (!client->recvall(&cmd, 1)) return;
|
|
|
|
if (cmd == COMMAND_CLOSE) return;
|
|
else if (cmd == COMMAND_READ) {
|
|
uint32_t addr, length;
|
|
if (!client->recvall(&addr, 4)) return;
|
|
if (!client->recvall(&length, 4)) return;
|
|
|
|
char *buffer = new char[length];
|
|
breakpoints.read(buffer, addr, length);
|
|
if (!client->sendall(buffer, length)) {
|
|
delete buffer;
|
|
return;
|
|
}
|
|
delete buffer;
|
|
}
|
|
else if (cmd == COMMAND_WRITE) {
|
|
uint32_t addr, length;
|
|
if (!client->recvall(&addr, 4)) return;
|
|
if (!client->recvall(&length, 4)) return;
|
|
if (!client->recvall((void *)addr, length)) return;
|
|
}
|
|
else if (cmd == COMMAND_WRITE_CODE) {
|
|
uint32_t addr, length;
|
|
if (!client->recvall(&addr, 4)) return;
|
|
if (!client->recvall(&length, 4)) return;
|
|
|
|
char *buffer = new char[length];
|
|
if (!client->recvall(buffer, length)) {
|
|
delete buffer;
|
|
return;
|
|
}
|
|
breakpoints.write(buffer, addr, length);
|
|
delete buffer;
|
|
}
|
|
else if (cmd == COMMAND_GET_MODULE_NAME) {
|
|
char name[0x40];
|
|
int length = 0x40;
|
|
OSDynLoad_GetModuleName(-1, name, &length);
|
|
|
|
length = strlen(name);
|
|
if (!client->sendall(&length, 4)) return;
|
|
if (!client->sendall(name, length)) return;
|
|
}
|
|
else if (cmd == COMMAND_GET_MODULE_LIST) {
|
|
OSLockMutex(OSDynLoad_gLoaderLock);
|
|
|
|
char buffer[0x1000]; //This should be enough
|
|
uint32_t offset = 0;
|
|
OSDynLoad_RPLInfo *current = FirstRPL;
|
|
while (current) {
|
|
OSDynLoad_NotifyData *info = current->notifyData;
|
|
|
|
uint32_t namelen = strlen(current->name);
|
|
if (offset + 0x18 + namelen > 0x1000) {
|
|
break;
|
|
}
|
|
|
|
uint32_t *infobuf = (uint32_t *)(buffer + offset);
|
|
infobuf[0] = info->textAddr;
|
|
infobuf[1] = info->textSize;
|
|
infobuf[2] = info->dataAddr;
|
|
infobuf[3] = info->dataSize;
|
|
infobuf[4] = (uint32_t)current->entryPoint;
|
|
infobuf[5] = namelen;
|
|
memcpy(&infobuf[6], current->name, namelen);
|
|
offset += 0x18 + namelen;
|
|
|
|
current = current->next;
|
|
}
|
|
|
|
OSUnlockMutex(OSDynLoad_gLoaderLock);
|
|
|
|
if (!client->sendall(&offset, 4)) return;
|
|
if (!client->sendall(buffer, offset)) return;
|
|
}
|
|
else if (cmd == COMMAND_GET_THREAD_LIST) {
|
|
int state = OSDisableInterrupts();
|
|
__OSLockScheduler(this);
|
|
|
|
char buffer[0x1000]; //This should be enough
|
|
uint32_t offset = 0;
|
|
OSThread *current = ThreadList;
|
|
while (current) {
|
|
const char *name = OSGetThreadName(current);
|
|
|
|
uint32_t namelen = 0;
|
|
if (name) {
|
|
namelen = strlen(name);
|
|
}
|
|
|
|
if (offset + 0x1C + namelen > 0x1000) {
|
|
break;
|
|
}
|
|
|
|
int priority = current->basePriority;
|
|
if (current->type == 1) {
|
|
priority -= 0x20;
|
|
}
|
|
else if (current->type == 2) {
|
|
priority -= 0x40;
|
|
}
|
|
|
|
uint32_t *infobuf = (uint32_t *)(buffer + offset);
|
|
infobuf[0] = (uint32_t)current;
|
|
infobuf[1] = current->attr & 7;
|
|
infobuf[2] = priority;
|
|
infobuf[3] = (uint32_t)current->stackBase;
|
|
infobuf[4] = (uint32_t)current->stackEnd;
|
|
infobuf[5] = (uint32_t)current->entryPoint;
|
|
infobuf[6] = namelen;
|
|
memcpy(&infobuf[7], name, namelen);
|
|
offset += 0x1C + namelen;
|
|
|
|
current = current->activeLink.next;
|
|
}
|
|
|
|
__OSUnlockScheduler(this);
|
|
OSRestoreInterrupts(state);
|
|
|
|
if (!client->sendall(&offset, 4)) return;
|
|
if (!client->sendall(buffer, offset)) return;
|
|
}
|
|
else if (cmd == COMMAND_GET_STACK_TRACE) {
|
|
OSThread *thread;
|
|
if (!client->recvall(&thread, 4)) return;
|
|
|
|
ExceptionState *state = exceptions.find(thread);
|
|
if (state) {
|
|
uint32_t sp = state->context.gpr[1];
|
|
uint32_t trace[100];
|
|
int index = 0;
|
|
while (checkDataRead(sp, 4) && index < 100) {
|
|
sp = *(uint32_t *)sp;
|
|
if (!checkDataRead(sp, 4)) break;
|
|
|
|
trace[index] = *(uint32_t *)(sp + 4);
|
|
index++;
|
|
}
|
|
|
|
if (!client->sendall(&index, 4)) return;
|
|
if (!client->sendall(trace, index * 4)) return;
|
|
}
|
|
else {
|
|
int index = 0;
|
|
if (!client->sendall(&index, 4)) return;
|
|
}
|
|
}
|
|
else if (cmd == COMMAND_TOGGLE_BREAKPOINT) {
|
|
uint32_t address;
|
|
if (!client->recvall(&address, 4)) return;
|
|
|
|
breakpoints.toggle(address);
|
|
}
|
|
else if (cmd == COMMAND_POKE_REGISTERS) {
|
|
OSThread *thread;
|
|
if (!client->recvall(&thread, 4)) return;
|
|
|
|
uint32_t gpr[32];
|
|
double fpr[32];
|
|
if (!client->recvall(gpr, 4 * 32)) return;
|
|
if (!client->recvall(fpr, 8 * 32)) return;
|
|
|
|
exceptions.lock();
|
|
ExceptionState *state = exceptions.find(thread);
|
|
if (state) {
|
|
memcpy(state->context.gpr, gpr, 4 * 32);
|
|
memcpy(state->context.fpr, fpr, 8 * 32);
|
|
}
|
|
exceptions.unlock();
|
|
}
|
|
else if (cmd == COMMAND_RECEIVE_MESSAGES) {
|
|
OSMessage messages[10];
|
|
|
|
int count = 0;
|
|
while (count < 10) {
|
|
if (!OSReceiveMessage(&eventQueue, &messages[count], OS_MESSAGE_FLAGS_NONE)) {
|
|
break;
|
|
}
|
|
count++;
|
|
}
|
|
|
|
if (!client->sendall(&count, 4)) return;
|
|
for (int i = 0; i < count; i++) {
|
|
if (!client->sendall(&messages[i], sizeof(OSMessage))) return;
|
|
if (messages[i].args[0]) {
|
|
void *data = (void *)messages[i].args[0];
|
|
size_t length = messages[i].args[1];
|
|
if (!client->sendall(data, length)) return;
|
|
}
|
|
}
|
|
}
|
|
else if (cmd == COMMAND_SEND_MESSAGE) {
|
|
OSMessage message;
|
|
if (!client->recvall(&message, sizeof(OSMessage))) return;
|
|
|
|
OSThread *thread = (OSThread *)message.args[0];
|
|
|
|
exceptions.lock();
|
|
ExceptionState *state = exceptions.find(thread);
|
|
if (state) {
|
|
OSSendMessage(&state->queue, &message, OS_MESSAGE_FLAGS_NONE);
|
|
}
|
|
exceptions.unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Debugger::threadFunc() {
|
|
int result = socket_lib_init();
|
|
if (result < 0) {
|
|
OSFatal("Failed to initialize socket library");
|
|
}
|
|
|
|
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);
|
|
|
|
Server server;
|
|
Client client;
|
|
|
|
initialized = true;
|
|
while (true) {
|
|
if (!server.init(Socket::TCP)) continue;
|
|
if (!server.bind(1560)) continue;
|
|
if (!server.accept(&client)) continue;
|
|
connected = true;
|
|
mainLoop(&client);
|
|
cleanup();
|
|
connected = false;
|
|
client.close();
|
|
server.close();
|
|
}
|
|
}
|
|
|
|
int Debugger::threadEntry(int argc, void *argv) {
|
|
debugger->threadFunc();
|
|
return 0;
|
|
}
|
|
|
|
void Debugger::start() {
|
|
initialized = false;
|
|
connected = false;
|
|
firstTrap = true;
|
|
|
|
OSInitMessageQueue(&eventQueue, eventMessages, MESSAGE_COUNT);
|
|
|
|
breakpoints.init();
|
|
exceptions.init();
|
|
stepper.init();
|
|
|
|
serverThread = new OSThread();
|
|
char *stack = new char[0x8000];
|
|
|
|
OSCreateThread(
|
|
serverThread, threadEntry, 0, 0,
|
|
stack + STACK_SIZE, STACK_SIZE,
|
|
0, 12
|
|
);
|
|
OSSetThreadName(serverThread, "Debug Server");
|
|
OSResumeThread(serverThread);
|
|
|
|
while (!initialized) {
|
|
OSSleepTicks(OSMillisecondsToTicks(20));
|
|
}
|
|
}
|
|
|
|
Debugger *debugger;
|