gdb基础操作

144 阅读5分钟

1. 内核转储

内核转储能保存问题发生时的状态,将执行环境保存到一个文件中.有利于我们在碰上及其罕见的bug,或者很难复现的bug.然后能够进行调试查找原因.

1.1 启用内核转储

默认都不会启用内核转储功能,比如执行下面指令来查看是否启用了

ulimit -c
0

-c 选项表示内核转储文件的大小限制.上面限制为0,表示内核转储无效.使用如下指令,即开启内核转储

ulimit -c unlimited

上述命令表示不限制文件大小.也可以设置指定大小上限.

ulimit -c 1024

指定核心转储文件在工作目录中:
/etc/sysctl.conf 文件添加如下指令

kernel.core_pattern = %t-%p-%e.core
kernel.core_uses_pid = 0  # 如果设置为1,表示文件名末尾添加 .PID

上述会生成如下格式的文件: 生成内核转储文件的时间-进程ID-可执行文件名.core kernel.core_pattern可以设置的格式如下表所示:

格式符说明
%%%字符本身
%u生成转储文件的进程ID(PID)
%g生成转储文件的用户ID
%s引发转储的信号编号
%t转储时刻(从1970年1月1日 00:00开始的秒数)
%h主机名(uname返回的nodename)
%e可执行文件名
%c转储文件的大小上限

1.2 在专用目录生成内核转储

在/var/cores目录下生成核心转储文件

kernel.core_pattern = /var/cores/%t-%p-%e.core
kernel.core_uses_pid = 0  

然后执行命令: sysctl -p

1.3 调试内核转储文件

使用gdb调试生成的内核转储文件,格式为: gdb -c 内核转储文件名 可执行文件名

gdb -c core.7561 ./a.out

2. gdb基础操作

2.1 启动调试

gdb 可执行文件名

启动显示如下信息:
image.png

2.2 设置断点

可以在函数名和行号设置断点,程序启动会在断点自动暂停

格式: break 断点
简写: b 断点

断点设置的方式有如下几种:

  1. break 函数名
  2. break 行号
  3. break 文件名:行号
  4. break 文件名:函数名
  5. break +偏移量
  6. break -偏移量
  7. break *地址

设置好的断点可以通过 info break显示 image.png

2.3 删除断点

使用delete(简写d)命令删除断点
格式: delete 编号

例如:
image.png

2.4 显示栈帧

backtrace命令可以在遇到断点而暂停执行时显示栈帧.该命令简写为bt.此外,backtrace的别名还有where和info stack(简写为info s)

格式:

显示所有栈帧

backtrace
bt

只显示开头N个栈帧

backtrace -N
bt -N

只显示最后N个栈帧

bactrace full
bt full
backtrace full N
bt full N
bactrace full -N
bt full -N

例如:
image.png

2.5 显示变量

print命令可以显示变量,也可以显示寄存器.简写为: p 格式: print 变量
例如:
image.png

显示寄存器
info registers显示所有寄存器,简写为: info reg
image.png 查看单个寄存器,在寄存器名前加上 $ image.png

print显示可以使用如下格式:
p/格式 变量

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

image.png

2.6 查看内存内容

格式: x/NFU 地址,N为重复次数;F为前面讲过的格式;U为所示的单位,如下表所示

单位说明
b字节
h半字(2字节)
w字(4字节)(默认值)
g双字(8字节)

image.png

下面显示从rip所之地址开始的10条指令
image.png

也有反汇编的命令 disassemble,简写为disas
格式:

  1. disassemble
  2. disassemble 程序计数器 image.png

也可以执行layout asm,显示汇编窗口
image.png

2.7 单步执行

执行源代码一行的命令为 next(简写为n).会跳过函数,不执行到函数内部.
执行时如果遇到函数调用,可能想执行到函数内部,使用**step(简写为s)**命令

如果要逐条执行汇编指令,分别使用nexti(简写ni)stepi(si) 命令

2.8 继续执行

调试时,可以使用 continue(简写c) 命令继续运行程序.程序会在遇到断点后再次暂停运行.如果没有遇到断点,就会一直运行到结束.
格式:

continue
continue 次数

指定次数可以忽略断点.例如, continue 5 则5次遇到断点不停止,第6次遇到断点时才暂停执行

2.9 监视点

大型软件或大量使用指针的程序中,很难弄清变量在什么地方被改变.要想找到变量在何处被改变.有如下几个watch命令

表达式发生变化时暂停运行
watch 表达式

表达式被访问、改变时暂停运行
awatch 表达式

表达式被访问时暂停运行
rwatch 表达式

例子:
image.png

image.png 设置监视点可能会降低运行速度

2.10 改变变量的值

格式: set variable 变量=表达式
例子:
image.png

2.11 生成内核转储文件

使用 generate-core-file 命令可将调试中的进程生成内核转储文件 image.png

还可以使用 gcore 命令从命令行直接生成核心转储文件

对名为aa的进程生成核心转储文件 image.png

image.png

2.12 attach到进程

要调试守护进程等已经启动的进程,或是调试陷入死循环而无法返回控制台的进程时,可以使用attach命令
格式: attach pid
image.png attach之后就能使用普通的gdb命令.因此可以通过print命令显示变量,也可以设置断点.
确认了行为之后,需要在gdb和进程分离时使用detach命令.这样调试中的进程就被从gdb的控制下释放出来.进程被detach后会继续运行


3. 函数调用约定

image.png 上面调用test函数,参数都是普通类型.在32位下,MSVC和gcc编译器都是从右向左压入栈中

MSVC编译器
image.png image.png

GCC编译器
image.png

在64位下
MSVC编译器
调用约定如下:
image.png 前四个参数分别是: RCX、RDX、R8、R9, 后续的参数就还是从右向左压入栈中
下面是msvc的汇编代码 image.png

GCC编译器
调用约定如下:

image.png 下面是gcc的汇编代码 image.png