while(i > 0)
i++;
基本操作
调试类操作
生成可调试文件
在使用GCC编译时,必须添加-g
选项,生成用于调试的符号表,才能使用GCC对可执行文件进行调试。
加载调试文件
1 | (gdb) file prog1 |
输入参数
有些时候我们的程序需要用户输入参数,这种情况下我们可以将输入写入一个文件input.txt
当中,然后执行下列语句
1 | gdb helloworld |
单步执行
starti
starti是GDB8.1之后的一个命令,作用是在载入待调试的程序后,在第一个指令前设置一个断点,然后执行到该断点。相当于从第一条指令开始调试。
si
si命令将会执行一条汇编命令
1 | (gdb) si |
需要注意的是,si打印的是下一条要执行的语句
n
n命令会一次执行一个函数,如果没有符号表,那么不能使用n命令。n x
表示某一行x次
断点
设置断点
在指定的地址设置断点的语句如下,其中0x0010000c为程序执行的断点
1 | (gdb) br * 0x0010000c |
其中,断点可以是一个实际内存地址,也可以是描述符(”mon_backtrace”或者”monitor.c:71”)。注意,如果使用描述符,那么在编译的时候gcc必须给定-g
选项并重新编译。
1 | (gdb) br monitor.c:71 #在monitor.c 71行设置断点 |
如果想要在特定条件下触发断点,可以使用条件断点,命令如下:
1 | (gdb) br *0x0010000c if |
这里总结一下断点的类型:
- 特定内存地址
- 描述符(例如某个具体函数)
- 行号(某个文件的行号)
查看断点
1 | info b |
删除/禁用/使能断点
1 | delete 1 # 删除第一个断点 |
监视点
watchpoints(监视点)和端点不同,当检测到某个表达式或变量的值发生改变时,将会停止运行
1 | (gdb) watch <expression> #如果指定表达式内容改变,则停止 |
继续执行类
继续直行至断点
继续执行命令为c
,程序会执行至下一个断点处
1 | (gdb) c |
继续直行至当前函数返回
1 | (gdb) finish |
如果不在函数内,那么finish没有意义
继续执行至特定位置
1 | (gdb) advance _start |
advance 将会执行代码至特定的符号或地址所在位置处
符号相关
切换符号文件
1 | symbol-file obj/kern/kernel # 这个命令将会加载kernel中的符号 |
显示类操作
反汇编
反汇编指令为x/N addr
,该指令可以将addr开始的N个连续指令进行反汇编
1 | (gdb) x/10 0x7c00 |
查看内存
x命令
使用x/Nxw addr
可以以16进制(后面的x)查看从addr开始的N个word大小的内存中的值
1 | (gdb) x/8x 0x00100000 |
有些情况下要看栈中内存的情况,由于栈是向下生长的,所以我们可以用
1 | (gdb) x/-8x 0x00100000 |
反向打印内存。如果想查看当前内存,可以使用x/i $pc
p命令
相比于x命令,p命令能够检验一个c语言表达式,并用合适的类型对结果进行打印,例如我们要打印某个内存位置处特定类型的变量,可以使用下面的语句:
1 | p *((struct Elf*)0x10000) |
这个命令将会打印从0x10000开始的Elf类型的变量,其结果非常清晰,可以将结构体中的所有内容展示出来:
1 | $1 = {e_magic = 0, e_elf = '\000',...} |
info命令
info命令能够查看相关信息,功能比较全,所以单独列出
查看寄存器的值
1 | // 查看所有寄存器 |
查看c语言类型
1 | info types |
查看变量
1 | info variables # 打印全局变量 |
查看当前函数栈
1 | (gdb) info frame |
其中:
- stack level 0:表示frame的序号
- frame at 0xb75f7390:表示栈的起始地址
- eip = 0x804877f in base::func() (testing.cpp:16); saved eip 0x804869a
- eip = 0x804877f表示下一条语句在0x804877fc处,在testing.cpp的16行
- saved eip 0x804869a表示函数返回地址,即返回后执行0x804869a
- called by frame at 0xb75f73b0:表示调用者的栈位于0xb75f73b0
- Arglist at 0xb75f7388, args: this=0x0:表示参数的起始地址以及参数
- Locals at 0xb75f7388:表示局部变量的位置
- Previous frame’s sp is 0xb75f7390:表示前一个栈帧(调用者栈的栈顶或被调用者栈的栈底)的栈顶
- 还有一些保存的寄存器的值
查看源码
使用list可以查看某个位置处的源码
1 | (gdb) list 123 # 打印源文件123行开始的几行代码 |
查看函数调用栈
使用bt命令可以查看函数的调用栈
1 | (gdb) bt |
每调用一个函数,则压入现在所在的函数,同时前面的序号依次递增,最上面的就是当前所在的函数,其中,序号后面的0x00007d26为函数栈中函数的返回地址,即被保存的eip值。
查看具体变量
当程序中断时,我们可以使用p命令查看具体的变量,例如现在我们所在的函数有一个输入参数cmd
,是一个结构体类型指针,那么我们可以用如下命令查看指针值及结构体中内容:
1 | (gdb) print cmd |
一个完整的例子
首先,我们要确保在gcc编译过程中加入了-g
选项,生成调试信息,否则gdb调试过程中能够查看的信息很少。
1 | gcc main.c -o build -g |
然后打开gdb,