前言
GDB 是由 GNU 软件系统社区提供的调试工具,同 GCC 配套组成了一套完整的开发环境,GDB 是 Linux 和许多类 Unix 系统中的标准开发环境。
一般来说,GDB 主要完成下面四个方面的功能:
启动程序:可以按照自定义的要求随心所欲的运行程序。
设置断点:可让被调试的程序在所指定的调置的断点处停住,断点可以是条件表达式。
打印信息:当程序被停住时,可以检查此时程序中所发生的事。
修改变量:可以通过修改程序中的变量,将一个 BUG 产生的影响修正从而测试其他 BUG。
debug版本与release版本
-
假设存在一段代码:
//文件为 gdb_test.cpp #include<iostream> int GetSum(int n){ //主要是测试 ;其功能为计算1至n之和; int ret = 0; for(int i = 1;i<=n;++i){ ret+=i; } return ret; } int main() { int n =0; std::cin>>n; int ret = GetSum(n); std::cout<<ret<<std::endl; return 0; }
再使用g++进行编译:
g++ gdb_test.cpp -o test
编译链接过后进行运行确保程序无误;
在这时若是想使用gdb进行调试:
gdb test
(前提是得有gdb),若是没有gdb则需要使用yum进行安装:
sudo yum install -y gdb
将会出现这样的提示:
(no debugging symbols found) … done.
没有找到调试符号
这正是因为,在使用gcc/g++进行编译链接所生成的可执行文件默认是release版本的,无法进行debug调试;
这里可以再回顾一下gcc/g++的特性;
链接 生成的可执行程序 gcc/g++ 在不使用-static修饰的时候默认为动态链接; 默认生成的可执行程序的版本为release版本,若是需要生成debug版本需要在末尾处加上-g
生成debug版本的可执行程序:
g++ gdb_test.cpp -o test_debug -g
添加-g选项代表该可执行程序为以debug方式发布的
生成debug版本的可执行程序后即可以进行调试;
gdb test_debug
在此之前可以使用readelf来查看ELF格式文件信息;
readelf -S 'filename' #使用-S选项显示节头信息
或者
readelf -S 'filename' | grep -i debug #使用-S选项显示节头信息,grep debug为找出所有带debug的信息,-i为不区分大小写;
以第二条命令查看release版本的可执行程序的段信息时可以看到
由于没有debug,所以不显示;同时因为没有debug也不能进行调试;
同时使用第二条命令对所生成的debug版本的可执行程序时为
debug版本和release版本的大小差是因为在debug版本中多出了许多调试信息
在进行调试之前先使用Makefile以方便构造与清除
GDB操作
启动与退出gdb
gdb 'filename' #使用该命令即可启动gdb调试,其中filename为可执行程序(debug版)
quit #要退出gdb时只要输入quit或者q即可退出
显示代码
list #当然大多数情况下会使用简写 l ,从头开始即为 l 0 并回车至所有代码显示完毕
一般使用list时,gdb将会按照自己的方式将代码进行显示,可能不显示全,单若是希望从头开始显示时则可以使用 l 0 即从头显示代码,单此时也并不会显示完全,所以要用回车至所有代码显示完全;
同时在这里该注意,gdb与平时的Linux操作中较为不同的是;
在gdb调试过程中,gdb将会记住你上次的指令,这也是在使用list(或者l)后按照回车能够继续显示代码,在此处按回车时将会继续list的命令;
控制程序执行
命令 | 功能 |
---|---|
run < arguments > 或者 r < arguments > | 运行或者重新运行程序,并传递指定的参数。如果设置了断点,程序会在断点处暂停,并显示当前的源代码和寄存器值; |
如 : run arg1 arg2 | 运行程序,并传递arg1和arg2为参数; |
continue 或者 c | 继续运行程序 (若是有断点则至下一个断点处停止,否则运行至程序结束) |
next 或者 n | 逐过程调试(不进入函数) |
step 或者 s | 逐行调试 (会进入函数) |
finish 或者 f | 执行程序至当前函数结束(返回),并显示返回值; |
return < expression >或者 r < expression > | 强制当前函数立即返回,并将返回值设定为指定的表达式。如果不指定表达式则返回0; |
jump < location >或者 j < location > | 强制程序跳转到指定位置,可以是行号、或者是地址;将可能改变程序的正常流程; |
断点设置与取消:
命令 | 功能 |
---|---|
(gdb) break main(main的这个位置可以是函数名、文件名: 、行号或者内存地址) | 在main函数的第1行的位置设置断点(以此类推,所有的函数都可以像这样进行断点设置) |
(gdb) break test.c:10 | 在同一目录下的test.c源文件设置断点(暂未证实,应该另有) |
(gdb) info breakpoints | 查看当前节点个数以及编号位置等信息;可以简写为i b 以此类推,但是此处的breakpoints并不能用来进行断点(不能使用breakpoints 10 这类的方式进行断点) |
(gdb) delete 1 | 删除编号为1 的节点 ;delete也可以简写为d; |
(gdb) delete | 删除所有节点 |
condition <number编号> <expression表达式>或者cond <number编号> <expression表达式> | 给指定编号的断点添加条件,当条件为真时,起到断点作用 |
查看(或修改)变量、寄存器与内存
命令 | 功能 |
---|---|
print 或者 p | 打印某个变量或者表达式的值。可以使用任何合法的C语言表达式,包括宏、指针、结构体等。 |
print x | 打印变量x的值 |
print *p | 打印指针p所指向的值 |
print add(10,20); | 打印调用add函数并传入10 20作为参数后的返回值; |
set < expression> | 修改某个变量或者表达式的值,修改规则同上; |
set x = 10 | 将变量x修改为10,同理该方法也同样可以用来修改指针 |
set add(10,20) = 100 | 修改调用函数并传递10与20作为参数后修改返回值为100 |
info registers或者i f | 显示所有寄存器的值 |
info registers < name >或者i f | 显示指定的寄存器的值 |
info registers eax | 显示eax寄存器的值 |
set $< name > = < value > | 修改寄存器的值 |
set $eax = 5 | 将寄存器eax 的值修改为5 |