Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GDB stub #1583

Merged
merged 28 commits into from
Oct 22, 2023
Merged

GDB stub #1583

merged 28 commits into from
Oct 22, 2023

Conversation

PoroCYon
Copy link
Contributor

@PoroCYon PoroCYon commented Dec 13, 2022

The GDB stub is a feature to add debug capabilities (using an external debugger like GDB) to melonDS, to reverse-engineer games and system software, or to help with making homebrew NDS/DSi code.

There's an option in the emu settings menu to enable the GDB stub, configure which ports it uses, and to optionally wait until a debugger is attached before starting the system. (Don't enable this last option for both CPU cores at once!)

The GDB stub can be disabled by setting the ENABLE_GDBSTUB CMake option to Off, it is on by default.

What works:

  • Register read/write
  • Memory read/write
  • Single-stepping
  • Code breakpoints

What doesn't work:

  • Loading a new binary through the debugger and executing it (you have to restart melonDS)
  • Data watchpoints
  • Sending over XML information about the memory map (cf. NWRAM mapping)

Oriignal PR comment below:

  • ARM9 port is 3333
  • ARM7 port is 3334

Marking this as WIP because I want more people to test it, to see if there's stuff they need that's not (properly) implemented yet, to see if they can find bugs, etc.

Currently, it's again behind a CMake option (ENABLE_GDBSTUB), and no UI options for enabling the stubs and configuring their ports is present (which should be added before this is merged).

@nathanfarlow
Copy link

This is amazing! Only tested out the ARM9 part. It works extremely well. With it, I was able to create a DSi browser exploit. Thank you for this!

One thing which I noticed is that reading memory is pretty slow, which makes searching in memory for things impossible. This is my first time interacting with ARM, but isn't the lsb of the pc register supposed to be 1 in thumb mode? The lsb was never set in thumb mode in my experience. Maybe I was supposed to look somewhere else to determine that? Setting the registers with set $r0 = 3 works, but only after a few seconds delay and a Ignoring packet error, continuing... message.

Again thanks for this addon. Couldn't have made the exploit without it.

@RSDuck
Copy link
Member

RSDuck commented Jan 10, 2023

This is my first time interacting with ARM, but isn't the lsb of the pc register supposed to be 1 in thumb mode?

no, now thinking about it, it's a bit confusing, but the bit is only mostly in return addresses to signal a mode switch. So it's put there by Thum blx in the link register and on ARMv5 all jumps will pick up this bit to switch to thumb mode. But it will always discard it afterwards. Whether the processor is currently in Thumb mode is stored though in CPSR bit 5.

@PoroCYon
Copy link
Contributor Author

One thing which I noticed is that reading memory is pretty slow, which makes searching in memory for things impossible.

Hm, if GDB sends these read packets as 4 bytes at a time (instead of an entire memory chunk), it could be that the event polling queue thingy is only processing one of those messages at a time, which could be slowing down the thing. There's a number of printf() statements commented out in gdbstub.c and gdbproto.c, could you uncomment these and give the output when you do a memory search?

This is my first time interacting with ARM, but isn't the lsb of the pc register supposed to be 1 in thumb mode?

Whether the processor is currently in Thumb mode is stored though in CPSR bit 5.

While what RSDuck says is true, I wouldn't be 100% sure that re-adding the bit isn't needed. For example, due to CPU pipelining, pc is always an instruction or two ahead compared to what GDB expects, so I have to correct for it, otherwise GDB gets things wrong. I do have a Cypress FX3 devboard here (with an ARM926EJ-S), which has a JTAG debugger so I'll check there whether it has to be set or not.

Setting the registers with set $r0 = 3 works, but only after a few seconds delay and a Ignoring packet error, continuing... message.

Hm, that's slightly weird. Can you also say which error exactly is happening here as well, when you uncomment the printf() statements in the code?

@PoroCYon
Copy link
Contributor Author

PoroCYon commented Jan 11, 2023

image

Looks like pc always has an even value in GDB on the FX3, so I guess the current behavior is fine.

Though, I should probably support all the mode-banked registers as well, that's currently not implemented. Sadly there's no way to get a good register index↔name linking afaik, so time to hunt through either the OpenOCD or the GDB sources, yaay...

EDIT: for future reference, for myself

@PoroCYon
Copy link
Contributor Author

Also note to self: currently, when trying to connect to the GDB stub with the JIT enabled, GDB will time out, and melonDS sometimes dies with a SIGILL(?).

Debugging and JIT are mutually exclusive, as the latter makes it impossible to single-step or to put breakpoints anywhere, and I don't see that changing anytime soon (unless @RSDuck wants to rearchitect the JIT, which I doubt.) So for now it's probably best to disable the GDB stubs completely when the JIT is enabled, and maybe emit a warning when someone tries to connect, instead of it crashing and burning without giving users a clue at all.

@SeleDreams
Copy link

I'm not too sure to understand how to use the gdb stub, like, are there CLI arguments to start it ? because i searched in the source it doesn't seem like it

@PoroCYon
Copy link
Contributor Author

No, currently it's always-on (which is also a thing that should change, ideally in the settings menu as well), as long as you pass the ENABLE_GDBSTUB cmake option.

@SeleDreams
Copy link

No, currently it's always-on (which is also a thing that should change, ideally in the settings menu as well), as long as you pass the ENABLE_GDBSTUB cmake option.

in this case what port is it using by default ? because i'd like to use it to debug homebrew i'm making but i'm not sure about how to go about it

@SeleDreams
Copy link

I found the port by looking at the source. 3333.
I feel like one thing that would be cool is the ability to wait for the debugger to attach before launching the nds file. this way it would be easier to catch issues happening at boot time

@PoroCYon
Copy link
Contributor Author

It's... literally the first thing I wrote in the description here: #1583 (comment)

I feel like one thing that would be cool is the ability to wait for the debugger to attach before launching the nds file. this way it would be easier to catch issues happening at boot time

Ah, alright, I'll put that in as an option when I get around to putting eg. the enable and port numbers in the option UI as well.

@patataofcourse
Copy link
Contributor

Something I just remembered- in my experience, reading from the GBA slot (whether ROM or save) always returned 0, any idea why that could be?

@SeleDreams
Copy link

SeleDreams commented Feb 10, 2023

It's... literally the first thing I wrote in the description here: #1583 (comment)

I feel like one thing that would be cool is the ability to wait for the debugger to attach before launching the nds file. this way it would be easier to catch issues happening at boot time

Ah, alright, I'll put that in as an option when I get around to putting eg. the enable and port numbers in the option UI as well.

don't mind me I spent the night working on some stuff and was half asleep while reading lol

@PoroCYon
Copy link
Contributor Author

Something I just remembered- in my experience, reading from the GBA slot (whether ROM or save) always returned 0, any idea why that could be?

From GDB or from regular code running in the emulator? The former needs some code duplication for things that don't go through the regular system bus (eg. the ARM9 TCMs), so maybe the GBA ROM is like that too. If the latter, no clue, sorry.

@axel7083
Copy link

axel7083 commented May 7, 2023

Does anyone was able to connect this to ghidra debugger ?

@PoroCYon PoroCYon force-pushed the gdbstub-standalone branch from cd15080 to 1f8d407 Compare July 8, 2023 21:37
@PoroCYon
Copy link
Contributor Author

PoroCYon commented Jul 9, 2023

Alright, so, updates! Finally!
I've also added support for listing all execution-mode-dependent CPU registers (with sp_svc and so on).

The GDB stub can now also be enabled (and the ports can be configured) in the emu settings window.

  • @nathanfarlow can you check whether you still get these errors? It looks like I forgot to send acks for the P (register set) command, that should be fixed now.
  • @patataofcourse can you also try the GBA cart thingy again? Because I'm not sure how that would happen (after I had fixed the TCMs not being mapped into memory issue, but that fix was done a long time ago already).
  • @axel7083 as long as it uses the GDB protocol it should work. Just try it. If it doesn't work, that's probably a bug on my end.

@PoroCYon PoroCYon marked this pull request as ready for review July 9, 2023 14:22
@PoroCYon
Copy link
Contributor Author

PoroCYon commented Jul 9, 2023

I feel like one thing that would be cool is the ability to wait for the debugger to attach before launching the nds file. this way it would be easier to catch issues happening at boot time

This is implemented now

@PoroCYon PoroCYon force-pushed the gdbstub-standalone branch from 481a2b1 to 2d9fc47 Compare July 9, 2023 17:34
@patataofcourse
Copy link
Contributor

patataofcourse commented Jul 9, 2023

@patataofcourse can you also try the GBA cart thingy again? Because I'm not sure how that would happen (after I had fixed the TCMs not being mapped into memory issue, but that fix was done a long time ago already).

works now! thank you

@jonko0493
Copy link

Tested on Windows connecting through VS code and haven't had any issues -- seems to work brilliantly. Thanks for putting this together!

Copy link
Member

@RSDuck RSDuck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only marked one instance of some stylistic things

src/ARM.h Outdated Show resolved Hide resolved
src/ARM.cpp Outdated Show resolved Hide resolved
src/debug/GdbArch.h Outdated Show resolved Hide resolved
src/debug/GdbProto.cpp Outdated Show resolved Hide resolved
src/ARM.cpp Outdated Show resolved Hide resolved
src/debug/GdbCmds.cpp Outdated Show resolved Hide resolved
src/debug/GdbProto.h Outdated Show resolved Hide resolved
src/debug/GdbStub.h Outdated Show resolved Hide resolved
@PoroCYon
Copy link
Contributor Author

