0%

GDB 使用手册

  • GDB 是类 Unix 系统下的调试工具,可以用来深入分析程序的运行过程,或者排查程序崩溃的原因。
  • 相比于在 IDE 中封装好图形化界面的调试工具,GDB 一般直接通过命令行操作,只需要一些简单的命令就可以完成大部分调试任务。
  • 本文面向 Linux 下的 C/C++ 程序,介绍 GDB 的基本命令、进阶用法、工程实践及调试原理,作为速查手册以供工程实践过程中随时查阅。

功能介绍

GDB主要有以下功能:

  • 运行程序,可以按照自定义的要求随心所欲的运行程序,比如设定断点,单步执行,逐过程执行等等。
  • 中断程序,可以在指定的地方中断程序的运行,然后检查此时程序中的变量的值。
  • 改变程序,可以改变程序的执行状态,比如改变临时变量的值,测试程序在不同的条件下的执行情况。
  • 崩溃分析,可以在程序崩溃时查看完整的调用栈,分析程序崩溃的原因。

在日常研发过程中的使用场景:

  • 程序发生 coredump,需要用 GDB 来排查,找出程序崩溃的原因。
  • 开发过程中调试程序,使用 GDB 来打断点,逐行执行,查看变量值,效率高于日志打印输出。

调试原理

register

寄存器 是 CPU 内部的一组存储单元。

CPU结构

在程序运行时,寄存器中会存储多种信息,如指令、数据、地址等,这些信息对于程序的执行和状态管理至关重要,GDB 可以查看和修改寄存器的值,进而控制程序的运行。

常见的寄存器包括:

  • 程序计数器(PC):存储下一条要执行的指令的地址。指示 CPU 正在执行的指令,控制程序的执行流程。
  • 基址寄存器(BP):存储栈帧的基地址。用于访问函数参数、局部变量、返回地址等,帮助管理堆栈帧。
  • 堆栈指针(SP):存储栈顶的地址。用于管理函数调用和局部变量的存储。
  • 通用寄存器(GP):存储临时数据。执行算术运算、逻辑运算、存储中间结果和临时变量等。
  • 状态寄存器(SR):存储 CPU 的状态信息。包括条件码、中断使能、特权级别等,控制 CPU 的运行状态。
  • 中断寄存器(IR):存储中断处理程序的地址。在发生中断时,指示 CPU 跳转到中断处理程序的地址。

ptrace

GDB 是通过 ptrace 系统调用来实现对被调试程序的控制的,利用 ptrace 系统调用,在被调试程序和 GDB 之间建立跟踪关系。然后所有发送给被调试程序的信号(除SIGKILL)都会被 GDB 截获,GDB 根据截获的信号,查看被调试程序相应的内存地址,寄存器等信息,从而实现调试功能。

ptrace 允许一个进程控制另一个进程的执行,用于调试器跟踪进程的执行,可以实现断点、单步执行、查看寄存器等功能,函数原型如下:

1
2
3
4
5
6
7
8
9
#include <sys/ptrace.h>

/**
* @request: 指示 ptrace() 要执行的操作类型
* @pid: 要操作的进程的进程 ID
* @addr: 要监控的内存地址
* @data: 存放要写入或读取的数据
*/
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
request description
PTRACE_TRACEME、PTRACE_ATTACH 建立进程间的追踪关系
PTRACE_PEEKTEXT、PTRACE_PEEKDATA、PTRACE_PEEKUSER 读取子进程内存/寄存器中保留的数据
PTRACE_POKETEXT、PTRACE_POKEDATA、PTRACE_POKEUSER 修改子进程内存/寄存器中保留的数据
PTRACE_CONT、PTRACE_SYSCALL、PTRACE_SINGLESTEP 控制被跟踪进程以不同方式继续执行
PTRACE_DETACH、PTRACE_KILL 终止进程间的跟踪关系

core dump

当进程崩溃时,会向操作系统发送一个信号操作系统会将进程的内存映像和寄存器状态信息保存到核心转储文件中,叫做 core dump 文件,默认名称为 core(名字来源与一种很古老的磁芯存储器,用于存储程序的状态信息)。

默认情况下,Linux 系统不会生成 core dump 文件,可以通过 ulimit -c 命令查看 core dump 文件的限制,如果为 0 表示系统不会生成 core dump 文件,可以通过 ulimit -c unlimited 命令设置为无限制。

