【GDB】gdb使用

1. 常用命令

list显示源程序
# ------------ list显示源程序 --------------
l [行号] # list ,显示对应的代码,每次10行
l [函数名] # 显示对应函数的源码
l # 显示当前行后面的源程序
l - # 显示当前行前面的源程序
l + # 往后显示源码
l [first,last] # 从first行显示到last
l [,last] # 从当前行显示到last行之间的源码

set listsize [count] # 设置一次显示源代码的行数
show listsize # 查看当前listsize的设置
查看源码的内存
# -------- 查看源码的内存 ------------
info line [参数] # 显示指定的源码在运行时的内存地址,参数可以是:
	[行号]
	[文件名:行号]
	[文件名:函数名]
# 示例
(gdb) info line test.c:func
Line 4 of "test.c" starts at address 0x400596 <func> and ends at 0x40059d <func+7>.
disassemble查看汇编代码
# ------ 查看源程序当前执行时的机器码 ---------
disassemble [函数名]

# 示例
(gdb) disassemble main
Dump of assembler code for function main:
   0x00000000004005cb <+0>:	push   %rbp
   0x00000000004005cc <+1>:	mov    %rsp,%rbp
   0x00000000004005cf <+4>:	sub    $0x10,%rsp
   0x00000000004005d3 <+8>:	movl   $0x0,-0x8(%rbp)
   0x00000000004005da <+15>:	movl   $0x1,-0x4(%rbp)
   0x00000000004005e1 <+22>:	jmp    0x4005ed <main+34>
=> 0x00000000004005e3 <+24>:	mov    -0x4(%rbp),%eax
   0x00000000004005e6 <+27>:	add    %eax,-0x8(%rbp)
   0x00000000004005e9 <+30>:	addl   $0x1,-0x4(%rbp)
   0x00000000004005ed <+34>:	cmpl   $0x5,-0x4(%rbp)
   0x00000000004005f1 <+38>:	jle    0x4005e3 <main+24>
   0x00000000004005f3 <+40>:	mov    -0x8(%rbp),%eax
   0x00000000004005f6 <+43>:	mov    %eax,%esi
   0x00000000004005f8 <+45>:	mov    $0x4006c8,%edi
   0x00000000004005fd <+50>:	mov    $0x0,%eax
   0x0000000000400602 <+55>:	callq  0x4004a0 <printf@plt>
   0x0000000000400607 <+60>:	mov    $0x3,%edi
   0x000000000040060c <+65>:	callq  0x400596 <func>
   0x0000000000400611 <+70>:	mov    %eax,%esi
   0x0000000000400613 <+72>:	mov    $0x4006d6,%edi
   0x0000000000400618 <+77>:	mov    $0x0,%eax
   0x000000000040061d <+82>:	callq  0x4004a0 <printf@plt>
   0x0000000000400622 <+87>:	mov    $0x0,%eax
   0x0000000000400627 <+92>:	leaveq 
   0x0000000000400628 <+93>:	retq   
End of assembler dump.
断点breakpoints
# --------- breakpoints --------------
b # 不加任何参数,表示在下一条指令处停住
b [行号] # breakpoint,在这一行打断点
b [源文件:函数名] # 在该函数的第一行加上断点
b [源文件:行号] # 在该源文件中的这一行打上断点
b +[offset] / b -[offset] : 在当前行号的前面或后面的offset处停住
b *[address] : 在程序运行的内存地址处停住
break ... if cond # ... 可以是参数,cond表示条件,在条件成立时停住。示例:break if i=100
info b # 查看断点信息
d [当前要删除的断点的编号] # delete,删除一个断点。(注:不能d+行号)
d [range] # range表示断点编号范围,e.g. d 2-4  删除编号234的断点
d breakpoints # 删除所有断点
disable b # 使所有断点无效, 默认缺省
enable b # 使所有断点有效,默认缺省
disable b [断点编号] # 使指定的断点无效
enable b [断点编号] # 使指定断点有效
disable [range]
enable [range]
观察点watchpoint
# ---------- WatchPoint -----------
# watchpoint观察点,一般来观察某个表达式(变量也是一种表达式)的值是否变化,若有变化则马上停止程序
watch [expr] # 给表达式/变量expr设置一个观察点
rwatch [expr] # 当表达式/变量被读取时,停住程序
awatch [expr] # 当表达式/变量的值被读或写时,停住程序
info watchpoints # 查看观察点
捕捉点catchpoint
# ---------- CatchPoint ---------------
# catchpoint捕捉点,用来捕捉程序运行时的一些事件
catch [event] # 当event发生时,停住程序,event可以是:
	throw 一个C抛出的异常 : throw为关键字
	catch 一个C捕捉到的异常 : catch为关键字
