December 24, 2023
STM32H747I-DISCO "Discovery kit with STM32H747XI MCU" -- debugging using Gdb
Back in 2016 under Fedora 24, the arm-none-eabi-gdb package was readily available in the Fedora
package collection. Then for some reason it vanished. I just got very lucky and tripped over
the following:
So, arm-none-eabi-gdb is available in Fedora "copr".
I follow the instructions. I already have dnf-command installed, so:
dnf copr enable rleh/arm-none-eabi-gdb
dnf install arm-none-eabi-gdb
It works! I start it and "show version" gives me "GNU gdb (GDB) 14.1".
How to start Gdb with openocd
I have some notes from when I played with this and the STM32F103.
I end up using at least 4 windows:
- One to run openocd
- One for "telnet localhost 4444" (not strictly needed)
- One for gdb
- One for building, and viewing the "dump" file (disassembled code).
In the gdb window, you type:
arm-none-eabi-gdb --eval-command="target remote localhost:3333" blink.elf
I put this into a little script "gdb_start".
A side note
Along with "gdb -tui" there is cgdb and ddd. One fellow says that
"GDB is not simply a tool, it’s a debugging framework for you to build upon".
How to use gdb
Compile with the "-g" flag to make things pleasant.
If you forget the elf file at the end of the gdb line use "file blink.elf" to get the debug information.
Here are some gdb commands:
i r -- shows the registers
stepi -- execute one assembly instruction
nexti -- as above, but a function call is executed until it returns
disas 0x50, 0x60 - disassemble from start to end
x/nfu 0x40 - examines memory
n = how many
f = format (usually x for hex), maybe x, i, s
u = how big b, h, w, g are 1,2,4,8 byte objects
continue -- run to breakpoint (or forever)
note that "reset halt" in openocd window will get control
gdb -tui
Here "tui" stands for "text user interface".
Also note that "-tui" is a switch to gdb.
Once again, I put the command to start this into a little bash script "tui_start" which has:
arm-none-eabi-gdb -tui --eval-command="target remote localhost:3333" debug.elf
Once you get it started, type this to get an assembly listing in the upper pane:
layout asm
Next you should type Control-X s to put it into single key mode,
then the following will be single key shortcuts for longer commands:
s = step
n = next
c = continue
r = run
f = finish
d = down
u = up
w = where
v = info locals
New frontiers
Some searching led me to this:
Although it deals with embedded linux rather than bare metal, it is very good and I learned many valuable things.
There is a program "ddd" that is a gui front end for gdb.
It is old, Motif based, and works by "scraping" the gdb console output,
but people like the end result. I should give it a try.
There is also "cgdb" which is a curses based front end to gdb, reportedly better than "gdb -tui"
And there is this wonderful quote from Brian W. Kernighan:
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the
code as cleverly as possible, you are, by definition, not smart enough to debug it."
This Gdb tutorial is very good:
What I do to debug a STM32 system
- Run "./ocd_start" in one window
- In another window, run "telnet localhost 4444"
- Run "./tui_start" in a third window
- Use a fourth window to view source or a dump disassembly
I load the code by typing "program xyz.elf" in the telnet window.
I do not do reset so it will be waiting for gdb to set breakpoints or
whatever.
Then in the "tui" window, I do:
- list main.c:1
- b startup
- b some_func
- disp some_variable
- continue
- next
There is plenty more to learn, but this gets me going in a basic way.
Amazingly, breakpoints work even with code in flash.
Be sure to compile with the -g option. Consider using the -Og option which limits the
optimizer to using only gdb friendly optimizations. Some variables may need to be declared
volatile if you intend to use them as sentinels for gdb
More about gdb
I am still not sure what the best way is to make changes, recompile, and then load the program
to get going again. I exit "tui" and use the telnet window to "program xyz.elf", then restart
the "tui" window, set breakpoints and on we go. Maybe typing "file xyz.elf" in gdb would do it.
- Use next to step over function calls (you are still single stepping)
- Use "step" to step into a function call.
- Use "finish" when you have stepped into a function and want to run to the end.
- return repeats the last command (handier than typing step or next over and over).
- Use "stepi" to step at the assembly language level.
- Use quit or exit to get out of gdb
- run doesn't work with openocd, use continue to get started
- Use i b (info breakpoints) to list all breakpoints
- you can enable or disable breakpoints by number
- you can clear a breakpoint (by name/address)
- use "undisplay" when you get tired of a variable (give the display number)
- use "i d" to get info about displayed variables
- use "print" to see a variable just once, i.e. "p var"
- use "p/x" to see a variable in hex "p/x var"
- use "bt" to get a backtrace
- use "advance" to set a temporary breakpoint and advance to it.
- set variable xyz = 99
- set (xyz = 99)
- use jump to run at a given location, set a breakpoint there first, maybe
- use "p &xyz" to get the address of a variable.
- or use "info address xyz"
- use "i r" to see registers
- use "i r pc" to see just the PC
- use "watch xyz" to watch when a variable is written to.
- use "awatch xyz" to watch when a variable is accessed (read or written).
- use "rwatch xyz" to watch when a variable is read.
- watch can take an expression (like a variable element or structure element)
- "layout asm" switches to assembly code
- "layout src" switches back to C source
Dumping memory:
- x addr
- return gives subsequent values
- x/nfu is the general form
- x/4 addr dumps 4 values
- x/4x dumps 4 values in hex (hex is the default)
- x/4xb dumps 4 bytes
- x/10i startup dumps 10 instructions (disassembly)
Interestingly, there is no explicit way to modify memory, but
you can do this:
- set *0x58024428 0x02020000
I got tired of it asking me about subprocesses every time I told it to quit.
To avoid this, I put this into my .gdbinit:
define hook-quit
set confirm off
end
Restarting
This did not work until I made this change to start gdb:
#arm-none-eabi-gdb -tui --eval-command="target remote localhost:3333" blink.elf
arm-none-eabi-gdb -tui --eval-command="target extended-remote localhost:3333" blink.elf
Now, with "extended-remote" I can type "r" to run, and this nicely restarts the code.
This means that I don't need to exit gdb to reload the software. I use "program xyz.elf"
in the telnet window connected to openocd, then I can do:
b rcc_init
r
And it sets a breakpoint and runs the new code. Very nice.
Fancy keyboard stuff
Consider putting this into your .inputrc file (this will also affect bash).
set editing-mode vi
I tried it, and it turns out that is only half the story.
Another trick is required. Type "focus cmd" and you will be able to use arrow keys.
And with the above line in your .inputrc, you will get the full vim style
line editing as well. Typing "focus src" lets you scroll the top window.
Control-X does all kinds of interesting stuff. Ctrl-X A toggles in and out of TUI mode.
Ctrl-X s enters TUI single key mode. There is more.
Colors
Gdb tries to do syntax highlighting. My terminal has a black background and the blue used for addresses
is all but unreadable. Here are several options:
- set style address foreground red
- set style address foreground white
- set style enabled off
You can put commands like this into a .gdbinit file.
Feedback? Questions?
Drop me a line!
Tom's Computer Info / tom@mmto.org