gdb调试原理
这篇文章介绍了GDB调试的原理:
用图文带你彻底弄懂GDB调试原理 - 腾讯云开发者社区-腾讯云 (tencent.com)
Gdb调试多进程程序 - zhenjing - 博客园 (cnblogs.com)
这篇文章以test.c程序为例:
#include <stdio.h>
int main(int argc, char *argv[])
{
int a = 1;
int b = 2;
int c = a + b;
printf("c = %d \n", c);
return 0;
}
输入gcc -g test.c -o test
通过编译命令生成 test可执行程序,然后输入gdb ./test
进入GDB
作者给出如下流程图:
在执行gdb ./test
命令时,操作系统会启用GDB进程,这个GDB进程fork出子进程,这个子进程做两件事:① 调用ptrace函数;② 通过execv加载test程序;
这里的ptrace函数的作用是让GDB父进程可以读写子进程的指令空间,数据空间,堆栈和寄存器,操作系统向test进程发送的信号都会被GDB进程截获。
作者然后以断点调试为例说明调试过程:
上面说到,在执行gdb ./test之后,gdb就会fork出一个子进程,这个子进程首先调用ptrace然后执test程序,这样就准备好调试环境了。
我们把源码和汇编代码放在一起,方便理解:
在调试窗口输入设置断点指令“break 5”,此时gdb做2件事情:
- 对第5行源码所对应的第10行汇编代码存储到断点链表中。
- 在汇编代码的第10行,插入中断指令INT3,也就是说:汇编代码中的第10行被替换为INT3。
然后,在调试窗口继续输入执行指令“run”(一直执行,直到遇到断点就暂停),汇编代码中PC指针(一个内部指针,指向即将执行的那行代码)执行第10行时,发现是INT3指令,于是操作系统就发送一个SIGTRAP信号给test进程。
此刻,第10行汇编代码被执行过了,PC指针就指向第11行了。
上面已经说过,操作系统发给test的任何信号,都被gdb接管了,也就是说gdb会首先接收到这SIGTRAP个信号,gdb发现当前汇编代码执行的是第10行,于是到断点链表中查找,发现链表中存储了第10行的代码,说明第10行被设置了断点。于是gdb又做了2个操作:
- 把汇编代码中的第10行"INT3"替换为断点链表中原来的代码。
- 把 PC 指针回退一步,也即是设置为指向第10 行。
然后,gdb继续等待用户的调试指令。
此刻,就相当于下一条执行的指令是汇编代码中的第10行,也就是源码中的第5行。从我们调试者角度看,就是被调试程序在第5行断点处暂停了下来,此时我们可以继续输入其他调试指令来debug,比如:查看变量值、查看堆栈信息、修改局部变量的值等等。