默认情况下,core dump 文件会生成在可执行文件运行命令的同一路径下,且所有核心转储文件的名称都为 core,新的 core 文件生成将覆盖原来的 core 文件。

可以通过修改 Linux 特有的 /proc/sys/kernel/core_pattern 文件所包含的格式化字符串来控制对系统上生成的所有核心转储文件的命名和存储路径,如:

1
2
➜  sudo sysctl -w kernel.core_pattern=/corefile/core.%e.%p.%t
➜ sudo sysctl -p
格式符 描述
%c 对核心文件大小的资源软限制(字节数)
%e 可执行文件名(不含路径前缀)
%g 遭转储进程的实际组 ID
%h 主机系统的名称
%p 遭转储进程的进程 ID
%s 导致进程终止的信号编号
%t 转储时间,始于 Epoch,以秒为单位
%u 遭转储进程的实际用户 ID
%% 单个%字符

核心转储文件是一个ELF格式的二进制文件,可能产生 core dump 的代码问题如下,使用 GDB 可以快速的定位以下问题:

  • Segmentation fault, 段错误
    • Null Pointer Dereference, 空指针解引用
    • Stack Overflow / Buffer Overflow, 栈溢出/缓冲区溢出
    • Use After Free, 释放后使用
    • Double Free, 重复释放
    • Out of Memory, 内存溢出

特定信号也会引发进程创建一个核心转储文件并终止运行,如下表所示。

名称 信号值 描述 名称 信号值 描述
SIGABRT 6 中止进程 SIGFPE 8 算术异常
SIGALRM 14 实时定时器过期 SIGHUP 1 挂起
SIGBUS 7 内存访问错误 SIGILL 4 非法指令
SIGCHLD 17 终止或者停止子进程 SIGINT 2 终端中断
SIGCONT 18 若停止则继续 SIGIO 29 I/O 时可能产生
SIGEMT undef 硬件错误 SIGKILL 9 必杀(确保杀死)
SIGPIPE 13 管道断开 SIGPROF 27 性能分析定时器过期
SIGPWR 30 电量行将耗尽 SIGQUIT 3 终端退出
SIGSEGV 11 无效的内存引用 SIGSTKFLT 16 协处理器栈错误
SIGSTOP 19 确保停止 SIGSYS 31 无效的系统调用
SIGTERM 15 终止进程 SIGTRAP 5 跟踪/断点陷阱
SIGTSTP 20 终端停止 SIGTTIN 21 从终端读取
SIGTTOU 22 向终端写 SIGURG 23 套接字上的紧急数据
SIGUSR1 10 用户自定义信号 1 SIGUSR2 12 用户自定义信号 2
SIGVTALRM 26 虚拟定时器过期 SIGWINCH 28 终端窗口尺寸发生变化
SIGXCPU 24 突破对 CPU 时间的限制 SIGXFSZ 25 突破对文件大小的限制

debug info

GDB 调试程序时,需要程序的调试信息,编译时加上 -g 选项,这样生成的 ELF 格式的可执行文件中就会包含源代码的调试信息。

ELF 用于存储可执行文件、目标文件、共享库和核心转储文件,是 Linux 平台上通用的二进制文件格式,它包括可执行文件、可重定位的目标文件(包括.o和.a文件)、core文件和共享对象(.so文件)等。

调试信息以 DWARF 格式被保存在 ELF 格式文件的几个 section 中,如果没有调试信息,GDB 会提示 No symbol table is loaded,无法查看变量名、函数名等信息,也无法打断点或打印函数调用栈。

调试信息格式主要面向开发者用以指导如何生成调试信息以及如何使用调试信息,DWARF是类Unix操作系统下的调试信息格式。PDB是Windows平台调试信息的主要格式。

ELF 文件由多个 section 组成,每个 section 用于存储不同的信息,DWARF 调试信息根据描述对象的不同,存储到不同的 section,名称均以前缀.debug_开头,下面是一些常见的调试信息:

