断点
在这篇文章中,我们要在已有的基础上为程序增加设置断点的功能。
中断是怎么形成的
我们知道,有两种生成中断的方法:硬件中断和软件中断。
硬件中断顾名思义是指通过硬件的输入(如键盘、鼠标)使得相应的寄存器值发生变化,从而中断当前的程序;软件中断则涉及到了修改正在执行的代码。软件中断也称为陷阱
在下面我们会专注于软件中断,也就是生成断点。
在介绍代码之前,请先自己思考如下三个问题
1、如何修改正在执行的代码?
2、断点的效果应该是什么?
3、如何修改上篇文章中的debugger类?
第一个问题的答案很简单,使用ptrace()函数。我们前面用ptrace()函数设置了tracee的启动和continue功能,它同样还可以用来读取、修改内存。
第二个问题,在设置断点后,处理器应进入停机(halt)状态,同时会向程序发送信号。在x86架构中,将int 3
指令覆盖到当前指令的地址即可实现中断。x86有一张中断向量表(interrupt vector table),根据不同的值,操作系统可以使用不同的寄存器handler进行不同的处理,例如页错误,保护错误等等。当值为int3
时,控制权交给了断点handler,在Linux中则是使用SIGTRAP
去通知进程。
关于最后一个问题,之前我们使用过waitpid()来监听信号,在这里也可以这样做:我们在设置断点后,tracer调用waitpid()直到收到SIGTRAP
。接下来就可以和用户交互。
断点类
我们使用一个断点类来代表断点
class breakpoint {
public:
breakpoint(pid_t pid, std::intptr_t addr)
: m_pid{
pid}, m_addr{
addr