This is Part 5 of my Cortex‑M7 without hardware series.
We already have an ELF (Parts 1-3) and understand what it holds (Part 4). Now we finally execute it (what we all came here for) in simulator known as Renode and attach GDB to see that the ELF is running while also demonstrating a couple of classic GDB debugging tricks (printing the loop variable, watchpoints, register/memory inspection).
Renode
Running the ELF executable produced by this project on standard operating systems is not possible due following message being printed:
…cannot execute binary file: Exec format error
As we want to run it without hardware, there is need for extra tooling and that where Renode comes in. 1
Installation
Renode is not a small project and it has plenty of dependencies. I recommend using the official packages / portable build as described in the Installation header on README. 2
The version used by the project is 1.16.0. 3
Minimal Renode script
The supported boards list shows STM32F7 targets, and the Renode repository contains STM32F746 platform descriptions. 4 5
Project contains renode/stm32f746.resc script, that utilizes said descriptions:
$name?="STM32F746"
# Create Machine & Load config
mach create $name
machine LoadPlatformDescription @platforms/cpus/stm32f746.repl
# Enable GDB
machine StartGdbServer 3333
macro reset
"""
sysbus LoadELF @arm.elf
"""
runMacro $reset
Here is further explanation on what it does:
- Machine is created for emulation with
mach create6- At this point, machine only has
sysbusperipheral
- At this point, machine only has
- STM32F746 platform is loaded with
machine LoadPlatformDescription. 7- This adds further peripheral such as memory and CPU, among other things, to machine
- Enable debugger to connect to target
with machine StartGdbServer 33338 - Create a reset macro variable with
macro reset9- This macro is used when machine Reset method is called
- Call the macro with
runMacro $reset9 - Macro executes
sysbus LoadELF @arm.elf10- This uploads the software to machine
Start Renode and the GDB server
Configure the project, just like in Part 3:
git clone \
--branch minimal --single-branch \
https://gitlab.com/sorhanp/arm.git \
arm-minimal
cd arm-minimal
cmake -DCMAKE_TOOLCHAIN_FILE=toolchain/arm-none-eabi.cmake \
-DCMAKE_BUILD_TYPE=Debug \
-B build
Build the target and run renode using CMake’s utility target:
cmake --build build/ --target run-renode
Renode’s documentation related to GDB debugging explains that emulation time does not progress while the CPU is in halted state (which is the default state if no additional parameters are set to StartGdbServer), making debugging deterministic. 8
GDB
Depending on the cross compiler toolchain, the command for debugger might differ, but main idea is to call the debugger that is designed for cross compile debugging, thus the command might be
arm-none-eabi-gdb build/arm.elf
Or
gdb-multiarch build/arm.elf
Which ever is used the GDB should start and this should be the last printout:
Reading symbols from build/arm.elf...
(gdb) target remote :3333
(gdb) monitor start
(gdb) b main
(gdb) continue
This is the standard remote attach flow along with starting the emulation. [11]
Read the loop variable (counter)
Because counter is declared volatile and build has debug info, it typically lives in memory and can be inspected.
(gdb) p counter
$1 = 0
(gdb) display counter
1: counter = 0
The print and display commands can be used inspecting current value variables. 11
Examine the raw memory backing counter
(gdb) p &counter
$2 = (int *) 0x2004ffec
(gdb) x/wx &counter
0x2004ffec: 0x00000000
The x command for examine memory and print formats are documented in GDB’s examining data section. 12
Watchpoints (data breakpoints)
Stop whenever counter changes:
(gdb) watch counter
Hardware watchpoint 2: counter
(gdb) continue
Continuing.
Hardware watchpoint 2: counter
Old value = 0
New value = 1
0x08000020 in main () at /home/sorhanp/projects/arm-minimal/arm/main.cpp:12
12 counter++;
1: counter = 1
Watchpoints stop execution when an expression changes and they can be hardware (like here) or software based. 13
Registers and instruction stream
(gdb) info registers
r0 0x0 0
r1 0x0 0
r2 0x0 0
r3 0x1 1
r4 0x0 0
r5 0x0 0
r6 0x0 0
r7 0x2004ffe8 537198568
r8 0x0 0
r9 0x0 0
r10 0x0 0
r11 0x0 0
r12 0x0 0
sp 0x2004ffe8 0x2004ffe8
lr 0x800002b 134217771
pc 0x8000020 0x8000020 <main()+16>
xpsr 0x1000000 16777216
fpscr 0x0 0
msp 0x0 0
psp 0x0 0
primask 0x0 0
basepri 0x0 0
faultmask 0x0 0
control 0x0 0
(gdb) p/x $pc
$3 = 0x8000020
(gdb) x/i $pc
=> 0x8000020 <main()+16>: b.n 0x800001a <main()+10>
info registers along with the $pc/$sp convenience names can be used to display machine’s registers. Here pc stands for program counter and it stores the address of next instruction to be run. 14
References
Github renode: https://github.com/renode/renode ↩︎
Github renode installation: https://github.com/renode/renode/tree/master?tab=readme-ov-file#installation ↩︎
Github renode 1.16.0: https://github.com/renode/renode/releases/tag/v1.16.0 ↩︎
Renode supported boards: https://renode.readthedocs.io/en/latest/introduction/supported-boards.html ↩︎
Renode platform description (STM32F746): https://github.com/renode/renode/blob/master/platforms/cpus/stm32f746.repl ↩︎
Renode creating machines: https://renode.readthedocs.io/en/latest/basic/machines.html#creating-machines ↩︎
Renode loading platforms: https://renode.readthedocs.io/en/latest/basic/machines.html#loading-platforms ↩︎
Renode debugging with GDB: https://renode.readthedocs.io/en/latest/debugging/gdb.html#debugging-with-gdb ↩︎ ↩︎
Renode monitor variable types: https://renode.readthedocs.io/en/latest/basic/monitor-syntax.html#monitor-variable-types ↩︎ ↩︎
Renode loading binaries: https://renode.readthedocs.io/en/latest/basic/machines.html#loading-binaries ↩︎
GDB program variables (
print,x, formats): https://sourceware.org/gdb/current/onlinedocs/gdb.html/Variables.html#Variables ↩︎GDB examining memory https://sourceware.org/gdb/current/onlinedocs/gdb.html/Memory.html#Memory ↩︎
GDB setting watchpoints: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Set-Watchpoints.html ↩︎
GDB registers : https://sourceware.org/gdb/current/onlinedocs/gdb.html/Registers.html ↩︎