U-boot debugging. part 2.

In previous chapter we got U-boot debuggable. Partially debuggable as we turned out. The thing we missed is relocation. The workaround provided in that document is simple enough but not might be comfortable. Here suggested workaround adopted to NetSoM to get fully functional u-boot debugging procedure.

Command line mode debugging

  • Start GDB server:
$ JLinkGDBServer -device MCIMX6Y2 -if JTAG -speed 1000
  • Start GDB session:
$ gdb-multiarch u-boot —nx

that command has to be called from U-boot folder (build_dir/target-arm_cortex-a7+neon-vfpv4_musl_eabi/u-boot-wirelessroad_ecspi3/u-boot-2017.07 in our case) with u-boot image compiled with debugging symbols

  • Connect to target device and load image:
(gdb) target remote localhost:2331
(gdb) monitor reset
(gdb) monitor halt
(gdb) monitor sleep 200
(gdb) load

check that program counter set correctly:

(gdb) display /x $pc
1: /x $pc = 0x87800000

here 0x87800000 is starting address u-boot image is placed.

  • Restore device tree binary blob:
(gdb) restore u-boot.dtb binary 0x87882d68
Restoring binary file u-boot.dtb into memory (0x87882d68 to 0x8788953b)

That address here is the end address of image.  It need to be done as in production mode (without debugger) U-boot checks for device tree binary blob at the address placed right after image:

$ ls -la u-boot.dtb u-boot-dtb.bin u-boot-nodtb.bin
-rw-rw-r— 1 al al 26579 окт 22 17:19 u-boot.dtb
-rw-rw-r— 1 al al 562491 окт 24 14:26 u-boot-dtb.bin
-rwxrwxr-x 1 al al 535912 окт 24 14:26 u-boot-nodtb.bin

here you see size of u-boot-nodtb.bin (u-boot image without dtb appended to the end) = 535912 = 0x82D68.
So if you will add it to the starting address of u-boot being placed: 0x82D68 + 0x87800000 = 0x87882D688 you will get it. Binary blob of dts — u-boot.dtb has size 26579. Being appended to u-boot-nodtb.bin (size 535912) gives you u-boot-dtb.bin (size 562491).
Another way to get this address — you can check for value of __end address in u-boot.map file:

$ cat u-boot.map | grep __end
.__end 0x0000000087882d68 0x0 arch/arm/lib/built-in.o
  • Set breakpoint at relocate_code to prevent jumping to unprepared memory area and start execution:

(gdb) b relocate_code
Breakpoint 1 at 0x87802db4: file arch/arm/lib/relocate.S, line 81.
(gdb) c
Breakpoint 1, relocate_code () at arch/arm/lib/relocate.S:81
81 stmdb sp!, {lr}
1: /x $pc = 0x87802db4

at this moment program counter passed for board_init_f initialization procedure so we can place symbol file at target memory area program counter going to jump:

(gdb) add-symbol-file u-boot 0x8FF2B000
add symbol table from file «u-boot» at
.text_addr = 0x8ff2b000
(y or n) y
Reading symbols from u-boot…

without it you can continue executeion. Nothing brakes but you will not be able to set breakpoints as they will be placed in memory area starting from 0x87800000 u-boot image place initially while actual execution will be at relocated area starting from 0x8FF2B000. So by adding debugging symbols by new address you will get duplicated addresses for each debugging symbol:

(gdb) b board_init_r
Breakpoint 2 at 0x87815b4c: board_init_r. (2 locations)

and now is time for controlled jump:

(gdb) display /x $pc
2: /x $pc = 0x87802db4
(gdb) fin
Run till exit from #0 relocate_code () at arch/arm/lib/relocate.S:81
_main () at arch/arm/lib/crt0.S:116
116 bl relocate_vectors
1: /x $pc = 0x8ff2d8cc
2: /x $pc = 0x8ff2d8cc
(gdb) display /x $pc
3: /x $pc = 0x8ff2d8cc

here we check for program counter before relocate_code function being executed — 0x87802db4 and after — 0x8ff2d8cc. As you see we jumped to new memory area. Continue execution:

(gdb) c

Breakpoint 2, board_init_r (new_gd=0x8ef28eb8, dest_addr=2415046656) at common/board_r.c:904
904 {
1: /x $pc = 0x8ff40b4c
2: /x $pc = 0x8ff40b4c
3: /x $pc = 0x8ff40b4c

Still able to catch PC by breakpoints.
How to determine relocation address? Explained in same document mentioned above. You can check it by typing bdinfo command in u-boot console:

=> bdinfo
arch_number = 0x00000000
boot_params = 0x80000100
DRAM bank = 0x00000000
-> start = 0x80000000
-> size = 0x10000000
eth0name = FEC
ethaddr = 86:72:04:c5:7e:83
current eth = FEC
ip_addr =
baudrate = 115200 bps
TLB addr = 0x8FFF0000
relocaddr = 0x8FF2B000
reloc off = 0x0872B000
irq_sp = 0x8EF216C0
sp start = 0x8EF216B0
Early malloc usage: 188 / 400
fdt_blob = 8ef216d8

here relocaddr is memory address u-boot going to relocate itself and fdt_blob — address device tree binary blob to be found. Another way to get relocation address is interrupt execution of u-boot under debugger and type following:

Program received signal SIGTRAP, Trace/breakpoint trap.
0x8ff62d00 in mxc_serial_getc () at drivers/serial/serial_mxc.c:161
161 while (__REG(UART_PHYS + UTS) & UTS_RXEMPTY)
1: /x $pc = 0x8ff62d00
2: /x $pc = 0x8ff62d00
3: /x $pc = 0x8ff62d00
(gdb) p /x gd->relocaddr
$1 = 0x8ff2b000


Doing same in Eclipse.

in eclipse we have to provide same commands to load dtb blob, to set breakpoint:

u-boot debugging

Then we have to start execution:

it should stop at relocate_code function. Same commands we entered to load debugging symbols to relocation are to be entered here in Debugger console:

u-boot debugging. relocation.

Continue execution and it will stop at board_init_r function.

So, finally we got fully debuggable and controllable u-boot. Next time we will pass u-boot initialization procedure from start till the end to learn how it is.

U-boot initialization sequence