Section Description
debug_info 存储核心DWARF数据,包含了描述变量、代码等的DIEs
.debug_abbrev 存储.debug_info中使用的缩写信息
.debug_arranges 存储一个加速访问的查询表,通过内存地址查询对应编译单元信息
.debug_frame 存储调用堆栈信息,包括函数调用、返回地址等
.debug_line 存储源代码行号信息
.debug_loc 存储变量的位置信息
.debug_pubnames 存储一个加速访问的查询表,通过名称查询全局对象和函数
.debug_pubtypes 存储一个加速访问的查询表,通过名称查询全局类型信息
.debug_ranges 存储一个加速访问的查询表,通过内存地址查询对应编译单元信息
.debug_str 存储字符串信息
.debug_types 存储类型信息

Ref:DWARF Debugging Information Format 1 2 3

可以使用readelf -S命令或者objdump -h命令查看 ELF 文件的 section 信息,如下以.debug_开头的 section 是 DWARF 调试信息:

1
2
3
4
5
6
7
8
9
10
11
12
28 .debug_aranges 000002a0  0000000000000000  0000000000000000  0000303b  2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
29 .debug_info 000051d2 0000000000000000 0000000000000000 000032db 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
30 .debug_abbrev 000009a8 0000000000000000 0000000000000000 000084ad 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
31 .debug_line 00000a77 0000000000000000 0000000000000000 00008e55 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
32 .debug_str 00003fea 0000000000000000 0000000000000000 000098cc 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
33 .debug_ranges 00000290 0000000000000000 0000000000000000 0000d8b6 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS

GDB就是根据这些调试信息从可执行程序中获取源代码的信息,然后进行调试的,关于调试器具体如何解析需要的信息,可参考这篇文章

stack frame

在程序执行过程中,现代计算机系统使用栈来管理函数调用的过程,调用栈(call stack)是函数调用时分配的栈空间,它被分成若干个栈帧(stack frame),每个栈帧对应一个函数调用和相关的所有数据,包括:

  • 函数实参和局部变量:这些变量都是在调用函数时自动创建的,函数返回时将自动销毁这些变量(因为栈帧会被释放)。
  • 函数调用的链接信息:每个函数都会用到一些 CPU 寄存器,比如程序计数器,其指向下一条将要执行的机器语言指令。每当一个函数调用另一函数时,会在被调用函数的栈帧中保存这些寄存器的副本,以便函数返回时能为函数调用者将寄存器恢复原状。

程序启动时只有一个栈帧,即 main 函数,又称初始栈帧或最外层栈帧。每次调用函数时,会在栈上新分配一帧,每当函数返回时,再从栈上将此帧移去。当前执行的函数所对应的栈帧又称最内层栈帧。

一个程序调用栈的示意图

GDB 给每个栈帧分配了一个数字,最内层栈帧的编号是 0,外层栈帧依次加 1。可以通过 bt 命令展示所有栈帧,只有在当前栈帧中的变量才能被访问,如果要访问其他栈帧的变量,需要先切换到对应的栈帧,可以通过 f 命令加上编号进入到对应的栈帧。

application binary interface

应用程序二进制接口(ABI)是一套规则,规定了二进制可执行文件在运行时应如何与某些服务(诸如内核或函数库所提供的服务)交换信息。ABI 特别规定了使用哪些寄存器和栈地址来交换信息以及所交换值的含义,一旦针对某个特定 ABI 进行了编译,其二进制可执行文件应能在 ABI 相同的任何系统上运行。

简单入门

当应用程序异常退出时,操作系统会生成 coredump 文件,记录程序退出时的所有内存状态。GDB 可以读取这个文件,查看程序退出时的变量值或者寄存器值,但是无法执行程序。即只能使用静态命令,如 p、bt、i 打印发生异常时的信息。

GDB 也可以直接加载一个二进制程序并执行。在这种情况下,GDB 不仅可以随时查看程序当前的变量值或其他内存状态,还可以控制程序的运行,如设置断点、单步执行、反向执行等。即不仅可以使用静态命令,还可以使用 r、b、c 等动态命令。

下面是一个简单的示例,展示如何使用 GDB 调试一个程序。

编译程序

现在有这么一个采用质数筛法求解小于 n 的所有质数个数的程序,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
int countPrimes(int n) {
int res = 0;
vector<int> isPrime(n, 1);
for (int i = 2; i < n; ++i) {
if (isPrime[i]) {
res++;
if ((long long) i * i >= n) {
continue;
}
for (int j = i * i; j < n; j += i) {
isPrime[j] = 0;
}
}
}
return res;
}
};

