Working with GDB

A simple example of working with GDB is shown here. The debugging process itself is too complex to learn it step-by-step, it requires existing knowledge and experience in software development.

To become more familiar with GDB you can search for other guides on the web, for example Debugging with GDB. 

Uploading the firmware to the Flipper Zero

For the debugger to work correctly, the firmware on the device should match the firmware files on your computer. To accomplish that you may need to flash the Flipper Zero. You can learn more on how to compile or acquire the needed firmware files on the First start page.

To upload the firmware to the Flipper Zero, run the load command:

(gdb) load Loading section .isr_vector, size 0x13c lma 0x8000000 Loading section .text, size 0xa92a4 lma 0x8000140 Loading section .rodata, size 0x24e18 lma 0x80a93e8 Loading section .ARM, size 0x8 lma 0x80ce200 Loading section .init_array, size 0x58 lma 0x80ce208 Loading section .fini_array, size 0x4 lma 0x80ce260 Loading section .data, size 0x5f4 lma 0x80ce264 Start address 0x08090928, load size 845904 Transfer rate: 24 KB/sec, 968 bytes/write.

Run the compare-sections command to verify that the data was written correctly.

(gdb) compare-sections Section .isr_vector, range 0x8000000 -- 0x800013c: matched. Section .text, range 0x8000140 -- 0x80a93e4: matched. Section .rodata, range 0x80a93e8 -- 0x80ce200: matched. Section .ARM, range 0x80ce200 -- 0x80ce208: matched. Section .init_array, range 0x80ce208 -- 0x80ce260: matched. Section .fini_array, range 0x80ce260 -- 0x80ce264: matched. Section .data, range 0x80ce264 -- 0x80ce858: matched.

If the sector data doesn't match, the MIS-MATCHED! message will appear. In that case, you'll need to re-upload the firmware to your device by following the steps above.

After finishing the upload, the firmware execution will be paused at the very beginning. The display will be blank and the LED won't be lit up. To run the execution of your program and for the device to boot, run the continue command, or just c.

(gdb) c Continuing.

Debugging the Music Player

Let's look at how one would debug an app on the device, for example, the Music Player app. It is located in the Plugins → Music Player menu.

We'll start by halting the app at the very beginning of the execution. To do this, you'll need to place a breakpoint, which will stop the code execution once reached, and will allow you to observe the code state and inner workings. After that, you'll launch the Music Player through the device menu.

Run the list command to find the line number at which the music_player_app function is defined. The command will show the 5 lines before and after the function definition:

(gdb) list music_player_app 288 289 free(instance->model); 290 free(instance); 291 } 292 293 int32_t music_player_app(void* p) { 294 MusicPlayer* music_player = music_player_alloc(); 295 296 string_t file_path; 297 string_init(file_path);

The function is located at line 293, let's set a breakpoint by running the break 293 command:

(gdb) break 293 Breakpoint 1 at 0x8019a90: file /home/jones/flipperzero-firmware/applications/music_player/music_player.c, line 293. Note: automatically using hardware breakpoints for read-only addresses.

Now continue the program execution with the c command

(gdb) c Continuing.

Now, pick up your Flipper Zero, open the app menu, and navigate to Plugins → Music Player. The breakpoint will trigger. The program will automatically halt, and the device will stop responding to any input.

Breakpoint 1, music_player_app (p=0x0) at /home/jones/flipperzero-firmware/applications/music_player/music_player.c:293 293 int32_t music_player_app(void* p) {

You can run the code line-by-line with the next (n) command, and see the order in which the code is run:

(gdb) n 294 MusicPlayer* music_player = music_player_alloc(); (gdb) n ^[[A297 string_init(file_path); (gdb) n 300 if(p) { (gdb) n 303 string_set_str(file_path, MUSIC_PLAYER_APP_PATH_FOLDER); (gdb) n ^[[A 305 DialogsApp* dialogs = furi_record_open("dialogs"); (gdb) n 306 bool res = dialog_file_browser_show( (gdb) n 315 furi_record_close("dialogs");

You can use the info locals command to look at the local variables' state to see the changes happening in the device's memory:

(gdb) info locals music_player = <optimized out> file_path = {{u = {heap = {size = 0, alloc = 2779096485}, stack = { buffer = "\000\000\000\000\245\245\245\245"}}, ptr = 0x2000ef28 "\002"}}

To finish working with GDB, run the quit command:

(gdb) quit

GDB command examples:

continue, c — continues the code execution

next, n — executes the next line of the program

backtrace,bt — shows the functions that have lead to the current state

info threads — shows the threads that are currently running

info locals — shows the local variables

stop — halts the firmware execution

info breakpoints — shows all set breakpoints

Updated 30 Jun 2022
Did this page help you?