一文看懂 | GDB底层实现原理
时间:2021-11-11 13:46:21
手机看文章
扫描二维码
随时随地手机看文章
[导读]在程序出现bug的时候,最好的解决办法就是通过 GDB 调试程序,然后找到程序出现问题的地方。比如程序出现 段错误(内存地址不合法)时,就可以通过 GDB 找到程序哪里访问了不合法的内存地址而导致的。本文不是介绍GDB的使用方式,而是大概介绍GDB的实现原理,当然GDB是一个庞大...
在程序出现bug的时候,最好的解决办法就是通过
GDB
调试程序,然后找到程序出现问题的地方。比如程序出现 段错误
(内存地址不合法)时,就可以通过 GDB
找到程序哪里访问了不合法的内存地址而导致的。本文不是介绍 GDB 的使用方式,而是大概介绍 GDB 的实现原理,当然 GDB 是一个庞大而复杂的项目,不可能只通过一篇文章就能解释清楚,所以本文主要是介绍 GDB 使用的核心的技术 - ptrace
。ptrace系统调用
ptrace()
系统调用是 Linux 提供的一个调试进程的工具,ptrace()
系统调用非常强大,它提供非常多的调试方式让我们去调试某一个进程,下面是 ptrace()
系统调用的定义:long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
下面解释一下 ptrace()
各个参数的作用:request
:指定调试的指令,指令的类型很多,如:PTRACE_TRACEME
、PTRACE_PEEKUSER
、PTRACE_CONT
、PTRACE_GETREGS
等等,下面会介绍不同指令的作用。pid
:进程的ID(这个不用解释了)。addr
:进程的某个地址空间,可以通过这个参数对进程的某个地址进行读或写操作。data
:根据不同的指令,有不同的用途,下面会介绍。
ptrace()
系统调用详细的介绍可以参考以下链接:https://man7.org/linux/man-pages/man2/ptrace.2.htmlptrace使用示例
下面通过一个简单例子来说明ptrace()
系统调用的使用,这个例子主要介绍怎么使用 ptrace()
系统调用获取当前被调试(追踪)进程的各个寄存器的值,代码如下(ptrace.c):#include
#include
#include
#include
#include
#include
int main()
{ pid_t child;
struct user_regs_struct regs;
child = fork(); // 创建一个子进程
if(child == 0) { // 子进程
ptrace(PTRACE_TRACEME, 0, NULL, NULL); // 表示当前进程进入被追踪状态
execl("/bin/ls", "ls", NULL); // 执行 `/bin/ls` 程序
}
else { // 父进程
wait(NULL); // 等待子进程发送一个 SIGCHLD 信号
ptrace(PTRACE_GETREGS, child, NULL,