Debugging U-Boot with GDB
Using a JTAG adapter it is possible to debug a running U-Boot with GDB. A common way is to connect a debug adapter to the JTAG connector of your board, run a GDB server, connect GDB to the GDB server, and use GDB as usual.
Similarly, QEMU can provide a GDB server.
Preparing build
Building U-Boot with reduced optimization (-Og) and without link time optimization is recommended for easier debugging:
CONFIG_CC_OPTIMIZE_FOR_DEBUG=y
CONFIG_LTO=n
Otherwise build, install, and run U-Boot as usual.
Using OpenOCD as GDB server
OpenOCD is an open-source tool supporting hardware debug probes, and providing a GDB server. It is readily available in major Linux distributions or you can build it from source.
Here is example of starting OpenOCD on Debian using a J-Link adapter and a board with an AT91 SAMA5D2 SoC:
$ openocd -f interface/jlink.cfg -f target/at91sama5d2.cfg -c 'adapter speed 4000'
Open On-Chip Debugger 0.12.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "jtag". To override use 'transport select <transport>'.
adapter speed: 4000 kHz
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : J-Link V10 compiled Jan 30 2023 11:28:07
Info : Hardware version: 10.10
Info : VTarget = 3.244 V
Info : clock speed 4000 kHz
Info : JTAG tap: at91sama5d2.cpu tap/device found: 0x5ba00477 (mfg: 0x23b (ARM Ltd), part: 0xba00, ver: 0x5)
Info : at91sama5d2.cpu_a5.0: hardware has 3 breakpoints, 2 watchpoints
Info : at91sama5d2.cpu_a5.0: MPIDR level2 0, cluster 0, core 0, mono core, no SMT
Info : starting gdb server for at91sama5d2.cpu_a5.0 on 3333
Info : Listening on port 3333 for gdb connections
Notice that OpenOCD is listening on port 3333 for GDB connections.
Using QEMU as GDB server
When running U-Boot on QEMU you can used the ‘-gdb’ parameter to provide a GDB server:
qemu-system-riscv64 -M virt -nographic -gdb tcp::3333 -kernel u-boot
Running a GDB session
You need a GDB suited for your target. This can be the GDB coming with your toolchain or gdb-multiarch available in your Linux distribution.
gdb-multiarch u-boot
In the above command-line u-boot is the U-boot binary in your build directory. You may need to adjust the path when calling GDB.
Connect to the GDB server like this:
(gdb) target extended-remote :3333
Remote debugging using :3333
0x27fa9ac6 in ?? ()
(gdb)
This is fine for debugging before U-Boot relocates itself.
For debugging U-Boot after relocation you need to indicate the relocation address to GDB. You can retrieve the relocation address from the U-Boot shell with the command bdinfo:
U-Boot> bdinfo
boot_params = 0x20000100
DRAM bank = 0x00000000
-> start = 0x20000000
-> size = 0x08000000
flashstart = 0x00000000
flashsize = 0x00000000
flashoffset = 0x00000000
baudrate = 115200 bps
relocaddr = 0x27f7a000
reloc off = 0x0607a000
Build = 32-bit
current eth = ethernet@f8008000
ethaddr = 00:50:c2:31:58:d4
IP addr = <NULL>
fdt_blob = 0x27b36060
new_fdt = 0x27b36060
fdt_size = 0x00003e40
lmb_dump_all:
memory.cnt = 0x1 / max = 0x10
memory[0] [0x20000000-0x27ffffff], 0x08000000 bytes flags: 0
reserved.cnt = 0x1 / max = 0x10
reserved[0] [0x27b31d00-0x27ffffff], 0x004ce300 bytes flags: 0
devicetree = separate
arch_number = 0x00000000
TLB addr = 0x27ff0000
irq_sp = 0x27b36050
sp start = 0x27b36040
Early malloc usage: cd8 / 2000
Look out for the line starting with relocaddr which has the address
you need, 0x27f7a000
in this case.
On most architectures (not sandbox, x86, Xtensa) the global data pointer is stored in a fixed register:
Architecture |
Register |
---|---|
arc |
r25 |
arm |
r9 |
arm64 |
x18 |
m68k |
d7 |
microblaze |
r31 |
mips |
k0 |
nios2 |
gp |
powerpc |
r2 |
riscv |
gp |
sh |
r13 |
On these architectures the relocation address can be determined by dereferencing the global data pointer stored in register, r9 in the example:
(gdb) p/x (*(struct global_data*)$r9)->relocaddr
$1 = 0x27f7a000
In the GDB shell discard the previously loaded symbol file and add it once again, with the relocation address like this:
(gdb) symbol-file
Discard symbol table from `/home/adahl/build/u-boot/v2024.04.x/u-boot'? (y or n) y
No symbol file now.
(gdb) add-symbol-file u-boot 0x27f7a000
add symbol table from file "u-boot" at
.text_addr = 0x27f7a000
(y or n) y
Reading symbols from u-boot...
(gdb)
You can now use GDB as usual, setting breakpoints, printing backtraces, inspecting variables, stepping through the code, etc.