Debugging is a crucial part of software development. Whether you’re fixing bugs, testing, or optimizing your code, debugging tools like the GNU Debugger (GDB) can significantly improve your efficiency. One of GDB’s most powerful features is its ability to set breakpoints. Breakpoints allow developers to pause a program’s execution at specific points and analyze its state, making it easier to track down problems. In this post, we’ll cover everything you need to know about setting different breakpoints in GDB to effectively debug your programs.
What are Breakpoints in GDB?
A breakpoint is a marker that tells GDB to stop the program execution at a particular line of code. When the program hits the breakpoint, you can inspect variables, memory, and the overall program state. This helps in isolating and identifying issues in your code.
GDB offers different types of breakpoints, such as:
- Line Breakpoints: Stop at a specific line of code.
- Function Breakpoints: Stop at the beginning of a function.
- Conditional Breakpoints: Stop only when a specific condition is true.
- Watchpoints: Stop when a variable changes its value.
- Hardware Breakpoints: Used for low-level debugging.
lets assume we have helloworld as ARM ELF binary, then start gdb
$ arm-linux-gnueabihf-gcc -g -c -o helloworld_src/add.o helloworld_src/add.c
$ arm-linux-gnueabihf-gcc -g -o helloworld helloworld_src/helloworld.c helloworld_src/add.o -static
Run the qemu ARM in one terminal as,
$ qemu-arm -L gcc-arm-none-eabi-10-2020-q4-major/lib -g 8090 ./helloworld
On second terminal start GDB
$ arm-none-eabi-gdb
(gdb) target remote:8090
Let’s walk through these breakpoint types and learn how to set them up.
1. Setting a Line Breakpoint
A line breakpoint stops the execution of the program at a specific line. This is the most basic type of breakpoint.
Example:
Suppose you have the following simple C code:
#include <stdio.h>
int main() {
int a = 5;
int b = 10;
int result = a + b;
printf("Result: %d\n", result);
return 0;
}
To set a breakpoint at line 5 (where the variable result
is calculated):
(gdb) break 5
Breakpoint 1 at 0x40053a: file main.c, line 5.
Now, when you run the program in GDB, it will stop at line 5, and you can inspect the values of variables a
, b
, and result
.
Another example of Set Breakpoint using Line Number
You can set breakpoints using line number of the source code in that file, for that you must compile your binary with debugging symbols enabled, which we did using “-g” with gcc.
Check the line numbers of the source file using “li” gdb command as,
gdb) li
warning: Source file is more recent than executable.
1 #include <stdio.h>
2
3 extern int add_two_numbers(int, int);
4
5 int main(int argc, char **argv) {
6 int num = 0;
7 printf("helloworld\n");
8 num = add_two_numbers(3, 2);
9 printf("addition: %d\n", 3+2);
10 return 0;
(gdb)
11 }
(gdb)
Now, lets say you want to set breakpoint at line number 8, the type “break 8” or simply “br 8” to set breakpoint using line number i.e. “break line_number”
(gdb) br 8
Breakpoint 1 at 0x1043c: file helloworld_src/helloworld.c, line 9.
(gdb) c
Continuing.
Breakpoint 1, main (argc=1, argv=0xfffef184) at helloworld_src/helloworld.c:9
9 printf("addition: %d\n", 3+2);
As you can see, it set the breakpoint at the next valid statement on line number 9 and our program stopped at that line number after we pressed c i.e. continue.
2. Setting a Function Breakpoint
Sometimes, you may want to stop execution whenever a specific function is called. You can do this by setting a function breakpoint.
Example:
(gdb) break main
Breakpoint 2 at 0x40053a: file main.c, line 4.
This will pause the program when it reaches the main()
function. Function breakpoints are useful for debugging larger programs where specific functions are key areas of interest.
Another example of Set Breakpoint at main
(gdb) break main
Breakpoint 1 at 0x1042e: file helloworld_src/helloworld.c, line 6.
(gdb) c
Continuing.
Breakpoint 1, main (argc=1, argv=0xfffef184) at helloworld_src/helloworld.c:6
warning: Source file is more recent than executable.
6 int num = 0;
3. Setting a Conditional Breakpoint
A conditional breakpoint stops the program only when a specified condition is true. This is helpful when you’re interested in debugging only under certain conditions, rather than pausing the program every time it hits the breakpoint.
Example:
(gdb) break 5 if a == 5
In this example, the program will stop at line 5 only if the value of a
is 5.
4. Using Watchpoints
A watchpoint is a special type of breakpoint that stops the program whenever the value of a specific variable changes. This is useful for tracking down bugs where the root cause may be an unexpected change in a variable’s value.
Example:
(gdb) watch a
Watchpoint 3: a
The program will now stop whenever the value of a
changes.
5. Setting Hardware Breakpoints
While most breakpoints in GDB are software breakpoints, GDB also allows you to set hardware breakpoints, which are especially useful in embedded systems or low-level debugging. These breakpoints are set directly in the CPU’s hardware, allowing for faster debugging without modifying the code in memory.
To set a hardware breakpoint:
(gdb) hbreak 5
This sets a breakpoint at line 5 using hardware instead of software.
Setting Breakpoint using Memory Address
First, we need to check the memory address using objdump as,
$ arm-linux-gnueabihf-objdump -D helloworld > helloworld_objdump.txt
then, we can check the address of main and code inside by reading from helloworld_object.txt as
00010424 <main>:
10424: b580 push {r7, lr}
10426: b084 sub sp, #16
10428: af00 add r7, sp, #0
1042a: 6078 str r0, [r7, #4]
1042c: 6039 str r1, [r7, #0]
1042e: 2300 movs r3, #0
10430: 60fb str r3, [r7, #12]
10432: 4b08 ldr r3, [pc, #32] ; (10454 <main+0x30>)
10434: 447b add r3, pc
10436: 4618 mov r0, r3
10438: f008 fc90 bl 18d5c <_IO_puts>
1043c: 2105 movs r1, #5
1043e: 4b06 ldr r3, [pc, #24] ; (10458 <main+0x34>)
10440: 447b add r3, pc
10442: 4618 mov r0, r3
10444: f004 faac bl 149a0 <_IO_printf>
10448: 2300 movs r3, #0
1044a: 4618 mov r0, r3
1044c: 3710 adds r7, #16
1044e: 46bd mov sp, r7
10450: bd80 pop {r7, pc}
10452: bf00 nop
10454: 00039950 andeq r9, r3, r0, asr r9
10458: 00039950 andeq r9, r3, r0, asr r9
As we can see above, we have a code where its printing using printf, and address is, 0x00043c , so we can set the breakpoint at this address using “star zero x” and then address as,
(gdb) br *0x0001043c
Breakpoint 1 at 0x1043c: file helloworld_src/helloworld.c, line 9.
Now, if we do continue, we can see our program stopped just before printf,
(gdb) c
Continuing.
Breakpoint 1, main (argc=1, argv=0xfffef184) at helloworld_src/helloworld.c:9
warning: Source file is more recent than executable.
9 printf("addition: %d\n", 3+2);
Managing Breakpoints
Once you’ve set several breakpoints, you may want to list, disable, or remove them.
- List all breakpoints:
(gdb) info breakpoints
This will show you all the breakpoints you have set.
- Disable a breakpoint (without deleting it):
(gdb) disable 1
- Remove a breakpoint:
(gdb) delete 1
- Re-enable a breakpoint:
(gdb) enable 1
These commands help you manage your breakpoints during long debugging sessions, making it easier to toggle on and off as needed.
Why Use Breakpoints in GDB?
Breakpoints allow you to pause the execution of your program and inspect its state at any given moment. This gives you deeper insights into what your program is doing behind the scenes and helps you quickly identify issues like incorrect variable values or unexpected control flow.
Debugging without breakpoints can be like searching for a needle in a haystack, especially in larger projects. By using breakpoints effectively, you can narrow down issues faster and with more precision.
Setting different types of breakpoints in GDB is essential for effective debugging. By learning how to use line, function, conditional, and watch breakpoints, you can gain more control over the debugging process, saving time and improving the quality of your code. Whether you are tracking down a tricky bug or optimizing your program, breakpoints will always be your best friend in GDB.
Start using breakpoints today and see how much faster and more efficient your debugging process can become.