问题描述
我想知道是否有可能在当前进程中添加观察点,以便在读取或写入内存(取决于标志)时将调用回调。
有一些相关的问题,但都与使用gdb或其他调试器进行调试有关。这不是为了调试,也不是在跟踪另一个进程时。我希望进程本身在其自己的地址空间的内存位置中设置监视点。通常,对于这种事情,我会使用ptrace,但是据我从手册页了解到的那样(“ ptrace()系统调用提供了一种方法,通过该方法,一个进程(“ tracer”)可以观察并控制...的执行) 另一个过程 (“跟踪”),...”(重点是我的),它不能用于在当前过程中添加观察点。
有没有一种方法可以不使用ptrace?还是可以在当前进程中使用ptrace做到这一点?
解决方法
观察点通常通过配置嵌入在CPU本身中的调试功能(即 hardware 观察点)来工作。 本质上,您加载地址范围以监视特殊寄存器。
每种CPU架构都需要专门的代码。 以下是gdbserver在ARM CPU上设置观察点的操作: https://github.com/facebookarchive/binutils/blob/a535268b59862077d95f34f1572ac0bce0b428c7/gdb/gdbserver/linux-arm-low.c#L552
请注意对update_registers_callback()
的调用-gdbserver必须使用ptrace
功能来在跟踪的上下文中更新寄存器。如果您希望进程自己监视,则可以直接访问这些寄存器。
以下是您的流程如何注意到自己的观察点被击中的方法:
它接收到SIGTRAP信号,其中的详细信息指示观察点。
https://github.com/facebookarchive/binutils/blob/a535268b59862077d95f34f1572ac0bce0b428c7/gdb/gdbserver/linux-arm-low.c#L632
在gdbserver上下文中,该通知必须再次通过ptrace
传递。在您自己的过程中,您可以直接在SIGTRAP的处理程序中检查信号信息。代码摘录(适用于ARM):
/* This must be a hardware breakpoint. */
if (siginfo.si_signo != SIGTRAP
|| (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
return 0;
/* If we are in a positive slot then we're looking at a breakpoint and not
a watchpoint. */
if (siginfo.si_errno >= 0)
return 0;
/* Cache stopped data address for use by arm_stopped_data_address. */
lwp->arch_private->stopped_data_address
= (CORE_ADDR) (uintptr_t) siginfo.si_addr;
进一步阅读后发现,跟踪器通过ptrace
访问的硬件断点“寄存器”不是直接的真实内容,而是在Linux内核中实现的抽象。
涉及一个“硬件断点”框架,该框架从CPU细节中抽象出来。找到此概述: https://www.kernel.org/doc/ols/2009/ols2009-pages-149-158.pdf
尽管这使人们希望实际上可以使用由进程自身应用的ptrace()
syscall以相当可移植的方式安装硬件观察点,但无法实现-参见下文
您将需要以下ptrace()
请求代码(通常的手册页中没有记录):
PTRACE_SETHBPREGS
进一步的实验:
- 一个进程本身不能
ptrace()
(权限被拒绝) - 一个进程可以调用
clone()
,然后一个线程可以成功ptrace(PTRACE_SEIZE)
另一个线程 - 在这一点上,您还可以
fork()
,并使用跟踪器exec()
gdb进行监视 - x86和x86_64似乎都没有实现PTRACE_SETHBPREGS(从内核4.15开始)
- aarch64可以,但是到目前为止,我只设法引起了一些总线错误
因此,我认为可以排除使用ptrace()
作为以任何可移植方式设置硬件观察点的选项。