tcatch [event]	 # 仅设置一次捕捉点,当程序停住后,捕捉点被自动删除
clear清除停止点
# ------------- clear 停止点 -----------------
# breakpoint, catchpoint, watchpoint都是停止点
clear # 清除所有的已定义的停止点
clear [function] # 清除所有设置在该函数上的停止点
clear [line] # 清除所有在指定行上的停止点
clear [文件名:行号] # 清除所有设置在指定文件指定行上的停止点
condition停止条件维护
# ------------ condition -----------------
# 条件的相关维护命令condition,当条件成立时,程序自动停止(只有break和watch命令支持if cond)
condition [断点编号] [expression] # 修改对应断点编号的停止条件为expression
condition [断点编号] # 清除对应断点号的停止条件
调试程序r/c/n/s
r # run,【F5】。没有断点直接运行;有断点从第一个断点处开始运行

c # continue,从一个断点处直接运行到下一个断点处
	c [ignore-count] # ignore-count表示忽略其后的断点次数

n # next,逐过程,相当于【F10】
	n [count] # count表示执行后边的count条执行,然后停住
nexti # 逐过程执行机器指令,若该条指令是函数调用则一直执行直至返回
	nexti [repeatCount] # 

s # step,逐语句,相当于【F11】
	s [count] # count表示执行后面的count条命令,然后停住
stepi # 单步跟踪一条机器指令,简写为si
	stepi [repeatCount] # 
print显示运行时数据
# ---- 查看运行时数据 ---------
p [变量名] # 打印变量值
p /f [expr] # /f是打印格式,例如十六进制是/x

# 变量:在gdb中可查看三中变量的值:全局变量/静态全局变量/局部变量
# 表达式
@ : 和数据相关的操作符,用于输出数组中指定区域的元素
:: : 指定一个在文件或函数中的变量
{type}addr : 表示一个指向内存地址的类型为type的对象

p [文件名]::[变量名] 
p [函数名]::[变量名]
p [数组]@[len]
	p *array@10 # 打印数组的前10个元素的值。*为解引用
	p array[10]@3 # 显示数组array的10到12个元素
p [指针] # 打印指针指向的类型以及指针的地址
p *(struct xxx *)ptr # 打印指向的结构体内容
函数栈
# ------------ 函数栈 ------------------
bt # 查看函数栈 backtrace
	bt [n] # n为正整数,表示仅打印函数栈顶上n层的信息
	bt [-n] # n为正整数,表示仅打印函数栈底下n层信息

# 若要查看某一层的信息,则需切换当前的栈
frame [n] # n是一个从0开始的整数,是栈中的层编号。frame 0表示栈顶;frame 1表示栈的第二层
frame [addr] # 栈中层的地址?
info f / info frame
info f [addr]

up [n] # 向栈的上面移动n层,直接up则上移一层
down [n] # 向栈的下面移动n层,直接down则下移一层

info args # 打印当前层的参数信息
info locals # 打印当前函数中所有局部变量和值
info catch # 打印当前函数中的异常处理信息

程序运行设置
# -------- 设置程序启动参数 ---------
set args # 例如set args 10 20 30
show args # 查看设置好的运行参数

# -------- 运行环境 --------------
path [dir] # 设置程序的运行路径
show paths # 查看程序的运行路径

# --------- 环境变量 --------------
set env [环境变量名]=[value] # 设置环境变量
show env [环境变量名] # 查看环境变量

# --------- 工作目录 --------------
cd # 相当于shell中的cd命令
pwd # 相当于shell中的cd命令

# ------- 执行shell命令
shell [command] 
变量修改/跟踪
# ----------- 变量修改 -----------
set var # 修改变量的值
	示例: set var value=1
# --------- 变量跟踪 -----------
display # 跟踪查看一个变量,每次停下来都显示它的值
undisplay [变量名编号] # 取消对先前设置的变量的跟踪
until跳转执行
until [行号] # 指定位置跳转,执行完区间代码,简写为u
finish
finish # 在一个函数内部,执行到当前函数返回,然后停下来等待命令
help显示帮助信息

进入gdb后,可以输入help查看帮助信息,回显如下:

(gdb) help
List of classes of commands:

aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands

help回显仅列出gdb的命令种类,若要看对应命令种类中的命令,可以输入类似help breakpoints的命令。
P.S. gdb下也支持Tab键补全

2. 使用示例:使用gdb调试core文件

2.1 什么是core文件

coredump,核心转储文件,程序运行过程中发生异常退出时,操作系统把程序当前的内存状态存储在core文件中,称为coredump。

2.2 生成core文件

查看能否生成core文件:

ulimit -c # 若回显为0,则表示关闭了生成core的功能,不会生成core文件

临时修改:

