Debugging U-Boot after relocating to RAM

If some day you find yourself debugging u-boot, or maybe another bootloader, you will probably face a problem when setting breakpoints at some symbols; the application doesn’t stop where you have told the gdb to.

Bootloaders usually are written to the system’s flash so the microprocessor starts executing there at a specific address and at some point they copy themselves to RAM in order to perform different kind of operations.

Once you have compiled u-boot, the symbol table you have is referenced to the flash starting address where u-boot resides; once it has been relocated to RAM the addresses for these symbols changes too.

In this post, I am just going to describe a little trick to get the offset used by u-boot to relocate itself when running on QEMU-ARM (versatilepb).

First step is building u-boot for QEMU, just grab the latest u-boot’s version and compile it as follows; adding first -ggdb option to config.mk’s PLATFORM_CPPFLAGS variable:

$ make versatilepbqemu_config ARCH=arm CROSS_COMPILE=
$ make

As result you should get several files. Among them, u-boot, containing the whole debugging information, and u-boot.bin, ready to be run on qemu as follows:

$ emu-system-arm -M versatilepb -nographic -kernel u-boot.bin -gdb tcp::3333 -S

This tells qemu to emulate a versatilepb board, to output text on the same terminal, to run u-boot.bin starting at 0x10000 and wait for a gdb connection on the specified port.

Somewhere else we have to tell gdb to attach to a remote server and set a break at board_init_f(), where u-boot calculates the new offset for later performing the relocation:

(gdb) file u-boot  
Reading symbols from /home/ggonzalez/Projects/u-boot-lua/u-boot...done.
(gdb) target remote :3333
Remote debugging using :3333
0x00000000 in ?? ()
(gdb) b board_init_f
Breakpoint 1 at 0x10840: file board.c, line 266.
(gdb) c
Continuing.
 
Breakpoint 1, board_init_f (bootflag=0) at board.c:266
266	{
(gdb)

Now that we have reached the desired function we continue executing a few lines forward till we see the line where u-boot calculates the relocation offset:

(gdb) next
440		gd->reloc_off = addr - _TEXT_BASE;
(gdb) p/x (addr - _TEXT_BASE)
$1 = 0x7fb4000
(gdb) p/x addr
$2 = 0x7fc4000

Now we have both the relocation offset and the address where the relocated version of u-boot is going to be placed, this is the one we need for loading again the symbol’s table.

We now discard the current symbols and reload them again using the address we just found. To check everything is working perfectly just set a breakpoint at do_version() which will be reached if you invoke version on u-boot’s shell.

(gdb) symbol-file 
Discard symbol table from `/home/ggonzalez/Projects/u-boot-lua/u-boot'? (y or n) y
No symbol file now.
(gdb) add-symbol-file u-boot 0x7fc4000
add symbol table from file "u-boot" at
	.text_addr = 0x7fc4000
(y or n) y
Reading symbols from /home/ggonzalez/Projects/u-boot-lua/u-boot...done.
(gdb) b do_version
Breakpoint 2 at 0x7fc79a4: file cmd_version.c, line 32.
(gdb) c
Continuing.
 
Breakpoint 2, do_version (cmdtp=0x7fec0e8, flag=0, argc=1, argv=0x7fa1ec8) at cmd_version.c:32
32	{
(gdb)