我,子牙老师,一个手写过操作系统、编程语言、Java虚拟机、docker、Ubuntu系统,玩透Windows内核、Linux内核的…硬核男人
前几天写了两篇调试器相关的文章《从零手写gdb调试器》《调试器是如何让代码断下来的》,在多个学员群里引发了大家的讨论。没想到大家对调试器这么有热情!今天再来把大家讨论比较多的话题写成文章分享:gdb调试器的底层实现原理
gdb的底层实现,一半是基于函数ptrace实现的,一半是调试器本身的业务实现。比如下断点,是基于ptrace函数实现的,但是支持多断点、条件断点,是调试器自身业务实现。这句话如果你不太理解,看上面列出的两篇文章打个底
搞明白gdb的底层实现,其实就是搞明白ptrace函数的底层实现,更准确的说,是ptrace在Linux内核中干了些什么

通过看前面的文章,大家应该知道,ptrace具体要做什么,是靠第一个参数request控制的

本篇文章就分析前五个request吧,其他的,后面再写文章分享。关注公众号【硬核子牙】,看Linux内核、调试器、编程语言、docker等底层硬核文章
以下,enjoy
变身可调试进程
Linux平台的任何调试器,如果是基于ptrace实现,一定是fork+exec+waitpid结构。看代码

通过fork创建子进程,子进程调用ptrace,request=PTRACE_TRACEME,变成可调试进程,就是让Linux内核允许调试器调试自己。这里的调试器就是父进程
我们要研究的第一个点就是:ptrace进入内核后做了什么让进程变成了可调试进程呢?父进程作为调试进程,与child被调试进程是如何建立联系的呢?
来,看ptrace进入内核后对应的系统调用sys_ptrace代码

核心逻辑在ptrace_traceme中,看代码

529行,给调试进程的task_struct.ptrace赋值PT_PTRACED
530行,调用ptrace_link。这个函数干了什么呢?看代码

75行,将child(调试进程)的task_struct.ptrace_entry与parent(调试进程)的task_struct.ptraced关联。
这里注意一下,调试进程与被调试进程是一对多的关系,即一个调试器可以调试多个进程,但是一个进程只能被一个调试器调试
76行,将child(调试进程)的task_struct.parent赋值为new_parent,即调试器进程
BTW,进程结构体task_struct中有两个parent,它们之间的区别如图

77行,配置调试证书,用于后面调试器操作的权限验证,比如是否有权限读写内存、读写寄存器等
来张总结图吧

大师,你悟了吗?
下断点
下断点应该是咱们使用调试器用的最多的功能吧,来看看gdb是如何实现的。看简版代码

看懂这里的代码需要一点基础,来补下。这里下的是软件断点,硬件断点是直接写DR寄存器。对软件断点与硬件断点不了的,看文章《调试器是如何让代码停下来的》
软件断点的本质是在对应的内存位置写入0xCC,翻译成汇编就是int3,对应的是3号软中断
比如想在main函数上下断点,就是把main函数开头位置的机器码0x55改成0xCC

如果直接改行不行呢?可以!但是会有问题!怎么理解呢?0x55对应的汇编指令是push rbp,就是把rbp寄存器的值存入栈顶,与后面的pop rbp是对称的。如果直接改,就不会做push rbp这件事,但是后面依然会执行pop rbp,就会导致程序执行出错
所以应该怎么做呢?先把0x55读出来,然后保存起来,当执行到断点的时候,写回去。24行代码,就是读机器码,默认是读8字节,执行完后data=0x400609bfe5894855。30行代码,就是把0x55取出来,保存起来。31行代码,就是把0x55改成0xCC,33行代码,将改成0xCC的data写入,完成下断点
那request=PTRACE_PEEKTEXT或request=PTRACE_POKETEXT,Linux内核层是怎么做的呢?
读写代码区跟读写数据区,走的函数是一样的,我就放在一起讲了
读写内存
直接看Linux内核中的代码

其实本质都是读写内存,只是传的参数不一样


核心函数是__access_remote_vm。这个函数是干嘛的呢?通过GUP机制跨进程读写内存!你要读懂这个函数的逻辑你就要了解4-level paging机制及Linux内核内存布局、伙伴系统。
我直接说答案吧,这个函数的底层,就是通过调试进程的vma找到addr对应的page对象,进而找到物理页,然后调用kmap将调试进程的物理页映射到调试器进程的虚拟内存中,进而完成读写
至此,调试器下断点的秘密,调试器读写内存的秘密,你已全部知晓!
其他的request的底层实现原理,我会陆续出文章。对调试器底层实现感兴趣的小伙伴,可以关注公众号【硬核子牙】看我写的硬核文章
最后再打个小广告,如果你想学习手写操作系统及实战Linux内核,如果你对调试器底层实现感兴趣,同时想写一个自己可以百分百控制的调试器,调试你自己写的操作系统,欢迎看我【个人简介】详细了解我的课程
1005

被折叠的 条评论
为什么被折叠?



