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
This commit is contained in:
parent
3d58a338a1
commit
3ab752b8ca
|
@ -1,4 +1,4 @@
|
|||
build
|
||||
build*/
|
||||
bin
|
||||
obj
|
||||
*.depend
|
||||
|
|
|
@ -99,6 +99,11 @@ if (CCACHE)
|
|||
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
|
||||
endif()
|
||||
|
||||
option(ENABLE_GDBSTUB "Enable GDB stub" ON)
|
||||
if (ENABLE_GDBSTUB)
|
||||
add_definitions(-DGDBSTUB_ENABLED)
|
||||
endif()
|
||||
|
||||
option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
|
286
src/ARM.cpp
286
src/ARM.cpp
|
@ -25,6 +25,7 @@
|
|||
#include "AREngine.h"
|
||||
#include "ARMJIT.h"
|
||||
#include "Platform.h"
|
||||
#include "GPU.h"
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
#include "ARMJIT.h"
|
||||
|
@ -34,6 +35,45 @@
|
|||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
void ARM::GdbCheckA()
|
||||
{
|
||||
if (!IsSingleStep && !BreakReq)
|
||||
{ // check if eg. break signal is incoming etc.
|
||||
Gdb::StubState st = GdbStub.Enter(false, Gdb::TgtStatus::NoEvent, ~(u32)0u, BreakOnStartup);
|
||||
BreakOnStartup = false;
|
||||
IsSingleStep = st == Gdb::StubState::Step;
|
||||
BreakReq = st == Gdb::StubState::Attach || st == Gdb::StubState::Break;
|
||||
}
|
||||
}
|
||||
void ARM::GdbCheckB()
|
||||
{
|
||||
if (IsSingleStep || BreakReq)
|
||||
{ // use else here or we single-step the same insn twice in gdb
|
||||
u32 pc_real = R[15] - ((CPSR & 0x20) ? 2 : 4);
|
||||
Gdb::StubState st = GdbStub.Enter(true, Gdb::TgtStatus::SingleStep, pc_real);
|
||||
IsSingleStep = st == Gdb::StubState::Step;
|
||||
BreakReq = st == Gdb::StubState::Attach || st == Gdb::StubState::Break;
|
||||
}
|
||||
}
|
||||
void ARM::GdbCheckC()
|
||||
{
|
||||
u32 pc_real = R[15] - ((CPSR & 0x20) ? 2 : 4);
|
||||
Gdb::StubState st = GdbStub.CheckBkpt(pc_real, true, true);
|
||||
if (st != Gdb::StubState::CheckNoHit)
|
||||
{
|
||||
IsSingleStep = st == Gdb::StubState::Step;
|
||||
BreakReq = st == Gdb::StubState::Attach || st == Gdb::StubState::Break;
|
||||
}
|
||||
else GdbCheckB();
|
||||
}
|
||||
#else
|
||||
ARM::GdbCheckA() {}
|
||||
ARM::GdbCheckB() {}
|
||||
ARM::GdbCheckC() {}
|
||||
#endif
|
||||
|
||||
|
||||
// instruction timing notes
|
||||
//
|
||||
// * simple instruction: 1S (code)
|
||||
|
@ -70,9 +110,22 @@ u32 ARM::ConditionTable[16] =
|
|||
|
||||
|
||||
ARM::ARM(u32 num)
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
: GdbStub(this, Platform::GetConfigInt(num ? Platform::GdbPortARM7 : Platform::GdbPortARM9))
|
||||
#endif
|
||||
{
|
||||
// well uh
|
||||
Num = num;
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
if (Platform::GetConfigBool(Platform::GdbEnabled)
|
||||
#ifdef JIT_ENABLED
|
||||
&& !Platform::GetConfigBool(Platform::JIT_Enable)
|
||||
#endif
|
||||
)
|
||||
GdbStub.Init();
|
||||
IsSingleStep = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
ARM::~ARM()
|
||||
|
@ -139,6 +192,13 @@ void ARM::Reset()
|
|||
FastBlockLookupSize = 0;
|
||||
#endif
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
IsSingleStep = false;
|
||||
BreakReq = false;
|
||||
BreakOnStartup = Platform::GetConfigBool(
|
||||
Num ? Platform::GdbARM7BreakOnStartup : Platform::GdbARM9BreakOnStartup);
|
||||
#endif
|
||||
|
||||
// zorp
|
||||
JumpTo(ExceptionBase);
|
||||
}
|
||||
|
@ -571,8 +631,15 @@ void ARMv5::DataAbort()
|
|||
JumpTo(ExceptionBase + 0x10);
|
||||
}
|
||||
|
||||
void ARM::CheckGdbIncoming()
|
||||
{
|
||||
GdbCheckA();
|
||||
}
|
||||
|
||||
void ARMv5::Execute()
|
||||
{
|
||||
GdbCheckB();
|
||||
|
||||
if (Halted)
|
||||
{
|
||||
if (Halted == 2)
|
||||
|
@ -596,6 +663,8 @@ void ARMv5::Execute()
|
|||
{
|
||||
if (CPSR & 0x20) // THUMB
|
||||
{
|
||||
GdbCheckC();
|
||||
|
||||
// prefetch
|
||||
R[15] += 2;
|
||||
CurInstr = NextInstr[0];
|
||||
|
@ -609,6 +678,8 @@ void ARMv5::Execute()
|
|||
}
|
||||
else
|
||||
{
|
||||
GdbCheckC();
|
||||
|
||||
// prefetch
|
||||
R[15] += 4;
|
||||
CurInstr = NextInstr[0];
|
||||
|
@ -723,6 +794,8 @@ void ARMv5::ExecuteJIT()
|
|||
|
||||
void ARMv4::Execute()
|
||||
{
|
||||
GdbCheckB();
|
||||
|
||||
if (Halted)
|
||||
{
|
||||
if (Halted == 2)
|
||||
|
@ -746,6 +819,8 @@ void ARMv4::Execute()
|
|||
{
|
||||
if (CPSR & 0x20) // THUMB
|
||||
{
|
||||
GdbCheckC();
|
||||
|
||||
// prefetch
|
||||
R[15] += 2;
|
||||
CurInstr = NextInstr[0];
|
||||
|
@ -758,6 +833,8 @@ void ARMv4::Execute()
|
|||
}
|
||||
else
|
||||
{
|
||||
GdbCheckC();
|
||||
|
||||
// prefetch
|
||||
R[15] += 4;
|
||||
CurInstr = NextInstr[0];
|
||||
|
@ -916,3 +993,212 @@ void ARMv4::FillPipeline()
|
|||
NextInstr[1] = CodeRead32(R[15]);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
u32 ARM::ReadReg(Gdb::Register reg)
|
||||
{
|
||||
using Gdb::Register;
|
||||
int r = static_cast<int>(reg);
|
||||
|
||||
if (reg < Register::pc) return R[r];
|
||||
else if (reg == Register::pc)
|
||||
{
|
||||
return R[r] - ((CPSR & 0x20) ? 2 : 4);
|
||||
}
|
||||
else if (reg == Register::cpsr) return CPSR;
|
||||
else if (reg == Register::sp_usr || reg == Register::lr_usr)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_usr);
|
||||
if (ModeIs(0x10) || ModeIs(0x1f))
|
||||
{
|
||||
return R[13 + r];
|
||||
}
|
||||
else switch (CPSR & 0x1f)
|
||||
{
|
||||
case 0x11: return R_FIQ[5 + r];
|
||||
case 0x12: return R_IRQ[0 + r];
|
||||
case 0x13: return R_SVC[0 + r];
|
||||
case 0x17: return R_ABT[0 + r];
|
||||
case 0x1b: return R_UND[0 + r];
|
||||
}
|
||||
}
|
||||
else if (reg >= Register::r8_fiq && reg <= Register::lr_fiq)
|
||||
{
|
||||
r -= static_cast<int>(Register::r8_fiq);
|
||||
return ModeIs(0x11) ? R[ 8 + r] : R_FIQ[r];
|
||||
}
|
||||
else if (reg == Register::sp_irq || reg == Register::lr_irq)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_irq);
|
||||
return ModeIs(0x12) ? R[13 + r] : R_IRQ[r];
|
||||
}
|
||||
else if (reg == Register::sp_svc || reg == Register::lr_svc)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_svc);
|
||||
return ModeIs(0x13) ? R[13 + r] : R_SVC[r];
|
||||
}
|
||||
else if (reg == Register::sp_abt || reg == Register::lr_abt)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_abt);
|
||||
return ModeIs(0x17) ? R[13 + r] : R_ABT[r];
|
||||
}
|
||||
else if (reg == Register::sp_und || reg == Register::lr_und)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_und);
|
||||
return ModeIs(0x1b) ? R[13 + r] : R_UND[r];
|
||||
}
|
||||
else if (reg == Register::spsr_fiq) return ModeIs(0x11) ? CPSR : R_FIQ[7];
|
||||
else if (reg == Register::spsr_irq) return ModeIs(0x12) ? CPSR : R_IRQ[2];
|
||||
else if (reg == Register::spsr_svc) return ModeIs(0x13) ? CPSR : R_SVC[2];
|
||||
else if (reg == Register::spsr_abt) return ModeIs(0x17) ? CPSR : R_ABT[2];
|
||||
else if (reg == Register::spsr_und) return ModeIs(0x1b) ? CPSR : R_UND[2];
|
||||
|
||||
Log(LogLevel::Warn, "GDB reg read: unknown reg no %d\n", r);
|
||||
return 0xdeadbeef;
|
||||
}
|
||||
void ARM::WriteReg(Gdb::Register reg, u32 v)
|
||||
{
|
||||
using Gdb::Register;
|
||||
int r = static_cast<int>(reg);
|
||||
|
||||
if (reg < Register::pc) R[r] = v;
|
||||
else if (reg == Register::pc) JumpTo(v);
|
||||
else if (reg == Register::cpsr) CPSR = v;
|
||||
else if (reg == Register::sp_usr || reg == Register::lr_usr)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_usr);
|
||||
if (ModeIs(0x10) || ModeIs(0x1f))
|
||||
{
|
||||
R[13 + r] = v;
|
||||
}
|
||||
else switch (CPSR & 0x1f)
|
||||
{
|
||||
case 0x11: R_FIQ[5 + r] = v; break;
|
||||
case 0x12: R_IRQ[0 + r] = v; break;
|
||||
case 0x13: R_SVC[0 + r] = v; break;
|
||||
case 0x17: R_ABT[0 + r] = v; break;
|
||||
case 0x1b: R_UND[0 + r] = v; break;
|
||||
}
|
||||
}
|
||||
else if (reg >= Register::r8_fiq && reg <= Register::lr_fiq)
|
||||
{
|
||||
r -= static_cast<int>(Register::r8_fiq);
|
||||
*(ModeIs(0x11) ? &R[ 8 + r] : &R_FIQ[r]) = v;
|
||||
}
|
||||
else if (reg == Register::sp_irq || reg == Register::lr_irq)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_irq);
|
||||
*(ModeIs(0x12) ? &R[13 + r] : &R_IRQ[r]) = v;
|
||||
}
|
||||
else if (reg == Register::sp_svc || reg == Register::lr_svc)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_svc);
|
||||
*(ModeIs(0x13) ? &R[13 + r] : &R_SVC[r]) = v;
|
||||
}
|
||||
else if (reg == Register::sp_abt || reg == Register::lr_abt)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_abt);
|
||||
*(ModeIs(0x17) ? &R[13 + r] : &R_ABT[r]) = v;
|
||||
}
|
||||
else if (reg == Register::sp_und || reg == Register::lr_und)
|
||||
{
|
||||
r -= static_cast<int>(Register::sp_und);
|
||||
*(ModeIs(0x1b) ? &R[13 + r] : &R_UND[r]) = v;
|
||||
}
|
||||
else if (reg == Register::spsr_fiq)
|
||||
{
|
||||
*(ModeIs(0x11) ? &CPSR : &R_FIQ[7]) = v;
|
||||
}
|
||||
else if (reg == Register::spsr_irq)
|
||||
{
|
||||
*(ModeIs(0x12) ? &CPSR : &R_IRQ[2]) = v;
|
||||
}
|
||||
else if (reg == Register::spsr_svc)
|
||||
{
|
||||
*(ModeIs(0x13) ? &CPSR : &R_SVC[2]) = v;
|
||||
}
|
||||
else if (reg == Register::spsr_abt)
|
||||
{
|
||||
*(ModeIs(0x17) ? &CPSR : &R_ABT[2]) = v;
|
||||
}
|
||||
else if (reg == Register::spsr_und)
|
||||
{
|
||||
*(ModeIs(0x1b) ? &CPSR : &R_UND[2]) = v;
|
||||
}
|
||||
else Log(LogLevel::Warn, "GDB reg write: unknown reg no %d (write 0x%08x)\n", r, v);
|
||||
}
|
||||
u32 ARM::ReadMem(u32 addr, int size)
|
||||
{
|
||||
if (size == 8) return BusRead8(addr);
|
||||
else if (size == 16) return BusRead16(addr);
|
||||
else if (size == 32) return BusRead32(addr);
|
||||
else return 0xfeedface;
|
||||
}
|
||||
void ARM::WriteMem(u32 addr, int size, u32 v)
|
||||
{
|
||||
if (size == 8) BusWrite8(addr, (u8)v);
|
||||
else if (size == 16) BusWrite16(addr, (u16)v);
|
||||
else if (size == 32) BusWrite32(addr, v);
|
||||
}
|
||||
|
||||
void ARM::ResetGdb()
|
||||
{
|
||||
NDS::Reset();
|
||||
GPU::StartFrame(); // need this to properly kick off the scheduler & frame output
|
||||
}
|
||||
int ARM::RemoteCmd(const u8* cmd, size_t len)
|
||||
{
|
||||
(void)len;
|
||||
|
||||
Log(LogLevel::Info, "[ARMGDB] Rcmd: \"%s\"\n", cmd);
|
||||
if (!strcmp((const char*)cmd, "reset") || !strcmp((const char*)cmd, "r"))
|
||||
{
|
||||
Reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1; // not implemented (yet)
|
||||
}
|
||||
|
||||
void ARMv5::WriteMem(u32 addr, int size, u32 v)
|
||||
{
|
||||
if (addr < ITCMSize)
|
||||
{
|
||||
if (size == 8) *(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)] = (u8)v;
|
||||
else if (size == 16) *(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)] = (u16)v;
|
||||
else if (size == 32) *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = (u32)v;
|
||||
else {}
|
||||
return;
|
||||
}
|
||||
else if ((addr & DTCMMask) == DTCMBase)
|
||||
{
|
||||
if (size == 8) *(u8*)&DTCM[addr & (DTCMPhysicalSize - 1)] = (u8)v;
|
||||
else if (size == 16) *(u16*)&DTCM[addr & (DTCMPhysicalSize - 1)] = (u16)v;
|
||||
else if (size == 32) *(u32*)&DTCM[addr & (DTCMPhysicalSize - 1)] = (u32)v;
|
||||
else {}
|
||||
return;
|
||||
}
|
||||
|
||||
ARM::WriteMem(addr, size, v);
|
||||
}
|
||||
u32 ARMv5::ReadMem(u32 addr, int size)
|
||||
{
|
||||
if (addr < ITCMSize)
|
||||
{
|
||||
if (size == 8) return *(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)];
|
||||
else if (size == 16) return *(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)];
|
||||
else if (size == 32) return *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)];
|
||||
else return 0xfeedface;
|
||||
}
|
||||
else if ((addr & DTCMMask) == DTCMBase)
|
||||
{
|
||||
if (size == 8) return *(u8*)&DTCM[addr & (DTCMPhysicalSize - 1)];
|
||||
else if (size == 16) return *(u16*)&DTCM[addr & (DTCMPhysicalSize - 1)];
|
||||
else if (size == 32) return *(u32*)&DTCM[addr & (DTCMPhysicalSize - 1)];
|
||||
else return 0xfeedface;
|
||||
}
|
||||
|
||||
return ARM::ReadMem(addr, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
121
src/ARM.h
121
src/ARM.h
|
@ -24,6 +24,10 @@
|
|||
#include "types.h"
|
||||
#include "NDS.h"
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
#include "debug/GdbStub.h"
|
||||
#endif
|
||||
|
||||
inline u32 ROR(u32 x, u32 n)
|
||||
{
|
||||
return (x >> (n&0x1F)) | (x << ((32-n)&0x1F));
|
||||
|
@ -39,6 +43,9 @@ const u32 ITCMPhysicalSize = 0x8000;
|
|||
const u32 DTCMPhysicalSize = 0x4000;
|
||||
|
||||
class ARM
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
: public Gdb::StubCallbacks
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
ARM(u32 num);
|
||||
|
@ -93,6 +100,18 @@ public:
|
|||
if (v) CPSR |= 0x10000000;
|
||||
}
|
||||
|
||||
inline bool ModeIs(u32 mode)
|
||||
{
|
||||
u32 cm = CPSR & 0x1f;
|
||||
mode &= 0x1f;
|
||||
|
||||
if (mode == cm) return true;
|
||||
if (mode == 0x17) return cm >= 0x14 && cm <= 0x17; // abt
|
||||
if (mode == 0x1b) return cm >= 0x18 && cm <= 0x1b; // und
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UpdateMode(u32 oldmode, u32 newmode, bool phony = false);
|
||||
|
||||
void TriggerIRQ();
|
||||
|
@ -114,6 +133,7 @@ public:
|
|||
virtual void AddCycles_CDI() = 0;
|
||||
virtual void AddCycles_CD() = 0;
|
||||
|
||||
void CheckGdbIncoming();
|
||||
|
||||
u32 Num;
|
||||
|
||||
|
@ -155,6 +175,9 @@ public:
|
|||
#endif
|
||||
|
||||
static u32 ConditionTable[16];
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
Gdb::GdbStub GdbStub;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
u8 (*BusRead8)(u32 addr);
|
||||
|
@ -163,6 +186,29 @@ protected:
|
|||
void (*BusWrite8)(u32 addr, u8 val);
|
||||
void (*BusWrite16)(u32 addr, u16 val);
|
||||
void (*BusWrite32)(u32 addr, u32 val);
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
bool IsSingleStep;
|
||||
bool BreakReq;
|
||||
bool BreakOnStartup;
|
||||
|
||||
public:
|
||||
int GetCPU() const override { return Num ? 7 : 9; }
|
||||
|
||||
u32 ReadReg(Gdb::Register reg) override;
|
||||
void WriteReg(Gdb::Register reg, u32 v) override;
|
||||
u32 ReadMem(u32 addr, int size) override;
|
||||
void WriteMem(u32 addr, int size, u32 v) override;
|
||||
|
||||
void ResetGdb() override;
|
||||
int RemoteCmd(const u8* cmd, size_t len) override;
|
||||
|
||||
protected:
|
||||
#endif
|
||||
|
||||
void GdbCheckA();
|
||||
void GdbCheckB();
|
||||
void GdbCheckC();
|
||||
};
|
||||
|
||||
class ARMv5 : public ARM
|
||||
|
@ -171,51 +217,51 @@ public:
|
|||
ARMv5();
|
||||
~ARMv5();
|
||||
|
||||
void Reset();
|
||||
void Reset() override;
|
||||
|
||||
void DoSavestate(Savestate* file);
|
||||
void DoSavestate(Savestate* file) override;
|
||||
|
||||
void UpdateRegionTimings(u32 addrstart, u32 addrend);
|
||||
|
||||
void FillPipeline();
|
||||
void FillPipeline() override;
|
||||
|
||||
void JumpTo(u32 addr, bool restorecpsr = false);
|
||||
void JumpTo(u32 addr, bool restorecpsr = false) override;
|
||||
|
||||
void PrefetchAbort();
|
||||
void DataAbort();
|
||||
|
||||
void Execute();
|
||||
void Execute() override;
|
||||
#ifdef JIT_ENABLED
|
||||
void ExecuteJIT();
|
||||
void ExecuteJIT() override;
|
||||
#endif
|
||||
|
||||
// all code accesses are forced nonseq 32bit
|
||||
u32 CodeRead32(u32 addr, bool branch);
|
||||
|
||||
void DataRead8(u32 addr, u32* val);
|
||||
void DataRead16(u32 addr, u32* val);
|
||||
void DataRead32(u32 addr, u32* val);
|
||||
void DataRead32S(u32 addr, u32* val);
|
||||
void DataWrite8(u32 addr, u8 val);
|
||||
void DataWrite16(u32 addr, u16 val);
|
||||
void DataWrite32(u32 addr, u32 val);
|
||||
void DataWrite32S(u32 addr, u32 val);
|
||||
void DataRead8(u32 addr, u32* val) override;
|
||||
void DataRead16(u32 addr, u32* val) override;
|
||||
void DataRead32(u32 addr, u32* val) override;
|
||||
void DataRead32S(u32 addr, u32* val) override;
|
||||
void DataWrite8(u32 addr, u8 val) override;
|
||||
void DataWrite16(u32 addr, u16 val) override;
|
||||
void DataWrite32(u32 addr, u32 val) override;
|
||||
void DataWrite32S(u32 addr, u32 val) override;
|
||||
|
||||
void AddCycles_C()
|
||||
void AddCycles_C() override
|
||||
{
|
||||
// code only. always nonseq 32-bit for ARM9.
|
||||
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
|
||||
Cycles += numC;
|
||||
}
|
||||
|
||||
void AddCycles_CI(s32 numI)
|
||||
void AddCycles_CI(s32 numI) override
|
||||
{
|
||||
// code+internal
|
||||
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
|
||||
Cycles += numC + numI;
|
||||
}
|
||||
|
||||
void AddCycles_CDI()
|
||||
void AddCycles_CDI() override
|
||||
{
|
||||
// LDR/LDM cycles. ARM9 seems to skip the internal cycle there.
|
||||
// TODO: ITCM data fetches shouldn't be parallelized, they say
|
||||
|
@ -228,7 +274,7 @@ public:
|
|||
// Cycles += numC + numD;
|
||||
}
|
||||
|
||||
void AddCycles_CD()
|
||||
void AddCycles_CD() override
|
||||
{
|
||||
// TODO: ITCM data fetches shouldn't be parallelized, they say
|
||||
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
|
||||
|
@ -302,6 +348,11 @@ public:
|
|||
u8* CurICacheLine;
|
||||
|
||||
bool (*GetMemRegion)(u32 addr, bool write, NDS::MemRegion* region);
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
u32 ReadMem(u32 addr, int size) override;
|
||||
void WriteMem(u32 addr, int size, u32 v) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
class ARMv4 : public ARM
|
||||
|
@ -309,15 +360,15 @@ class ARMv4 : public ARM
|
|||
public:
|
||||
ARMv4();
|
||||
|
||||
void Reset();
|
||||
void Reset() override;
|
||||
|
||||
void FillPipeline();
|
||||
void FillPipeline() override;
|
||||
|
||||
void JumpTo(u32 addr, bool restorecpsr = false);
|
||||
void JumpTo(u32 addr, bool restorecpsr = false) override;
|
||||
|
||||
void Execute();
|
||||
void Execute() override;
|
||||
#ifdef JIT_ENABLED
|
||||
void ExecuteJIT();
|
||||
void ExecuteJIT() override;
|
||||
#endif
|
||||
|
||||
u16 CodeRead16(u32 addr)
|
||||
|
@ -330,14 +381,14 @@ public:
|
|||
return BusRead32(addr);
|
||||
}
|
||||
|
||||
void DataRead8(u32 addr, u32* val)
|
||||
void DataRead8(u32 addr, u32* val) override
|
||||
{
|
||||
*val = BusRead8(addr);
|
||||
DataRegion = addr;
|
||||
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
|
||||
}
|
||||
|
||||
void DataRead16(u32 addr, u32* val)
|
||||
void DataRead16(u32 addr, u32* val) override
|
||||
{
|
||||
addr &= ~1;
|
||||
|
||||
|
@ -346,7 +397,7 @@ public:
|
|||
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
|
||||
}
|
||||
|
||||
void DataRead32(u32 addr, u32* val)
|
||||
void DataRead32(u32 addr, u32* val) override
|
||||
{
|
||||
addr &= ~3;
|
||||
|
||||
|
@ -355,7 +406,7 @@ public:
|
|||
DataCycles = NDS::ARM7MemTimings[addr >> 15][2];
|
||||
}
|
||||
|
||||
void DataRead32S(u32 addr, u32* val)
|
||||
void DataRead32S(u32 addr, u32* val) override
|
||||
{
|
||||
addr &= ~3;
|
||||
|
||||
|
@ -363,14 +414,14 @@ public:
|
|||
DataCycles += NDS::ARM7MemTimings[addr >> 15][3];
|
||||
}
|
||||
|
||||
void DataWrite8(u32 addr, u8 val)
|
||||
void DataWrite8(u32 addr, u8 val) override
|
||||
{
|
||||
BusWrite8(addr, val);
|
||||
DataRegion = addr;
|
||||
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
|
||||
}
|
||||
|
||||
void DataWrite16(u32 addr, u16 val)
|
||||
void DataWrite16(u32 addr, u16 val) override
|
||||
{
|
||||
addr &= ~1;
|
||||
|
||||
|
@ -379,7 +430,7 @@ public:
|
|||
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
|
||||
}
|
||||
|
||||
void DataWrite32(u32 addr, u32 val)
|
||||
void DataWrite32(u32 addr, u32 val) override
|
||||
{
|
||||
addr &= ~3;
|
||||
|
||||
|
@ -388,7 +439,7 @@ public:
|
|||
DataCycles = NDS::ARM7MemTimings[addr >> 15][2];
|
||||
}
|
||||
|
||||
void DataWrite32S(u32 addr, u32 val)
|
||||
void DataWrite32S(u32 addr, u32 val) override
|
||||
{
|
||||
addr &= ~3;
|
||||
|
||||
|
@ -397,19 +448,19 @@ public:
|
|||
}
|
||||
|
||||
|
||||
void AddCycles_C()
|
||||
void AddCycles_C() override
|
||||
{
|
||||
// code only. this code fetch is sequential.
|
||||
Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?1:3];
|
||||
}
|
||||
|
||||
void AddCycles_CI(s32 num)
|
||||
void AddCycles_CI(s32 num) override
|
||||
{
|
||||
// code+internal. results in a nonseq code fetch.
|
||||
Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2] + num;
|
||||
}
|
||||
|
||||
void AddCycles_CDI()
|
||||
void AddCycles_CDI() override
|
||||
{
|
||||
// LDR/LDM cycles.
|
||||
s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2];
|
||||
|
@ -436,7 +487,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void AddCycles_CD()
|
||||
void AddCycles_CD() override
|
||||
{
|
||||
// TODO: max gain should be 5c when writing to mainRAM
|
||||
s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2];
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
#include "debug/GdbStub.h"
|
||||
#endif
|
||||
|
||||
namespace ARMInterpreter
|
||||
{
|
||||
|
||||
|
@ -34,6 +38,9 @@ namespace ARMInterpreter
|
|||
void A_UNK(ARM* cpu)
|
||||
{
|
||||
Log(LogLevel::Warn, "undefined ARM%d instruction %08X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-8);
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
cpu->GdbStub.Enter(true, Gdb::TgtStatus::FaultInsn, cpu->R[15]-8);
|
||||
#endif
|
||||
//for (int i = 0; i < 16; i++) printf("R%d: %08X\n", i, cpu->R[i]);
|
||||
//NDS::Halt();
|
||||
u32 oldcpsr = cpu->CPSR;
|
||||
|
@ -49,6 +56,9 @@ void A_UNK(ARM* cpu)
|
|||
void T_UNK(ARM* cpu)
|
||||
{
|
||||
Log(LogLevel::Warn, "undefined THUMB%d instruction %04X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-4);
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
cpu->GdbStub.Enter(true, Gdb::TgtStatus::FaultInsn, cpu->R[15]-4);
|
||||
#endif
|
||||
//NDS::Halt();
|
||||
u32 oldcpsr = cpu->CPSR;
|
||||
cpu->CPSR &= ~0xBF;
|
||||
|
|
|
@ -94,3 +94,8 @@ ARM_RestoreContext:
|
|||
mov sp, x17
|
||||
|
||||
br x18
|
||||
|
||||
#ifndef __APPLE__
|
||||
.section .note.GNU-stack,"",@progbits
|
||||
#endif
|
||||
|
||||
|
|
|
@ -104,3 +104,8 @@ ARM_Ret:
|
|||
#endif
|
||||
|
||||
ret
|
||||
|
||||
#if !defined(__APPLE__) && !defined(WIN64)
|
||||
.section .note.GNU-stack,"",@progbits
|
||||
#endif
|
||||
|
||||
|
|
|
@ -61,6 +61,15 @@ add_library(core STATIC
|
|||
tiny-AES-c/aes.c
|
||||
xxhash/xxhash.c)
|
||||
|
||||
if (ENABLE_GDBSTUB)
|
||||
message(NOTICE "Enabling GDB stub")
|
||||
target_sources(core PRIVATE
|
||||
debug/GdbStub.cpp
|
||||
debug/GdbProto.cpp
|
||||
debug/GdbCmds.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_OGLRENDERER)
|
||||
target_sources(core PRIVATE
|
||||
GPU_OpenGL.cpp
|
||||
|
@ -131,7 +140,7 @@ if (ENABLE_JIT)
|
|||
endif()
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(core PRIVATE ole32 comctl32 ws2_32)
|
||||
target_link_libraries(core PRIVATE ole32 comctl32 wsock32 ws2_32)
|
||||
elseif(NOT APPLE)
|
||||
check_library_exists(rt shm_open "" NEED_LIBRT)
|
||||
if (NEED_LIBRT)
|
||||
|
@ -143,3 +152,14 @@ if (ENABLE_JIT_PROFILING)
|
|||
target_include_directories(core PRIVATE "${VTUNE_INCLUDE_DIR}")
|
||||
target_link_libraries(core PRIVATE "${VTUNE_LIBRARY}")
|
||||
endif()
|
||||
|
||||
#if(CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
# set(
|
||||
# CMAKE_C_FLAGS
|
||||
# "${CMAKE_C_FLAGS} -fsanitize=undefined -fsanitize=address"
|
||||
# )
|
||||
# target_link_options(core
|
||||
# BEFORE PUBLIC -fsanitize=undefined PUBLIC -fsanitize=address
|
||||
# )
|
||||
#endif()
|
||||
|
||||
|
|
|
@ -1068,6 +1068,9 @@ u32 RunFrame()
|
|||
bool runFrame = Running && !(CPUStop & 0x40000000);
|
||||
if (runFrame)
|
||||
{
|
||||
ARM9->CheckGdbIncoming();
|
||||
ARM7->CheckGdbIncoming();
|
||||
|
||||
GPU::StartFrame();
|
||||
|
||||
while (Running && GPU::TotalScanlines==0)
|
||||
|
|
|
@ -134,7 +134,15 @@ enum ConfigEntry
|
|||
|
||||
AudioBitDepth,
|
||||
|
||||
DSi_FullBIOSBoot
|
||||
DSi_FullBIOSBoot,
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
GdbEnabled,
|
||||
GdbPortARM7,
|
||||
GdbPortARM9,
|
||||
GdbARM7BreakOnStartup,
|
||||
GdbARM9BreakOnStartup,
|
||||
#endif
|
||||
};
|
||||
|
||||
int GetConfigInt(ConfigEntry entry);
|
||||
|
|
|
@ -856,9 +856,13 @@ void Mix(u32 dummy)
|
|||
|
||||
// OutputBufferFrame can never get full because it's
|
||||
// transfered to OutputBuffer at the end of the frame
|
||||
// FIXME: apparently this does happen!!!
|
||||
if (OutputBackbufferWritePosition * 2 < OutputBufferSize - 1)
|
||||
{
|
||||
OutputBackbuffer[OutputBackbufferWritePosition ] = leftoutput >> 1;
|
||||
OutputBackbuffer[OutputBackbufferWritePosition + 1] = rightoutput >> 1;
|
||||
OutputBackbufferWritePosition += 2;
|
||||
}
|
||||
|
||||
NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, Mix, 0);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
|
||||
#ifndef GDBARCH_H_
|
||||
#define GDBARCH_H_
|
||||
|
||||
namespace Gdb
|
||||
{
|
||||
|
||||
enum class Register : int
|
||||
{
|
||||
r0,
|
||||
r1,
|
||||
r2,
|
||||
r3,
|
||||
r4,
|
||||
r5,
|
||||
r6,
|
||||
r7,
|
||||
r8,
|
||||
r9,
|
||||
r10,
|
||||
r11,
|
||||
r12,
|
||||
sp,
|
||||
lr,
|
||||
pc,
|
||||
|
||||
cpsr,
|
||||
sp_usr,
|
||||
lr_usr,
|
||||
|
||||
r8_fiq,
|
||||
r9_fiq,
|
||||
r10_fiq,
|
||||
r11_fiq,
|
||||
r12_fiq,
|
||||
|
||||
sp_fiq,
|
||||
lr_fiq,
|
||||
sp_irq,
|
||||
lr_irq,
|
||||
sp_svc,
|
||||
lr_svc,
|
||||
sp_abt,
|
||||
lr_abt,
|
||||
sp_und,
|
||||
lr_und,
|
||||
|
||||
spsr_fiq,
|
||||
spsr_irq,
|
||||
spsr_svc,
|
||||
spsr_abt,
|
||||
spsr_und,
|
||||
|
||||
COUNT
|
||||
};
|
||||
|
||||
constexpr int GDB_ARCH_N_REG = (int)Register::COUNT;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
#ifndef GDBSTUB_H_
|
||||
#error "DO NOT INCLUDE THIS FILE YOURSELF!"
|
||||
#endif
|
||||
|
||||
private:
|
||||
static ExecResult Handle_g(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_G(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_m(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_M(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_X(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
|
||||
static ExecResult Handle_c(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_s(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_p(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_P(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_H(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
|
||||
static ExecResult Handle_Question(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_Exclamation(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_D(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_r(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_R(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_k(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
|
||||
static ExecResult Handle_z(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_Z(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
|
||||
static ExecResult Handle_q(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_v(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_Q(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
|
||||
static ExecResult Handle_q_HostInfo(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_q_Rcmd(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_q_Supported(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_q_CRC(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_q_C(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_q_fThreadInfo(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_q_sThreadInfo(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_q_TStatus(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_q_features(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_q_Attached(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
|
||||
static ExecResult Handle_v_Attach(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_v_Kill(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_v_Run(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_v_Stopped(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_v_MustReplyEmpty(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
static ExecResult Handle_v_ContQuery(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
|
||||
static ExecResult Handle_Q_StartNoAckMode(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
|
|
@ -0,0 +1,389 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
#include <WS2tcpip.h>
|
||||
#include <winsock.h>
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <poll.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include "../Platform.h"
|
||||
#include "hexutil.h"
|
||||
|
||||
#include "GdbProto.h"
|
||||
|
||||
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
namespace Gdb
|
||||
{
|
||||
|
||||
/*
|
||||
* TODO commands to support:
|
||||
* m M g G c s p P H
|
||||
* ? D r
|
||||
* qC qfThreadInfo qsThreadInfo
|
||||
* z0 Z0 z1 Z1 z4 Z4
|
||||
* qCRC
|
||||
* vAttach;addr
|
||||
* vKill;pid
|
||||
* qRcmd? qSupported?
|
||||
*/
|
||||
u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY];
|
||||
ssize_t Cmdlen;
|
||||
|
||||
namespace Proto
|
||||
{
|
||||
|
||||
u8 PacketBuf[GDBPROTO_BUFFER_CAPACITY];
|
||||
u8 RespBuf[GDBPROTO_BUFFER_CAPACITY+5];
|
||||
|
||||
ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/])
|
||||
{
|
||||
static ssize_t dataoff = 0;
|
||||
|
||||
ssize_t recv_total = dataoff;
|
||||
ssize_t cksumoff = -1;
|
||||
u8 sum = 0;
|
||||
|
||||
bool first = true;
|
||||
|
||||
//printf("--- dataoff=%zd\n", dataoff);
|
||||
if (dataoff != 0) {
|
||||
printf("--- got preexisting: %s\n", PacketBuf);
|
||||
|
||||
ssize_t datastart = 0;
|
||||
while (true)
|
||||
{
|
||||
if (PacketBuf[datastart] == '\x04') return ReadResult::Eof;
|
||||
else if (PacketBuf[datastart] == '+' || PacketBuf[datastart] == '-')
|
||||
{
|
||||
/*if (PacketBuf[datastart] == '+') SendAck(connfd);
|
||||
else SendNak(connfd);*/
|
||||
++datastart;
|
||||
continue;
|
||||
}
|
||||
else if (PacketBuf[datastart] == '$')
|
||||
{
|
||||
++datastart;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
__builtin_trap();
|
||||
return ReadResult::Wut;
|
||||
}
|
||||
}
|
||||
printf("--- datastart=%zd\n", datastart);
|
||||
|
||||
for (ssize_t i = datastart; i < dataoff; ++i)
|
||||
{
|
||||
if (PacketBuf[i] == '#')
|
||||
{
|
||||
cksumoff = i + 1;
|
||||
printf("--- cksumoff=%zd\n", cksumoff);
|
||||
break;
|
||||
}
|
||||
|
||||
sum += PacketBuf[i];
|
||||
}
|
||||
|
||||
if (cksumoff >= 0)
|
||||
{
|
||||
recv_total = dataoff - datastart + 1;
|
||||
dataoff = cksumoff + 2 - datastart + 1;
|
||||
cksumoff -= datastart - 1;
|
||||
|
||||
memmove(&PacketBuf[1], &PacketBuf[datastart], recv_total);
|
||||
PacketBuf[0] = '$';
|
||||
PacketBuf[recv_total] = 0;
|
||||
|
||||
printf("=== cksumoff=%zi recv_total=%zi datastart=%zi dataoff=%zi\n==> %s\n",
|
||||
cksumoff, recv_total, datastart, dataoff, PacketBuf);
|
||||
//break;
|
||||
}
|
||||
}
|
||||
|
||||
while (cksumoff < 0)
|
||||
{
|
||||
u8* pkt = &PacketBuf[dataoff];
|
||||
ssize_t n, blehoff = 0;
|
||||
|
||||
memset(pkt, 0, sizeof(PacketBuf) - dataoff);
|
||||
int flag = 0;
|
||||
#if MOCKTEST
|
||||
static bool FIRST = false;
|
||||
if (FIRST) {
|
||||
printf("%s", "[==>] TEST DONE\n");
|
||||
__builtin_trap();
|
||||
}
|
||||
FIRST = true;
|
||||
|
||||
const char* testinp1 = "+$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77";
|
||||
const char* testinp2 = "+$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77---+$vMustReplyEmpty#3a";
|
||||
|
||||
const char* testinp = testinp1;
|
||||
|
||||
n = strlen(testinp);
|
||||
memcpy(pkt, testinp, strlen(testinp));
|
||||
#else
|
||||
#ifndef _WIN32
|
||||
if (first) flag |= MSG_DONTWAIT;
|
||||
n = recv(connfd, pkt, sizeof(PacketBuf) - dataoff, flag);
|
||||
#else
|
||||
// fuck windows
|
||||
n = recv(connfd, (char*)pkt, sizeof(PacketBuf) - dataoff, flag);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (n <= 0)
|
||||
{
|
||||
if (first) return ReadResult::NoPacket;
|
||||
else
|
||||
{
|
||||
Log(LogLevel::Debug, "[GDB] recv() error %zi, errno=%d (%s)\n", n, errno, strerror(errno));
|
||||
return ReadResult::Eof;
|
||||
}
|
||||
}
|
||||
|
||||
Log(LogLevel::Debug, "[GDB] recv() %zd bytes: '%s' (%02x)\n", n, pkt, pkt[0]);
|
||||
first = false;
|
||||
|
||||
do
|
||||
{
|
||||
if (dataoff == 0)
|
||||
{
|
||||
if (pkt[blehoff] == '\x04') return ReadResult::Eof;
|
||||
else if (pkt[blehoff] == '\x03') return ReadResult::Break;
|
||||
else if (pkt[blehoff] != '$')
|
||||
{
|
||||
++blehoff;
|
||||
--n;
|
||||
}
|
||||
else break;
|
||||
|
||||
if (n == 0) goto next_outer;
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
|
||||
if (blehoff > 0)
|
||||
{
|
||||
memmove(pkt, &pkt[blehoff], n - blehoff + 1);
|
||||
n -= blehoff - 1; // ???
|
||||
}
|
||||
|
||||
recv_total += n;
|
||||
|
||||
Log(LogLevel::Debug, "[GDB] recv() after skipping: n=%zd, recv_total=%zd\n", n, recv_total);
|
||||
|
||||
for (ssize_t i = (dataoff == 0) ? 1 : 0; i < n; ++i)
|
||||
{
|
||||
u8 v = pkt[i];
|
||||
if (v == '#')
|
||||
{
|
||||
cksumoff = dataoff + i + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
sum += pkt[i];
|
||||
}
|
||||
|
||||
if (cksumoff < 0)
|
||||
{
|
||||
// oops, need more data
|
||||
dataoff += n;
|
||||
}
|
||||
|
||||
next_outer:;
|
||||
}
|
||||
|
||||
u8 ck = (hex2nyb(PacketBuf[cksumoff+0]) << 4)
|
||||
| hex2nyb(PacketBuf[cksumoff+1]);
|
||||
|
||||
Log(LogLevel::Debug, "[GDB] got pkt, checksum: %02x vs %02x\n", ck, sum);
|
||||
|
||||
if (ck != sum)
|
||||
{
|
||||
//__builtin_trap();
|
||||
return ReadResult::CksumErr;
|
||||
}
|
||||
|
||||
if (cksumoff + 2 > recv_total)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] BIG MISTAKE: %zi > %zi which shouldn't happen!\n", cksumoff + 2, recv_total);
|
||||
//__builtin_trap();
|
||||
return ReadResult::Wut;
|
||||
}
|
||||
else
|
||||
{
|
||||
Cmdlen = cksumoff - 2;
|
||||
memcpy(Cmdbuf, &PacketBuf[1], Cmdlen);
|
||||
Cmdbuf[Cmdlen] = 0;
|
||||
|
||||
if (cksumoff + 2 < recv_total) {
|
||||
// huh, we have the start of the next packet
|
||||
dataoff = recv_total - (cksumoff + 2);
|
||||
memmove(PacketBuf, &PacketBuf[cksumoff + 2], (size_t)dataoff);
|
||||
PacketBuf[dataoff] = 0;
|
||||
Log(LogLevel::Debug, "[GDB] got more: cksumoff=%zd, recvtotal=%zd, remain=%zd\n==> %s\n", cksumoff, recv_total, dataoff, PacketBuf);
|
||||
}
|
||||
else dataoff = 0;
|
||||
}
|
||||
|
||||
return ReadResult::CmdRecvd;
|
||||
}
|
||||
|
||||
int SendAck(int connfd)
|
||||
{
|
||||
Log(LogLevel::Debug, "[GDB] send ack\n");
|
||||
u8 v = '+';
|
||||
#if MOCKTEST
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// fuck windows
|
||||
return send(connfd, (const char*)&v, 1, 0);
|
||||
#else
|
||||
return send(connfd, &v, 1, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int SendNak(int connfd)
|
||||
{
|
||||
Log(LogLevel::Debug, "[GDB] send nak\n");
|
||||
u8 v = '-';
|
||||
#if MOCKTEST
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// fuck windows
|
||||
return send(connfd, (const char*)&v, 1, 0);
|
||||
#else
|
||||
return send(connfd, &v, 1, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int WaitAckBlocking(int connfd, u8* ackp, int to_ms)
|
||||
{
|
||||
#if MOCKTEST
|
||||
*ackp = '+';
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
fd_set infd, outfd, errfd;
|
||||
FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd);
|
||||
FD_SET(connfd, &infd);
|
||||
|
||||
struct timeval to;
|
||||
to.tv_sec = to_ms / 1000;
|
||||
to.tv_usec = (to_ms % 1000) * 1000;
|
||||
|
||||
int r = select(connfd+1, &infd, &outfd, &errfd, &to);
|
||||
|
||||
if (FD_ISSET(connfd, &errfd)) return -1;
|
||||
else if (FD_ISSET(connfd, &infd))
|
||||
{
|
||||
r = recv(connfd, (char*)ackp, 1, 0);
|
||||
if (r < 0) return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
#else
|
||||
struct pollfd pfd;
|
||||
|
||||
pfd.fd = connfd;
|
||||
pfd.events = POLLIN;
|
||||
pfd.revents = 0;
|
||||
|
||||
ssize_t r = (ssize_t)poll(&pfd, 1, to_ms);
|
||||
if (r < 0) return r;
|
||||
if (r == 0) return -1;
|
||||
|
||||
if (pfd.revents & (POLLHUP|POLLERR)) return -69;
|
||||
|
||||
r = recv(connfd, ackp, 1, 0);
|
||||
if (r < 0) return r;
|
||||
|
||||
return (r == 1) ? 0 : -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack)
|
||||
{
|
||||
u8 cksum = 0;
|
||||
int tries = 0;
|
||||
|
||||
size_t totallen = len1 + len2;
|
||||
|
||||
if (totallen >= GDBPROTO_BUFFER_CAPACITY)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] packet with len %zu can't fit in buffer!\n", totallen);
|
||||
return -42;
|
||||
}
|
||||
|
||||
RespBuf[0] = '$';
|
||||
for (size_t i = 0; i < len1; ++i)
|
||||
{
|
||||
cksum += data1[i];
|
||||
RespBuf[i+1] = data1[i];
|
||||
}
|
||||
for (size_t i = 0; i < len2; ++i)
|
||||
{
|
||||
cksum += data2[i];
|
||||
RespBuf[len1+i+1] = data2[i];
|
||||
}
|
||||
RespBuf[totallen+1] = '#';
|
||||
hexfmt8(&RespBuf[totallen+2], cksum);
|
||||
RespBuf[totallen+4] = 0;
|
||||
|
||||
do
|
||||
{
|
||||
ssize_t r;
|
||||
u8 ack;
|
||||
|
||||
Log(LogLevel::Debug, "[GDB] send resp: '%s'\n", RespBuf);
|
||||
#if MOCKTEST
|
||||
r = totallen+4;
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
r = send(connfd, (const char*)RespBuf, totallen+4, 0);
|
||||
#else
|
||||
r = send(connfd, RespBuf, totallen+4, 0);
|
||||
#endif
|
||||
#endif
|
||||
if (r < 0) return r;
|
||||
|
||||
if (noack) break;
|
||||
|
||||
r = WaitAckBlocking(connfd, &ack, 2000);
|
||||
//Log(LogLevel::Debug, "[GDB] got ack: '%c'\n", ack);
|
||||
if (r == 0 && ack == '+') break;
|
||||
|
||||
++tries;
|
||||
}
|
||||
while (tries < 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
#ifndef GDBPROTO_H_
|
||||
#define GDBPROTO_H_
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "GdbStub.h" /* class GdbStub */
|
||||
|
||||
|
||||
#define MOCKTEST 0
|
||||
|
||||
|
||||
namespace Gdb {
|
||||
|
||||
constexpr int GDBPROTO_BUFFER_CAPACITY = 1024+128;
|
||||
|
||||
extern u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY];
|
||||
extern ssize_t Cmdlen;
|
||||
|
||||
namespace Proto {
|
||||
|
||||
extern u8 PacketBuf[GDBPROTO_BUFFER_CAPACITY];
|
||||
extern u8 RespBuf[GDBPROTO_BUFFER_CAPACITY+5];
|
||||
|
||||
Gdb::ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/]);
|
||||
|
||||
int SendAck(int connfd);
|
||||
int SendNak(int connfd);
|
||||
|
||||
int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack);
|
||||
|
||||
int WaitAckBlocking(int connfd, u8* ackp, int to_ms);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,693 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
#include <WS2tcpip.h>
|
||||
#include <winsock.h>
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/socket.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "../Platform.h"
|
||||
#include "GdbProto.h"
|
||||
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
static int SocketSetBlocking(int fd, bool block)
|
||||
{
|
||||
#if MOCKTEST
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
if (fd < 0) return -1;
|
||||
|
||||
#ifdef _WIN32
|
||||
unsigned long mode = block ? 0 : 1;
|
||||
return ioctlsocket(fd, FIONBIO, &mode);
|
||||
#else
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
if (flags == -1) return -1;
|
||||
flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
|
||||
return fcntl(fd, F_SETFL, flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace Gdb
|
||||
{
|
||||
|
||||
GdbStub::GdbStub(StubCallbacks* cb, int port)
|
||||
: Cb(cb), Port(port)
|
||||
, SockFd(0), ConnFd(0)
|
||||
, Stat(TgtStatus::None), CurBkpt(0), CurWatchpt(0), StatFlag(false), NoAck(false)
|
||||
, ServerSA((void*)new struct sockaddr_in())
|
||||
, ClientSA((void*)new struct sockaddr_in())
|
||||
{ }
|
||||
|
||||
bool GdbStub::Init()
|
||||
{
|
||||
Log(LogLevel::Info, "[GDB] initializing GDB stub for core %d on port %d\n",
|
||||
Cb->GetCPU(), Port);
|
||||
|
||||
#if MOCKTEST
|
||||
SockFd = 0;
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
/*void* fn = SIG_IGN;
|
||||
struct sigaction act = { 0 };
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
act.sa_sigaction = (sighandler_t)fn;
|
||||
if (sigaction(SIGPIPE, &act, NULL) == -1) {
|
||||
Log(LogLevel::Warn, "[GDB] couldn't ignore SIGPIPE, stuff may fail on GDB disconnect.\n");
|
||||
}*/
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#else
|
||||
WSADATA wsa;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] winsock could not be initialized (%d).\n", WSAGetLastError());
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
int r;
|
||||
struct sockaddr_in* server = (struct sockaddr_in*)ServerSA;
|
||||
struct sockaddr_in* client = (struct sockaddr_in*)ClientSA;
|
||||
|
||||
int typ = SOCK_STREAM;
|
||||
#ifdef __linux__
|
||||
typ |= SOCK_NONBLOCK;
|
||||
#endif
|
||||
SockFd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (SockFd < 0)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] err: can't create a socket fd\n");
|
||||
goto err;
|
||||
}
|
||||
#ifndef __linux__
|
||||
SocketSetBlocking(SockFd, false);
|
||||
#endif
|
||||
|
||||
server->sin_family = AF_INET;
|
||||
server->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
server->sin_port = htons(Port);
|
||||
|
||||
r = bind(SockFd, (const sockaddr*)server, sizeof(*server));
|
||||
if (r < 0)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] err: can't bind to address <any> and port %d\n", Port);
|
||||
goto err;
|
||||
}
|
||||
|
||||
r = listen(SockFd, 5);
|
||||
if (r < 0)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] err: can't listen to SockFd\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
if (SockFd != 0)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
closesocket(SockFd);
|
||||
#else
|
||||
close(SockFd);
|
||||
#endif
|
||||
SockFd = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GdbStub::Close()
|
||||
{
|
||||
Disconnect();
|
||||
if (SockFd > 0) close(SockFd);
|
||||
SockFd = 0;
|
||||
}
|
||||
|
||||
void GdbStub::Disconnect()
|
||||
{
|
||||
if (ConnFd > 0) close(ConnFd);
|
||||
ConnFd = 0;
|
||||
}
|
||||
|
||||
GdbStub::~GdbStub()
|
||||
{
|
||||
Close();
|
||||
delete (struct sockaddr_in*)ServerSA;
|
||||
delete (struct sockaddr_in*)ClientSA;
|
||||
}
|
||||
|
||||
SubcmdHandler GdbStub::Handlers_v[] = {
|
||||
{ .MainCmd = 'v', .SubStr = "Attach;" , .Handler = GdbStub::Handle_v_Attach },
|
||||
{ .MainCmd = 'v', .SubStr = "Kill;" , .Handler = GdbStub::Handle_v_Kill },
|
||||
{ .MainCmd = 'v', .SubStr = "Run" , .Handler = GdbStub::Handle_v_Run },
|
||||
{ .MainCmd = 'v', .SubStr = "Stopped" , .Handler = GdbStub::Handle_v_Stopped },
|
||||
{ .MainCmd = 'v', .SubStr = "MustReplyEmpty", .Handler = GdbStub::Handle_v_MustReplyEmpty },
|
||||
{ .MainCmd = 'v', .SubStr = "Cont?" , .Handler = GdbStub::Handle_v_ContQuery },
|
||||
{ .MainCmd = 'v', .SubStr = "Cont" , .Handler = GdbStub::Handle_v_Cont },
|
||||
|
||||
{ .MainCmd = 'v', .SubStr = NULL, .Handler = NULL }
|
||||
};
|
||||
|
||||
SubcmdHandler GdbStub::Handlers_q[] = {
|
||||
{ .MainCmd = 'q', .SubStr = "HostInfo" , .Handler = GdbStub::Handle_q_HostInfo },
|
||||
{ .MainCmd = 'q', .SubStr = "ProcessInfo", .Handler = GdbStub::Handle_q_HostInfo },
|
||||
{ .MainCmd = 'q', .SubStr = "Rcmd," , .Handler = GdbStub::Handle_q_Rcmd },
|
||||
{ .MainCmd = 'q', .SubStr = "Supported:" , .Handler = GdbStub::Handle_q_Supported },
|
||||
{ .MainCmd = 'q', .SubStr = "CRC:" , .Handler = GdbStub::Handle_q_CRC },
|
||||
{ .MainCmd = 'q', .SubStr = "C" , .Handler = GdbStub::Handle_q_C },
|
||||
{ .MainCmd = 'q', .SubStr = "fThreadInfo", .Handler = GdbStub::Handle_q_fThreadInfo },
|
||||
{ .MainCmd = 'q', .SubStr = "sThreadInfo", .Handler = GdbStub::Handle_q_sThreadInfo },
|
||||
{ .MainCmd = 'q', .SubStr = "Attached" , .Handler = GdbStub::Handle_q_Attached },
|
||||
{ .MainCmd = 'q', .SubStr = "Xfer:features:read:target.xml:", .Handler = GdbStub::Handle_q_features },
|
||||
|
||||
{ .MainCmd = 'q', .SubStr = NULL, .Handler = NULL },
|
||||
};
|
||||
|
||||
SubcmdHandler GdbStub::Handlers_Q[] = {
|
||||
{ .MainCmd = 'Q', .SubStr = "StartNoAckMode", .Handler = GdbStub::Handle_Q_StartNoAckMode },
|
||||
|
||||
{ .MainCmd = 'Q', .SubStr = NULL, .Handler = NULL },
|
||||
};
|
||||
|
||||
ExecResult GdbStub::Handle_q(GdbStub* stub, const u8* cmd, ssize_t len)
|
||||
{
|
||||
return stub->SubcmdExec(cmd, len, Handlers_q);
|
||||
}
|
||||
|
||||
ExecResult GdbStub::Handle_v(GdbStub* stub, const u8* cmd, ssize_t len)
|
||||
{
|
||||
return stub->SubcmdExec(cmd, len, Handlers_v);
|
||||
}
|
||||
|
||||
ExecResult GdbStub::Handle_Q(GdbStub* stub, const u8* cmd, ssize_t len)
|
||||
{
|
||||
return stub->SubcmdExec(cmd, len, Handlers_Q);
|
||||
}
|
||||
|
||||
CmdHandler GdbStub::Handlers_top[] = {
|
||||
{ .Cmd = 'g', .Handler = GdbStub::Handle_g },
|
||||
{ .Cmd = 'G', .Handler = GdbStub::Handle_G },
|
||||
{ .Cmd = 'm', .Handler = GdbStub::Handle_m },
|
||||
{ .Cmd = 'M', .Handler = GdbStub::Handle_M },
|
||||
{ .Cmd = 'X', .Handler = GdbStub::Handle_X },
|
||||
{ .Cmd = 'c', .Handler = GdbStub::Handle_c },
|
||||
{ .Cmd = 's', .Handler = GdbStub::Handle_s },
|
||||
{ .Cmd = 'p', .Handler = GdbStub::Handle_p },
|
||||
{ .Cmd = 'P', .Handler = GdbStub::Handle_P },
|
||||
{ .Cmd = 'H', .Handler = GdbStub::Handle_H },
|
||||
{ .Cmd = 'T', .Handler = GdbStub::Handle_H },
|
||||
|
||||
{ .Cmd = '?', .Handler = GdbStub::Handle_Question },
|
||||
{ .Cmd = '!', .Handler = GdbStub::Handle_Exclamation },
|
||||
{ .Cmd = 'D', .Handler = GdbStub::Handle_D },
|
||||
{ .Cmd = 'r', .Handler = GdbStub::Handle_r },
|
||||
{ .Cmd = 'R', .Handler = GdbStub::Handle_R },
|
||||
{ .Cmd = 'k', .Handler = GdbStub::Handle_k },
|
||||
|
||||
{ .Cmd = 'z', .Handler = GdbStub::Handle_z },
|
||||
{ .Cmd = 'Z', .Handler = GdbStub::Handle_Z },
|
||||
|
||||
{ .Cmd = 'q', .Handler = GdbStub::Handle_q },
|
||||
{ .Cmd = 'v', .Handler = GdbStub::Handle_v },
|
||||
{ .Cmd = 'Q', .Handler = GdbStub::Handle_Q },
|
||||
|
||||
{ .Cmd = 0, .Handler = NULL }
|
||||
};
|
||||
|
||||
|
||||
StubState GdbStub::HandlePacket()
|
||||
{
|
||||
ExecResult r = CmdExec(Handlers_top);
|
||||
|
||||
if (r == ExecResult::MustBreak)
|
||||
{
|
||||
if (Stat == TgtStatus::None || Stat == TgtStatus::Running)
|
||||
Stat = TgtStatus::BreakReq;
|
||||
return StubState::Break;
|
||||
}
|
||||
else if (r == ExecResult::InitialBreak)
|
||||
{
|
||||
Stat = TgtStatus::BreakReq;
|
||||
return StubState::Attach;
|
||||
/*}
|
||||
else if (r == ExecResult::Detached)
|
||||
{
|
||||
Stat = TgtStatus::None;
|
||||
return StubState::Disconnect;*/
|
||||
}
|
||||
else if (r == ExecResult::Continue)
|
||||
{
|
||||
Stat = TgtStatus::Running;
|
||||
return StubState::Continue;
|
||||
}
|
||||
else if (r == ExecResult::Step)
|
||||
{
|
||||
return StubState::Step;
|
||||
}
|
||||
else if (r == ExecResult::Ok || r == ExecResult::UnkCmd)
|
||||
{
|
||||
return StubState::None;
|
||||
}
|
||||
else
|
||||
{
|
||||
Stat = TgtStatus::None;
|
||||
return StubState::Disconnect;
|
||||
}
|
||||
}
|
||||
|
||||
StubState GdbStub::Poll(bool wait)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (ConnFd <= 0)
|
||||
{
|
||||
SocketSetBlocking(SockFd, wait);
|
||||
|
||||
// not yet connected, so let's wait for one
|
||||
// nonblocking only done in part of read_packet(), so that it can still
|
||||
// quickly handle partly-received packets
|
||||
struct sockaddr_in* client = (struct sockaddr_in*)ClientSA;
|
||||
socklen_t len = sizeof(*client);
|
||||
#if MOCKTEST
|
||||
ConnFd = 0;
|
||||
#else
|
||||
#ifdef __linux__
|
||||
ConnFd = accept4(SockFd, (struct sockaddr*)client, &len, /*SOCK_NONBLOCK|*/SOCK_CLOEXEC);
|
||||
#else
|
||||
ConnFd = accept(SockFd, (struct sockaddr*)client, &len);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (ConnFd < 0) return StubState::NoConn;
|
||||
|
||||
u8 a;
|
||||
if (Proto::WaitAckBlocking(ConnFd, &a, 1000) < 0)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] inital handshake: didn't receive inital ack!\n");
|
||||
close(ConnFd);
|
||||
ConnFd = 0;
|
||||
return StubState::Disconnect;
|
||||
}
|
||||
|
||||
if (a != '+')
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] inital handshake: unexpected character '%c'!\n", a);
|
||||
}
|
||||
SendAck();
|
||||
|
||||
Stat = TgtStatus::Running; // on connected
|
||||
StatFlag = false;
|
||||
}
|
||||
|
||||
if (StatFlag)
|
||||
{
|
||||
StatFlag = false;
|
||||
//Log(LogLevel::Debug, "[GDB] STAT FLAG WAS TRUE\n");
|
||||
|
||||
Handle_Question(this, NULL, 0); // ugly hack but it should work
|
||||
}
|
||||
|
||||
#if MOCKTEST
|
||||
// nothing...
|
||||
#else
|
||||
#ifndef _WIN32
|
||||
struct pollfd pfd;
|
||||
pfd.fd = ConnFd;
|
||||
pfd.events = POLLIN;
|
||||
pfd.revents = 0;
|
||||
|
||||
r = poll(&pfd, 1, wait ? -1 : 0);
|
||||
|
||||
if (r == 0) return StubState::None; // nothing is happening
|
||||
|
||||
if (pfd.revents & (POLLHUP|POLLERR|POLLNVAL))
|
||||
{
|
||||
// oopsie, something happened
|
||||
Disconnect();
|
||||
return StubState::Disconnect;
|
||||
}
|
||||
#else
|
||||
fd_set infd, outfd, errfd;
|
||||
FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd);
|
||||
FD_SET(ConnFd, &infd);
|
||||
|
||||
struct timeval to;
|
||||
if (wait)
|
||||
{
|
||||
to.tv_sec = ~(time_t)0;
|
||||
to.tv_usec = ~(long)0;
|
||||
}
|
||||
else
|
||||
{
|
||||
to.tv_sec = 0;
|
||||
to.tv_usec = 0;
|
||||
}
|
||||
|
||||
r = select(ConnFd+1, &infd, &outfd, &errfd, &to);
|
||||
|
||||
if (FD_ISSET(ConnFd, &errfd))
|
||||
{
|
||||
Disconnect();
|
||||
return StubState::Disconnect;
|
||||
}
|
||||
else if (!FD_ISSET(ConnFd, &infd))
|
||||
{
|
||||
return StubState::None;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
ReadResult res = Proto::MsgRecv(ConnFd, Cmdbuf);
|
||||
|
||||
switch (res)
|
||||
{
|
||||
case ReadResult::NoPacket:
|
||||
return StubState::None;
|
||||
case ReadResult::Break:
|
||||
return StubState::Break;
|
||||
case ReadResult::Wut:
|
||||
Log(LogLevel::Info, "[GDB] WUT\n");
|
||||
case_gdbp_eof:
|
||||
case ReadResult::Eof:
|
||||
Log(LogLevel::Info, "[GDB] EOF!\n");
|
||||
close(ConnFd);
|
||||
ConnFd = 0;
|
||||
return StubState::Disconnect;
|
||||
case ReadResult::CksumErr:
|
||||
Log(LogLevel::Info, "[GDB] checksum err!\n");
|
||||
if (SendNak() < 0) {
|
||||
Log(LogLevel::Error, "[GDB] send nak after cksum fail errored!\n");
|
||||
goto case_gdbp_eof;
|
||||
}
|
||||
return StubState::None;
|
||||
case ReadResult::CmdRecvd:
|
||||
/*if (SendAck() < 0) {
|
||||
Log(LogLevel::Error, "[GDB] send packet ack failed!\n");
|
||||
goto case_gdbp_eof;
|
||||
}*/
|
||||
break;
|
||||
}
|
||||
|
||||
return HandlePacket();
|
||||
}
|
||||
|
||||
ExecResult GdbStub::SubcmdExec(const u8* cmd, ssize_t len, const SubcmdHandler* handlers)
|
||||
{
|
||||
//Log(LogLevel::Debug, "[GDB] subcommand in: '%s'\n", cmd);
|
||||
|
||||
for (size_t i = 0; handlers[i].Handler != NULL; ++i) {
|
||||
// check if prefix matches
|
||||
if (!strncmp((const char*)cmd, handlers[i].SubStr, strlen(handlers[i].SubStr)))
|
||||
{
|
||||
if (SendAck() < 0)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] send packet ack failed!\n");
|
||||
return ExecResult::NetErr;
|
||||
}
|
||||
return handlers[i].Handler(this, &cmd[strlen(handlers[i].SubStr)], len-strlen(handlers[i].SubStr));
|
||||
}
|
||||
}
|
||||
|
||||
Log(LogLevel::Info, "[GDB] unknown subcommand '%s'!\n", cmd);
|
||||
/*if (SendNak() < 0)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] send nak after cksum fail errored!\n");
|
||||
return ExecResult::NetErr;
|
||||
}*/
|
||||
//Resp("E99");
|
||||
Resp(NULL, 0);
|
||||
return ExecResult::UnkCmd;
|
||||
}
|
||||
|
||||
ExecResult GdbStub::CmdExec(const CmdHandler* handlers)
|
||||
{
|
||||
Log(LogLevel::Debug, "[GDB] command in: '%s'\n", Cmdbuf);
|
||||
|
||||
for (size_t i = 0; handlers[i].Handler != NULL; ++i)
|
||||
{
|
||||
if (handlers[i].Cmd == Cmdbuf[0])
|
||||
{
|
||||
if (SendAck() < 0)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] send packet ack failed!\n");
|
||||
return ExecResult::NetErr;
|
||||
}
|
||||
return handlers[i].Handler(this, &Cmdbuf[1], Cmdlen-1);
|
||||
}
|
||||
}
|
||||
|
||||
Log(LogLevel::Info, "[GDB] unknown command '%c'!\n", Cmdbuf[0]);
|
||||
/*if (SendNak() < 0)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] send nak after cksum fail errored!\n");
|
||||
return ExecResult::NetErr;
|
||||
}*/
|
||||
//RespStr("E99");
|
||||
Resp(NULL, 0);
|
||||
return ExecResult::UnkCmd;
|
||||
}
|
||||
|
||||
|
||||
void GdbStub::SignalStatus(TgtStatus stat, u32 arg)
|
||||
{
|
||||
//Log(LogLevel::Debug, "[GDB] SIGNAL STATUS %d!\n", stat);
|
||||
|
||||
this->Stat = stat;
|
||||
StatFlag = true;
|
||||
|
||||
if (stat == TgtStatus::Bkpt) CurBkpt = arg;
|
||||
else if (stat == TgtStatus::Watchpt) CurWatchpt = arg;
|
||||
}
|
||||
|
||||
|
||||
StubState GdbStub::Enter(bool stay, TgtStatus stat, u32 arg, bool wait_for_conn)
|
||||
{
|
||||
if (stat != TgtStatus::NoEvent) SignalStatus(stat, arg);
|
||||
|
||||
StubState st;
|
||||
bool do_next = true;
|
||||
do
|
||||
{
|
||||
bool was_conn = ConnFd > 0;
|
||||
st = Poll(wait_for_conn);
|
||||
bool has_conn = ConnFd > 0;
|
||||
|
||||
if (has_conn && !was_conn) stay = true;
|
||||
|
||||
switch (st)
|
||||
{
|
||||
case StubState::Break:
|
||||
Log(LogLevel::Info, "[GDB] break execution\n");
|
||||
SignalStatus(TgtStatus::BreakReq, ~(u32)0);
|
||||
break;
|
||||
case StubState::Continue:
|
||||
Log(LogLevel::Info, "[GDB] continue execution\n");
|
||||
do_next = false;
|
||||
break;
|
||||
case StubState::Step:
|
||||
Log(LogLevel::Info, "[GDB] single-step\n");
|
||||
do_next = false;
|
||||
break;
|
||||
case StubState::Disconnect:
|
||||
Log(LogLevel::Info, "[GDB] disconnect\n");
|
||||
SignalStatus(TgtStatus::None, ~(u32)0);
|
||||
do_next = false;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
while (do_next && stay);
|
||||
|
||||
if (st != StubState::None && st != StubState::NoConn)
|
||||
{
|
||||
Log(LogLevel::Debug, "[GDB] enter exit: %d\n", st);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
void GdbStub::AddBkpt(u32 addr, int kind)
|
||||
{
|
||||
BpWp np;
|
||||
np.addr = addr ^ (addr & 1); // clear lowest bit to not break on thumb mode weirdnesses
|
||||
np.len = 0;
|
||||
np.kind = kind;
|
||||
|
||||
{
|
||||
// already in the map
|
||||
auto search = BpList.find(np.addr);
|
||||
if (search != BpList.end()) return;
|
||||
}
|
||||
|
||||
BpList.insert({np.addr, np});
|
||||
|
||||
Log(LogLevel::Debug, "[GDB] added bkpt:\n");
|
||||
size_t i = 0;
|
||||
for (auto search = BpList.begin(); search != BpList.end(); ++search, ++i)
|
||||
{
|
||||
Log(LogLevel::Debug, "\t[%zu]: addr=%08x, kind=%d\n", i, search->first, search->second.kind);
|
||||
}
|
||||
}
|
||||
void GdbStub::AddWatchpt(u32 addr, u32 len, int kind)
|
||||
{
|
||||
BpWp np;
|
||||
np.addr = addr;
|
||||
np.len = len;
|
||||
np.kind = kind;
|
||||
|
||||
for (auto search = WpList.begin(); search != WpList.end(); ++search)
|
||||
{
|
||||
if (search->addr > addr)
|
||||
{
|
||||
WpList.insert(search, np);
|
||||
return;
|
||||
}
|
||||
else if (search->addr == addr && search->kind == kind)
|
||||
{
|
||||
if (search->len < len) search->len = len;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WpList.push_back(np);
|
||||
}
|
||||
|
||||
void GdbStub::DelBkpt(u32 addr, int kind)
|
||||
{
|
||||
addr = addr ^ (addr & 1);
|
||||
|
||||
auto search = BpList.find(addr);
|
||||
if (search != BpList.end())
|
||||
{
|
||||
BpList.erase(search);
|
||||
}
|
||||
}
|
||||
void GdbStub::DelWatchpt(u32 addr, u32 len, int kind)
|
||||
{
|
||||
(void)len; (void)kind;
|
||||
|
||||
for (auto search = WpList.begin(); search != WpList.end(); ++search)
|
||||
{
|
||||
if (search->addr == addr && search->kind == kind)
|
||||
{
|
||||
WpList.erase(search);
|
||||
return;
|
||||
}
|
||||
else if (search->addr > addr) return;
|
||||
}
|
||||
}
|
||||
|
||||
void GdbStub::DelAllBpWp()
|
||||
{
|
||||
BpList.erase(BpList.begin(), BpList.end());
|
||||
WpList.erase(WpList.begin(), WpList.end());
|
||||
}
|
||||
|
||||
StubState GdbStub::CheckBkpt(u32 addr, bool enter, bool stay)
|
||||
{
|
||||
addr ^= (addr & 1); // clear lowest bit to not break on thumb mode weirdnesses
|
||||
|
||||
auto search = BpList.find(addr);
|
||||
if (search == BpList.end()) return StubState::CheckNoHit;
|
||||
|
||||
if (enter)
|
||||
{
|
||||
StubState r = Enter(stay, TgtStatus::Bkpt, addr);
|
||||
Log(LogLevel::Debug, "[GDB] ENTER st=%d\n", r);
|
||||
return r;
|
||||
}
|
||||
else
|
||||
{
|
||||
SignalStatus(TgtStatus::Bkpt, addr);
|
||||
return StubState::None;
|
||||
}
|
||||
}
|
||||
StubState GdbStub::CheckWatchpt(u32 addr, int kind, bool enter, bool stay)
|
||||
{
|
||||
for (auto search = WpList.begin(); search != WpList.end(); ++search)
|
||||
{
|
||||
if (search->addr > addr) break;
|
||||
|
||||
if (addr >= search->addr && addr < search->addr + search->len && search->kind == kind)
|
||||
{
|
||||
if (enter) return Enter(stay, TgtStatus::Watchpt, addr);
|
||||
else
|
||||
{
|
||||
SignalStatus(TgtStatus::Watchpt, addr);
|
||||
return StubState::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return StubState::CheckNoHit;
|
||||
}
|
||||
|
||||
int GdbStub::SendAck()
|
||||
{
|
||||
if (NoAck) return 1;
|
||||
return Proto::SendAck(ConnFd);
|
||||
}
|
||||
int GdbStub::SendNak()
|
||||
{
|
||||
if (NoAck) return 1;
|
||||
return Proto::SendNak(ConnFd);
|
||||
}
|
||||
|
||||
int GdbStub::Resp(const u8* data1, size_t len1, const u8* data2, size_t len2)
|
||||
{
|
||||
return Proto::Resp(ConnFd, data1, len1, data2, len2, NoAck);
|
||||
}
|
||||
int GdbStub::RespC(const char* data1, size_t len1, const u8* data2, size_t len2)
|
||||
{
|
||||
return Proto::Resp(ConnFd, (const u8*)data1, len1, data2, len2, NoAck);
|
||||
}
|
||||
#if defined(__GCC__) || defined(__clang__)
|
||||
__attribute__((__format__(printf, 2/*includes implicit this*/, 3)))
|
||||
#endif
|
||||
int GdbStub::RespFmt(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int r = vsnprintf((char*)&Proto::RespBuf[1], sizeof(Proto::RespBuf)-5, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (r < 0) return r;
|
||||
|
||||
if ((size_t)r >= sizeof(Proto::RespBuf)-5)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] truncated response in send_fmt()! (lost %zd bytes)\n",
|
||||
(ssize_t)r - (ssize_t)(sizeof(Proto::RespBuf)-5));
|
||||
r = sizeof(Proto::RespBuf)-5;
|
||||
}
|
||||
|
||||
return Resp(&Proto::RespBuf[1], r);
|
||||
}
|
||||
|
||||
int GdbStub::RespStr(const char* str)
|
||||
{
|
||||
return Resp((const u8*)str, strlen(str));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
|
||||
#ifndef GDBSTUB_H_
|
||||
#define GDBSTUB_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
#include "GdbArch.h"
|
||||
|
||||
namespace Gdb
|
||||
{
|
||||
|
||||
enum class TgtStatus
|
||||
{
|
||||
NoEvent,
|
||||
|
||||
None,
|
||||
Running,
|
||||
SingleStep,
|
||||
BreakReq, // "break" command from gdb client
|
||||
Bkpt,
|
||||
Watchpt,
|
||||
BkptInsn, // "bkpt" instruction
|
||||
FaultData, // data abort
|
||||
FaultIAcc, // instruction fetch abort
|
||||
FaultInsn, // illegal instruction
|
||||
};
|
||||
|
||||
class StubCallbacks
|
||||
{
|
||||
public:
|
||||
StubCallbacks(){}
|
||||
virtual ~StubCallbacks(){};
|
||||
|
||||
virtual int GetCPU() const = 0; // 7 or 9 (currently, maybe also xtensa in the future?)
|
||||
|
||||
// 0..14: as usual
|
||||
// 15: pc *pipeline-corrected*
|
||||
// 16: cpsr
|
||||
virtual u32 ReadReg (Register reg) = 0;
|
||||
virtual void WriteReg(Register reg, u32 value) = 0;
|
||||
|
||||
virtual u32 ReadMem (u32 addr, int len) = 0;
|
||||
virtual void WriteMem(u32 addr, int len, u32 value) = 0;
|
||||
|
||||
virtual void ResetGdb() = 0;
|
||||
virtual int RemoteCmd(const u8* cmd, size_t len) = 0;
|
||||
};
|
||||
|
||||
enum class StubState
|
||||
{
|
||||
NoConn,
|
||||
None,
|
||||
Break,
|
||||
Continue,
|
||||
Step,
|
||||
Disconnect,
|
||||
Attach,
|
||||
CheckNoHit
|
||||
};
|
||||
|
||||
enum class ReadResult
|
||||
{
|
||||
NoPacket,
|
||||
Eof,
|
||||
CksumErr,
|
||||
CmdRecvd,
|
||||
Wut,
|
||||
Break
|
||||
};
|
||||
|
||||
enum class ExecResult
|
||||
{
|
||||
Ok,
|
||||
UnkCmd,
|
||||
NetErr,
|
||||
InitialBreak,
|
||||
MustBreak,
|
||||
Detached,
|
||||
Step,
|
||||
Continue
|
||||
};
|
||||
|
||||
class GdbStub;
|
||||
|
||||
typedef ExecResult (*GdbProtoCmd)(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
|
||||
struct SubcmdHandler
|
||||
{
|
||||
char MainCmd;
|
||||
const char* SubStr;
|
||||
GdbProtoCmd Handler;
|
||||
};
|
||||
|
||||
struct CmdHandler
|
||||
{
|
||||
char Cmd;
|
||||
GdbProtoCmd Handler;
|
||||
};
|
||||
|
||||
class GdbStub
|
||||
{
|
||||
public:
|
||||
struct BpWp
|
||||
{
|
||||
public:
|
||||
u32 addr, len;
|
||||
int kind;
|
||||
};
|
||||
|
||||
GdbStub(StubCallbacks* cb, int port);
|
||||
~GdbStub();
|
||||
|
||||
bool Init();
|
||||
void Close();
|
||||
|
||||
StubState Poll(bool wait = false);
|
||||
void SignalStatus(TgtStatus stat, u32 arg);
|
||||
StubState Enter(bool stay, TgtStatus stat=TgtStatus::NoEvent, u32 arg=~(u32)0u, bool wait_for_conn=false);
|
||||
|
||||
// kind: 2=thumb, 3=thumb2 (not relevant), 4=arm
|
||||
void AddBkpt(u32 addr, int kind);
|
||||
void DelBkpt(u32 addr, int kind);
|
||||
// kind: 2=read, 3=write, 4=rdwr
|
||||
void AddWatchpt(u32 addr, u32 len, int kind);
|
||||
void DelWatchpt(u32 addr, u32 len, int kind);
|
||||
|
||||
void DelAllBpWp();
|
||||
|
||||
StubState CheckBkpt(u32 addr, bool enter, bool stay);
|
||||
StubState CheckWatchpt(u32 addr, int kind, bool enter, bool stay);
|
||||
|
||||
#include "GdbCmds.h"
|
||||
|
||||
Gdb::ExecResult SubcmdExec(const u8* cmd, ssize_t len, const SubcmdHandler* handlers);
|
||||
Gdb::ExecResult CmdExec(const CmdHandler* handlers);
|
||||
|
||||
public:
|
||||
int SendAck();
|
||||
int SendNak();
|
||||
|
||||
int Resp(const u8* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0);
|
||||
int RespC(const char* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0);
|
||||
#if defined(__GCC__) || defined(__clang__)
|
||||
__attribute__((__format__(printf, 2, 3)))
|
||||
#endif
|
||||
int RespFmt(const char* fmt, ...);
|
||||
|
||||
int RespStr(const char* str);
|
||||
|
||||
private:
|
||||
void Disconnect();
|
||||
StubState HandlePacket();
|
||||
|
||||
private:
|
||||
StubCallbacks* Cb;
|
||||
|
||||
//struct sockaddr_in server, client;
|
||||
void *ServerSA, *ClientSA;
|
||||
int Port;
|
||||
int SockFd;
|
||||
int ConnFd;
|
||||
|
||||
TgtStatus Stat;
|
||||
u32 CurBkpt, CurWatchpt;
|
||||
bool StatFlag;
|
||||
bool NoAck;
|
||||
|
||||
std::map<u32, BpWp> BpList;
|
||||
std::vector<BpWp> WpList;
|
||||
|
||||
static SubcmdHandler Handlers_v[];
|
||||
static SubcmdHandler Handlers_q[];
|
||||
static SubcmdHandler Handlers_Q[];
|
||||
static CmdHandler Handlers_top[];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
obj/
|
||||
test-gdb
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
default: all
|
||||
|
||||
all: test-gdb
|
||||
|
||||
CPPFLAGS += -Werror=implicit-function-declaration -Werror=int-conversion \
|
||||
-Werror=return-type -Werror=uninitialized \
|
||||
-I../ -I../../ -Og -g -Wall \
|
||||
-Wno-switch -Wno-pointer-sign
|
||||
|
||||
obj/:
|
||||
@mkdir -vp "$@"
|
||||
|
||||
test-gdb: obj/GdbProto.o obj/GdbStub.o obj/GdbCmds.o obj/main.o obj/CRC32.o
|
||||
$(CXX) $(CPPFLAGS) $(LDFLAGS) -o "$@" $^
|
||||
|
||||
obj/Gdb%.o: ../Gdb%.cpp obj/
|
||||
$(CXX) $(CPPFLAGS) -c -o "$@" "$<"
|
||||
|
||||
obj/main.o: main.cpp obj/
|
||||
$(CXX) $(CPPFLAGS) -c -o "$@" "$<"
|
||||
|
||||
obj/CRC32.o: ../../CRC32.cpp obj/
|
||||
$(CXX) $(CPPFLAGS) -c -o "$@" "$<"
|
||||
|
||||
clean:
|
||||
@$(RM) -rv obj/ test-gdb
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "GdbStub.h"
|
||||
#include "Platform.h"
|
||||
|
||||
class Debug : public Gdb::StubCallbacks
|
||||
{
|
||||
public:
|
||||
Debug(){}
|
||||
~Debug(){}
|
||||
|
||||
int GetCPU() const override { return 9; }
|
||||
|
||||
u32 ReadReg(Gdb::Register reg) override
|
||||
{
|
||||
printf("[==>] read reg %d\n", (int)reg);
|
||||
if (reg == Gdb::Register::pc) return 0x000000df; // cpsr: irq,fiq disabled, arm, sys mode
|
||||
else return 0x69420;
|
||||
}
|
||||
void WriteReg(Gdb::Register reg, u32 value) override
|
||||
{
|
||||
printf("[==>] write reg %d: 0x%08x\n", (int)reg, value);
|
||||
}
|
||||
|
||||
u32 ReadMem(u32 addr, int len) override
|
||||
{
|
||||
static const u32 words[] = {
|
||||
0xeafffffe,
|
||||
0xe0211002,
|
||||
0xe12fff1e,
|
||||
0
|
||||
};
|
||||
|
||||
printf("[==>] read mem 0x%08x (size %u)\n", addr, len);
|
||||
|
||||
// $: b $ (arm)
|
||||
return words[(addr>>2)&3] & ((1uLL<<len)-1);
|
||||
}
|
||||
void WriteMem(u32 addr, int len, u32 value) override
|
||||
{
|
||||
printf("[==>] write addr 0x%08x (size %u): 0x%08x\n", addr, len, value);
|
||||
}
|
||||
|
||||
void ResetGdb() override
|
||||
{
|
||||
printf("[==>] RESET!!!\n");
|
||||
}
|
||||
int RemoteCmd(const u8* cmd, size_t len) override
|
||||
{
|
||||
printf("[==>] Rcmd: %s\n", cmd);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
Debug debug;
|
||||
|
||||
Gdb::GdbStub stub(&debug, (argc > 1) ? atoi(argv[1]) : 3333);
|
||||
if (!stub.Init()) return 1;
|
||||
|
||||
do
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Gdb::StubState s = stub.Poll();
|
||||
|
||||
if (s == Gdb::StubState::None || s == Gdb::StubState::NoConn)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 1000*1000; // 1 ms
|
||||
nanosleep(&ts, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (s)
|
||||
{
|
||||
case Gdb::StubState::Attach:
|
||||
printf("[==>] attached\n");
|
||||
break;
|
||||
case Gdb::StubState::Break:
|
||||
printf("[==>] break execution\n");
|
||||
stub.SignalStatus(Gdb::TgtStatus::BreakReq, ~(u32)0);
|
||||
break;
|
||||
case Gdb::StubState::Continue:
|
||||
printf("[==>] continue execution\n");
|
||||
// TODO: send signal status on SIGSTOP? eh.
|
||||
break;
|
||||
case Gdb::StubState::Step:
|
||||
printf("[==>] single-step\n");
|
||||
stub.SignalStatus(Gdb::TgtStatus::SingleStep, ~(u32)0);
|
||||
break;
|
||||
case Gdb::StubState::Disconnect:
|
||||
printf("[==>] disconnect\n");
|
||||
stub.SignalStatus(Gdb::TgtStatus::None, ~(u32)0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (s == Gdb::StubState::Disconnect) break;
|
||||
}
|
||||
}
|
||||
while (false);
|
||||
|
||||
stub.Close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
void Log(LogLevel level, const char* fmt, ...)
|
||||
{
|
||||
if (fmt == nullptr) return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
|
||||
#ifndef HEXUTIL_GDB_H_
|
||||
#define HEXUTIL_GDB_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
inline static uint8_t hex2nyb(uint8_t v)
|
||||
{
|
||||
if (v >= '0' && v <= '9') return v - '0';
|
||||
else if (v >= 'A' && v <= 'F') return v - 'A' + 0xa;
|
||||
else if (v >= 'a' && v <= 'f') return v - 'a' + 0xa;
|
||||
else
|
||||
{
|
||||
__builtin_trap();
|
||||
return 0xcc;
|
||||
}
|
||||
}
|
||||
inline static uint8_t nyb2hex(uint8_t v)
|
||||
{
|
||||
v &= 0xf;
|
||||
if (v >= 0xa) return v - 0xa + 'a';
|
||||
else return v - 0x0 + '0';
|
||||
}
|
||||
|
||||
inline static void hexfmt8(uint8_t* dst, uint8_t v)
|
||||
{
|
||||
dst[0] = nyb2hex(v>>4);
|
||||
dst[1] = nyb2hex(v>>0);
|
||||
}
|
||||
inline static uint8_t unhex8(const uint8_t* src)
|
||||
{
|
||||
return (hex2nyb(src[0]) << 4) | hex2nyb(src[1]);
|
||||
}
|
||||
|
||||
inline static void hexfmt16(uint8_t* dst, uint16_t v)
|
||||
{
|
||||
dst[0] = nyb2hex(v>> 4);
|
||||
dst[1] = nyb2hex(v>> 0);
|
||||
dst[2] = nyb2hex(v>>12);
|
||||
dst[3] = nyb2hex(v>> 8);
|
||||
}
|
||||
inline static uint16_t unhex16(const uint8_t* src)
|
||||
{
|
||||
return unhex8(&src[0*2]) | ((uint16_t)unhex8(&src[1*2]) << 8);
|
||||
}
|
||||
|
||||
inline static void hexfmt32(uint8_t* dst, uint32_t v)
|
||||
{
|
||||
for (size_t i = 0; i < 4; ++i, v >>= 8)
|
||||
{
|
||||
dst[2*i+0] = nyb2hex(v>>4);
|
||||
dst[2*i+1] = nyb2hex(v>>0);
|
||||
}
|
||||
}
|
||||
inline static uint32_t unhex32(const uint8_t* src)
|
||||
{
|
||||
uint32_t v = 0;
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
{
|
||||
v |= (uint32_t)unhex8(&src[i*2]) << (i*8);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -139,6 +139,7 @@ else()
|
|||
)
|
||||
target_link_libraries(melonDS PRIVATE "${X11_LIBRARIES}" "${EGL_LIBRARIES}")
|
||||
target_include_directories(melonDS PRIVATE "${X11_INCLUDE_DIR}")
|
||||
add_compile_definitions(QAPPLICATION_CLASS=QApplication)
|
||||
endif()
|
||||
|
||||
|
||||
|
|
|
@ -146,6 +146,14 @@ bool DSiBatteryCharging;
|
|||
|
||||
bool DSiFullBIOSBoot;
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
bool GdbEnabled;
|
||||
int GdbPortARM7;
|
||||
int GdbPortARM9;
|
||||
bool GdbARM7BreakOnStartup;
|
||||
bool GdbARM9BreakOnStartup;
|
||||
#endif
|
||||
|
||||
CameraConfig Camera[2];
|
||||
|
||||
|
||||
|
@ -337,6 +345,14 @@ ConfigEntry ConfigFile[] =
|
|||
|
||||
{"DSiFullBIOSBoot", 1, &DSiFullBIOSBoot, false, true},
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
{"GdbEnabled", 1, &GdbEnabled, false, false},
|
||||
{"GdbPortARM7", 0, &GdbPortARM7, 3334, true},
|
||||
{"GdbPortARM9", 0, &GdbPortARM9, 3333, true},
|
||||
{"GdbARM7BreakOnStartup", 1, &GdbARM7BreakOnStartup, false, true},
|
||||
{"GdbARM9BreakOnStartup", 1, &GdbARM9BreakOnStartup, false, true},
|
||||
#endif
|
||||
|
||||
// TODO!!
|
||||
// we need a more elegant way to deal with this
|
||||
{"Camera0_InputType", 0, &Camera[0].InputType, 0, false},
|
||||
|
|
|
@ -193,6 +193,12 @@ extern bool DSiFullBIOSBoot;
|
|||
|
||||
extern CameraConfig Camera[2];
|
||||
|
||||
extern bool GdbEnabled;
|
||||
extern int GdbPortARM7;
|
||||
extern int GdbPortARM9;
|
||||
extern bool GdbARM7BreakOnStartup;
|
||||
extern bool GdbARM9BreakOnStartup;
|
||||
|
||||
|
||||
void Load();
|
||||
void Save();
|
||||
|
|
|
@ -89,7 +89,22 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new
|
|||
ui->spnJITMaximumBlockSize->setDisabled(true);
|
||||
#endif
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
ui->cbGdbEnabled->setChecked(Config::GdbEnabled);
|
||||
ui->intGdbPortA7->setValue(Config::GdbPortARM7);
|
||||
ui->intGdbPortA9->setValue(Config::GdbPortARM9);
|
||||
ui->cbGdbBOSA7->setChecked(Config::GdbARM7BreakOnStartup);
|
||||
ui->cbGdbBOSA9->setChecked(Config::GdbARM9BreakOnStartup);
|
||||
#else
|
||||
ui->cbGdbEnabled->setDisabled(true);
|
||||
ui->intGdbPortA7->setDisabled(true);
|
||||
ui->intGdbPortA9->setDisabled(true);
|
||||
ui->cbGdbBOSA7->setDisabled(true);
|
||||
ui->cbGdbBOSA9->setDisabled(true);
|
||||
#endif
|
||||
|
||||
on_chkEnableJIT_toggled();
|
||||
on_cbGdbEnabled_toggled();
|
||||
on_chkExternalBIOS_toggled();
|
||||
|
||||
const int imgsizes[] = {256, 512, 1024, 2048, 4096, 0};
|
||||
|
@ -223,6 +238,12 @@ void EmuSettingsDialog::done(int r)
|
|||
bool dsiSDFolderSync = ui->cbDSiSDFolder->isChecked();
|
||||
std::string dsiSDFolderPath = ui->txtDSiSDFolder->text().toStdString();
|
||||
|
||||
bool gdbEnabled = ui->cbGdbEnabled->isChecked();
|
||||
int gdbPortA7 = ui->intGdbPortA7->value();
|
||||
int gdbPortA9 = ui->intGdbPortA9->value();
|
||||
bool gdbBOSA7 = ui->cbGdbBOSA7->isChecked();
|
||||
bool gdbBOSA9 = ui->cbGdbBOSA9->isChecked();
|
||||
|
||||
if (consoleType != Config::ConsoleType
|
||||
|| directBoot != Config::DirectBoot
|
||||
#ifdef JIT_ENABLED
|
||||
|
@ -231,6 +252,13 @@ void EmuSettingsDialog::done(int r)
|
|||
|| jitBranchOptimisations != Config::JIT_BranchOptimisations
|
||||
|| jitLiteralOptimisations != Config::JIT_LiteralOptimisations
|
||||
|| jitFastMemory != Config::JIT_FastMemory
|
||||
#endif
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
|| gdbEnabled != Config::GdbEnabled
|
||||
|| gdbPortA7 != Config::GdbPortARM7
|
||||
|| gdbPortA9 != Config::GdbPortARM9
|
||||
|| gdbBOSA7 != Config::GdbARM7BreakOnStartup
|
||||
|| gdbBOSA9 != Config::GdbARM9BreakOnStartup
|
||||
#endif
|
||||
|| externalBiosEnable != Config::ExternalBIOSEnable
|
||||
|| bios9Path != Config::BIOS9Path
|
||||
|
@ -285,13 +313,20 @@ void EmuSettingsDialog::done(int r)
|
|||
Config::DSiSDFolderSync = dsiSDFolderSync;
|
||||
Config::DSiSDFolderPath = dsiSDFolderPath;
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
#ifdef JIT_ENABLED
|
||||
Config::JIT_Enable = jitEnable;
|
||||
Config::JIT_MaxBlockSize = jitMaxBlockSize;
|
||||
Config::JIT_BranchOptimisations = jitBranchOptimisations;
|
||||
Config::JIT_LiteralOptimisations = jitLiteralOptimisations;
|
||||
Config::JIT_FastMemory = jitFastMemory;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
Config::GdbEnabled = gdbEnabled;
|
||||
Config::GdbPortARM7 = gdbPortA7;
|
||||
Config::GdbPortARM9 = gdbPortA9;
|
||||
Config::GdbARM7BreakOnStartup = gdbBOSA7;
|
||||
Config::GdbARM9BreakOnStartup = gdbBOSA9;
|
||||
#endif
|
||||
|
||||
Config::ConsoleType = consoleType;
|
||||
Config::DirectBoot = directBoot;
|
||||
|
@ -506,6 +541,31 @@ void EmuSettingsDialog::on_chkEnableJIT_toggled()
|
|||
ui->chkJITFastMemory->setDisabled(disabled);
|
||||
#endif
|
||||
ui->spnJITMaximumBlockSize->setDisabled(disabled);
|
||||
|
||||
on_cbGdbEnabled_toggled();
|
||||
}
|
||||
|
||||
void EmuSettingsDialog::on_cbGdbEnabled_toggled()
|
||||
{
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
bool disabled = !ui->cbGdbEnabled->isChecked();
|
||||
bool jitenable = ui->chkEnableJIT->isChecked();
|
||||
|
||||
if (jitenable && !disabled) {
|
||||
ui->cbGdbEnabled->setChecked(false);
|
||||
disabled = true;
|
||||
}
|
||||
#else
|
||||
bool disabled = true;
|
||||
bool jitenable = true;
|
||||
ui->cbGdbEnabled->setChecked(false);
|
||||
#endif
|
||||
|
||||
ui->cbGdbEnabled->setDisabled(jitenable);
|
||||
ui->intGdbPortA7->setDisabled(disabled);
|
||||
ui->intGdbPortA9->setDisabled(disabled);
|
||||
ui->cbGdbBOSA7->setDisabled(disabled);
|
||||
ui->cbGdbBOSA9->setDisabled(disabled);
|
||||
}
|
||||
|
||||
void EmuSettingsDialog::on_chkExternalBIOS_toggled()
|
||||
|
|
|
@ -77,6 +77,8 @@ private slots:
|
|||
void on_chkEnableJIT_toggled();
|
||||
void on_chkExternalBIOS_toggled();
|
||||
|
||||
void on_cbGdbEnabled_toggled();
|
||||
|
||||
private:
|
||||
void verifyFirmware();
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>5</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
|
@ -568,6 +568,101 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_6">
|
||||
<attribute name="title">
|
||||
<string>Devtools</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_16">
|
||||
<property name="text">
|
||||
<string>ARM9 port</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
<string>ARM7 port</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="cbGdbEnabled">
|
||||
<property name="text">
|
||||
<string>Enable GDB stub</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="7">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>Note: melonDS must be restarted in order for these changes to have effect</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="7">
|
||||
<widget class="QLabel" name="label_19">
|
||||
<property name="text">
|
||||
<string>Note: GDB stub cannot be used together with the JIT recompiler</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="6">
|
||||
<widget class="QCheckBox" name="cbGdbBOSA9">
|
||||
<property name="text">
|
||||
<string>Break on startup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="5">
|
||||
<widget class="QSpinBox" name="intGdbPortA9">
|
||||
<property name="minimum">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>3333</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="5">
|
||||
<widget class="QSpinBox" name="intGdbPortA7">
|
||||
<property name="minimum">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>3334</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="6">
|
||||
<widget class="QCheckBox" name="cbGdbBOSA7">
|
||||
<property name="text">
|
||||
<string>Break on startup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -590,7 +685,6 @@
|
|||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>tabWidget</tabstop>
|
||||
<tabstop>cbxConsoleType</tabstop>
|
||||
<tabstop>chkDirectBoot</tabstop>
|
||||
<tabstop>chkExternalBIOS</tabstop>
|
||||
|
@ -639,8 +733,8 @@
|
|||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>257</x>
|
||||
<y>349</y>
|
||||
<x>266</x>
|
||||
<y>379</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
|
@ -655,8 +749,8 @@
|
|||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>325</x>
|
||||
<y>349</y>
|
||||
<x>334</x>
|
||||
<y>379</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
|
|
|
@ -213,6 +213,11 @@ int GetConfigInt(ConfigEntry entry)
|
|||
case Firm_Color: return Config::FirmwareFavouriteColour;
|
||||
|
||||
case AudioBitDepth: return Config::AudioBitDepth;
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
case GdbPortARM7: return Config::GdbPortARM7;
|
||||
case GdbPortARM9: return Config::GdbPortARM9;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -241,6 +246,12 @@ bool GetConfigBool(ConfigEntry entry)
|
|||
|
||||
case Firm_OverrideSettings: return Config::FirmwareOverrideSettings != 0;
|
||||
case DSi_FullBIOSBoot: return Config::DSiFullBIOSBoot != 0;
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
case GdbEnabled: return Config::GdbEnabled;
|
||||
case GdbARM7BreakOnStartup: return Config::GdbARM7BreakOnStartup;
|
||||
case GdbARM9BreakOnStartup: return Config::GdbARM9BreakOnStartup;
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
Loading…
Reference in New Issue