Brief Instructions for Using GDB
What is GDB?
GDB is the GNU Project debugger that allows you to see what’s going on “inside” another program while it’s executing – or what another program was doing when it crashed.
There are four main things GDB can do (plus other things to support them) to help you catch errors on the fly:
- Start the program, specifying anything that might affect its behavior.
- Causes your program to stop on specified conditions.
- Check what happens when the program stops.
- Change things in a program so you can try to correct the effects of one bug and move on to another bug.
These programs may be executed on the same machine as GDB (native), on another machine (remote), or on an emulator. GDB runs on most popular UNIX and Microsoft Windows variants, as well as Mac OS X.
GDB support many programming languages, including:
- Ada
- Assembly
- C
- C++
- D
- Fortran
- Go
- Objective-C
- OpenCL
- Modula-2
- Pascal
- Rust
How to run a GDB?
If we use [tldr](tldr pages) to see how to use gdb, we can get the following prompt:
$ tldr gdb
gdb
The GNU Debugger.More information: https://www.gnu.org/software/gdb.
- Debug an executable:
gdb {{executable}}
- Attach a process to gdb:
gdb -p {{procID}}
- Debug with a core file:
gdb -c {{core}} {{executable}}
- Execute given GDB commands upon start:
gdb -ex "{{commands}}" {{executable}}
- Start gdb and pass arguments to the executable:
gdb --args {{executable}} {{argument1}} {{argument2}}
Here are several common uses of GDB, namely:
- Execute a program and debug;
- Enter a running program and debug it by specifying the program’s
proceID; - By specifying the [core file](Core Files in Linux (perforce.com)) generated when the program fails, restore the scene when the program fails to debug;
- Execute a program and pass in parameters and debug.
We write a simple C language program as our debugger.
#include <stdio.h>
int main()
{
int a = 1, b = 2, c = 0;
c = a + b;
printf("c = %d\n", c);
return 0;
}
Compile and link to an executable file with debugging information:
gcc -g a.c -o a
Start GDB debugging with the following command:
gdb a
After entering GDB, information similar to the following will be displayed:
GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a...
(No debugging symbols found in a)
(gdb)
It gives us a hint that apropos word can be used to find related commands, so we can quickly find out how to use a certain command without querying the document.
For more startup related commands, see the official documentation Running (Debugging with GDB) (sourceware.org)。
How to debug a program in GDB?
Debugging is nothing more than understanding the running logic of the program and the state of the program in a certain execution step, so as an introduction, you only need to know how to interrupt, start, query, and how to use display to provide code display.
Interrupt
Break can use Breakpoints, Watchpoints, Catchpoints. A breakpoint is where the program runs and breaks; an Watchpoint is used to observe a variable or even many variables combined by operators, and break if the value of the variable changes; a Catchpoint is to break when the program triggers an event, Such as catch, throw, assert, exec, syscall, fork, vfork, signal.
You can use the list command to query the code. Each execution will output ten lines of code. If you need to get more, you can use the list command multiple times. Similarly, list also supports printing the code of the specified line. The usage is as follows:
(gdb) list
(gdb) list 3,5
Set a breakpoint with the following command:
(gdb) break locspec
locspecc can specify the function name, line number, instruction address, etc., and break can be abbreviated as b.
Use the following command to query breakpoints:
(gdb) info breakpoints
(gdb) info b
Use the following command to set the watchpoint:
(gdb) watch expr
Use the following method to query watchpoints:
(gdb) info watchpoints
Use the following method to set the catchpoints:
(gdb) catch event
The catchpoints is also viewed using info b. It is worth mentioning that this command will also list the watchpoints we set.
Start
Startup is divided into starting the program from the beginning and continuing the program after interruption.
There are two commands to start the program from the very beginning: starti and run. Among them, starti is to start the program and stop at the first instruction. This instruction is _start instead of the main function in Linux, because the initial state of a series of programs such as the stack and global functions has not been set. The run command starts the program and interrupts when it reaches the set breakpoint.
Continuing to execute the program after interruption is actually Continuing and Stepping. Only the most commonly used ones are introduced here A few commands:
(gdb) continue / c
# Resume program execution, at the address where your program last stopped; any breakpoints set at that address are bypassed until the next breakpoint is reached.
(gdb) step / s
# Continue running the program until control reaches another source line, then stop it and return control to GDB, stepping into a function if it encounters it.
(gdb) step count
# Continue to run step by step, but count times. If a breakpoint or a signal unrelated to single stepping occurs before the count step, single stepping stops immediately.
(gdb) next [count] / n [count]
# Continue to the next source line in the current (innermost) stack frame. This is similar to `step`, but the occurrence of a function call within a line of code does not stop execution, ie the function is treated as an atomic instruction.
(gdb) finish
# Continue running until after the function in the selected stack frame returns.
(gdb) until locspec
# Continue running the program until the program reaches a location in the code that resolves the `locspec`, or the current stack frame returns.
(gdb) stepi / si
# Executes an instruction, then stops and returns to the debugger.
(gdb) nexti / ni
# Executes an instruction, but if it is a function call, continues execution until the function returns.
Queries
During program execution, we may need to obtain certain information, such as the query breakpoint instruction info breakpoints mentioned above, the following are commonly used queries:
(gdb) print expr # print the value of the expression
(gdb) info breakpoints # query breakpoint
(gdb) info frame # Query stack information
(gdb) info registers # Query the value of the register
Friendlier page
To see exactly where the program is going, we can show the source code, or even the assembly code.
Display the currently running source code:
(gdb) layout src
We can get the following page:
┌─a.c────────────────────────────────────────────────────────────────────────────┐
│ 1 #include <stdio.h> │
│ 2 │
│ 3 int main() │
│ 4 { │
│B+>5 int a = 1, b = 2, c = 0; │
│ 6 c = a + b; │
│ 7 printf("c = %d\n", c); │
│ 8 │
│ 9 return 0; │
│ 10 } │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
└────────────────────────────────────────────────────────────────────────────────┘
native process 134526 In: main L5 PC: 0x55555555513d
(gdb) layout asm
(gdb)
Show assembly code:
(gdb) layout src
Get the following page:
┌────────────────────────────────────────────────────────────────────────────────┐
│ 0x555555555135 <main> push %rbp │
│ 0x555555555136 <main+1> mov %rsp,%rbp │
│ 0x555555555139 <main+4> sub $0x10,%rsp │
│B+>0x55555555513d <main+8> movl $0x1,-0x4(%rbp) │
│ 0x555555555144 <main+15> movl $0x2,-0x8(%rbp) │
│ 0x55555555514b <main+22> movl $0x0,-0xc(%rbp) │
│ 0x555555555152 <main+29> mov -0x4(%rbp),%edx │
│ 0x555555555155 <main+32> mov -0x8(%rbp),%eax │
│ 0x555555555158 <main+35> add %edx,%eax │
│ 0x55555555515a <main+37> mov %eax,-0xc(%rbp) │
│ 0x55555555515d <main+40> mov -0xc(%rbp),%eax │
│ 0x555555555160 <main+43> mov %eax,%esi │
│ 0x555555555162 <main+45> lea 0xe9b(%rip),%rdi # 0x555555556 │
│ 0x555555555169 <main+52> mov $0x0,%eax │
│ 0x55555555516e <main+57> call 0x555555555030 <printf@plt> │
│ 0x555555555173 <main+62> mov $0x0,%eax │
│ 0x555555555178 <main+67> leave │
│ 0x555555555179 <main+68> ret │
│ 0x55555555517a nopw 0x0(%rax,%rax,1) │
└────────────────────────────────────────────────────────────────────────────────┘
native process 134526 In: main L5 PC: 0x55555555513d
(gdb) layout asm
(gdb)