aboutsummaryrefslogtreecommitdiff
path: root/src/debug/GdbCmds.cpp
diff options
context:
space:
mode:
authorPoroCYon <3253268+PoroCYon@users.noreply.github.com>2023-10-22 15:35:31 +0200
committerGitHub <noreply@github.com>2023-10-22 15:35:31 +0200
commit3ab752b8ca7878246c3d7f8a338a8bc3b0de26dd (patch)
treeb4a8e319bbe4897af2669c01352f7f3787ade537 /src/debug/GdbCmds.cpp
parent3d58a338a16bc41b9106857645fabc0221de711d (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.cpp924
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(&regstrbuf[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", &reg) != 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", &reg, &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;
+}
+
+}
+