PoroCYon commented Aug 21, 2023

Heh, looks like LLDB is also not quite standard-compliant in how it works (it's only supposed to send QStartNoAckMode after seeing that a response to qSupported advertises that the server does in fact support this, which my implementation currently doesn't). So I've opened an issue on their side. Though, in the meantime, I'll see if I can implement no-ack mode anyway.

EDIT: @turtleisaac pushed the 'fix', does it work now?

@turtleisaac
Copy link

turtleisaac commented Aug 21, 2023

Heh, looks like LLDB is also not quite standard-compliant in how it works (it's only supposed to send QStartNoAckMode after seeing that a response to qSupported advertises that the server does in fact support this, which my implementation currently doesn't). So I've opened an issue on their side. Though, in the meantime, I'll see if I can implement no-ack mode anyway.

EDIT: @turtleisaac pushed the 'fix', does it work now?

Pulled, rebuilt and gave it a shot. Unfortunately, melonDS crashes after I attempt the connection.

Game is now booting
--- dataoff=0
[GDB] recv() 1 bytes: '+' (2b)
[GDB] recv() 19 bytes: '$QStartNoAckMode#b0' (24)
[GDB] recv() after skipping: n=19, recv_total=19
[GDB] got pkt, checksum: b0 vs b0
[GDB] command in: 'QStartNoAckMode'
[GDB] send ack
[GDB] send ack
[GDB] send resp: '$OK#9a'
[GDB] send resp: '$OK#9a'
[GDB] send resp: '$OK#9a'
--- dataoff=0
[GDB] recv() 111 bytes: 'upported:xmlRegisters=i386,arm,mips,arc;multiprocess+#04+$QThreadSuffixSupported#e4+$QListThreadsInStopReply#21' (75)
zsh: bus error /Users/USER/Documents/melonDS/build/melonDS.app/Contents/MacOS/melonDS

Here's the Apple error report if you need it.

