1

我想知道是否可以在当前进程中添加观察点,以便在读取或写入内存时(取决于标志)将调用回调。

有相关的问题,但它们都是关于使用 gdb 或其他调试器进行调试的。这不是为了调试,也不是在跟踪另一个进程时。我希望进程本身在自己的地址空间中的内存位置设置观察点。通常对于这种事情,我会使用 ptrace,但据我从手册页了解(“ptrace() 系统调用提供了一种方法,一个进程(“跟踪器”)可以通过该方法观察和控制另一个进程(“tracee”),...” - 强调我的)它不能用于在当前进程中添加观察点。

有没有办法在不使用 ptrace 的情况下做到这一点?或者我可以在当前进程中使用 ptrace 来执行此操作吗?

4

1 回答 1

2

观察点通常通过配置嵌入在 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()可以排除使用作为以任何便携式方式设置硬件观察点的选项。

于 2020-10-12T17:20:58.630 回答