diff options
author | PoroCYon <3253268+PoroCYon@users.noreply.github.com> | 2023-10-22 15:35:31 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-22 15:35:31 +0200 |
commit | 3ab752b8ca7878246c3d7f8a338a8bc3b0de26dd (patch) | |
tree | b4a8e319bbe4897af2669c01352f7f3787ade537 /src/debug/GdbCmds.cpp | |
parent | 3d58a338a16bc41b9106857645fabc0221de711d (diff) |
GDB stub (#1583)
* gdbstub beginnings
* gdbstub: finish gdb impl things, next up is integration with melonDS
* holy fuck the gdbstub works
* gdb breakpoints work, but there's a mysterious crash on continue
* fix memory corruption that sometimes happened, and make resetting the console thru gdb work
* remove some gdb debug printing
* fix things in gdbstub
* separate option for enabling gdbstub
* add mode-dependent CPU registers
* C++ize the GDBstub code
* add gdbstub config in emu settings dialog
* make sure gdb is disabled when jit is enabled
* Remove unnecessary compiler flags, mark ARMJIT assembly code as no-execute-stack
This hardens the binary a little bit against common exploitation methods
* add option to wait for debugger attach on startup
* only insert GNU stack notes on linux
* disable gdbstub enable checkbox when jit is enabled
* fix non-linux incompatibilities
* enable gdbstub by default
* fix issues with gdbstub settings disable stuff
* format stuff
* update gdb test code
* Fix segfault when calling StubCallbacks->GetCPU()
C++ overrides are hard. Please I'm just a lowly C programmer.
* fix packet size not being sent correctly
Thanks to @GlowingUmbreon on Github for troubleshooting this
* fix select(2) calls (i should read docs more properly)
* fix GDB command sequencing/parsing issue (hopefully)
* [GDB] implement no-ack mode
* fix sending ack on handshake
* get lldb to work
Diffstat (limited to 'src/debug/GdbCmds.cpp')
-rw-r--r-- | src/debug/GdbCmds.cpp | 924 |
1 files changed, 924 insertions, 0 deletions
diff --git a/src/debug/GdbCmds.cpp b/src/debug/GdbCmds.cpp new file mode 100644 index 0000000..057502f --- /dev/null +++ b/src/debug/GdbCmds.cpp @@ -0,0 +1,924 @@ + +#include <stdio.h> +#include <assert.h> + +#include "../CRC32.h" +#include "../Platform.h" +#include "hexutil.h" + +#include "GdbProto.h" + +using Platform::Log; +using Platform::LogLevel; + +namespace Gdb +{ + +enum class GdbSignal : int +{ + INT = 2, + TRAP = 5, + EMT = 7, // "emulation trap" + SEGV = 11, + ILL = 4 +}; + +// 12: llvm::MachO::CPU_TYPE_ARM +// 5: llvm::MachO::CPU_SUBTYPE_ARM_V4T +// 7: llvm::MachO::CPU_SUBTYPE_ARM_V5TEJ +const char* TARGET_INFO_ARM7 = "cputype:12;cpusubtype:5;triple:arm-none-eabi;ostype:none;vendor:none;endian:little;ptrsize:4;"; +const char* TARGET_INFO_ARM9 = "cputype:12;cpusubtype:7;triple:arm-none-eabi;ostype:none;vendor:none;endian:little;ptrsize:4;"; + + +#define TARGET_XML__CORE_REGS \ + "<reg name=\"r0\" bitsize=\"32\" type=\"uint32\"/>" \ + "<reg name=\"r1\" bitsize=\"32\" type=\"uint32\"/>" \ + "<reg name=\"r2\" bitsize=\"32\" type=\"uint32\"/>" \ + "<reg name=\"r3\" bitsize=\"32\" type=\"uint32\"/>" \ + "<reg name=\"r4\" bitsize=\"32\" type=\"uint32\"/>" \ + "<reg name=\"r5\" bitsize=\"32\" type=\"uint32\"/>" \ + "<reg name=\"r6\" bitsize=\"32\" type=\"uint32\"/>" \ + "<reg name=\"r7\" bitsize=\"32\" type=\"uint32\"/>" \ + "<reg name=\"r8\" bitsize=\"32\" type=\"uint32\"/>" \ + "<reg name=\"r9\" bitsize=\"32\" type=\"uint32\"/>" \ + "<reg name=\"r10\" bitsize=\"32\" type=\"uint32\"/>" \ + "<reg name=\"r11\" bitsize=\"32\" type=\"uint32\"/>" \ + "<reg name=\"r12\" bitsize=\"32\" type=\"uint32\"/>" \ + "<reg name=\"sp\" bitsize=\"32\" type=\"data_ptr\"/>" \ + "<reg name=\"lr\" bitsize=\"32\" type=\"code_ptr\"/>" \ + "<reg name=\"pc\" bitsize=\"32\" type=\"code_ptr\"/>" \ + /* 16 regs */ \ + +#define TARGET_XML__MODE_REGS \ + "<reg name=\"cpsr\" bitsize=\"32\" regnum=\"25\"/>" \ + "<reg name=\"sp_usr\" bitsize=\"32\" regnum=\"26\" type=\"data_ptr\"/>" \ + "<reg name=\"lr_usr\" bitsize=\"32\" regnum=\"27\" type=\"code_ptr\"/>" \ + "<reg name=\"r8_fiq\" bitsize=\"32\" type=\"uint32\" regnum=\"28\"/>" \ + "<reg name=\"r9_fiq\" bitsize=\"32\" type=\"uint32\" regnum=\"29\"/>" \ + "<reg name=\"r10_fiq\" bitsize=\"32\" type=\"uint32\" regnum=\"30\"/>" \ + "<reg name=\"r11_fiq\" bitsize=\"32\" type=\"uint32\" regnum=\"31\"/>" \ + "<reg name=\"r12_fiq\" bitsize=\"32\" type=\"uint32\" regnum=\"32\"/>" \ + "<reg name=\"sp_fiq\" bitsize=\"32\" regnum=\"33\" type=\"data_ptr\"/>" \ + "<reg name=\"lr_fiq\" bitsize=\"32\" regnum=\"34\" type=\"code_ptr\"/>" \ + "<reg name=\"sp_irq\" bitsize=\"32\" regnum=\"35\" type=\"data_ptr\"/>" \ + "<reg name=\"lr_irq\" bitsize=\"32\" regnum=\"36\" type=\"code_ptr\"/>" \ + "<reg name=\"sp_svc\" bitsize=\"32\" regnum=\"37\" type=\"data_ptr\"/>" \ + "<reg name=\"lr_svc\" bitsize=\"32\" regnum=\"38\" type=\"code_ptr\"/>" \ + "<reg name=\"sp_abt\" bitsize=\"32\" regnum=\"39\" type=\"data_ptr\"/>" \ + "<reg name=\"lr_abt\" bitsize=\"32\" regnum=\"40\" type=\"code_ptr\"/>" \ + "<reg name=\"sp_und\" bitsize=\"32\" regnum=\"41\" type=\"data_ptr\"/>" \ + "<reg name=\"lr_und\" bitsize=\"32\" regnum=\"42\" type=\"code_ptr\"/>" \ + "<reg name=\"spsr_fiq\" bitsize=\"32\" regnum=\"43\"/>" \ + "<reg name=\"spsr_irq\" bitsize=\"32\" regnum=\"44\"/>" \ + "<reg name=\"spsr_svc\" bitsize=\"32\" regnum=\"45\"/>" \ + "<reg name=\"spsr_abt\" bitsize=\"32\" regnum=\"46\"/>" \ + "<reg name=\"spsr_und\" bitsize=\"32\" regnum=\"47\"/>" \ + /* 23 regs */ \ + + +const char* TARGET_XML_ARM7 = + "<target version=\"1.0\">" + "<architecture>armv4t</architecture>" + "<osabi>none</osabi>" + "<feature name=\"org.gnu.gdb.arm.core\">" + TARGET_XML__CORE_REGS + TARGET_XML__MODE_REGS + // 39 regs total + "</feature>" + "</target>"; + + +const char* TARGET_XML_ARM9 = + "<target version=\"1.0\">" + "<architecture>armv5te</architecture>" + "<osabi>none</osabi>" + "<feature name=\"org.gnu.gdb.arm.core\">" + TARGET_XML__CORE_REGS + TARGET_XML__MODE_REGS + // 39 regs total + "</feature>" + "</target>"; + // TODO: CP15? + + +static int DoQResponse(GdbStub* stub, const u8* query, const char* data, const size_t len) +{ + size_t qaddr, qlen; + + Log(LogLevel::Debug, "[GDB qresp] query='%s'\n", query); + if (sscanf((const char*)query, "%zx,%zx", &qaddr, &qlen) != 2) + { + return stub->RespStr("E01"); + } + else if (qaddr > len) + { + return stub->RespStr("E01"); + } + else if (qaddr == len) + { + return stub->RespStr("l"); + } + + size_t bleft = len - qaddr; + size_t outlen = qlen; + if (outlen > bleft) outlen = bleft; + Log(LogLevel::Debug, "[GDB qresp] qaddr=%zu qlen=%zu left=%zu outlen=%zu\n", + qaddr, qlen, bleft, outlen); + + return stub->RespC("m", 1, (const u8*)&data[qaddr], outlen); +} + +__attribute__((__aligned__(4))) +static u8 tempdatabuf[1024]; + +ExecResult GdbStub::Handle_g(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u8* regstrbuf = tempdatabuf; + + for (size_t i = 0; i < GDB_ARCH_N_REG; ++i) + { + u32 v = stub->Cb->ReadReg(static_cast<Register>(i)); + hexfmt32(®strbuf[i*4*2], v); + } + + stub->Resp(regstrbuf, GDB_ARCH_N_REG*4*2); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_G(GdbStub* stub, const u8* cmd, ssize_t len) +{ + if (len != GDB_ARCH_N_REG*4*2) + { + Log(LogLevel::Error, "[GDB] REG WRITE ERR: BAD LEN: %zd != %d!\n", len, GDB_ARCH_N_REG*4*2); + stub->RespStr("E01"); + return ExecResult::Ok; + } + + for (int i = 0; i < GDB_ARCH_N_REG; ++i) + { + u32 v = unhex32(&cmd[i*4*2]); + stub->Cb->WriteReg(static_cast<Register>(i), v); + } + + stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_m(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u32 addr = 0, llen = 0, end; + + if (sscanf((const char*)cmd, "%08X,%08X", &addr, &llen) != 2) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + else if (llen > (GDBPROTO_BUFFER_CAPACITY/2)) + { + stub->RespStr("E02"); + return ExecResult::Ok; + } + end = addr + llen; + + u8* datastr = tempdatabuf; + u8* dataptr = datastr; + + // pre-align: byte + if ((addr & 1)) + { + if ((end-addr) >= 1) + { + u32 v = stub->Cb->ReadMem(addr, 8); + hexfmt8(dataptr, v&0xff); + ++addr; + dataptr += 2; + } + else goto end; + } + + // pre-align: short + if ((addr & 2)) + { + if ((end-addr) >= 2) + { + u32 v = stub->Cb->ReadMem(addr, 16); + hexfmt16(dataptr, v&0xffff); + addr += 2; + dataptr += 4; + } + else if ((end-addr) == 1) + { // last byte + u32 v = stub->Cb->ReadMem(addr, 8); + hexfmt8(dataptr, v&0xff); + ++addr; + dataptr += 2; + } + else goto end; + } + + // main loop: 4-byte chunks + while (addr < end) + { + if (end - addr < 4) break; // post-align stuff + + u32 v = stub->Cb->ReadMem(addr, 32); + hexfmt32(dataptr, v); + addr += 4; + dataptr += 8; + } + + // post-align: short + if ((end-addr) & 2) + { + u32 v = stub->Cb->ReadMem(addr, 16); + hexfmt16(dataptr, v&0xffff); + addr += 2; + dataptr += 4; + } + + // post-align: byte + if ((end-addr) == 1) + { + u32 v = stub->Cb->ReadMem(addr, 8); + hexfmt8(dataptr, v&0xff); + ++addr; + dataptr += 2; + } + +end: + assert(addr == end); + + stub->Resp(datastr, llen*2); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_M(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u32 addr, llen, end; + int inoff; + + if (sscanf((const char*)cmd, "%08X,%08X:%n", &addr, &llen, &inoff) != 2) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + else if (llen > (GDBPROTO_BUFFER_CAPACITY/2)) + { + stub->RespStr("E02"); + return ExecResult::Ok; + } + end = addr + llen; + + const u8* dataptr = cmd + inoff; + + // pre-align: byte + if ((addr & 1)) + { + if ((end-addr) >= 1) + { + u8 v = unhex8(dataptr); + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 2; + } + else goto end; + } + + // pre-align: short + if ((addr & 2)) + { + if ((end-addr) >= 2) + { + u16 v = unhex16(dataptr); + stub->Cb->WriteMem(addr, 16, v); + addr += 2; + dataptr += 4; + } + else if ((end-addr) == 1) + { // last byte + u8 v = unhex8(dataptr); + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 2; + } + else goto end; + } + + // main loop: 4-byte chunks + while (addr < end) + { + if (end - addr < 4) break; // post-align stuff + + u32 v = unhex32(dataptr); + stub->Cb->WriteMem(addr, 32, v); + addr += 4; + dataptr += 8; + } + + // post-align: short + if ((end-addr) & 2) + { + u16 v = unhex16(dataptr); + stub->Cb->WriteMem(addr, 16, v); + addr += 2; + dataptr += 4; + } + + // post-align: byte + if ((end-addr) == 1) + { + u8 v = unhex8(dataptr); + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 2; + } + +end: + assert(addr == end); + + stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_X(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u32 addr, llen, end; + int inoff; + + if (sscanf((const char*)cmd, "%08X,%08X:%n", &addr, &llen, &inoff) != 2) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + else if (llen > (GDBPROTO_BUFFER_CAPACITY/2)) + { + stub->RespStr("E02"); + return ExecResult::Ok; + } + end = addr + llen; + + const u8* dataptr = cmd + inoff; + + // pre-align: byte + if ((addr & 1)) + { + if ((end-addr) >= 1) + { + u8 v = *dataptr; + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 1; + } + else goto end; + } + + // pre-align: short + if ((addr & 2)) + { + if ((end-addr) >= 2) + { + u16 v = dataptr[0] | ((u16)dataptr[1] << 8); + stub->Cb->WriteMem(addr, 16, v); + addr += 2; + dataptr += 2; + } + else if ((end-addr) == 1) + { // last byte + u8 v = *dataptr; + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 1; + } + else goto end; + } + + // main loop: 4-byte chunks + while (addr < end) + { + if (end - addr < 4) break; // post-align stuff + + u32 v = dataptr[0] | ((u32)dataptr[1] << 8) + | ((u32)dataptr[2] << 16) | ((u32)dataptr[3] << 24); + stub->Cb->WriteMem(addr, 32, v); + addr += 4; + dataptr += 4; + } + + // post-align: short + if ((end-addr) & 2) + { + u16 v = dataptr[0] | ((u16)dataptr[1] << 8); + stub->Cb->WriteMem(addr, 16, v); + addr += 2; + dataptr += 2; + } + + // post-align: byte + if ((end-addr) == 1) + { + u8 v = unhex8(dataptr); + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 1; + } + +end: + assert(addr == end); + + stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_c(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u32 addr = ~(u32)0; + + if (len > 0) + { + if (len <= 8) + { + if (sscanf((const char*)cmd, "%08X", &addr) != 1) + { + stub->RespStr("E01"); + } // else: ok + } + else + { + stub->RespStr("E01"); + } + } // else: continue at current + + if (~addr) + { + stub->Cb->WriteReg(Register::pc, addr); + } + + return ExecResult::Continue; +} + +ExecResult GdbStub::Handle_s(GdbStub* stub, const u8* cmd, ssize_t len) { + u32 addr = ~(u32)0; + + if (len > 0) + { + if (len <= 8) + { + if (sscanf((const char*)cmd, "%08X", &addr) != 1) { + stub->RespStr("E01"); + return ExecResult::Ok; + } // else: ok + } + else + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + } // else: continue at current + + if (~addr != 0) + { + stub->Cb->WriteReg(Register::pc, addr); + } + + return ExecResult::Step; +} + +ExecResult GdbStub::Handle_p(GdbStub* stub, const u8* cmd, ssize_t len) +{ + int reg; + if (sscanf((const char*)cmd, "%x", ®) != 1 || reg < 0 || reg >= GDB_ARCH_N_REG) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + u32 v = stub->Cb->ReadReg(static_cast<Register>(reg)); + hexfmt32(tempdatabuf, v); + stub->Resp(tempdatabuf, 4*2); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_P(GdbStub* stub, const u8* cmd, ssize_t len) +{ + int reg, dataoff; + + if (sscanf((const char*)cmd, "%x=%n", ®, &dataoff) != 1 || reg < 0 + || reg >= GDB_ARCH_N_REG || dataoff + 4*2 > len) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + u32 v = unhex32(&cmd[dataoff]); + stub->Cb->WriteReg(static_cast<Register>(reg), v); + + stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_H(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u8 operation = cmd[0]; + u32 thread_id; + sscanf((const char*)&cmd[1], "%u", &thread_id); + + (void)operation; + if (thread_id <= 1) + { + stub->RespStr("OK"); + } + else + { + stub->RespStr("E01"); + } + + return ExecResult::Ok; +} + + +ExecResult GdbStub::Handle_Question(GdbStub* stub, const u8* cmd, ssize_t len) +{ + // "request reason for target halt" (which must also halt) + + TgtStatus st = stub->Stat; + u32 arg = ~(u32)0; + int typ = 0; + + switch (st) + { + case TgtStatus::None: // no target! + stub->RespStr("W00"); + break; + + case TgtStatus::Running: // will break very soon due to retval + case TgtStatus::BreakReq: + stub->RespFmt("S%02X", GdbSignal::INT); + break; + + case TgtStatus::SingleStep: + stub->RespFmt("S%02X", GdbSignal::TRAP); + break; + + case TgtStatus::Bkpt: + arg = stub->CurBkpt; + typ = 1; + goto bkpt_rest; + case TgtStatus::Watchpt: + arg = stub->CurWatchpt; + typ = 2; + bkpt_rest: + if (!~arg) + { + stub->RespFmt("S%02X", GdbSignal::TRAP); + } + else + { + switch (typ) + { + case 1: + stub->RespFmt("S%02X", GdbSignal::TRAP); + //stub->RespFmt("T%02Xhwbreak:"/*"%08X"*/";", GdbSignal::TRAP/*, arg*/); + break; + case 2: + stub->RespFmt("S%02X", GdbSignal::TRAP); + //stub->RespFmt("T%02Xwatch:"/*"%08X"*/";", GdbSignal::TRAP/*, arg*/); + break; + default: + stub->RespFmt("S%02X", GdbSignal::TRAP); + break; + } + } + break; + case TgtStatus::BkptInsn: + stub->RespFmt("T%02Xswbreak:%08X;", GdbSignal::TRAP, + stub->Cb->ReadReg(Register::pc)); + break; + + // these three should technically be a SIGBUS but gdb etc don't really + // like that (plus it sounds confusing) + case TgtStatus::FaultData: + case TgtStatus::FaultIAcc: + stub->RespFmt("S%02X", GdbSignal::SEGV); + break; + case TgtStatus::FaultInsn: + stub->RespFmt("S%02X", GdbSignal::ILL); + break; + default: break; + } + + return ExecResult::InitialBreak; +} + +ExecResult GdbStub::Handle_Exclamation(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("OK"); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_D(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("OK"); + return ExecResult::Detached; +} + +ExecResult GdbStub::Handle_r(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->Cb->ResetGdb(); + return ExecResult::Ok; +} +ExecResult GdbStub::Handle_R(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->Cb->ResetGdb(); + return ExecResult::Ok; +} +ExecResult GdbStub::Handle_k(GdbStub* stub, const u8* cmd, ssize_t len) +{ + return ExecResult::Detached; +} + + +ExecResult GdbStub::Handle_z(GdbStub* stub, const u8* cmd, ssize_t len) +{ + int typ; + u32 addr, kind; + + if (sscanf((const char*)cmd, "%d,%x,%u", &typ, &addr, &kind) != 3) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + switch (typ) + { + case 0: case 1: // remove breakpoint (we cheat & always insert a hardware breakpoint) + stub->DelBkpt(addr, kind); + break; + case 2: case 3: case 4: // watchpoint. currently not distinguishing between reads & writes oops + stub->DelWatchpt(addr, kind, typ); + break; + default: + stub->RespStr("E02"); + return ExecResult::Ok; + } + + stub->RespStr("OK"); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_Z(GdbStub* stub, const u8* cmd, ssize_t len) +{ + int typ; + u32 addr, kind; + + if (sscanf((const char*)cmd, "%d,%x,%u", &typ, &addr, &kind) != 3) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + switch (typ) + { + case 0: case 1: // insert breakpoint (we cheat & always insert a hardware breakpoint) + stub->AddBkpt(addr, kind); + break; + case 2: case 3: case 4: // watchpoint. currently not distinguishing between reads & writes oops + stub->AddWatchpt(addr, kind, typ); + break; + default: + stub->RespStr("E02"); + return ExecResult::Ok; + } + + stub->RespStr("OK"); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_HostInfo(GdbStub* stub, const u8* cmd, ssize_t len) +{ + const char* resp = ""; + + switch (stub->Cb->GetCPU()) + { + case 7: resp = TARGET_INFO_ARM7; break; + case 9: resp = TARGET_INFO_ARM9; break; + default: break; + } + + stub->RespStr(resp); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_Rcmd(GdbStub* stub, const u8* cmd, ssize_t len) +{ + + memset(tempdatabuf, 0, sizeof tempdatabuf); + for (ssize_t i = 0; i < len/2; ++i) + { + tempdatabuf[i] = unhex8(&cmd[i*2]); + } + + int r = stub->Cb->RemoteCmd(tempdatabuf, len/2); + + if (r) stub->RespFmt("E%02X", r&0xff); + else stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_Supported(GdbStub* stub, + const u8* cmd, ssize_t len) { + // TODO: support Xfer:memory-map:read:: + // but NWRAM is super annoying with that + stub->RespFmt("PacketSize=%X;qXfer:features:read+;swbreak-;hwbreak+;QStartNoAckMode+", GDBPROTO_BUFFER_CAPACITY-1); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_CRC(GdbStub* stub, + const u8* cmd, ssize_t llen) +{ + static u8 crcbuf[128]; + + u32 addr, len; + if (sscanf((const char*)cmd, "%x,%x", &addr, &len) != 2) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + u32 val = 0; // start at 0 + u32 caddr = addr; + u32 realend = addr + len; + + for (; caddr < addr + len; ) + { + // calc partial CRC in 128-byte chunks + u32 end = caddr + sizeof(crcbuf)/sizeof(crcbuf[0]); + if (end > realend) end = realend; + u32 clen = end - caddr; + + for (size_t i = 0; caddr < end; ++caddr, ++i) + { + crcbuf[i] = stub->Cb->ReadMem(caddr, 8); + } + + val = CRC32(crcbuf, clen, val); + } + + stub->RespFmt("C%x", val); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_C(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("QC1"); // current thread ID is 1 + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_fThreadInfo(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("m1"); // one thread + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_sThreadInfo(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("l"); // end of thread list + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_features(GdbStub* stub, const u8* cmd, ssize_t len) +{ + const char* resp; + + Log(LogLevel::Debug, "[GDB] CPU type = %d\n", stub->Cb->GetCPU()); + switch (stub->Cb->GetCPU()) + { + case 7: resp = TARGET_XML_ARM7; break; + case 9: resp = TARGET_XML_ARM9; break; + default: resp = ""; break; + } + + DoQResponse(stub, cmd, resp, strlen(resp)); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_Attached(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("1"); // always "attach to a process" + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Attach(GdbStub* stub, const u8* cmd, ssize_t len) +{ + + TgtStatus st = stub->Stat; + + if (st == TgtStatus::None) + { + // no target + stub->RespStr("E01"); + return ExecResult::Ok; + } + + stub->RespStr("T05thread:1;"); + + if (st == TgtStatus::Running) return ExecResult::MustBreak; + else return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Kill(GdbStub* stub, const u8* cmd, ssize_t len) +{ + TgtStatus st = stub->Stat; + + stub->Cb->ResetGdb(); + + stub->RespStr("OK"); + + return (st != TgtStatus::Running && st != TgtStatus::None) ? ExecResult::Detached : ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Run(GdbStub* stub, const u8* cmd, ssize_t len) +{ + TgtStatus st = stub->Stat; + + stub->Cb->ResetGdb(); + + // TODO: handle cmdline for homebrew? + + return (st != TgtStatus::Running && st != TgtStatus::None) ? ExecResult::Continue : ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Stopped(GdbStub* stub, const u8* cmd, ssize_t len) +{ + TgtStatus st = stub->Stat; + + static bool notified = true; + + // not sure if i understand this correctly + if (st != TgtStatus::Running) + { + if (notified) stub->RespStr("OK"); + else stub->RespStr("W00"); + + notified = !notified; + } + else stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_MustReplyEmpty(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->Resp(NULL, 0); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len) +{ + if (len < 1) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + switch (cmd[0]) + { + case 'c': + stub->RespStr("OK"); + return ExecResult::Continue; + case 's': + stub->RespStr("OK"); + return ExecResult::Step; + case 't': + stub->RespStr("OK"); + return ExecResult::MustBreak; + default: + stub->RespStr("E01"); + return ExecResult::Ok; + } +} + +ExecResult GdbStub::Handle_v_ContQuery(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("vCont;c;s;t"); + return ExecResult::Ok; +} + + +ExecResult GdbStub::Handle_Q_StartNoAckMode(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->NoAck = true; + stub->RespStr("OK"); + return ExecResult::Ok; +} + +} + |