Linux开发学习记录(1):如何愉快(?)地Debug程序

成元 2026-01-11 { 教程, 笔记 } [ Linux, C, GDB ]

前言

又一个新系列()开始了!这个系列我会发布一些自己学习Linux开发过程中写下的笔记。
第一篇笔记便是如何愉快(?)的在Linux环境下进行Debug工作(以C语言举例)。
使用的主要工具为 systemd-coredump、GDB。

Core dump 介绍

Core Dump,中文译名为 内核转储 或者 核心转储。
Core Dump 是程序崩溃时,内核将进程的内存镜像、寄存器状态、文件描述符等信息写入磁盘的文件。
简单地说,其相当于程序崩溃瞬间的“快照”,可用于事后调试(如通过 gdb 分析崩溃原因)。

Core dump 应仅与受信任的方共享,因为它们可能包含敏感数据(如密码或加密密钥)。

Core dump 通常不被普通用户使用,但开发人员可以将其作为程序崩溃时状态的事后快照,特别是当故障难以可靠复现时。
可以通过 systemd-coredump 等工具进行管理。本文基于 systemd-coredump 演示。
如果你已经有了心仪的工具,可以直接跳过下一小结。

systemd-coredump 介绍

systemd-coredump 是 systemd 的一个组件,用于管理 Core dump 文件。
可以通过如下的方式查看当前设备的核心转储处理方式,

~$ cat /proc/sys/kernel/core_pattern
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t 9223372036854775808 %h %d

如果在你的电脑上没有显示管道传输到 systemd-coredump 中,说明你用的不是 systemd-coredump。
一般来说,会出现在一些比较有想法的Linux版本中,比如Ubuntu24.04,其结果可能会如下:

~$ cat /proc/sys/kernel/core_pattern
|/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -F%F -- %E

如果你像我一样更喜欢用 systemd-coredump,而非 apport ,你可以执行下面的命令。

