mGBA GDB Remote Debugging for GBA Homebrew: ELF Symbols and Breakpoints
Connect GDB to mGBA's GBA core, load matching ELF symbols, inspect ARM/Thumb execution, and keep the local debugger stub off untrusted networks.
An emulator debugger can answer more than “why does this screen look wrong?” For Game Boy Advance homebrew, mGBA’s GDB remote stub lets a desktop GDB client stop the emulated CPU, inspect registers and memory, and relate instruction addresses to symbols from the program’s ELF file. The workflow is most useful when the ROM, ELF, linker layout, and debugger all refer to the same build.
This is not a hardware probe. The GDB client is speaking to an emulated target, and a debugger stop changes the timing of the emulated session. A breakpoint can make a register or memory state inspectable, but it does not establish that the same issue occurs on a physical console or that a cycle-sensitive condition survives without the debugger attached.
Preserve the ROM-to-symbol relationship
Keep the linked ELF that contains symbols next to the .gba image produced from that build. Compile and link the development program with debug information, retain the map file if the toolchain generates one, and record the ROM and ELF hashes together. Do not strip or rebuild one artifact after generating the other and then assume that familiar function names still point at the matching bytes.
GDB needs an unstripped host-side executable or symbol file to resolve source and symbol names. mGBA loads the ROM into the emulated GBA core; GDB’s local ELF argument supplies the symbol information. These are separate files with separate roles. If the emulator loads one ROM while GDB reads an ELF from another build, the target may still stop but source-level labels and line mappings can be misleading.
The command-line details below follow the mGBA 0.10.5 manual and source. Check the upstream release page and the installed build before depending on a port, frontend, or debugger detail; downstream packages can disable optional debugger features.
Start the emulator stub and attach a cross-debugger
The mGBA manual documents -g as starting a GDB session, with port 2345 as the default. For a development ROM, start mGBA with the .gba file and that option:
mgba -g build/demo.gba
Use the executable and launch conventions provided by the installed mGBA package. Keep this process local while testing. In the pinned 0.10.5 implementation, the GDB listener is initialized on the IPv4 loopback address and the default port 2345; it is not intended as a network service for other hosts.
In a second terminal, start an ARM-targeting GDB build with the matching ELF and attach to the local stub:
$ arm-none-eabi-gdb build/demo.elf
(gdb) target remote 127.0.0.1:2345
(gdb) info registers
(gdb) break main
(gdb) continue
The target remote form is standard GDB syntax. mGBA starts the emulated target and its stub; GDB attaches to that remote target rather than launching a local inferior. Pass the ELF on the GDB command line or use file build/demo.elf before setting symbolic breakpoints. Confirm that main exists in the symbol table and that the debugger selected an ARM-compatible target configuration when source names or disassembly look wrong.
The GBA CPU can execute both ARM and Thumb code. A raw program counter or ROM-file offset should not be substituted for a linked symbol address without understanding the ELF load layout and instruction state. Start from a symbol, inspect the disassembly around the stop, and compare the map file and linker script when the displayed source line does not match the expected code. Do not infer that a break at a C function covers every inlined or optimized source statement; compile a debug build whose optimization level is appropriate for the question being asked.
Inspect a stop as a bounded observation
After a breakpoint, record the stop reason and registers before changing state. Then inspect only the memory range needed to test the hypothesis. For example, if the test program intentionally owns a work-RAM structure at an address established by its linker map, a GDB memory examination such as x/8wx ADDRESS can show eight 32-bit words at that address. Replace ADDRESS with a confirmed target address; do not copy a host pointer or a symbol from an unrelated build.
For repeatable bug reports, write down the mGBA version and build flavor, ROM and ELF hashes, GDB version, breakpoint or watch expression, initial save/state files, and the exact input sequence that reaches the stop. Save the register dump and a short disassembly window along with the source commit. An emulator save state can help reproduce a setup, but it is not a substitute for recording the ROM, external assets, firmware/BIOS choice, or configuration that produced it.
If the debugger cannot connect, check that the local emulator was launched with -g, that its build includes the GDB stub, and that another process is not already using port 2345. Do not “fix” a local connection problem by binding the debugger to all interfaces or forwarding the port from a router. A remote debugger can inspect and modify emulated execution state; keep it on loopback or an intentionally isolated development network.
If a breakpoint never triggers, confirm that the ELF and ROM came from the same build, that the symbol is present, and that the program actually reaches the address. Check whether startup is in a boot path before main, whether optimization changed the source-level breakpoint location, and whether an interrupt or alternate execution mode is involved. If a source breakpoint is unreliable, set a breakpoint at a verified disassembly address and document the address together with the ELF hash. Then reproduce with the command-line debugger or a second known mGBA build only as a controlled comparison, not as proof that the first observation was wrong.
Know what the emulator debugger does not establish
mGBA’s GDB support complements its command-line debugger; it is a separate interface, not a guarantee that every GDB feature or remote-protocol extension behaves like a hardware probe. The upstream repository describes GDB remote debugging as a supported game-debugging workflow, and the release-pinned source shows the local listener and debugger attachment path. Validate specific operations against the exact version in use rather than assuming that a watchpoint mode, memory region, or frontend build exposes identical behavior everywhere.
Use the emulator to make software state observable, then verify timing-sensitive or device-specific behavior against documented hardware constraints and, when necessary, a real console or a dedicated test suite. Debugging results are strongest when the executable identity is controlled, the stopping point is reproducible, the target boundary is explicit, and the report distinguishes emulated evidence from hardware evidence.
Related:
- Game Boy Advance WAITCNT: Game Pak Timing, Sequential Access, and Prefetch
- Game Boy Advance DMA: Channel Priority, Trigger Timing, and Sound FIFOs
Sources:
- mGBA 0.10.5 release-pinned manual: GDB option and default port
- mGBA 0.10.5 release-pinned GDB listener implementation
- mGBA 0.10.5 release-pinned GDB remote stub
- mGBA 0.10.5 build configuration: optional GDB-stub feature
- mGBA 0.10.5 release-pinned command-line debugger
- mGBA upstream release page
- GNU GDB manual: Connecting to a remote target and host symbol files