int main() {
Solution s;
int n = 10;
int res = s.countPrimes(n);
cout << res << endl;
return 0;
}

编译这个程序,生成可执行文件,同时加上 -g 选项,生成调试信息:

1
2
3
4
5
6
➜  gdb-demo ls
count_primes.cpp
➜ gdb-demo g++ count_primes.cpp -o count_primes_without_debuginfo.out
➜ gdb-demo g++ -g count_primes.cpp -o count_primes_with_debuginfo.out
➜ gdb-demo ls
count_primes.cpp count_primes_with_debuginfo.out count_primes_without_debuginfo.out

进入 GDB

使用 GDB 打开一个二进制文件,可以直接输入 gdb 命令,然后在 GDB 中输入 file 命令加载二进制文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
➜  gdb-demo gdb count_primes_without_debuginfo.out                    
GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git
Copyright (C) 2024 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 count_primes_without_debuginfo.out...

This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.ubuntu.com>
Enable debuginfod for this session? (y or [n]) y
Debuginfod has been enabled.
To make this setting permanent, add 'set debuginfod enabled on' to .gdbinit.
Downloading separate debug info for /home/xiejunjie/Work/PersonalCodes/gdb-demo/count_primes_without_debuginfo.out
(No debugging symbols found in count_primes_without_debuginfo.out)

如果二进制文件没有调试信息,GDB 会提示 No debugging symbols found in xxx,无法查看变量名、函数名以及backtrace等信息。

或者直接在命令行中指定二进制文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
➜  gdb-demo gdb                                   
GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git
Copyright (C) 2024 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".
>>> file count_primes_with_debuginfo.out
Reading symbols from count_primes_with_debuginfo.out...
>>>

如果二进制文件有调试信息,GDB 会自动加载调试信息,Reading symbols from xxx 表示调试信息加载成功。

调试程序

