简体   繁体   中英

Adding a watchpoint in the current process (not in gdb, not for debugging)

I'm wondering if it's possible to add watchpoints in the current process so that when the memory is read or written (depending on the flags) a callback will be called.

There are related questions but they're all about debugging with gdb or another debugger. This is not for debugging, and not while tracing another process. I want the process itself to set a watchpoint in a memory location in its own address space. Normally for this kind of thing I'd use ptrace, but as far as I understand from the man page ("The ptrace() system call provides a means by which one process (the "tracer") may observe and control the execution of another process (the "tracee"), ..." -- emphasis mine) it can't be used to add a watchpoint in the current process.

Is there a way to do this without using ptrace? Or can I use ptrace to do this in the current process?

Watchpoints normally work by configuring debugging functionality embedded in the CPU itself ( hardware watchpoints, that is). Essentially you load the address range to watch into special registers.

You need specialised code for each CPU architecture. Here's what gdbserver does to set watchpoints on ARM CPUs: https://github.com/facebookarchive/binutils/blob/a535268b59862077d95f34f1572ac0bce0b428c7/gdb/gdbserver/linux-arm-low.c#L552

Note the call to update_registers_callback() - gdbserver has to use ptrace functionality to update the registers in the context of the tracee. If you want the process to watch itself, it can access those registers directly.

Here's how your process would notice its own watchpoint being hit: it receives a SIGTRAP signal, with detail info indicating the watchpoint. https://github.com/facebookarchive/binutils/blob/a535268b59862077d95f34f1572ac0bce0b428c7/gdb/gdbserver/linux-arm-low.c#L632 In gdbserver context, the notification again has to be passed over ptrace . In your own process, you could examine the signal info directly in a handler for SIGTRAP. Code excerpt (for 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;

On further reading it turns out that the hardware breakpoint "registers" accessed by the tracer by means of ptrace are not directly the real thing, but actually an abstraction implemented in the Linux kernel.

There's an "hw-breakpoint" framework involved, which abstracts from the CPU specifics. Found this overview: https://www.kernel.org/doc/ols/2009/ols2009-pages-149-158.pdf

While this gives hope that it could in fact be possible to use the ptrace() syscall, applied by a process to itself, to install hardware watchpoints in a fairly portable manner, it doesn't work out - see below.

You'd need this ptrace() request code (which is not documented on the usual man page):

PTRACE_SETHBPREGS

Upon further experimentation:

  • a process can't ptrace() itself (permission denied)
  • a process can call clone() , and then one thread can successfully ptrace(PTRACE_SEIZE) the other
  • at this point, you could as well just fork() , and have the tracer exec() gdb to do the watching
  • neither x86 nor x86_64 seem to implement PTRACE_SETHBPREGS (as of kernel 4.15)
  • aarch64 does, but I only managed to raise some bus errors so far

So I guess using ptrace() can be ruled out as an option to set hardware watchpoints in any portable way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM