观察点通常通过配置嵌入在 CPU 本身中的调试功能(即硬件观察点)来工作。本质上,您将要监视的地址范围加载到特殊寄存器中。
每个 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()
由进程应用于自身的系统调用以相当可移植的方式安装硬件观察点,但它不起作用 - 见下文。
您需要这个ptrace()
请求代码(通常的手册页上没有记录):
PTRACE_SETHBPREGS
经过进一步的实验:
- 进程
ptrace()
本身不能(权限被拒绝)
- 一个进程可以调用
clone()
,然后一个线程可以成功另一个ptrace(PTRACE_SEIZE)
线程
- 在这一点上,您也可以只
fork()
使用跟踪器exec()
gdb 进行观察
- x86 和 x86_64 似乎都没有实现 PTRACE_SETHBPREGS(从内核 4.15 开始)
- aarch64 可以,但到目前为止我只设法引发了一些总线错误
所以我猜想ptrace()
可以排除使用作为以任何便携式方式设置硬件观察点的选项。