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”
Jump to
- Community
- General discussion
- Announcements
- Other languages
- Deutsch
- Español
- Français
- Italiano
- Nederlands
- 日本語
- Polski
- Português
- Русский
- Türkçe
- User groups and events
- The MagPi
- Using the Raspberry Pi
- Beginners
- Troubleshooting
- Advanced users
- Assistive technology and accessibility
- Education
- Picademy
- Teaching and learning resources
- Staffroom, classroom and projects
- Astro Pi
- Mathematica
- High Altitude Balloon
- Weather station
- Programming
- C/C++
- Java
- Python
- Scratch
- Other programming languages
- Windows 10 for IoT
- Wolfram Language
- Bare metal, Assembly language
- Graphics programming
- OpenGLES
- OpenVG
- OpenMAX
- General programming discussion
- Projects
- Networking and servers
- Automation, sensing and robotics
- Graphics, sound and multimedia
- Other projects
- Gaming
- Media centres
- AIY Projects
- Hardware and peripherals
- Camera board
- Compute Module
- Official Display
- HATs and other add-ons
- Device Tree
- Interfacing (DSI, CSI, I2C, etc.)
- Raspberry Pi 400
- Raspberry Pi Pico
- General
- SDK
- MicroPython
- Other RP2040 boards
- Operating system distributions
- Raspberry Pi OS
- Raspberry Pi Desktop for PC and Mac
- Other
- Android
- Debian
- FreeBSD
- Gentoo
- Linux Kernel
- NetBSD
- openSUSE
- Plan 9
- Puppy
- Arch
- Pidora / Fedora
- RISCOS
- Ubuntu
- Ye Olde Pi Shoppe
- For sale
- Wanted
- Off topic
- Off topic discussion