Process:               melonDS [26020]
Path:                  /Users/USER/Documents/*/melonDS.app/Contents/MacOS/melonDS
Identifier:            net.kuribo64.melonDS
Version:               0.9.5 (0.9.5)
Code Type:             ARM-64 (Native)
Parent Process:        zsh [7392]
Responsible:           Terminal [95029]
User ID:               502

Date/Time:             2023-08-21 14:44:24.3935 -0500
OS Version:            macOS 13.4 (22F66)
Report Version:        12
Anonymous UUID:        41FCB3B0-D5AA-8E08-C9E2-A14766F8C4F2

Sleep/Wake UUID:       F51B6370-E05E-491E-A6AD-5E0A79C3911F

Time Awake Since Boot: 250000 seconds
Time Since Wake:       10168 seconds

System Integrity Protection: enabled

Crashed Thread:        12  EmuThread

Exception Type:        EXC_BAD_ACCESS (SIGBUS)
Exception Codes:       KERN_PROTECTION_FAILURE at 0x0000000103183fe0
Exception Codes:       0x0000000000000002, 0x0000000103183fe0

Termination Reason:    Namespace SIGNAL, Code 10 Bus error: 10
Terminating Process:   exc handler [26020]

VM Region Info: 0x103183fe0 is in 0x103168000-0x103184000;  bytes after start: 114656  bytes before end: 31
      REGION TYPE                    START - END         [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
      __TEXT                      102f44000-103168000    [ 2192K] r-x/r-x SM=COW  ...MacOS/melonDS
--->  __DATA_CONST                103168000-103184000    [  112K] r--/rw- SM=COW  ...MacOS/melonDS
      __DATA                      103184000-103190000    [   48K] rw-/rw- SM=COW  ...MacOS/melonDS

...

Thread 12 Crashed:: EmuThread
0   libsystem_platform.dylib      	       0x19a096858 _platform_memmove + 536
1   melonDS                       	       0x10304b230 Gdb::Proto::MsgRecv(int, unsigned char*) + 456
2   melonDS                       	       0x10304a1c4 Gdb::GdbStub::Poll(bool) + 260
3   melonDS                       	       0x10304a5a0 Gdb::GdbStub::Enter(bool, Gdb::TgtStatus, unsigned int, bool) + 132
4   melonDS                       	       0x103024094 unsigned int NDS::RunFrame<false, 0>() + 248
5   melonDS                       	       0x102f53f9c EmuThread::run() + 2740
6   QtCore                        	       0x105568b40 QThreadPrivate::start(void*) + 332
7   libsystem_pthread.dylib       	       0x19a067fa8 _pthread_start + 148
8   libsystem_pthread.dylib       	       0x19a062da0 thread_start + 8

On the client side of things, it seems like at the very least the handshake process did get completed because the debugger did seem to be connected.

@PoroCYon
Copy link
Contributor Author

What happens if you swap these two lines?

@turtleisaac
Copy link

What happens if you swap these two lines?

Ok we're moving along here. Here's what happened:

melonDS output:

Game is now booting
--- dataoff=0
[GDB] recv() 1 bytes: '+' (2b)
[GDB] recv() 19 bytes: '$QStartNoAckMode#b0' (24)
[GDB] recv() after skipping: n=19, recv_total=19
[GDB] got pkt, checksum: b0 vs b0
[GDB] command in: 'QStartNoAckMode'
[GDB] send ack
[GDB] subcommand in: 'StartNoAckMode'
[GDB] send ack
[GDB] send resp: '$OK#9a'
--- dataoff=0
[GDB] recv() 59 bytes: '$qSupported:xmlRegisters=i386,arm,mips,arc;multiprocess+#04' (24)
[GDB] recv() after skipping: n=59, recv_total=59
[GDB] got pkt, checksum: 04 vs 04
[GDB] command in: 'qSupported:xmlRegisters=i386,arm,mips,arc;multiprocess+'
[GDB] subcommand in: 'Supported:xmlRegisters=i386,arm,mips,arc;multiprocess+'
[GDB] send resp: '$PacketSize=47F;qXfer:features:read+;swbreak-;hwbreak+;QStartNoAckMode+#73'
--- dataoff=0
[GDB] recv() 27 bytes: '+$QThreadSuffixSupported#e4' (2b)
[GDB] recv() after skipping: n=26, recv_total=26
[GDB] got pkt, checksum: e4 vs e4
[GDB] command in: 'QThreadSuffixSupported'
[GDB] subcommand in: 'ThreadSuffixSupported'
[GDB] unknown subcommand 'ThreadSuffixSupported'!
[GDB] send resp: '$#00'
--- dataoff=0
[GDB] recv() 42 bytes: '+$QListThreadsInStopReply#21+$qHostInfo#9b' (2b)
[GDB] recv() after skipping: n=41, recv_total=41
[GDB] got pkt, checksum: 21 vs 21
[GDB] got more: cksumoff=25, recvtotal=41, remain=14
==> +$qHostInfo#9b
[GDB] command in: 'QListThreadsInStopReply'
[GDB] subcommand in: 'ListThreadsInStopReply'
[GDB] unknown subcommand 'ListThreadsInStopReply'!
[GDB] send resp: '$#00'
--- dataoff=14
--- got preexisting: +$qHostInfo#9b
--- datastart=2
--- cksumoff=12
=== cksumoff=11 recv_total=13 datastart=2 dataoff=13
==> $qHostInfo#9b
[GDB] got pkt, checksum: 9b vs 9b
[GDB] command in: 'qHostInfo'
[GDB] subcommand in: 'HostInfo'
[GDB] send resp: '$cputype:arm;cpusubtype:armv5te:ostype:none;vendor:none;endian:little;ptrsize:4;#f3'
--- dataoff=0
[GDB] recv() 39 bytes: '+$vCont?#49+$qVAttachOrWaitSupported#38' (2b)
[GDB] recv() after skipping: n=38, recv_total=38
[GDB] got pkt, checksum: 49 vs 49
[GDB] got more: cksumoff=8, recvtotal=38, remain=28
==> +$qVAttachOrWaitSupported#38
[GDB] command in: 'vCont?'
[GDB] subcommand in: 'Cont?'
[GDB] unknown subcommand 'Cont?'!
[GDB] send resp: '$#00'
--- dataoff=28
--- got preexisting: +$qVAttachOrWaitSupported#38
--- datastart=2
--- cksumoff=26
=== cksumoff=25 recv_total=27 datastart=2 dataoff=27
==> $qVAttachOrWaitSupported#38
[GDB] got pkt, checksum: 38 vs 38
[GDB] command in: 'qVAttachOrWaitSupported'
[GDB] subcommand in: 'VAttachOrWaitSupported'
[GDB] unknown subcommand 'VAttachOrWaitSupported'!
[GDB] send resp: '$#00'
--- dataoff=0
[GDB] recv() 41 bytes: '+$QEnableErrorStrings#8c+$qProcessInfo#dc' (2b)
[GDB] recv() after skipping: n=40, recv_total=40
[GDB] got pkt, checksum: 8c vs 8c
[GDB] got more: cksumoff=21, recvtotal=40, remain=17
==> +$qProcessInfo#dc
[GDB] command in: 'QEnableErrorStrings'
[GDB] subcommand in: 'EnableErrorStrings'
[GDB] unknown subcommand 'EnableErrorStrings'!
[GDB] send resp: '$#00'
--- dataoff=17
--- got preexisting: +$qProcessInfo#dc
--- datastart=2
--- cksumoff=15
=== cksumoff=14 recv_total=16 datastart=2 dataoff=16
==> $qProcessInfo#dc
[GDB] got pkt, checksum: dc vs dc
[GDB] command in: 'qProcessInfo'
[GDB] subcommand in: 'ProcessInfo'
[GDB] unknown subcommand 'ProcessInfo'!
[GDB] send resp: '$#00'
--- dataoff=0
[GDB] recv() 24 bytes: '+$qC#b4+$qfThreadInfo#bb' (2b)
[GDB] recv() after skipping: n=23, recv_total=23
[GDB] got pkt, checksum: b4 vs b4
[GDB] got more: cksumoff=4, recvtotal=23, remain=17
==> +$qfThreadInfo#bb
[GDB] command in: 'qC'
[GDB] subcommand in: 'C'
[GDB] send resp: '$QC1#c5'
--- dataoff=17
--- got preexisting: +$qfThreadInfo#bb
--- datastart=2
--- cksumoff=15
=== cksumoff=14 recv_total=16 datastart=2 dataoff=16
==> $qfThreadInfo#bb
[GDB] got pkt, checksum: bb vs bb
[GDB] command in: 'qfThreadInfo'
[GDB] subcommand in: 'fThreadInfo'
[GDB] send resp: '$m1#9e'
--- dataoff=0
[GDB] recv() 6 bytes: '+$?#3f' (2b)
[GDB] recv() after skipping: n=5, recv_total=5
[GDB] got pkt, checksum: 3f vs 3f
[GDB] command in: '?'
[GDB] send resp: '$S02#b5'
--- dataoff=0
[GDB] recv() 1 bytes: '+' (2b)
[GDB] recv() 6 bytes: '$D#44+' (24)
[GDB] recv() after skipping: n=6, recv_total=6
[GDB] got pkt, checksum: 44 vs 44
[GDB] got more: cksumoff=3, recvtotal=6, remain=1
==> +
[GDB] command in: 'D'
[GDB] send resp: '$OK#9a'
[GDB] disconnect
[GDB] enter exit: 5
PU: region 0 = 04000033 : enabled, 04000000-08000000
PU: region 1 = 0200002D : enabled, 02000000-02800000
PU: region 2 = 027E0021 : enabled, 027E0000-02800000
PU: region 3 = 08000035 : enabled, 08000000-10000000
PU: region 4 = 027E001B : enabled, 027E0000-027E4000
PU: region 5 = 0100002F : enabled, 01000000-02000000
PU: region 6 = FFFF001D : enabled, FFFF0000-FFFF8000
PU: region 7 = 027FF017 : enabled, 027FF000-02800000
PU region 0: 04000000-08000000, user=00 priv=07, 15111011/05100011
PU region 1: 02000000-02800000, user=70 priv=77, 15111011/05100011
PU region 2: 027E0000-02800000, user=00 priv=00, 15111011/05100011
PU region 3: 08000000-10000000, user=00 priv=03, 15111011/05100011
PU region 4: 027E0000-027E4000, user=00 priv=03, 15111011/05100011
PU region 5: 01000000-02000000, user=00 priv=07, 15111011/05100011
PU region 6: FFFF0000-FFFF8000, user=50 priv=55, 15111011/05100011
PU region 7: 027FF000-02800000, user=00 priv=03, 15111011/05100011
[GDB] send resp: '$W00#b7'
[GDB] disconnect
[GDB] enter exit: 5

On the CLion side of things:

Process 1 was reported after connecting to 'connect://localhost:3333', but no stop reply packet was received
Debugger detached

@PoroCYon PoroCYon force-pushed the gdbstub-standalone branch from d3f48ed to fae3642 Compare August 21, 2023 20:20
@PoroCYon
Copy link
Contributor Author

@turtleisaac Ok, I've implemented a few more commands which should be able to keep lldb happy. Can you try now?

@turtleisaac
Copy link

turtleisaac commented Aug 21, 2023

@turtleisaac Ok, I've implemented a few more commands which should be able to keep lldb happy. Can you try now?

Still no luck unfortunately. Definitely seems to be a different series of errors tho?

Game is now booting
[GDB] inital handshake: didn't receive inital ack!
[GDB] disconnect
[GDB] enter exit: 5
PU: region 0 = 04000033 : enabled, 04000000-08000000
PU: region 1 = 0200002D : enabled, 02000000-02800000
PU: region 2 = 027E0021 : enabled, 027E0000-02800000
PU: region 3 = 08000035 : enabled, 08000000-10000000
PU: region 4 = 027E001B : enabled, 027E0000-027E4000
PU: region 5 = 0100002F : enabled, 01000000-02000000
PU: region 6 = FFFF001D : enabled, FFFF0000-FFFF8000
PU: region 7 = 027FF017 : enabled, 027FF000-02800000
PU region 0: 04000000-08000000, user=00 priv=07, 15111011/05100011
PU region 1: 02000000-02800000, user=70 priv=77, 15111011/05100011
PU region 2: 027E0000-02800000, user=00 priv=00, 15111011/05100011
PU region 3: 08000000-10000000, user=00 priv=03, 15111011/05100011
PU region 4: 027E0000-027E4000, user=00 priv=03, 15111011/05100011
PU region 5: 01000000-02000000, user=00 priv=07, 15111011/05100011
PU region 6: FFFF0000-FFFF8000, user=50 priv=55, 15111011/05100011
PU region 7: 027FF000-02800000, user=00 priv=03, 15111011/05100011
PU: region 1 = 0200002B : enabled, 02000000-02400000
PU region 0: 04000000-08000000, user=00 priv=07, 15111011/05100011
PU region 1: 02000000-02400000, user=70 priv=77, 15111011/05100011
PU region 2: 027E0000-02800000, user=00 priv=00, 15111011/05100011
PU region 3: 08000000-10000000, user=00 priv=03, 15111011/05100011
PU region 4: 027E0000-027E4000, user=00 priv=03, 15111011/05100011
PU region 5: 01000000-02000000, user=00 priv=07, 15111011/05100011
PU region 6: FFFF0000-FFFF8000, user=50 priv=55, 15111011/05100011
PU region 7: 027FF000-02800000, user=00 priv=03, 15111011/05100011
PU: region 2 = 023E0021 : enabled, 023E0000-02400000
PU region 0: 04000000-08000000, user=00 priv=07, 15111011/05100011
PU region 1: 02000000-02400000, user=70 priv=77, 15111011/05100011
PU region 2: 023E0000-02400000, user=00 priv=00, 15111011/05100011
PU region 3: 08000000-10000000, user=00 priv=03, 15111011/05100011
PU region 4: 027E0000-027E4000, user=00 priv=03, 15111011/05100011
PU region 5: 01000000-02000000, user=00 priv=07, 15111011/05100011
PU region 6: FFFF0000-FFFF8000, user=50 priv=55, 15111011/05100011
PU region 7: 027FF000-02800000, user=00 priv=03, 15111011/05100011
PU region 3: 08000000-10000000, user=00 priv=01, 15115011/05100011
unknown ARM9 IO write32 04001060 00000000 01FF8548
unknown ARM9 IO write32 04001064 00000000 01FF8548
unknown ARM9 IO write32 04001068 00000000 01FF8548
[GDB] send ack
[GDB] recv() 19 bytes: '$QStartNoAckMode#b0' (24)
[GDB] recv() after skipping: n=19, recv_total=19
[GDB] got pkt, checksum: b0 vs b0
[GDB] command in: 'QStartNoAckMode'
[GDB] send ack
[GDB] send ack
[GDB] send resp: '$OK#9a'
[GDB] recv() 59 bytes: '$qSupported:xmlRegisters=i386,arm,mips,arc;multiprocess+#04' (24)
[GDB] recv() after skipping: n=59, recv_total=59
[GDB] got pkt, checksum: 04 vs 04
[GDB] command in: 'qSupported:xmlRegisters=i386,arm,mips,arc;multiprocess+'
[GDB] send resp: '$PacketSize=47F;qXfer:features:read+;swbreak-;hwbreak+;QStartNoAckMode+#73'
[GDB] recv() 27 bytes: '+$QThreadSuffixSupported#e4' (2b)
[GDB] recv() after skipping: n=26, recv_total=26
[GDB] got pkt, checksum: e4 vs e4
[GDB] command in: 'QThreadSuffixSupported'
[GDB] unknown subcommand 'ThreadSuffixSupported'!
[GDB] send resp: '$#00'
[GDB] recv() 28 bytes: '+$QListThreadsInStopReply#21' (2b)
[GDB] recv() after skipping: n=27, recv_total=27
[GDB] got pkt, checksum: 21 vs 21
[GDB] command in: 'QListThreadsInStopReply'
[GDB] unknown subcommand 'ListThreadsInStopReply'!
[GDB] send resp: '$#00'
[GDB] recv() 14 bytes: '+$qHostInfo#9b' (2b)
[GDB] recv() after skipping: n=13, recv_total=13
[GDB] got pkt, checksum: 9b vs 9b
[GDB] command in: 'qHostInfo'
[GDB] send resp: '$cputype:12;cpusubtype:7;triple:arm-none-eabi;ostype:none;vendor:none;endian:little;ptrsize:4;#6a'
[GDB] recv() 39 bytes: '+$vCont?#49+$qVAttachOrWaitSupported#38' (2b)
[GDB] recv() after skipping: n=38, recv_total=38
[GDB] got pkt, checksum: 49 vs 49
[GDB] got more: cksumoff=8, recvtotal=38, remain=28
==> +$qVAttachOrWaitSupported#38
[GDB] command in: 'vCont?'
[GDB] send resp: '$vCont;c;s;t#05'
--- got preexisting: +$qVAttachOrWaitSupported#38
--- datastart=2
--- cksumoff=26
=== cksumoff=25 recv_total=27 datastart=2 dataoff=27
==> $qVAttachOrWaitSupported#38
[GDB] got pkt, checksum: 38 vs 38
[GDB] command in: 'qVAttachOrWaitSupported'
[GDB] unknown subcommand 'VAttachOrWaitSupported'!
[GDB] send resp: '$#00'
[GDB] recv() 24 bytes: '+$QEnableErrorStrings#8c' (2b)
[GDB] recv() after skipping: n=23, recv_total=23
[GDB] got pkt, checksum: 8c vs 8c
[GDB] command in: 'QEnableErrorStrings'
[GDB] unknown subcommand 'EnableErrorStrings'!
[GDB] send resp: '$#00'
[GDB] recv() 24 bytes: '+$qProcessInfo#dc+$qC#b4' (2b)
[GDB] recv() after skipping: n=23, recv_total=23
[GDB] got pkt, checksum: dc vs dc
[GDB] got more: cksumoff=14, recvtotal=23, remain=7
==> +$qC#b4
[GDB] command in: 'qProcessInfo'
[GDB] send resp: '$cputype:12;cpusubtype:7;triple:arm-none-eabi;ostype:none;vendor:none;endian:little;ptrsize:4;#6a'
--- got preexisting: +$qC#b4
--- datastart=2
--- cksumoff=5
=== cksumoff=4 recv_total=6 datastart=2 dataoff=6
==> $qC#b4
[GDB] got pkt, checksum: b4 vs b4
[GDB] command in: 'qC'
[GDB] send resp: '$QC1#c5'
[GDB] recv() 17 bytes: '+$qfThreadInfo#bb' (2b)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: bb vs bb
[GDB] command in: 'qfThreadInfo'
[GDB] send resp: '$m1#9e'
[GDB] recv() 6 bytes: '+$?#3f' (2b)
[GDB] recv() after skipping: n=5, recv_total=5
[GDB] got pkt, checksum: 3f vs 3f
[GDB] command in: '?'
[GDB] send resp: '$S02#b5'
[GDB] recv() 1 bytes: '+' (2b)
[GDB] recv() error -1, errno=35 (Resource temporarily unavailable)
[GDB] EOF!
[GDB] disconnect
[GDB] enter exit: 5

@PoroCYon
Copy link
Contributor Author

Can you try running the LLDB command log enable gdb-remote all before running the gdb-remote command, and give me the output? Because the melonDS log sounds like LLDB closed the connection on its side.

Also, the "inital handshake: didn't receive inital ack!" at the beginning sounds cursed (and also contradicting what's being said here, hooray). Can you also try the same when commenting away this code, and then with also this code uncommented?

@turtleisaac
Copy link

turtleisaac commented Aug 21, 2023

Can you try running the LLDB command log enable gdb-remote all before running the gdb-remote command, and give me the output? Because the melonDS log sounds like LLDB closed the connection on its side.

Also, the "inital handshake: didn't receive inital ack!" at the beginning sounds cursed (and also contradicting what's being said here, hooray). Can you also try the same when commenting away this code, and then with also this code uncommented?

Will do in a sec, but yeah I think it is detaching purposefully. Look at what I sent earlier:

On the CLion side of things:

Process 1 was reported after connecting to 'connect://localhost:3333', but no stop reply packet was received
Debugger detached

@PoroCYon ok tried it out and got a shit ton of stuff here.

LLDB output (truncated to just the stuff going on at the end):

 <   6> send packet: $pd#d4
 <  12> read packet: $7c2f0003#f5
 <   6> send packet: $pe#d5
 <  12> read packet: $00080002#8a
 <  16> send packet: $jThreadsInfo#c1
(lldb)  <   4> read packet: $#00
(lldb)  Fetching extended information for thread 0001
 <  24> send packet: $jThreadExtendedInfo:#b9
 <   4> read packet: $#00
Process 1 stopped
* thread #1, stop reason = signal SIGINT
    frame #0: 0x02000800 main.elf`NitroMain + 33556480
main.elf`NitroMain:
->  0x2000800 <+33556480>: mov    r12, #67108864
    0x2000804 <+33556484>: str    r12, [r12, #0x208]
    0x2000808 <+33556488>: ldrh   r0, [r12, #6]
    0x200080c <+33556492>: cmp    r0, #0
Target 0: (main.elf) stopped.

melonDS ceases execution of the game here unless I use continue in lldb. As for the output of melonDS... well there's so much that I question how much of it is even necessary to send. This is a minuscule fraction of it.

[GDB] recv() 16 bytes: '$m1fff5cc,21c#bd' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: bd vs bd
[GDB] command in: 'm1fff5cc,21c'
[GDB] send resp: '$000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000#80'
[GDB] recv() 16 bytes: '$m1fff7e8,21c#96' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: 96 vs 96
[GDB] command in: 'm1fff7e8,21c'
[GDB] send resp: '$000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000#80'
[GDB] recv() 16 bytes: '$m1fffa04,21c#87' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: 87 vs 87
[GDB] command in: 'm1fffa04,21c'
[GDB] send resp: '$000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000#80'
[GDB] recv() 16 bytes: '$m1fffc20,21c#87' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: 87 vs 87
[GDB] command in: 'm1fffc20,21c'
[GDB] send resp: '$000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000#80'
[GDB] recv() 16 bytes: '$m1fffe3c,21c#bd' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: bd vs bd
[GDB] command in: 'm1fffe3c,21c'
[GDB] send resp: '$0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffdeffe7ffdeffe7ffdeffe7ffdee231a3e9ae7b927575b169821de322cb55aecedc4d181b3a9e4d429f0cffdf859080fcf6bee974881de2390ba065cb7a613b8dad1265195cca7743d15ea4290ff5dc3eb5566f5ecc411f#cd'
[GDB] recv() 16 bytes: '$m2000058,21c#be' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: be vs be
[GDB] command in: 'm2000058,21c'
[GDB] send resp: '$6001a08c330e56348778fae187d4de01700e3ce52f62ddab8fb27d3f00ef104ede360a5b7023455fd190bbdf9e69c95c2fe309df081c7047ec4eb6ece6b3fd76957c06890f08c19d3210be63e60d9e9d93677b9b9dd1d6e4f34ca6f0e6cdbde3139b3b880f8de182d4ad89e0a1335d8937a94f3411df7047e86b879a545db1a41c52e0dccd9a8a5aea09c15a30b81a0ab8702c2e0b77f20512fccff24a932771e8885eea903a949e25a29281048cab4eff820d1edb0f3f4dce7c4d1c181f5b078468edf3677aef9a358bce52b8169fd90222a6b5176120ab4262fffd8dcd6bbb63b0f48a528c10df7047d3e43a2825e3886a4b9d724bf03bc3a6b5198501e527a63029ec4034ec131d8b0ee8c2a48813d9405a2aa2095603ae00f959a8a08615484b6ce2e66c0a2f36bd850d6d13d3da439e5ad1a9c9a3cd65360b90fbcb300d21c020e075aade8d5e784e3d58e7fcfb1f306c0dec8ab4ffdd25f404d41f24b2ea400bdf7047b1821c5ac0bc967103a28d348756541773f690ae66ab181fc7cb4d7930f8108ea3445b448dbc98adf63e081602743451171123631348f2e9ebc710f388c21bf3e8b0243bd42a3700fa1c9411cf9c358741d49f728ca3402689ccbe6e058cb81f7cba3c25de0306df704711487475e938aab24ce790ce9be95e7c0c8f8f65e8b976761ec3fc7b9e8db27b2c390c0c6cab860a786e6e07adaba98d11aecd0320883e3173559b3342deddb7f82f4c0e474656089fa03a36#6f'
[GDB] recv() 16 bytes: '$m2000274,21c#be' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: be vs be
[GDB] command in: 'm2000274,21c'
[GDB] send resp: '$010265c10118b4c0d9b89265a2f890720cdf704748eecb4acae45ba39b86fd37de99c15f35b733634c77c049467778fb67ea8385310acd461db1d84b1719846c1f926d23ff37ba841e60736a06fb3a4bec73a971f05527aed983402c0edf7047fbb68a9cabaab37cfde56e10b49cc56f8f1f8a47def3d4d63fac3a10a5a99b98aae96561a6bd9d1e53f274cc0447d161b5635dc3b0b117486d4c0b5bfd1166e970adf45c45a7e39c3d29a278e4dad91a9e5583725b0f64278254e716ad8815df7047e999b1397217259fa63da4f2afeee3f2b41e15324160dc190607dea37491529a030c4be2cb8b48790077e5f539a7fef24f7b7e3651f031ca3aafc02ea48457fd0308c940ea75ce1e7047da5b55cc5f4b45a4d75e3a4991e3fb0ddecf0a3e5d6b09df70479ac3053c9aff181d2e0b9030fd4e8705a4fb4951337eca19858899068464b95011dcd64acb0af03246610b9d8c48355d34574faf4913a1416313b161213f44fab21fa82c687257ec5ff07fde028886dd774c0d44292be22358a56d0bef4e68a94ddaeab5048130aa0eaee4c21ddc80f114df70475413e22c4f7c734519c6cc58dbb4688159574a6adbd9723580ae20d8261df6a7315387449d6932e51e87794d50cb26a1cde1f3387a36e3ad36060b2460fda74f24e80cb0ccc0bca0a13459630b6b4ff1e944fae6141e55ded68b136ede0a43a641ecd79b801b1a859f23c3f50590fb814d422ae2337405a2cef135660ddf70477d41#b7'
[GDB] recv() 16 bytes: '$m2000490,21c#be' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: be vs be
[GDB] command in: 'm2000490,21c'
[GDB] send resp: '$10298a9bb9bed548dcfd0268c63a5d13617d4e5e5db215db6e172c3c24335f359674a1cd4e57498fa3f27b45237c530460fb9f8b1f2a4040f5469225b0e31d1ce4d6525c3be0b6c2192cf93bb71d102c4f37b022eab9473b4cdc3637ed76b764ae7859540adb2967ad7e6eef69ce5daa00fa002204df70474681535441e62a0477512413ebbebc691d73b80b0287c2b8b1368aeaa09f8839460bc4fcfbe65bcc92a50c23688fb2456d6d26e0ff926b6b5dc9be61a4cc883a6ecc1944d6b16286c7e6f46cc09de5d0289aee4e7903de6ddad3316c658b071195d8bf6cebd3544d7e840fdf7047021c1bf9046b644395e8f4000b2ddfb82d70eab0345e065fe4f91eb61ee892b587327662f66a492a34e227c7033d92570913c818e83b05441348cbad39a07068551bd897b9ce2a263e88c3934e3db10edd0ab185806f459f8f46b57847d8052134de1e5299dcf434cc1eec357395c0766a11aaebc2ce036a00df704702e0e140f7e99560457e709dad87f2f930540ca5fb9f200fb6918f28e1e06576da777edaaece7eb3044a39217a883c1818159b516c178a50722e52f663d91090aeaca6d4951d12bf400b64f7f791094f13df7047b8bdad1343e71599c96773a21346a63b74238e58a69426776e4d90eaddad927f2aaa86bc558efcd73e4c4ce707c6b0ab6ec723c0712576525f60ba1b109c0efd6ed903f2507fcd22fb02f85969e44f5fe75834472b01b9cc69f2a46cb36b94a0abc0986616fc#80'
[GDB] recv() 16 bytes: '$m20006ac,21c#1b' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: 1b vs 1b
[GDB] command in: 'm20006ac,21c'
[GDB] send resp: '$de28da5b698f81b4f3a70564002205df7047c8bf87b7930ecd5ba891e09020b67b2c25de159844a33211326011f99c3663b9380f6bb1bc1c26581c2a3e17c13d50227f048d5566286f0880e14cdc6f4f3037bc9ca5629868e85487db92b6fe3297065d60696402fc310ae33548a45d879efb15cf8f495bcecdbf82ca9a1d12df70475ba8b825cdfb2ea5dd1aea5abcb957b245a2226eb36eca7a23563693e2d63a77ce1af6e0a973ece3709d19fb415f9258bcc879e1d88e46397f427bcfd2c673a39d0293ed19ad8fd7c26de62fd1a30ed176b0b47fb909371cde4fb2f902c7c23946dd63a7aefb20247d23108c625c00d703df704717e1c24dcec616a957d529a443e14b09e9e41dc41dce4bb85b062f46700519e688294eccffb524103736e930acf60faccd2535578fb056c3986b9696921169814b6042672740a9f9e38c8b8fa0558023b6634fbf2f02548bab1f3a09000001c3a0e308c28ce5b600dce1000050e3fcffff1aa50000eb1300a0e300f021e108019fe5ff0d80e200d0a0e11200a0e300f021e1f4009fe5ff0d80e2400040e204d040e204001de30000000a000000ea04d04de2d8109fe5011040e01f00a0e300f02fe104d041e20000a0e3bc109fe50129a0e3360000eb0000a0e3b4109fe5012ba0e3320000eb020ca0e3a8109fe5012ba0e32e0000eba0109fe5140091e5320000eb5c0000eb90009fe50c1090e5102090e50130a0e10000a0e3020051e10000003a000000ea#19'
[GDB] recv() 16 bytes: '$m20008c8,21c#f4' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: f4 vs f4
[GDB] command in: 'm20008c8,21c'
[GDB] send resp: '$040081e4faffff3a1f10c3e39a0f07ee351f07ee3e1f07ee201081e2020051e1f9ffffba50109fe5000081e534109fe5ff1d81e23c1081e240009fe5000081e575ca03eba10000eb50d003eb30109fe530e09fe504001de30000001a000000ea04d04de211ff2fe100007e02000800000000000500000007a00b00029cff7f020080ff01a50c00020000ffff02c081e00c0051e1000000ba000000ea0100a1e8faffffba1eff2fe1000050e32700000af0002de9060010e9022080e0213c40e0ff14c1e3011040e00240a0e1010053e1150000da015073e50860a0e3016056e2f9ffffba800015e30200001a010073e5010062e5090000ea01c073e5017073e50c7487e10f7ac7e3027087e220c08ce20700d2e7010062e510c05ce2fbffffaa010053e18550a0e1ebffffca0000a0e31f30c1e39a0f07ee353f07ee3e3f07ee203083e2040053e1f9ffffbaf000bde81eff2fe184009fe5001090e5042090e5083090e5020051e11b00000a045091e4047091e4076085e00540a0e1060054e10000004a000000ea047093e40000004a000000ea047084e4f7ffff4a047091e4076084e00070a0e3060054e10000003a000000ea047084e4faffff3a1f40c5e39a7f07ee354f07ee3e4f07ee204084e2060054e1f9ffffbae1ffffea000000eaa00b00021eff2fe1100f11eeac109fe50100c0e1100f01ee0000a0e3150f07ee160f07ee9a0f07ee94009fe5100f06ee90009fe5110f06ee8c009fe5#ea'
[GDB] recv() 16 bytes: '$m2000ae4,21c#1b' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: 1b vs 1b
[GDB] command in: 'm2000ae4,21c'
[GDB] send resp: '$120f06ee88009fe5130f06ee84009fe51a0080e3010080e3140f06ee78009fe5150f06ee74009fe5160f06ee70009fe5170f06ee2000a0e3310f09ee54009fe50a0080e3110f09ee4200a0e3300f02ee4200a0e3100f02ee0200a0e3100f03ee40009fe5700f05ee3c009fe5500f05ee100f11ee34109fe5010080e1100f01ee1eff2fe105900f00330000042d00000221007e023500000800007e022f0000011d00ffff17f07f0211001005111011157d7005001eff2fe11eff2fe1e01e1102f81e1102601811026018110200591e0214a30b02337502042106c0dedec006215b53444b2b4e494e54454e444f3a445743322e322e33303030382e3038303633302e313930365f4457435f325f325f504c5553385d0000005b53444b2b4e494e54454e444f3a57694669322e312e33303030332e303730393230303232395d005b53444b2b554249515549544f55533a4350535d000000005b53444b2b554249515549544f55533a53534c5d000000005b53444b2b4162696f73736f3a6c696256435420312e332e315d00005b53444b2b4e494e54454e444f3a4241434b55505d0000005b53444b2b4e494e54454e444f3a4457435f4c4f4242595d00000000f8b519f0abfa19f04dfb19f0d3fb002019f0fcfb63480021daf07aeb26f0b6f813f0b6fc00f0d0f801f0ecff0020032102f00af80120032102f006f80320011c02f002f800215848c943816126f05efa5549086205f002f85348006a#0a'
[GDB] recv() 16 bytes: '$m2000d00,16c#e9' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: e9 vs e9
[GDB] command in: 'm2000d00,16c'
[GDB] send resp: '$2cf0b0f8041c5148006a28f0cdf8011c201c03f02ffa24f075fb032039f06ef9032803d10320002139f0b4ff4748006a26f0cefb002803d1002091f07ffc20e043480068002802d0012808d017e03f480021c1613f48404900f0ccf811e00020011c0ef047ff012000210ef043ff37480121c16139483a4900f0bcf801e024f0cdfb3848012101670021016300f09cf90af0cafb17f0f4fa2c4800214160d2f010ebc327304e2f4c0025bf0000f0d8f800f0acf919f092fbc320a16b80000840b84205d1307b002802d1002000f0eaf835f0b8f9002817d000f0c2f8d2f068eb00f050f8d2f064eba0691ef049fd606a1ef046fd206b002806d10120011cd0f036e8e06a401ce06213f030fc17f0d0fa26f026f8606a1ef033fd0120011cd0f026e8e06a401ce06225630af0b3fb0ef07dfe2168002901d06068884703f0e4f9206a1ef01dfdb1e7601811026018110220fc7f023c00000030b01e0224000000045c1e020c111d026c111d02#4e'
[GDB] recv() 6 bytes: '$pd#d4' (24)
[GDB] recv() after skipping: n=6, recv_total=6
[GDB] got pkt, checksum: d4 vs d4
[GDB] command in: 'pd'
[GDB] send resp: '$7c2f0003#f5'
[GDB] recv() 6 bytes: '$pe#d5' (24)
[GDB] recv() after skipping: n=6, recv_total=6
[GDB] got pkt, checksum: d5 vs d5
[GDB] command in: 'pe'
[GDB] send resp: '$00080002#8a'
[GDB] recv() 16 bytes: '$jThreadsInfo#c1' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: c1 vs c1
[GDB] command in: 'jThreadsInfo'
[GDB] unknown command 'j'!
[GDB] send resp: '$#00'
[GDB] recv() 24 bytes: '$jThreadExtendedInfo:#b9' (24)
[GDB] recv() after skipping: n=24, recv_total=24
[GDB] got pkt, checksum: b9 vs b9
[GDB] command in: 'jThreadExtendedInfo:'
[GDB] unknown command 'j'!
[GDB] send resp: '$#00'
[GDB] recv() 5 bytes: '$c#63' (24)
[GDB] recv() after skipping: n=5, recv_total=5
[GDB] got pkt, checksum: 63 vs 63
[GDB] command in: 'c'
[GDB] continue execution
[GDB] enter exit: 3
PU: region 0 = 04000033 : enabled, 04000000-08000000
PU: region 1 = 0200002D : enabled, 02000000-02800000
PU: region 2 = 027E0021 : enabled, 027E0000-02800000
PU: region 3 = 08000035 : enabled, 08000000-10000000
PU: region 4 = 027E001B : enabled, 027E0000-027E4000
PU: region 5 = 0100002F : enabled, 01000000-02000000
PU: region 6 = FFFF001D : enabled, FFFF0000-FFFF8000
PU: region 7 = 027FF017 : enabled, 027FF000-02800000
PU region 0: 04000000-08000000, user=00 priv=07, 15111011/05100011
PU region 1: 02000000-02800000, user=70 priv=77, 15111011/05100011
PU region 2: 027E0000-02800000, user=00 priv=00, 15111011/05100011
PU region 3: 08000000-10000000, user=00 priv=03, 15111011/05100011
PU region 4: 027E0000-027E4000, user=00 priv=03, 15111011/05100011
PU region 5: 01000000-02000000, user=00 priv=07, 15111011/05100011
PU region 6: FFFF0000-FFFF8000, user=50 priv=55, 15111011/05100011
PU region 7: 027FF000-02800000, user=00 priv=03, 15111011/05100011
PU: region 1 = 0200002B : enabled, 02000000-02400000
PU region 0: 04000000-08000000, user=00 priv=07, 15111011/05100011
PU region 1: 02000000-02400000, user=70 priv=77, 15111011/05100011
PU region 2: 027E0000-02800000, user=00 priv=00, 15111011/05100011
PU region 3: 08000000-10000000, user=00 priv=03, 15111011/05100011
PU region 4: 027E0000-027E4000, user=00 priv=03, 15111011/05100011
PU region 5: 01000000-02000000, user=00 priv=07, 15111011/05100011
PU region 6: FFFF0000-FFFF8000, user=50 priv=55, 15111011/05100011
PU region 7: 027FF000-02800000, user=00 priv=03, 15111011/05100011
PU: region 2 = 023E0021 : enabled, 023E0000-02400000
PU region 0: 04000000-08000000, user=00 priv=07, 15111011/05100011
PU region 1: 02000000-02400000, user=70 priv=77, 15111011/05100011
PU region 2: 023E0000-02400000, user=00 priv=00, 15111011/05100011
PU region 3: 08000000-10000000, user=00 priv=03, 15111011/05100011
PU region 4: 027E0000-027E4000, user=00 priv=03, 15111011/05100011
PU region 5: 01000000-02000000, user=00 priv=07, 15111011/05100011
PU region 6: FFFF0000-FFFF8000, user=50 priv=55, 15111011/05100011
PU region 7: 027FF000-02800000, user=00 priv=03, 15111011/05100011
PU region 3: 08000000-10000000, user=00 priv=01, 15115011/05100011
unknown ARM9 IO write32 04001060 00000000 01FF8548
unknown ARM9 IO write32 04001064 00000000 01FF8548
unknown ARM9 IO write32 04001068 00000000 01FF8548

I think that [GDB] send resp: '$#00' was the last line present in the melonDS output before I did continue? That or the line above it.

Now to be clear, at this point, game execution does continue and I haven't tested beyond that point yet. Perhaps everything does work fine in terms of raw manual LLDB in Terminal, and I'll have to try placing some breakpoints and seeing how things go. However, the goal evolves into using this in conjunction with CLion so I can debug in a more friendly environment which should let me go line by line. I know this works with melonDS for Visual Studio and GDB so that's the result I'm trying to work towards on my end.

@PoroCYon
Copy link
Contributor Author

The log output means that it seems to work. But, what you're saying is, it works with terminal LLDB but not CLion's LLDB? That's a bit annoying, and not something I can troubleshoot easily (beyond guessing things, like commenting/uncommenting the code I highlighted).

@axel7083
Copy link

@Arisotura @RSDuck is this planning to be merged soon ? It would be nice to have access to releases for everyone who do not want to build themself. (Maybe adding a specific debug version for each plateforme in the release channel.)

@PoroCYon made an incredible work that already helped a lot of people and I think it would be nice to see it merged

@RSDuck
Copy link
Member

RSDuck commented Aug 22, 2023

I already wanted to merge it a while ago, but I've been kept off by the bug reports in this PR.

@PoroCYon
Copy link
Contributor Author

@axel7083 did you actually manage to get it to work with a different GDB? (or maybe one of the newer commits automagically fixed things.)

@turtleisaac
Copy link

turtleisaac commented Aug 23, 2023

I've made my own attempts to intercept the communication between CLion and its internal LLDB but that hasn't gone anywhere. However, I did get a different CLion installation on Linux which is actually able to use GDB to connect to melonDS (granted the memory viewer and breakpoints aren't working, just the fact that the debugger seems to connect), so the culprit definitely seems to be down to the implementation of their LLDB in terms of what it expects from the connected gdb-server, but the possibility to make it happy does exist.

Anyways, considering the following error message I've still been getting on the Mac side of things:

Process 1 was reported after connecting to 'connect://localhost:3333', but no stop reply packet was received
Debugger detached

I think it has something to do with this page, but I'm not smart enough (and mostly just don't have enough information about CLion's internal LLDB and the sequence of events that led to that error) to understand what it is saying in relation to what melonDS either is or isn't doing...

Oh also, further testing involving LLDB (the manual Terminal one, not the one inside of CLion) shows things fall apart pretty quickly when I actually try to use it. It's freaking out about the thumb instructions is my theory, because it freaks out about unknown opcodes, halts execution, and after a few times of that and doing continue it eventually disconnects.

(lldb) _regexp-break ScrCmd_End
Breakpoint 1: 2 locations.
Process 1 stopped
* thread #1, stop reason = signal SIGTRAP
    frame #0: 0x020d3f60 main.elf`OS_Halt + 8
main.elf`OS_Halt:
->  0x20d3f60 <+8>: bx     lr

main.elf`MI_SetWramBank:
    0x20d3f64 <+0>: ldr    r1, [pc, #0x4]            ; main.elf.PT_LOAD[0] + 868208
    0x20d3f68 <+4>: strb   r0, [r1]
    0x20d3f6c <+8>: bx     lr
Target 0: (main.elf) stopped.
(lldb) continue
Process 1 resuming
(lldb) _regexp-break ScrCmd_End
Breakpoint 2: 2 locations.
(lldb) continue
error: Process is running.  Use 'process interrupt' to pause execution.
Process 1 stopped
* thread #1, stop reason = breakpoint 1.2 2.2
    frame #0: 0x02040898 main.elf`sSoundWork + 264096
main.elf`sSoundWork:
->  0x2040898 <+264096>: .long  0xf7ffb508                ; unknown opcode
    0x204089c <+264100>: andhs  pc, r0, r1, ror #20
    0x20408a0 <+264104>: andeq  r11, r0, r8, lsl #26
    0x20408a4 <+264108>: .long  0x1c05b5f8                ; unknown opcode
Target 0: (main.elf) stopped.
(lldb) continue
Process 1 resuming
Process 1 exited with status = -1 (0xffffffff) lost connection
[GDB] send resp: '$0500002ab640d1e1b4e091e1041081e0041081e29e1221e0000000ea0010a0e3000051e30000a0033880bd080c20a0e11bffffeb0100a0e33880bde8f8402de9000050e3081090150050a0e300005113016080100060a003b000d6e1004086e00100d4e5000050e3f880bd980570a0e1000054e30800000a0100d4e5000055e10500002ab610d4e1b10094e1011084e0041081e2901521e0000000ea0710a0e10300d1e5010010e30100000a0600a0e147ffffeb0100d4e5015085e2000055e1eaffff3af880bde8f0412de92c2092e5b050d1e10228a0e12228a0e1b240d3e10e22c2e30228a0e1010014e305c080e02258a0e1b040d3e10300001a8427a0e18537a0e12248a0e12358a0e10220d1e50030a0e3000052e31e00009a052084e002e8a0e1042080e22e48a0e10360a0e10350a0e1000050e30380dce70f00000a000052e30800000a0570d0e5070058e10500002aba70d0e1b7e092e1077082e0047087e29e7827e0000000ea0670a0e1000057e300e097150e7080100000001a0570a0e1bc41c7e102e0d1e5013083e20e0053e1e6ffff3a0300d1e5010080e30300c1e5f081bde8f84f2de901a0a0e1000050e3081090150170a0e300005113018080100080a003b200d8e10060a0e3005088e00100d5e5000050e33100009a0690a0e10640a0e106b0a0e1000055e30800000a0100d5e5000056e10500002ab600d5e1001085e0b200d1e1000081e0091080e0000000ea0410a0e1#59'
[GDB] recv() 16 bytes: '$m20beb20,21c#4c' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: 4c vs 4c
[GDB] command in: 'm20beb20,21c'
[GDB] send resp: '$00005ae3b403da11000050130b30a0010200000a00008ae0361300eb0030a0e1000053e31200000a000055e30800000a0100d5e5000056e10500002ab610d5e1b10095e1011085e0041081e2901621e0000000ea0010a0e30300d1e5010010e30400001a0800a0e10a20a0e18effffeb000000ea0070a0e30100d5e5016086e2109089e2000056e1d0ffff3a0700a0e1f88fbde838402de901c0a0e1000050e30810901500005113010080100000a003b210d0e100005ce3b443dc11011080e0000054130c00000a04509ce00800000a0140d5e5040053e10500002ab6e0d5e1be4095e10ee085e004e08ee294e323e0020000ea0030a0e3000000ea0030a0e3000051e30800000a0140d1e5040052e10500002ab640d1e1b4e091e1041081e0041081e29e1221e0000000ea0010a0e3000051e30600000a0320d1e5010012e30300001a0c20a0e157ffffeb0100a0e33880bde80000a0e33880bde8000050e30810901500c0a0e300005113011080100010a003b200d1e1003081e00100d3e5000050e31eff2f910c10a0e1000053e30800000a0100d3e500005ce10500002ab620d3e1b20093e1022083e0042082e2902c22e0000000ea0120a0e10300d2e501c08ce2010010e30100c0130300c2150100d3e500005ce1ebffff3a1eff2fe1f84f2de900a0a0e10900dae50190a0e10170a0e3000050e30060a0e32100009a08508ae20640a0e106b0a0e100005ae30f00000a000055e30800000a#14'
[GDB] recv() 15 bytes: '$m20bed3c,44#24' (24)
[GDB] recv() after skipping: n=15, recv_total=15
[GDB] got pkt, checksum: 24 vs 24
[GDB] command in: 'm20bed3c,44'
[GDB] send resp: '$0900dae5000056e10500002abe10dae1b10095e1011085e0041081e2901621e0000000ea0410a0e1000051e30000911500808a100000001a0b80a0e10800a0e10910a0e1#01'
[GDB] recv() 6 bytes: '$pd#d4' (24)
[GDB] recv() after skipping: n=6, recv_total=6
[GDB] got pkt, checksum: d4 vs d4
[GDB] command in: 'pd'
[GDB] send resp: '$30377e02#cb'
[GDB] recv() 17 bytes: '$m827e3600,200#fa' (24)
[GDB] recv() after skipping: n=17, recv_total=17
[GDB] got pkt, checksum: fa vs fa
[GDB] command in: 'm827e3600,200'
[GDB] send resp: '$0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000#00'
[GDB] recv() 6 bytes: '$pb#d2' (24)
[GDB] recv() after skipping: n=6, recv_total=6
[GDB] got pkt, checksum: d2 vs d2
[GDB] command in: 'pb'
[GDB] send resp: '$00000000#80'
[GDB] recv() 16 bytes: '$z0,2040898,4#a5' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: a5 vs a5
[GDB] command in: 'z0,2040898,4'
[GDB] send resp: '$OK#9a'
[GDB] recv() 16 bytes: '$vCont;s:0001#b3' (24)
[GDB] recv() after skipping: n=16, recv_total=16
[GDB] got pkt, checksum: b3 vs b3
[GDB] command in: 'vCont;s:0001'
[GDB] send resp: '$E01#a6'
[GDB] disconnect
[GDB] enter exit: 5
[GDB] ENTER st=5

@PoroCYon
Copy link
Contributor Author

I think it has something to do with this page, but I'm not smart enough (and mostly just don't have enough information about CLion's internal LLDB and the sequence of events that led to that error) to understand what it is saying in relation to what melonDS either is or isn't doing...

I don't think it means much. A "stop reply packet" is the packet sent from the gdbstub that tells the debug client why the debugged program is stopped (breakpoint, crash, user hit the 'pause now' button, etc.). Such a packet is supposed to be sent after a ? or vCont command. My gdbstub does implement this (as you can see in the log you just sent me):

[GDB] command in: 'vCont;s:0001'
[GDB] send resp: '$E01#a6'

As the CLion debugger thingy disconnects immediately after sending a single packet (without requesting the stop reply packet in the first place), it seems to be another meaningless error on their end.

As for the thumb thing, I'm not sure what's up. It should be reported correctly in the CPSR. Maybe LLDB does want the thumb bit set in the program counter, too (unlike gdb, cf this earlier discussion). Maybe you could try changing the 2 in this line to 1, but I'm not sure if it'll help.

@pleonex
Copy link

pleonex commented Sep 23, 2023

Thanks a lot for bringing this. It would be very useful for reverse engineering. I decided to give it a try but unfortunately I am couldn't make it on Windows with Ghidra or IDA. I think it's almost working.

For my setup, Ghidra and IDA do work right now with the gdbstub of desmume.

Ghidra

  • OS : Windows 11
  • Ghida 10.3.2
  • gdb-multiarch from msys2: GDB 13.2
  • GDB launch command: C:\\msys64\\mingw64\\bin\\gdb-multiarch.exe -i mi2 -iex 'set architecture arm'
  • Result
    • Registers work
    • Memory viewing doesn't work (errors parsing hexadecimal output)
    • Step into: I need to send the command 2-3 times before it goes to the next instruction
    • Breakpoints work
    • Break execution: it fails to pause after running but it happens also with desmume, seems to be a bug of pty of Ghidra
    • Continue work
    • Stop: melonDS finishes
  • Same results if I run gdb without Ghidra directly (but memory display works)
  • Logs:

gcc-arm-none-eabi-10.3-2021.10

Same issue with step into as Ghidra. Rest seems to work via CLI but for some reason Ghidra rejects this GDB.

IDA

  • IDA Home ARM Version 8.3.230608 Windows x64
  • Result:
    • Register work
    • Memory view: work
    • Step into: IDA throws a popup with the error Single-stepping a thread failed; possibly stub does not support single-stepping. Try disabling the single-step functionality in debugger-specific options.
    • Breakpoint: ? can't step or continue but the breakpoint is added in the UI
    • Continue: fails with Command "ProcessStart" failed
    • Break execution: works if I join the emulator without setting "break on startup"
    • Stop: melonDS still running even after closing window, ignores control+c
  • Logs: melonds_idahomearm.txt

@PoroCYon
Copy link
Contributor Author

I'm having trouble reproducing some of these, and if I don't, it's exceptionally hard to even figure out what's going on in the other tools. This difficulty in reproduction might be because I'm using a different platform (Linux, also I don't have IDA). But either way,

  • GDB itself seems to work. The only 'real' issue with GDB itself, from your report, seems to be the single-stepping. Right after reset release, the very first instruction seems to be 'duplicated' a few times, but after that, things work normally:
0xffff0000 in ?? ()
(gdb)stepi
0xffff0000 in ?? ()
(gdb)
(gdb)stepi
0xffff0000 in ?? ()
(gdb)stepi
0xffff00c0 in ?? ()
(gdb)stepi
0xffff8000 in ?? ()
(gdb)stepi
0xffff8004 in ?? ()
(gdb)stepi
0xffff8008 in ?? ()
(gdb)stepi
0xffff8078 in ?? ()
(gdb)stepi
0xffff807c in ?? ()
(gdb)stepi
0xffff8080 in ?? ()
(gdb)stepi
0xffff8084 in ?? ()
(gdb)stepi
0xffff8084 in ?? ()
(gdb)stepi
0xffff8088 in ?? ()
(gdb)stepi
0xffff808c in ?? ()
(gdb)stepi
0xffff8090 in ?? ()
(gdb)stepi
0xffff8094 in ?? ()
(gdb)stepi
0xffff8098 in ?? ()
(gdb)disassemble $pc,+0x20
Dump of assembler code from 0xffff8098 to 0xffff80b8:
=> 0xffff8098:	ldr	r0, [pc, #148]	@ 0xffff8134
   0xffff809c:	mcr	15, 0, r0, cr6, cr0, {0}
   0xffff80a0:	ldr	r0, [pc, #144]	@ 0xffff8138
   0xffff80a4:	mcr	15, 0, r0, cr6, cr1, {0}
   0xffff80a8:	ldr	r0, [pc, #140]	@ 0xffff813c
   0xffff80ac:	mcr	15, 0, r0, cr6, cr2, {0}
   0xffff80b0:	ldr	r0, [pc, #136]	@ 0xffff8140
   0xffff80b4:	mcr	15, 0, r0, cr6, cr3, {0}
End of assembler dump.
  • Ghidra does indeed seem to give errors wrt. something about threads not working. Poking around in the relevant Ghidra sources, however, lead to not much results because the Ghidra source code is an impenetrable mess of Enterprise-Grade Java Bullshit. I thus have no idea what it wants or what it's doing (it's even discarding the exception messages, which is great for debugging this /s).
  • As for the memory errors, I have no idea what's going on here. In the melonDS log, everything seems to be going fine. The Ghidra output complains about an "invalid hex digit 103" (that's the ascii code of g in decimal), but this doesn't even appear in the data melonDS' gdbstub is sending. The problem thus lies elsewhere.
  • Re: gcc-arm-none-eabi-10.3-2021.10: First of all, that's a build from Arm (the company), not from the people who make GDB (10.3 isn't even a real release number in the official version). So that's already starting to be fishy. But I downloaded the source and went ahead anyway, and saw no difference with GDB-multiarch 13.2. So I suppose it's just a matter of Ghidra being annoying.
  • I don't have IDA, so I can't debug this. However, Command "ProcessStart" failed sounds like IDA is doing something it isn't supposed to (trying to create a new process, instead of using the gdbstub remote), which will naturally fail. This might be because it's either misconfigured, or the implementation is bugged and doesn't support remote gdbstubs.

@pleonex
Copy link

pleonex commented Sep 25, 2023

Thanks for taking a look.

I think I have found the potential issue with the step / continue. IDA uses the command vCont;s to step in. The method Handle_v_Cont checks the command at cmd[0] but that's the semicolon ;. Changing to cmd[1] fixes that issue.
The following patch fixes it, now it can step, continue (no more ProcessStart failed errors) and stop with IDA.

diff --git a/src/debug/GdbCmds.cpp b/src/debug/GdbCmds.cpp
index 057502f2..d642ef6d 100644
--- a/src/debug/GdbCmds.cpp
+++ b/src/debug/GdbCmds.cpp
@@ -883,13 +883,13 @@ ExecResult GdbStub::Handle_v_MustReplyEmpty(GdbStub* stub, const u8* cmd, ssize_
 
 ExecResult GdbStub::Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len)
 {
-       if (len < 1)
+       if (len < 2)
        {
                stub->RespStr("E01");
                return ExecResult::Ok;
        }
 
-       switch (cmd[0])
+       switch (cmd[1])
        {
        case 'c':
                stub->RespStr("OK");

There is still the issue that sometimes it requires to send several times the step command to advance. I can reproduce that one with GDB directly. It happens at the beginning and randomly from time to time. In the following output it happened at: 0x02004800 , 0x02004a30, 0x02004a78 and 0x02004a94
doublestepi_gdboutput.txt
doublestepi_melonds.txt

So the last issue before I can easily use it, it's that step over doesn't work. It seems IDA is sending a regular vCont;s when we need a step over, so it goes inside the subroutine as expected. Comparing with desmume, in that case IDA first requests to setup a temp breakpoint after the jump and then it sends the command to continue (c in its case). So I am guessing something in the initial information melonds sends is configuring IDA differently (desmume doesn't support even half of the commands melonds does). I'll play with it to see if I find the cause.

@zurgeg
Copy link

zurgeg commented Oct 22, 2023

For me, GDB (with PEDA) doesn't show as much information. Take for instance an example executable:

[----------------------------------registers-----------------------------------]
RAX: 0x555555555175 (<main>:    push   rbp)
RBX: 0x7fffffffe1a8 --> 0x7fffffffe5c1 ("/home/jg/lame-patcher/example/main")
RCX: 0x555555557dd8 --> 0x5555555550f0 (endbr64)
RDX: 0x7fffffffe1b8 --> 0x7fffffffe5e4 ("PWD=/home/jg/lame-patcher/example")
RSI: 0x7fffffffe1a8 --> 0x7fffffffe5c1 ("/home/jg/lame-patcher/example/main")
RDI: 0x1 
RBP: 0x7fffffffe090 --> 0x1 
RSP: 0x7fffffffe090 --> 0x1 
RIP: 0x555555555179 (<main+4>:  lea    rax,[rip+0xea0]        # 0x555555556020)
R8 : 0x0 
R9 : 0x7ffff7fcecd0 (endbr64)
R10: 0x7fffffffddc0 --> 0x800000 
R11: 0x202 
R12: 0x0 
R13: 0x7fffffffe1b8 --> 0x7fffffffe5e4 ("PWD=/home/jg/lame-patcher/example")
R14: 0x7ffff7ffd000 --> 0x7ffff7ffe2d0 --> 0x555555554000 --> 0x10102464c457f 
R15: 0x555555557dd8 --> 0x5555555550f0 (endbr64)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x555555555174 <callme+43>:  ret
   0x555555555175 <main>:       push   rbp
   0x555555555176 <main+1>:     mov    rbp,rsp
=> 0x555555555179 <main+4>:     lea    rax,[rip+0xea0]        # 0x555555556020
   0x555555555180 <main+11>:    mov    rdi,rax
   0x555555555183 <main+14>:    call   0x555555555030 <puts@plt>
   0x555555555188 <main+19>:    mov    DWORD PTR [rip+0x2e92],0x1        # 0x555555558024 <global_variable>
   0x555555555192 <main+29>:    mov    eax,DWORD PTR [rip+0x2e8c]        # 0x555555558024 <global_variable>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe090 --> 0x1 
0008| 0x7fffffffe098 --> 0x7ffff7dd6cd0 (mov    edi,eax)
0016| 0x7fffffffe0a0 --> 0x7fffffffe190 --> 0x7fffffffe198 --> 0x7ffff7f91000 --> 0x7ffff7daf000 --> 0x3010102464c457f 
0024| 0x7fffffffe0a8 --> 0x555555555175 (<main>:        push   rbp)
0032| 0x7fffffffe0b0 --> 0x155554040 
0040| 0x7fffffffe0b8 --> 0x7fffffffe1a8 --> 0x7fffffffe5c1 ("/home/jg/lame-patcher/example/main")
0048| 0x7fffffffe0c0 --> 0x7fffffffe1a8 --> 0x7fffffffe5c1 ("/home/jg/lame-patcher/example/main")
0056| 0x7fffffffe0c8 --> 0x50a969a1adaea7a0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

and then on melonDS:

0x02083590 in ?? ()

Obviously I can just examine PC:

gdb-peda$ x/i $pc
=> 0x2083590:   swpb    r5, r5, [r1]

Maybe this is just an issue with PEDA or GDB. I don't really know. But yeah, there you go

@PoroCYon
Copy link
Contributor Author

I'm not sure what you're trying to convey. Why is the disassembly x86?

If you want symbols and a usable backtrace in the NDS binary being debugged, you have to load it first:

arm-none-eabi-gdb -r 'target remote localhost:3333' path/to/executable.elf

@zurgeg
Copy link

zurgeg commented Oct 22, 2023

I'm not sure what you're trying to convey. Why is the disassembly x86?

If you want symbols and a usable backtrace in the NDS binary being debugged, you have to load it first:

arm-none-eabi-gdb -r 'target remote localhost:3333' path/to/executable.elf

The example executable is x86. I was showing what normally happens on a standard debugging situation.

I'm not sure how to get the ELF file correctly. I attempted to objcopy the arm9.bin into an ELF file, because when I load it it gives "/home/jg/pokemon-white-shenanigans/arm9.elf" is not a core dump: file format not recognized.

But anyway, that's a problem for elsewhere

@RSDuck
Copy link
Member

RSDuck commented Oct 22, 2023

I really don't want to let this PR rot, so I'm just going to merge for now.

@RSDuck RSDuck merged commit 3ab752b into melonDS-emu:master Oct 22, 2023
@PoroCYon
Copy link
Contributor Author

I'm not sure how to get the ELF file correctly. I attempted to objcopy the arm9.bin into an ELF file, because when I load it it gives "/home/jg/pokemon-white-shenanigans/arm9.elf" is not a core dump: file format not recognized.

Yeah, no, you're supposed to get your ELF file from the compiler you use to compile the source code with. If you aren't debugging something that comes from an ELF file or with source code, there isn't much you can do to get symbols or stack traces.

I really don't want to let this PR rot, so I'm just going to merge for now.

I guess that's one way of doing it :D

@zurgeg
Copy link

zurgeg commented Oct 22, 2023

I'm not sure how to get the ELF file correctly. I attempted to objcopy the arm9.bin into an ELF file, because when I load it it gives "/home/jg/pokemon-white-shenanigans/arm9.elf" is not a core dump: file format not recognized.

Yeah, no, you're supposed to get your ELF file from the compiler you use to compile the source code with. If you aren't debugging something that comes from an ELF file or with source code, there isn't much you can do to get symbols or stack traces.

I really don't want to let this PR rot, so I'm just going to merge for now.

I guess that's one way of doing it :D

Ah okay, I was more wondering about it showing the current instruction and stack, but that's off topic. It's good that this got merged though!

kvnp pushed a commit to kvnp/melonDS that referenced this pull request Dec 25, 2023
* 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
@SeleDreams
Copy link

SeleDreams commented Sep 29, 2024

i noticed this is supposed to be merged in master but i don't see the option on melonds 0.9.5.
does it mean it's not yet in public releases and i have to build it from source ?

Edit : I just realised but yes since 0.9.5 is from 2022. It's very old.
I feel like a new build would be good, tho in the meantime i'll build it myself

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.