# 彻底关闭 apport
# 1. 删除 apport 可能产生的崩溃报告
~$ rm -rf /var/crash/*
# 2. 编辑如下的文件,将 enabled 的值修改为 0
~$ vim /etc/default/apport
    enabled=0
# 3. 关闭 apport 的启动
~$ systemctl stop apport
~$ systemctl disable apport

# 安装 systemd-coredump (如果你的设备上没有)
~$ sudo apt install systemd-coredump

# 此时执行一开始的命令,即可看到变化(如果没有请自行修改)
~$ cat /proc/sys/kernel/core_pattern
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t 9223372036854775808 %h %d

在 systemd 系统中,systemd-coredump 的行为由 /etc/systemd/coredump.conf 或 /etc/systemd/coredump.conf.d/*.conf 控制。
根据手册,建议在后者,也就是 /etc/systemd/coredump.conf.d/*.conf 中进行配置,如下:

~$ cat /etc/systemd/coredump.conf.d/custom.conf            
[Coredump]
# 保存到磁盘
Storage=external
# 启用xz压缩以节省空间
Compress=yes
# 核心文件处理最大限制
ProcessSizeMax=2G
# 核心文件的最大存储限制与日志的最大存储限制
ExternalSizeMax=2G
JournalSizeMax=767M
# 核心存储目录最多可以使用多少磁盘空间
# 分区上至少保存多少空闲空间
MaxUse=5G
KeepFree=1G 

具体的配置可以查看手册 coredump.conf(5) 。
创建完成后可以执行重启守护程序与单元的命令,如下:

~$ sudo systemctl daemon-reload
~$ sudo systemctl restart systemd-coredump.socket

值得注意的是一些文章可能会说还要执行 ulimit -c 命令查看是否有进程级限制
但是据我查询手册 core(5),发现并非如此。手册的原文如下:
The RLIMIT_CORE (core file size) or RLIMIT_FSIZE (file size) resource limits for the process are set to zero; see getrlimit(2) and the documentation of the shell's ulimit command (limit in csh(1)). However, RLIMIT_CORE will be ignored if the system is configured to pipe core dumps to a program.
也就是我们将 coredump 管道传输到 systemd-coredump 等程序,Linux 内核本身会忽略 ulimit 设置。
实际上传统的配置方法才会受此影响,具体可以查阅我给出的参考文章

如果你不想让 systemd-coredump 处理 Core dump 文件,那么应该如下设置配置文件:

~$ cat /etc/systemd/coredump.conf.d/custom.conf   
[Coredump]
Storage=none
ProcessSizeMax=0

如果你配置好了,那么设备上的 Core dump 文件默认会保存于 /var/lib/systemd/coredump/ 目录下。
不过一般通过 systemd-coredump 的管理工具 coredumpctl 查询,如下:

coredumpctl list                                                                             
TIME                           PID  UID  GID SIG     COREFILE EXE                                     SIZE
Sun 2026-01-11 17:51:40 CST 359684 1000 1000 SIGSEGV present  /home/bigshot/project/c/error/test.exe 19.4K
Sun 2026-01-11 18:06:00 CST 370138 1000 1000 SIGSEGV present  /home/bigshot/project/c/error/test.exe 19.3K
Sun 2026-01-11 18:25:08 CST 378385 1000 1000 SIGSEGV present  /home/bigshot/project/c/error/test.exe 19.3K

具体的用法可以通过命令coredumpctl help或者手册 coredumpctl(1) 查询。

GDB 介绍

GDB (GNU Debugger) 应该是 Linux 系统下最常用的程序调试工具了,程序员们都或多或少用过它或者基于它的工具进行Debug。
如果想要使用GDB进行调试,那么编译C代码时必须使用 debug 模式(或者尽可能保留Debug信息),比如使用 gcc 的 -g 选项。
使用 gdb <可执行文件> 即可开启对指定程序的Debug,

GDB初始化文件

和shell类似,GDB也有初始化文件,不同的是GDB的初始化文件使用GDB命令
其会按照以下的顺序读取初始化文件,并依次执行:

  1. $HOME/.gdbinit
  2. 运行命令行选项
  3. ./.gdbinit
  4. 通过-x选项给出的命令文件

此外通过 source 命令也能引入文件。

常用的GDB命令

具体如下(部分不常用的命令或格式省略):
举例演示的补充说明跳转到下一小节

命令 简写 作用 跳转目录
info inf i 查询有关信息 info inf i 信息
sharedlibrary share 手动添加共享库(动态库) sharedlibrary share 手动添加共享库(动态库)
break brea bre br b 设置断点 break brea bre br b 断点
hbreak tbreak thbreak 设置其它断点 hbreak tbreak thbreak 其它断点
clear cl 清除断点 clear cl 清除断点
enable en 启用断点 enable en / disable disa dis 启用/禁用断点
disable disa dis 禁用断点 enable disable 启用/禁用断点
conditon 断点条件的添加(替换)与删除 conditon 断点条件的添加(替换)与删除
run r (重头开始)运行 run r 运行
start (重头开始)运行到main函数 start 开始
backtrace bt where 显示栈帧 backtrace bt where 显示栈帧
print p inspect 显示变量 print p 显示变量
x 显示内容中的内容 x 显示内存
disassemble disas 反汇编 disassemble disas 反汇编
next n 单步(不进入函数内部) next n 单步(不进入函数内部)
step s 单步(进入函数内部) step s 单步(进入函数内部)
ignore 忽略断点、监视点、捕获点 ignore 忽略断点、监视点、捕获点
continue c fg 继续运行 continue c fg 继续运行
finish fin 执行完当前函数 finish fin 执行完当前函数
until u 运行到某一位置 until u 运行到某一位置
watch 添加监视点(改变) watch awatch rwatch 监视点
awatch 添加监视点(被访问、改变) watch awatch rwatch 监视点
rwatch 添加监视点(被访问) watch awatch rwatch 监视点
delete d del 删除断点和监视点 delete d del 删除断点和监视点
set 设置值 set 设置值
generate-core-file gcore 生成Core dump文件 generate-core-file gcore 生成Core dump文件
attach 调试(附加到)已启动进程 attach detach 调试(附加到)和脱离已启动进程
detach 脱离已启动进程 attach detach 调试(附加到)和脱离已启动进程
commands 断点命令 commands 断点命令
list l 显示函数或行 list l 显示函数或行
help h 帮助文档 help h 帮助文档
show 显示数据 show 显示数据
define 命令定义 define 命令定义 / document 命令说明
document 命令说明 define 命令定义 / document 命令说明
source 引入初始化文件 source 引入初始化文件

举例演示的补充说明

演示程序如下:

~$ cat main.c  
#include <stdio.h>

void deepdark()
{
    printf("deepdark\n");
}

void unhappy()
{   
    printf("unhappy\n");
    deepdark();
}

void happy()
{
    int i = 5;
    printf("happyi %d\n", i);
    unhappy();
}

int main()
{
    printf("this is a gdb test program\n");
    happy();
    return 0;
}

~$ gcc main.c -o gdb_test.exe -g

~$ ls gdb_test.exe  main.c
gdb_test.exe  main.c

info inf i 信息

格式 同义 作用
info break info breakpoints, info b 查看当前设置的所有断点
info stack info s backtrace
info registers info reg 显示寄存器
info proc 显示进程信息
info sharedlibrary info share, info shared 显示已加载的共享库(动态库)的状态
info set show

sharedlibrary share 手动添加共享库(动态库)

格式:sharedlibrary 共享库(动态库)路径
手动添加共享库(动态库)到当前进程。

break brea bre br b 断点

断点的设置:

格式 作用
break 函数名 在当前文件指定函数处打断点
break 行号 在当前文件指定行打断点
break 文件名:行号 在指定文件的指定行处打断点
break 文件名:函数名 在指定文件的指定函数处处打断点
break +偏移量 在当前暂停位置往后几行处打断点
break -偏移量 在当前暂停位置往前几行处处打断点
break *地址 在指定地址处打断点

断点可以附加条件: 格式:break 断点 if 条件
举例,如下:

gdb ./gdb_test.exe
......
(gdb) b 17 if i==5
Breakpoint 1 at 0x11ba: file main.c, line 17.
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000000011ba in happy at main.c:17
	stop only if i==5
(gdb) run
Starting program: /home/yuangod/project/c/gdb_test/gdb_test.exe 
Breakpoint 1, happy () at main.c:17
17	    printf("happyi %d\n", i);
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00005555555551ba in happy at main.c:17
	stop only if i==5
	breakpoint already hit 1 time

hbreak tbreak thbreak 其它断点

硬件断点(hbreak),适用于ROM 空间等无法修改的内存区域中的程序。在有些架构中无法使用。
临时断点(tbreak)和临时硬件断点(thbreak),与断点(硬件断点)相同,都会在运行到该处时暂停,
不同之处就是临时断点(临时硬件断点)会在此时被删除,所以在只需要停止一次时用起来很方便。
三者的语法与break一样

clear cl 清除断点

break 的反命令,
格式与break类似,不过 break 会创建断点,而 clear 会删除断点。

enable en / disable disa dis 启用/禁用断点

对于 enable:

格式 作用
enable 启用所有断点
enable 断点编号 启用指定的断点

对于 disable:

格式 作用
disable 禁用所有断点
disable 断点编号 禁用指定的断点

conditon 断点条件的添加(替换)与删除

格式 作用
condition 断点编号 删除断点的条件
condition 断点编号 条件 添加(替换)断点的条件(如果没有条件就添加,有条件就替换)

举例,如下:

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00005555555551ba in happy at main.c:17
	stop only if i==5
	breakpoint already hit 1 time
(gdb) condition 1
Breakpoint 1 now unconditional.
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00005555555551ba in happy at main.c:17
	breakpoint already hit 1 time
(gdb) condition 1 i==5
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00005555555551ba in happy at main.c:17
	stop only if i==5
	breakpoint already hit 1 time
(gdb) condition 1 i!=6
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00005555555551ba in happy at main.c:17
	stop only if i!=6
	breakpoint already hit 1 time
(gdb) condition 1 i!=6 && i==5
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00005555555551ba in happy at main.c:17
	stop only if i!=6 && i==5
	breakpoint already hit 1 time

run r 运行

格式:run [参数...]
(重头开始)运行,直到结束或者遇到断点。
添加的参数会传递给程序(而非run本身有参数)。

start 开始

格式:start [参数...]
run 类似,但是运行到 main 函数停止,
等价于在 main 函数处打上断点( break main ),然后执行 run。

backtrace bt where 显示栈帧

格式 作用
backtrace 显示所有栈帧
backtrace full 显示所有栈帧(及其局部变量)
backtrace N 显示开头(栈顶) N 个栈帧
backtrace full N 显示开头(栈顶) N 个栈帧(及其局部变量)
backtrace -N 显示最后(栈底) N 个栈帧
backtrace full -N 显示最后(栈底) N 个栈帧(及其局部变量)

格式①:print 变量

举例 作用
print argv 查看参数字符串组的地址
print *argv 查看参数字符串组的首元素(程序名)
print argv[0] 查看参数字符串组的首元素(程序名)
print argv[1] 查看参数字符串组的第二个元素(第一个参数)

格式②: print/格式 $寄存器

格式 作用
x 显示为十六进制数
d 显示为十进制数
u 显示为无符号十进制数
o 显示八进制数
t 显示为二进制数,t的由来是two
a 地址
c 显示为字符(ASCII)
f 浮点小数
s 显示为字符串
i 显示为机器语言(仅在显示内存的x命令中可用)

如下举例:

(gdb) p $1
$4 = 5
(gdb) p/t $1
$5 = 101

x 显示内存

x 源自 eXamining。
格式:x/NFU 地址
N:重复的次数
F:同print p 显示变量中提到的格式。
U:表示单位( b、h、w、g , 分别为 字节、半字(2字节)、字(四字接)、双字(8字节) )
如下举例:

# 读取pc当前指向的汇编指令
(gdb) x/i $pc
=> 0x555555555171 <deepdark+8>:	lea    0xe8c(%rip),%rax        # 0x555555556004

disassemble disas 反汇编

格式 作用
disassemble 反汇编当前整个函数
disassemble 程序计数器 从pc开始,反汇编其所在函数的末尾
disassemble 开始地址,结束地址 反汇编从开始地址到结束地址之前的部分(注意逗号要加上)

next n 单步(不进入函数内部)

格式 作用
next (源代码级别)单步(不进入函数内部)执行 1 行
next N (源代码级别)单步(不进入函数内部)执行 n 行
nexti (机械指令、汇编级别)单步(不进入函数内部)执行 1 行
nexti N (机械指令、汇编级别)单步(不进入函数内部)执行 n 行

step s 单步(进入函数内部)

格式 作用
step (源代码级别)单步(进入函数内部)执行 1 行
step N (源代码级别)单步(进入函数内部)执行 n 行
stepi (机械指令、汇编级别)单步(进入函数内部)执行 1 行
stepi N (机械指令、汇编级别)单步(进入函数内部)执行 n 行

ignore 忽略断点、监视点、捕获点

格式:ignore 断点编号 N
忽略指定编号的忽略断点、监视点、捕获点接下来会忽略 N 次命中。
效果类似与 continue N ,但有所不同。

continue c fg 继续运行

格式 作用
continue 继续运行,直到结束或者遇到断点
continue N 同 continue,不过当前停止处的断点接下来会忽略 N-1 次命中(即使使用run等命令重启程序也会生效)
  1. continue N 效果类似 ignore,但由所不同。
  2. 如果没有停在断点处,并执行格式 continue N,那么会自动退化为 continue。
  3. continue 1 效果上等同于 continue

举例,如下:

......
Breakpoint 2, happy () at main.c:16
16	    int i = 5;
(gdb) c 3
Will ignore next 2 crossings of breakpoint 2.  Continuing.
happyi 5

Breakpoint 3, unhappy () at main.c:10
10	    printf("unhappy\n");
(gdb) info b
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x00005555555551b3 in happy at main.c:16
	breakpoint already hit 1 time
	ignore next 2 hits
3       breakpoint     keep y   0x000055555555518b in unhappy at main.c:10
	breakpoint already hit 1 time
......

finish fin 执行完当前函数

格式 finish
执行后会直接运行到当前函数结束(退出当前函数)。

until u 运行到某一位置

格式 作用
until 运行到源代码中下一行(比当前行号大的行)。如果位于循环体的最后一行,会执行完循环体,然后停在下一行,如果在函数体的最后一行,那么会退出函数。
until 位置 运行,直到指定的位置(可以是行号、函数、地址等等)
  1. 会触发断点
  2. 格式 until 位置位置必须在当前栈帧内,如果不在,大概率会运行到下个断点或者程序结束处。

watch awatch rwatch 监视点

大型软件或大量使用指针的程序中,很难弄清变量在什么地方被改变。
要想找到变量在何处被改变,可以使用 watch 相关的命令(监视点,watchpoint)

格式 作用
watch <表达式> 监视<表达式>改变(写入)
awatch <表达式> 监视<表达式>被访问(读取)、改变(写入)
rwatch <表达式> 监视<表达式>被访问(读取)

<表达式>一般为常量或者变量等等
使用监视点可能会降低运行速度

举例,如下:

(gdb) rwatch i
Hardware read watchpoint 6: i
(gdb) c
Continuing.

Hardware read watchpoint 6: i

Value = 5
0x00005555555551bd in happy () at main.c:17
17	    printf("happyi %d\n", i);
(gdb) info break
Num     Type            Disp Enb Address            What
2       breakpoint      keep y   0x0000555555555171 in deepdark at main.c:5
4       breakpoint      keep y   0x00005555555551b3 in happy at main.c:16
	breakpoint already hit 1 time
6       read watchpoint keep y                      i
	breakpoint already hit 1 time

delete d del 删除断点和监视点

格式:delete <编号>
删除<编号>所指示的断点或监视点。

set 设置值

格式 作用
set variable <变量>=<表达式> 改变变量的值
set $<便利变量名>=<表达式> 设置 gdb 内的"便利变量"(类似于宏)
set history [功能] 开启命令历史的功能

格式 set variable <变量>=<表达式> 举例,如下:

(gdb) p i
$14 = 5
(gdb) set variable i = 2026
(gdb) p i
$15 = 2026

格式 set $<便利变量名>=<表达式> 举例,如下:

(gdb) set $my_var=10
(gdb) p $my_var
$1 = 10
(gdb) set $log10 = (double (*)(double)) log10
(gdb) p $log10(10)
$4 = 1
(gdb) p $log10($my_var)
$5 = 1

generate-core-file gcore 生成Core dump文件

用于在调试的过程中生成 Core dump 文件,制作进程的"快照"。
如下:

(gdb) generate-core-file 
warning: Memory read failed for corefile section, 4096 bytes at 0xffffffffff600000.
Saved corefile core.638095
(gdb) q
A debugging session is active.

	Inferior 1 [process 638095] will be killed.

Quit anyway? (y or n) y

~$ gdb -c core.638095 ./gdb_test.exe
......

值得注意的是 gcore 也是一个GNU工具( 详情见 gcore(1) ),可以直接在 shell 中使用,如下:

# gcore [-a] [-o filename] pid[s]
~$ ps aux | grep vim                                                   
bigshot   633690  0.3  0.0  28244 13948 pts/1    Sl+  18:30   0:00 vim hello
bigshot   633706  0.0  0.0   9304  2460 pts/2    S+   18:30   0:00 grep --color=auto vim

~$ gcore -o core_vim 633690
[New LWP 633691]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x000072ecb0126d07 in select () from /lib/x86_64-linux-gnu/libc.so.6
warning: Memory read failed for corefile section, 4096 bytes at 0xffffffffff600000.
Saved corefile core_vim.633690

~$ ls core_vim.633690                                                  
core_vim.633690

# 之后边可以执行下面的命令复原保存时的 vim 进程环境
~$ gdb -c core_vim.633690 vim
......

attach detach 调试(附加到)和脱离已启动进程

要调试守护进程(daemon process)等已经启动的进程,或是调试陷入死循环而无法返回控制台的进程时,可以使用 attach 命令。
格式:attach <pid>
而脱离直接输入 detach 即可。

attach 之后就能使用普通的 gdb 命令,因此可以通过 print 命令显示变量,也可以设置断点。
此外,恢复程序运行一般可以使用 continue命令(简写为c)。
确认了行为之后,需要在 gdb 和 进程 分离时使用 detach 命令。这样调试中的进程就被从 gdb 的控制下释放出来。进程在被 detach 后会继续运行。

举例,如下:

~$ sleep 10000 &
[1] 647246

~$ gdb
......
(gdb) attach 647246
......
# 这里通过 attach 和 bt 直接看到 sleep 程序的栈帧
(gdb) bt
#0  0x000078618caeca7a in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, 
    req=0x7ffcb34c71e0, rem=0x7ffcb34c71d0) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
#1  0x000078618caf9a27 in __GI___nanosleep (req=<optimized out>, rem=<optimized out>)
    at ../sysdeps/unix/sysv/linux/nanosleep.c:25
#2  0x000059a509e9da7f in ?? ()
#3  0x000078618ca2a1ca in __libc_start_call_main (main=main@entry=0x59a509e9d5d0, argc=argc@entry=2, 
    argv=argv@entry=0x7ffcb34c7358) at ../sysdeps/nptl/libc_start_call_main.h:58
#4  0x000078618ca2a28b in __libc_start_main_impl (main=0x59a509e9d5d0, argc=2, argv=0x7ffcb34c7358, 
    init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffcb34c7348)
--Type <RET> for more, q to quit, c to continue without paging--
    at ../csu/libc-start.c:360
#5  0x000059a509e9dba5 in ?? ()

# 查看进程信息
(gdb) info proc
process 647246
cmdline = 'sleep 10000'
cwd = '/home/yuangod/project/c/gdb_test'
exe = '/usr/bin/sleep'

# 脱离
(gdb) detach
Detaching from program: /usr/bin/sleep, process 647246
[Inferior 1 (process 647246) detached]
(gdb) info proc
No current process: you must name one.

commands 断点命令

格式:

commands 断点编号  
命令  
...  
end  

举例,如下:

......
Temporary breakpoint 1, main () at main.c:23
23	    printf("this is a gbd test program\n");
(gdb) b 17
Breakpoint 2 at 0x5555555551ba: file main.c, line 17.
(gdb) commands 2
Type commands for breakpoint(s) 2, one per line.
End with a line saying just "end".
>silent
>p i
>end
(gdb) c
Continuing.
this is a gbd test program
$1 = 5

# silent命令的作用是不显示断点处暂停的消息。
......

list l 显示函数或行

格式 作用
list 列出上次列出内容之后的十行代码,或者围绕上次列出位置(取决于上下文)。
list + 列出上一次列出的十行代码之后的十行代码。
list - 列出上一次列出的十行代码之前的十行代码。
list . 列出当前执行点(即程序暂停的位置)周围十行的代码。
list 行号 列出该行周围十行的代码。
list 开始行号,结束行号 列出从开始行号到结束行号范围内的代码。
  1. 位置还可以为地址等,因为过多,这里省略。
  2. 特别地, list *$pc 可以显示当前行周围的代码(与 list . 不同,这显示当前的地址与行号)。

help h 帮助文档

格式:help 命令 举例,如下:

......
(gdb) help help
help, h
Print list of commands.
......

show 显示数据

格式 作用
show value print 命令显示历史的最新十个记录
show history [功能] 显示命令历史功能的开关与说明,与 set history [功能] 配合使用

define 命令定义 / document 命令说明

define 可以用于自定义命令(类似函数),格式如下:

define 命令名
  命令
  ...
end

document 可以用于给自定义命令添加说明,格式如下:

document 命令名
  说明
end

举例,如下:

(gdb) define li
Type commands for definition of "li".
End with a line saying just "end".
>x/10i $pc
>end
(gdb) document li
Type documentation for "li".
End with a line saying just "end".
>list machine instruction
>end
(gdb) li
=> 0x5555555551e8 <main+8>:	lea    0xe31(%rip),%rax        # 0x555555556020
   0x5555555551ef <main+15>:	mov    %rax,%rdi
   0x5555555551f2 <main+18>:	call   0x555555555060 <puts@plt>
   0x5555555551f7 <main+23>:	mov    $0x0,%eax
   0x5555555551fc <main+28>:	call   0x5555555551a7 <happy>
   0x555555555201 <main+33>:	mov    $0x0,%eax
   0x555555555206 <main+38>:	pop    %rbp
   0x555555555207 <main+39>:	ret
   0x555555555208 <_fini>:	endbr64
   0x55555555520c <_fini+4>:	sub    $0x8,%rsp
(gdb) help li
list machine instruction

source 引入初始化文件

格式:source 文件名
除了初始化文件,这种方式也能引入命令进行设置。

远程调试

有时候我们需要在一台设备中远程调试另一台设备,比如在x86_64架构的PC上调试在ARM架构的Linux开发板上运行的程序(下文以这个举例)。

为了完成远程调试,我们需要对应架构的 gdb 工具与 gdbserver 工具。一般来说,交叉编译工具链会提供对应架构的gdb与gdbserver。

获取对应架构的工具后,我们需要将 gdbserver 与用交叉编译工具链编译的对应架构可执行文件放到开发板上,而主机上需要对应的源码,可执行文件,与对应架构的gdb工具。

假设可执行文件的名字叫 gdbtest ,而对应的源码叫 gdbtest.c ,我们需要在开发板上如下操作:

~$ gdbserver <主机IP地址>:<任意端口> gdbtest

然后在主机下进行如下操作:

# 这里 arm-none-linux-gnueabihf-gdb 为交叉编译工具链
# 注意 gdbtest 要在本地备份一个供 gdb 读取
~$ arm-none-linux-gnueabihf-gdb gdbtest
......
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from gdbtest...

# 这里要输入值令进行连接
(gdb) target remote <开发板IP地址>:<与上文对应的端口>
Remote debugging using 192.168.1.50:2001
Reading symbols from /usr/local/arm/arm-gnu-toolchain-15.2.rel1-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/lib/ld-linux-armhf.so.3...
0x76feff30 in _start ()
   from /usr/local/arm/arm-gnu-toolchain-15.2.rel1-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/lib/ld-linux-armhf.so.3
(gdb) start
The "remote" target does not support "run".  Try "help target" or "continue".
(gdb) b main
Breakpoint 1 at 0x10422: file gdbtest.c, line 6.
(gdb) c
Continuing.

Breakpoint 1, main (argc=1, argv=0x7efffd74) at gdbtest.c:6
6		unsigned int times = 0;

# 下文正常运行 gdb 相关值令即可

参考文章