ulimit -c [filesize] # 设置限制core文件大小,单位KB。若生成的core超过了该限制,则会被裁剪
ulimit -c unlimited # 表示core文件大小不受限制

永久修改:使用上述命令修改仅是临时修改,重启后不生效。

# 方法1
/etc/rc.local 在该文件中新增一行:ulimit -c unlimited
# 方法2
/etc/profile 在该文件中新增一行:ulimit -c unlimited
# 方法3
/etc/security/limits.conf 在该文件中新增两行
@root soft core unlimited
@root hard core unlimited

core默认的文件名为core.pid,默认路径为产生segmentation default的程序的当前目录。修改:

# 默认的core文件路径和名称在文件 /proc/sys/kernel/core_pattern
# 临时修改
echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
# 永久修改
sysctl -w kernel.core_pattern=/corefile/core-%e-%p-%t

c源码示例:当输入的字符个数超过十个,就会产生段错误:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void process_data(const char *data)
{
    char buffer[10];
    strcpy(buffer, data);
}

int main(int argc, char *argv[])
{
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <data>n", argv[0]);
        exit(EXIT_FAILURE);
    }
    process_data(argv[1]);
    return 0;
}

编译生成可执行文件,注意加上-g选项:

gcc -g -o coretest coretest.c

运行可执行文件,并输入超过10个字符:

[root@server8 coretest]# ./coretest "aaaaaaaaaaaaaaaaaaaaaaaaaaaa"
段错误 (核心已转储)

ll查看core文件:

[root@server8 coretest]# ll /corefile/
总用量 160
-rw-------. 1 root root 249856 419 12:53 core-coretest-5784-1745081616

2.3 gdb调试core文件

gdb core-coretest-5784-1745081616

出现如下报错:

Missing separate debuginfo for the main executable file
Try: yum --enablerepo='*debug*' install /usr/lib/debug/.build-id/12/c6e0d3d8433c7d1f4af57e8af66ec562a9472b

这是因为缺少debuginfo:

1. 修改CentOS-Linux-Debuginfo.repo , enable=1
2. 首先安装 yum install -y yum-utils # debuginfo-install在这个包里
3. 使用debuginfo-install安装debuginfo相关包
	debuginfo-install glibc
	debuginfo-install libgcc
	debuginfo-install libstdc++
	debuginfo-install nss-softokn-freebl

如果按照上述步骤执行还是会告警,可能是glibc版本不匹配,或是没导入符号表,可以这样:

# 先cd到可执行文件的路径下,假设可执行文件名为coretest
# 然后gdb+可执行文件名
gdb coretest
# 进入gdb环境后,使用core-file加载core文件
core-file /corefile/core-coretest-5784-1745081616

查看函数调用栈:

(gdb) bt
#0  0x0000000000400687 in process_data (data=0x7fffa478a2bb 'a' <repeats 43 times>) at coretest.c:11
#1  0x6161616161616161 in ?? ()
#2  0x6161616161616161 in ?? ()
#3  0x6161616161616161 in ?? ()
#4  0x0000000000400061 in ?? ()
#5  0x00007fb8198ec493 in __libc_start_main (main=0x400688 <main>, argc=2, argv=0x7fffa4789b28, 
    init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffa4789b18)
    at ../csu/libc-start.c:314
#6  0x00000000004005ae in _start ()

3. gdb导入符号表

3.1 什么是符号表

gdb符号表是gdb在调试过程中使用的一种数据结构,包含了程序中函数、变量等信息,用于在gdb调试过程中进行符号解析和地址定位。
gdb符号表的主要组成:

  • 函数符号表:包含程序中定义的函数名称、参数、返回值等信息;
  • 变量符号表:包含程序中定义的变量名称、类型、作用域等信息;
  • 类型符号表:包含程序中定义的数据类型,例如结构体、枚举、类型别名等;
  • 地址映射表:将程序中的虚拟地址映射到物理地址。

gdb符号表在编译时生成,通常由编译器在生成目标文件时自动生成,在调试时,gdb会读取符号表,将程序中的地址解析为符号名称。同时gdb也可根据符号表对程序进行反向映射,例如查看某个函数或变量的调用栈、内存占用情况等。

3.2 导入符号表

gcc -g -o test test.c
# 获取程序的二进制文件和符号表文件
objcopy -O binary test test.bin
addr2line -e test.bin -f -s -n 1>test.sym # 生成名为test.bin的二进制文件和名为test.sym的符号表文件
# 使用gdb打开二进制文件获取符号表
gdb test.bin
# 将addr替换为需要查看的函数或变量的地址,gdb将显示该地址处的函数或变量的源代码和符号表信息
(gdb)list(addr)
# 进入gdb环境
(gdb)info files # 该命令可查看进程文件地址绑定信息
# 加载符号表命令
(gdb)add-symbol-file

参考内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值