在 GDB 中,使用 break 命令设置断点,然后使用 run 命令运行程序,程序会在断点处停下来,可以使用 next 命令单步执行,使用 step 命令进入函数内部,使用 print 命令打印变量的值,使用 backtrace 命令打印函数调用栈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> b count_primes.cpp:28
Breakpoint 1 at 0x128b: file count_primes.cpp, line 28.
>>> r
Starting program: /home/xiejunjie/Work/PersonalCodes/gdb-demo/count_primes_with_debuginfo.out
Breakpoint 1, main () at count_primes.cpp:28
28 int res = s.countPrimes(n);
>>> s
Solution::countPrimes (this=0x7fffffffd62f, n=10) at count_primes.cpp:7
7 int countPrimes(int n) {
>>> n 3
10 for (int i = 2; i < n; ++i) {
>>> p isPrime
$1 = std::vector of length 10, capacity 10 = {[0] = 1, [1] = 1, [2] = 1, [3] = 1, [4] = 1, [5] = 1, [6] = 1, [7] = 1, [8] = 1, [9] = 1}
>>> bt
#0 Solution::countPrimes (this=0x7fffffffd62f, n=10) at count_primes.cpp:10
#1 0x000055555555529c in main () at count_primes.cpp:28
>>> c
4
[Inferior 1 (process 75796) exited normally]
>>> q

continue 命令继续执行程序,直到遇到下一个断点,这里没有下一个断点了,所以程序执行完毕,退出,quit 命令退出 GDB。

命令缩写

命令 简写 说明
run r 运行程序
break b 设置断点
next n 单步执行,执行完当前函数,停在函数后下一行(step over)
step s 单步执行,进入函数内部,停在函数内第一行(step into)
continue c 继续执行程序,直到遇到下一个断点
print p 打印变量的值,支持数字、字符串、结构体、指针、表达式等
backtrace bt 打印函数调用栈,显示从程序开始执行到当前位置的函数调用关系
frame f 切换栈帧,进入到指定的栈帧
list l 显示源代码,可以指定行号或函数名
info i 显示程序信息,如断点、栈帧、寄存器等
set 设置变量的值
watch 设置监视点,当变量的值发生变化时,停止程序执行
until u 执行程序直到达到指定行

命令详解

启动程序

命令 说明
gdb object 正常启动,加载可执行
gdb object core 对可执行 + core 文件进行调试
gdb object pid 对正在执行的进程进行调试
gdb 正常启动,启动后需要 file 命令手动加载
gdb -tui 启用 gdb 的文本界面(或 ctrl-x ctrl-a 更换 CLI/TUI)

帮助信息

命令 说明
help 列出命令分类
help running 查看某个类别的帮助信息
help run 查看命令 run 的帮助
help info 列出查看程序运行状态相关的命令
help info line 列出具体的一个运行状态命令的帮助
help show 列出 GDB 状态相关的命令
help show commands 列出 show 命令的帮助

断点设置

命令 说明
break main 对函数 main 设置一个断点,可简写为 b main
break 101 对源代码的行号设置断点,可简写为 b 101
break basic.c:101 对源代码和行号设置断点
break basic.c:foo 对源代码和函数名设置断点
break *0x00400448 对内存地址 0x00400448 设置断点
info breakpoints 列出当前的所有断点信息,可简写为 info break
delete 1 按编号删除一个断点
delete 删除所有断点
clear 删除在当前行的断点
clear function 删除函数断点
clear line 删除行号断点
clear basic.c:101 删除文件名和行号的断点
clear basic.c:main 删除文件名和函数名的断点
clear *0x00400448 删除内存地址的断点
disable 2 禁用某断点,但是不删除
enable 2 允许某个之前被禁用的断点,让它生效
rbreak {regexpr} 匹配正则的函数前断点,如 ex_* 将断点 ex_ 开头的函数
tbreak function line
hbreak function line
ignore {id} {count} 忽略某断点 N-1 次
condition {id} {expr} 条件断点,只有在条件生效时才发生
condition 2 i == 20 2号断点只有在 i == 20 条件为真时才生效
watch {expr} 对变量设置监视点
info watchpoints 显示所有观察点
catch exec 断点在exec事件,即子进程的入口地址

运行程序

命令 说明
run 运行程序
run {args} 以某参数运行程序
run < file 以某文件为标准输入运行程序
run < <(cmd) 以某命令的输出作为标准输入运行程序
run <<< $(cmd) 以某命令的输出作为标准输入运行程序
set args {args} … 设置运行的参数
show args 显示当前的运行参数
continue 继续运行,可简写为 c 或 cont
step 单步进入,碰到函数会进去(Step in)
step {count} 单步多少次
next 单步跳过,碰到函数不会进入(Step Over)
next {count} 单步多少次
finish 运行到当前函数结束(Step Out)
until 持续执行直到代码行号大于当前行号(跳出循环)
until {line} 持续执行直到执行到某行
CTRL+C 发送 SIGINT 信号,中断程序执行
attach {process-id} 链接上当前正在运行的进程,开始调试
detach 断开进程链接
kill 杀死当前运行的函数

栈帧操作

命令 说明
bt 打印 backtrace (命令 where 是 bt 的别名)
frame 显示当前运行的栈帧
up 向上移动栈帧(向着 main 函数)
down 向下移动栈帧(远离 main 函数)
info locals 打印帧内的相关变量
info args 打印函数的参数

代码查看

命令 说明
list 101 显示第 101 行周围 10 行代码
list 1,10 显示 1 到 10 行代码
list main 显示函数周围代码
list basic.c:main 显示另外一个源代码文件的函数周围代码
list - 重复之前 10 行代码
list *0x22e4 显示特定地址的代码
cd dir 切换当前目录
pwd 显示当前目录
search {regexpr} 向前进行正则搜索
reverse-search {regexp} 向后进行正则搜索
dir {dirname} 增加源代码搜索路径
dir 复位源代码搜索路径(清空)
show directories 显示源代码路径

浏览数据

命令 说明
print {expression} 打印表达式,并且增加到打印历史
print /x {expression} 十六进制输出,print 可以简写为 p
print array[i]@count 打印数组范围
print $ 打印之前的变量
print *$->next 打印 list
print $1 输出打印历史里第一条
print ::gx 将变量可视范围(scope)设置为全局
print ‘basic.c’::gx 打印某源代码里的全局变量,(gdb 4.6)
print /x &main 打印函数地址
x *0x11223344 显示给定地址的内存数据
x /nfu {address} 打印内存数据,n 是多少个,f 是格式,u 是单位大小
x /10xb *0x11223344 按十六进制打印内存地址 0x11223344 处的十个字节
x/x &gx 按十六进制打印变量 gx,x 和斜杆后参数可以连写
x/4wx &main 按十六进制打印位于 main 函数开头的四个 long
x/gf &gd1 打印 double 类型
help x 查看关于 x 命令的帮助
info locals 打印本地局部变量
info functions {regexp} 打印函数名称
info variables {regexp} 打印全局变量名称
ptype name 查看类型定义,比如 ptype FILE,查看 FILE 结构体定义
whatis {expression} 查看表达式的类型
set var = {expression} 变量赋值
display {expression} 在单步指令后查看某表达式的值
undisplay 删除单步后对某些值的监控
info display 显示监视的表达式
show values 查看记录到打印历史中的变量的值 (gdb 4.0)
info history 查看打印历史的帮助 (gdb 3.5)

文件操作

命令 说明
file {object} 加载新的可执行文件供调试
file 放弃可执行和符号表信息
symbol-file {object} 仅加载符号表
exec-file {object} 指定用于调试的可执行文件(非符号表)
core-file {core} 加载 core 用于分析

信号控制

命令 说明
info signals 打印信号设置
handle {signo} {actions} 设置信号的调试行为
handle INT print 信号发生时打印信息
handle INT noprint 信号发生时不打印信息
handle INT stop 信号发生时中止被调试程序
handle INT nostop 信号发生时不中止被调试程序
handle INT pass 调试器接获信号,不让程序知道
handle INT nopass 调试器不接获信号
signal signo 继续并将信号转移给程序
signal 0 继续但不把信号给程序

线程调试

命令 说明
info threads 查看当前线程和 id
thread {id} 切换当前调试线程为指定 id 的线程
break {line} thread all 所有线程在指定行号处设置断点
thread apply {id..} cmd 指定多个线程共同执行 gdb 命令
thread apply all cmd 所有线程共同执行 gdb 命令
set schedule-locking ? 调试一个线程时,其他线程是否执行,off
set non-stop on/off 调试一个线程时,其他线程是否运行
set pagination on/off 调试一个线程时,分页是否停止
set target-async on/off 同步或者异步调试,是否等待线程中止的信息

进程调试

命令 说明
info inferiors 查看当前进程和 id
inferior {id} 切换某个进程
kill inferior {id…} 杀死某个进程
set detach-on-fork on/off 设置当进程调用 fork 时 gdb 是否同时调试父子进程
set follow-fork-mode parent/child 设置当进程调用 fork 时是否进入子进程

汇编调试

命令 说明
info registers 打印普通寄存器
info all-registers 打印所有寄存器
print/x $pc 打印单个寄存器
stepi 指令级别单步进入,可以简写为 si
nexti 指令级别单步跳过,可以简写为 ni
display/i $pc 监控寄存器(每条单步完以后会自动打印值)
x/x &gx 十六进制打印变量
info line 22 打印行号为 22 的内存地址信息
info line *0x2c4e 打印给定内存地址对应的源代码和行号信息
disassemble {addr} 对地址进行反汇编,比如 disassemble 0x2c4e

历史信息

命令 说明
show commands 显示历史命令 (gdb 4.0)
info editing 显示历史命令 (gdb 3.5)
ESC-CTRL-J 切换到 Vi 命令行编辑模式
set history expansion on 允许类 c-shell 的历史
break class::member 在类成员处设置断点
list class:member 显示类成员代码
ptype class 查看类包含的成员
print *this 查看 this 指针

其他命令

命令 说明
define command … end 定义用户命令
{return} 直接按回车执行上一条指令
shell {command} [args] 执行 shell 命令
source {file} 从文件加载 gdb 命令
quit 退出 gdb

进阶用法

GitHub 上有一些很好的插件和工具,可以帮助我们更好地使用 GDB,提高调试效率。

  • gdb-dashboard:可视化的调试信息仪表板拓展,帮助用户更直观地查看寄存器、堆栈、内存等信息。
  • GdbInit: 允许用户通过 .gdbinit 文件自动加载常用命令和配置,简化 GDB 的使用。
  • pwndbg:用于 CTF 和二进制安全研究的 GDB 插件,提供诸如反汇编、堆栈分析、ROP 链生成等功能。
  • gef:提供多种增强功能,如可视化堆栈、寄存器和内存信息,特别是在安全研究和逆向工程中常用。

参考资料