0. 前言
自古调试分两派,printf
直接输出派 和 gdb
调试派。
printf
派直观暴力,但不全面。
gdb
派全面却麻烦。
但为了更深入全面的学习,有必要了解gdb
这款强大的调试工具。
1. gdb调试前准备
gcc -g -o hello hello.c
使用gcc
编译时,一定要加上参数-g
,不然生成的可执行文件hello
不能调试,使用(gdb) file hello
或gdb hello
时,会报错:No symbol table is loaded. Use the "file" command.
(这个结论尚且不准确。)
2. gdb指令
指令可Tab
键补全,可上下键翻阅。
示例源程序如下:
#include<stdio.h>
int fun(int a, int b) {
int val = 0;
if (a > b) {
val = a + b;
} else {
val = a - b;
}
return val;
}
int main() {
printf("hello gdb!\n");
int i;
for (i=0; i<10; i++) {
printf("what is gdb? %d\n", i);
}
int a = 1, b=3;
printf("%d\n", fun(a, b));
return 0;
}
file
格式:
-
file <可执行文件名>
,加载要调试的可执行文件。
示例:
(gdb) file hello1
Reading symbols from hello1...done.
list
格式:
-
list <linenum>
,显示程序第linenum行周围的源程序 -
list <function>
,显示函数名为function的函数的源程序 -
list
,显示当前行周围的源程序(10行),多次list
,会自动向后显示源程序 -
list -
,向前显示源程序
简写:l
示例:
(gdb) l
warning: Source file is more recent than executable.
1 #include<stdio.h>
2
3 int fun(int a, int b) {
4 int val = 0;
5 if (a > b) {
6 val = a + b;
7 } else {
8 val = a - b;
9 }
10 return val;
(gdb) l
11 }
12
13 int main() {
14 printf("hello gdb!\n");
15 int i;
16 for (i=0; i<10; i++) {
17 printf("what is gdb? %d\n", i);
18 }
19 return 0;
20 }
(gdb) l -
1 #include<stdio.h>
2
3 int fun(int a, int b) {
4 int val = 0;
5 if (a > b) {
6 val = a + b;
7 } else {
8 val = a - b;
9 }
10 return val;
start
说明:
开始执行程序,并在main函数的第一条语句前面停下来
示例:
(gdb) start
Temporary breakpoint 1 at 0x617: file hello1.c, line 14.
Starting program: /home/dounine/gdb/hello1
Temporary breakpoint 1, main () at hello1.c:14
14 printf("hello gdb!\n");
run
说明:运行加载的程序。在run
前,需先设定一个或多个断点,不然程序会运行直到结束。
简写:r
break
格式:
-
break <function>
,在指定的函数入口处设置断点 -
break <linenum>
,在指定的行号处设置断点 -
break *<address>
,在指定地址对应的代码处(指令处)设置断点 -
break +<offset> / break -<offset>
,在当前行的下offset
行/上offset
行,设置断点
简写:b
(gdb) start
Temporary breakpoint 1 at 0x617: file hello1.c, line 14.
Starting program: /home/dounine/gdb/hello1
Temporary breakpoint 1, main () at hello1.c:14
warning: Source file is more recent than executable.
14 printf("hello gdb!\n");
(gdb) l
9 }
10 return val;
11 }
12
13 int main() {
14 printf("hello gdb!\n");
15
16 int i;
17 for (i=0; i<10; i++) {
18 printf("what is gdb? %d\n", i);
(gdb) break 17
Breakpoint 2 at 0x400629: file hello1.c, line 17.
(gdb) break fun
Breakpoint 3 at 0x4005d0: file hello1.c, line 4.
(gdb) break *0x400630
Breakpoint 4 at 0x400630: file hello1.c, line 17.
(gdb) break +2
Note: breakpoint 2 also set at pc 0x400629.
Breakpoint 5 at 0x400629: file hello1.c, line 16.
break *<address>
在了解并查看汇编位置后再下断点。
break +2
在第16行设置了断点,是因为当前行在第14行。
continue
格式:
-
continue
,当程序被停住后,可以使用该命令恢复程序的运行直到程序结束,或到达下一个断点 -
continue <ignore-count>
,忽略ignore-count
个断点
简写:c
next
格式:
-
next
,执行下一步/下一个语句,如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句) -
next <count>
,执行后面的count
条语句(不进入函数),然后再停住
简写:n
示例:
(gdb) break 22
Breakpoint 2 at 0x40065f: file hello1.c, line 22.
(gdb) c
Continuing.
hello gdb!
what is gdb? 0
what is gdb? 1
what is gdb? 2
what is gdb? 3
what is gdb? 4
what is gdb? 5
what is gdb? 6
what is gdb? 7
what is gdb? 8
what is gdb? 9
Breakpoint 2, main () at hello1.c:22
22 printf("%d\n", fun(a, b));
(gdb) n
-2
24 return 0;
(gdb) n
25 }
step
格式:
-
step
,执行下一步/下一条语句,然后停住。如果该语句为函数调用,则进入函数执行其中的第一条语句 -
step <count>
,执行后面的count
条语句,然后再停住
简写:s
示例:
(gdb) start
Temporary breakpoint 1 at 0x617: file hello1.c, line 14.
Starting program: /home/dounine/gdb/hello1
Temporary breakpoint 1, main () at hello1.c:14
warning: Source file is more recent than executable.
14 printf("hello gdb!\n");
(gdb) b 22
Breakpoint 2 at 0x40065f: file hello1.c, line 22.
(gdb) c
Continuing.
hello gdb!
what is gdb? 0
what is gdb? 1
what is gdb? 2
what is gdb? 3
what is gdb? 4
what is gdb? 5
what is gdb? 6
what is gdb? 7
what is gdb? 8
what is gdb? 9
Breakpoint 2, main () at hello1.c:22
22 printf("%d\n", fun(a, b));
(gdb) s
fun (a=1, b=3) at hello1.c:4
4 int val = 0;
(gdb) n
5 if (a > b) {
注意:使用step
命令会进入函数调用,而使用next
显然不会。但是,如果一直使用step
,那将会进入一些如printf
之类的函数。
-
print <expr>
,打印指定表达式的值(表达式可以是变量,也可以是地址) -
print /<f> <expr>
,将指定表达式的值以f
格式输出
f
的格式包括:
-
x
按十六进制格式显示变量。 -
d
按十进制格式显示变量。 -
u
按十六进制格式显示无符号整型。 -
o
按八进制格式显示变量。 -
t
按二进制格式显示变量。 -
a
按十六进制格式显示变量。 -
c
按字符格式显示变量。 -
f
按浮点数格式显示变量。
示例:
(gdb) print &i
$1 = (int *) 0xbffff46c
(gdb) print i
$2 = 2
(gdb) print i+=1
$3 = 3
(gdb) print i
$4 = 3
(gdb) start
Temporary breakpoint 1 at 0x5ac: file hello2.c, line 5.
Starting program: /home/dounine/gdb/hello2
Temporary breakpoint 1, main () at hello2.c:5
5 char *c = "hello world";
(gdb) p c
$1 = 0x400601 <__libc_csu_init+33> "\215\203\354\376\377\377)\306\301\376\002\205\366t%1\377\215\266"
(gdb) p /s c
$2 = 0x400601 <__libc_csu_init+33> "\215\203\354\376\377\377)\306\301\376\002\205\366t%1\377\215\266"
(gdb) p (char*)c
$3 = 0x400601 <__libc_csu_init+33> "\215\203\354\376\377\377)\306\301\376\002\205\366t%1\377\215\266"
(gdb) p *((char*)c)='H'
$4 = 72 'H'
(gdb) p c
$5 = 0x400601 <__libc_csu_init+33> "H\203\354\376\377\377)\306\301\376\002\205\366t%1\377\215\266"
注意:可以在print <expr>
的时候,通过表达式设定某个变量的值。
简写:p
examine
说明:用于打印某个地址所指向的内存中的内容,十分有用!!!
格式:x/nfu <addr>
,x
是examine
的缩写。
n
:表示要显示的内存单元个数。取值为:1 2 3...-
f
:表示显示方式, 可取如下值:-
x
按十六进制格式显示变量。 -
d
按十进制格式显示变量。 -
u
按十进制格式显示无符号整型。 -
o
按八进制格式显示变量。 -
t
按二进制格式显示变量。 -
i
指令地址格式 -
c
按字符格式显示变量。 -
f
按浮点数格式显示变量。
-
-
u
:表示一个地址单元的长度,与n一起表示显示的地址长度。取值如下:-
b
表示单字节, -
h
表示双字节, -
w
表示四字节, -
g
表示八字节
-
示例:
# 显示0x7fffffffd708(这是个地址)处1个(n=1)单字节(u=b)的内容,以十六进制(f=x)表示;
(gdb)x/1xb 0x7fffffffd708
0x7fffffffd708: 0x26
也可以省略n
f
u
参数:
(gdb) x/ 0x402470
0x402470: 0x00400f7c
display
-
display
,跟踪查看某个变量,每次停下来都显示它的值
简写:disp
watch
-
watch
,一般用来观察某个表达式(变量也是一种表达式)的值是否有变化。相当于,在每次该表达式值变化的地方,都设置一个断点。我们需要使用continue
命令前进。
示例:
(gdb) start
Temporary breakpoint 1 at 0x617: file hello1.c, line 14.
Starting program: /home/dounine/gdb/hello1
Temporary breakpoint 1, main () at hello1.c:14
14 printf("hello gdb!\n");
(gdb) watch i
Hardware watchpoint 2: i
(gdb) c
Continuing.
hello gdb!
Hardware watchpoint 2: i
Old value = 4196033
New value = 0
0x00400630 in main () at hello1.c:17
17 for (i=0; i<10; i++) {
(gdb) c
Continuing.
what is gdb? 0
Hardware watchpoint 2: i
Old value = 0
New value = 1
0x0040064b in main () at hello1.c:17
17 for (i=0; i<10; i++) {
info
说明:info
命令可以在调试时用来查看寄存器、断点、观察点(watch)和信号等信息。
info
可以查看的所有信息如下:
(gdb) info
"info" must be followed by the name of an info command.
List of info subcommands:
info address -- Describe where symbol SYM is stored
info all-registers -- List of all registers and their contents
info args -- Argument variables of current stack frame
info auto-load -- Print current status of auto-loaded files
info auxv -- Display the inferior's auxiliary vector
info bookmarks -- Status of user-settable bookmarks
info breakpoints -- Status of specified breakpoints (all user-settable breakpoints if no argument)
info checkpoints -- IDs of currently known checkpoints
info classes -- All Objective-C classes
info common -- Print out the values contained in a Fortran COMMON block
info copying -- Conditions for redistributing copies of GDB
info dcache -- Print information on the dcache performance
info display -- Expressions to display when program stops
info exceptions -- List all Ada exception names
info extensions -- All filename extensions associated with a source language
info files -- Names of targets and files being debugged
info float -- Print the status of the floating point unit
info frame -- All about selected stack frame
info frame-filter -- List all registered Python frame-filters
info functions -- All function names
info guile -- Prefix command for Guile info displays
info handle -- What debugger does when program gets various signals
info inferiors -- IDs of specified inferiors (all inferiors if no argument)
info line -- Core addresses of the code for a source line
info locals -- Local variables of current stack frame
info macro -- Show the definition of MACRO
info macros -- Show the definitions of all macros at LINESPEC
info mem -- Memory region attributes
info os -- Show OS data ARG
info pretty-printer -- GDB command to list all registered pretty-printers
info probes -- Show available static probes
info proc -- Show /proc process information about any running process
info program -- Execution status of the program
info record -- Info record options
info registers -- List of integer registers and their contents
info scope -- List the variables local to a scope
info selectors -- All Objective-C selectors
info set -- Show all GDB settings
info sharedlibrary -- Status of loaded shared object libraries
info signals -- What debugger does when program gets various signals
info skip -- Display the status of skips
info source -- Information about the current source file
info sources -- Source files in the program
info stack -- Backtrace of the stack
info static-tracepoint-markers -- List target static tracepoints markers
info symbol -- Describe what symbol is at location ADDR
info target -- Names of targets and files being debugged
info tasks -- Provide information about all known Ada tasks
info terminal -- Print inferior's saved terminal status
info threads -- Display currently known threads
info tracepoints -- Status of specified tracepoints (all tracepoints if no argument)
info tvariables -- Status of trace state variables and their values
info type-printers -- GDB command to list all registered type-printers
info types -- All type names
info unwinder -- GDB command to list unwinders
info variables -- All global and static variable names
info vector -- Print the status of the vector unit
info vtbl -- Show the virtual function table for a C++ object
info warranty -- Various kinds of warranty you do not have
info watchpoints -- Status of specified watchpoints (all watchpoints if no argument)
info win -- List of all displayed windows
info xmethod -- GDB command to list registered xmethod matchers
其中,常用的查看指令和作用如下:
-
info breakpoints
,查看设置了所有断点的信息 -
info display
,查看display
命令监视的变量 -
info registers
,查看寄存器中的值 -
info stack
,查看栈(各函数栈帧)的信息 -
info watchpoints
,查看watch
命令监视的变量
delete
格式:
-
delete <breakpoints num>
,删除断点编号所对应的断点,断点编号通过info breakpoints
查看。 -
delete
,删除所有断点。
示例:
(gdb) br 17
Breakpoint 2 at 0x400629: file hello1.c, line 17.
(gdb) info breakpoints
Num Type Disp Enb Address What
2 breakpoint keep y 0x00400629 in main at hello1.c:17
(gdb) delete 2
(gdb) info breakpoints
No breakpoints or watchpoints.
(gdb) br 16
Breakpoint 3 at 0x400629: file hello1.c, line 16.
(gdb) br 17
Note: breakpoint 3 also set at pc 0x400629.
Breakpoint 4 at 0x400629: file hello1.c, line 17.
(gdb) br 18
Breakpoint 5 at 0x400632: file hello1.c, line 18.
(gdb) info breakpoints
Num Type Disp Enb Address What
3 breakpoint keep y 0x00400629 in main at hello1.c:16
4 breakpoint keep y 0x00400629 in main at hello1.c:17
5 breakpoint keep y 0x00400632 in main at hello1.c:18
(gdb) delete
Delete all breakpoints? (y or n) y
(gdb) info breakpoints
No breakpoints or watchpoints.
backtrace
说明:查看栈(各函数栈帧)信息,与info stack
一样。
简写:bt
disassemble
说明:查看当前函数的反汇编代码。
quit
说明:退出gdb
简写:q
3. 安装peda插件
peda
是用python
开发的一款gdb插件,很受欢迎,github地址为https://github.com/longld/peda
3.1. 使用方法
git
下载项目到本地,然后修改~/.gdbinit
(当前用户的gdb
配置目录)如下:
source xxx/peda/peda.py
修改后的效果是,每次启动gdb
都会运行这条命令source xxx/peda/peda.py
。这条命令的意思是:加载peda中的python脚本,xxx
是peda的安装目录。
4. 使用自己python的脚本
在python中import gdb
,就可以使用gdb
模块了。
但是,import gdb
直接运行会报错。
只有在gdb
中source xxx.py
时,才可以使用。
python中的gdb
模块使用教程见官网:https://sourceware.org/gdb/onlinedocs/gdb/Python.html#Python