docdoob
Posts: 3
Joined: Fri May 03, 2013 3:25 pm

Simple debugging of an RPi0 program with QEMU/GDB

Thu Dec 24, 2020 6:49 pm

I needed to debug the part of my program that sets BSS to zero - I had modified that part of the start-up file from the standard one in the run-time library. I couldn’t get the compiler to set up a variety of test data (i.e. different BSS sizes) so I tried to learn what QEMU (an emulator) and then GDB (a debugger) could do. I managed to get far enough to prove my code and here is what I did. I’d appreciate any ideas for improvement for when I turn to QEMU/GDB again. I didn’t need to do any I/O so I didn’t try that.

I am using Linux (Ubuntu). First install qemu-system-arm and gdb-multiarch, however that is normally done on your PC.
I opened a terminal, and the QEMU command I used is:
qemu-system-arm -kernel ELF-file -M raspi2 -cpu arm1176 -m 256M -s -S -nographic
• -kernel means your program is In ELF-file, in linkable binary not the pure binary that you put onto the Rpi0.
• -M specifies the ‘machine’ being emulated, which is a raspi2.
• arm1176 is the RPi0 CPU; -m 256M is the memory allowed.
• -s means wait to connect to GDB on TCP port 1234.
• -S means ‘Do not start CPU at startup’.
• No graphical output is expected.

Enter “Ctrl-A c” to switch to the QEMU console.

Open a second terminal and make it a large window. To make use of GDB you need to compile your program files(s) with debugging enabled (usually the -g option). I even had to recompile the run-time library as the code I was testing is in there.
The GDB command I used is:
gdb-multiarch ELF-file -command=gdb-init-file
I immediately needed to execute the same GDB commands each time, so these are what are in the command file gdb-init-file. There is a standard init file: .gdbinit, which you can use instead.
These initial commands are:
layout regs # use TUI, the 3-window interface
target remote localhost:1234 # connect to QEMU
winheight regs +3 # make the registers window 3 lines taller
set scheduler-locking on # restrict QEMU to 1 core
thread 1 # use core No. 1
Remember that the RPi2 has 4 cores. QEMU runs all 4 in pseudo-parallel, in fact going through them in sequence whenever you ask GDB to run your program. This makes testing quite tedious so it’s worth avoiding this. I think you can also avoid this by using a dummy(?) machine: versatilepb, which seems to be popular. But I wanted to stick to an RPi.

The 3 windows are named: regs; src; cmd. To use the arrow keys to wander about them you first need to focus. For example, to use the up arrow to re-use previous GDB commands you first have to:
focus cmd
You can also execute a file of commands from within GDB:
source filename
It is also useful to have sight of the source files and the disassembly of the binary, created using arm-linux-gnueabi-objdump. I put these into an editor.
Then you run your program from GDB with the usual commands:
b set breakpoint, using source line numbers is easiest
s a single step
c run until breakpoint
My results were always in memory and I didn’t have variable names as the code was assembler. So I examined memory with e.g.:
x /27w 0x8000
This means display 27 words starting at hex 8000.
I also needed to change memory values to test different BSS sizes. I used the set command for this, e.g.:
set {int}0xac24 = 0x11111111
When debugging the output from a compiler you can use variable names for displaying and changing values, e.g.:
p count
for displaying the value of “count” in the current file, or, more generally:
p source-filename::variable
Finally, I needed to restart the program for each test run. You can do this in QEMU with the system_reset command at the console.

I learnt from the following information sources, in the main:
• A useful list of GDB commands: http://www.yolinux.com/TUTORIALS/GDB-Co ... B_COMMANDS
• The official GDB user documentation: https://sourceware.org/gdb/current/onl ... C_Contents
• More information on using QEMU with GDB: https://qemu.readthedocs.io/en/latest/system/gdb.html
• The list of official QEMU documentation: https://wiki.qemu.org/Documentation
I’d appreciate any advice to take this introduction further. Particularly, I have no idea on how to handle I/O, e.g. with GPIO or the graphics co-processor.

Return to “Bare metal, Assembly language”