Files
melonDS/src/debug/GdbCmds.cpp
RSDuck a3d696121e rework gdb packet parsing
it should be a bit more robust now
2024-09-15 07:30:53 +02:00

930 lines
20 KiB
C++

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "../CRC32.h"
#include "../Platform.h"
#include "hexutil.h"
#include "GdbStub.h"
using namespace melonDS;
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)
{
printf("must reply empty\n");
stub->Resp(NULL, 0);
return ExecResult::Ok;
}
ExecResult GdbStub::Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len)
{
if (len < 1)
{
printf("insufficient length");
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:
printf("invalid continue %c %s\n", cmd[0], cmd);
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;
}
}