I've prepared interface for hardware watchpoints: http://netbsd.org/~kamil/patch-00023-ptrace-watchpoints.txt For the purpose of this task I propose to call monitoring operations of data as "watchpoints" and monitoring of instruction's executaion as "breakpoints". However this interface is not limited to neither, as a port might expose any other type of events to be monitored (like branch instructions). Sometimes I'm referring to "hardware" watchpoints it means just any type of trap realized with hardware association without need for software trap. My goals of this project: - restrict code in the kernel-side to functional minimum, - restrict performance impact to minimum, - security - don't expose weaker points, - make common MI parts where applicable, - if something is doable with a userlevel debugger code, don't put extra functions to the kernel. Benefits of this project: - hardware breakpoints without violating mproctect restrictions, - make possible observability of data changes, - basic and common interface to set breakpoints within few lines of code [1]. [1] Software breakpoints are definitely more complex, as they overwrite target's .text section inserting there instructions to generate traps.. and then they need to move PC backwards and insert original instruction for target... The design is as follows: 1. Accessors through: - PT_WRITE_WATCHPOINT - write new watchpoint's state (set, unset, ...), - PT_READ_WATCHPOINT - read watchpoints's state, - PT_COUNT_WATCHPOINT - receive the number of available watchpoints. 2. Hardware watchpoint API is designed to be MI with MD specialization. MI parts: - ptrace(2) calls as mentioned in 1. - struct ptrace_watchpoint of the following shape: /* * Hardware Watchpoints * * MD code handles switch informing whether a particular watchpoint is enabled */ typedef struct ptrace_watchpoint { int pw_index; /* HW Watchpoint ID (count from 0) */ lwpid_t pw_lwpid; /* LWP described */ struct mdpw pw_md; /* MD fields */ } ptrace_watchpoint_t; - example specialization for amd64: /* * This MD structure translates into x86_hw_watchpoint * * pw_address - 0 represents disabled hardware watchpoint * * conditions: * 0b00 - execution * 0b01 - data write * 0b10 - io read/write (not implemented) * 0b11 - data read/write * * length: * 0b00 - 1 byte * 0b01 - 2 bytes * 0b10 - undefined (8 bytes in modern CPUs - not implemented) * 0b11 - 4 bytes * * Helper symbols for conditions and length are available in <x86/dbregs.h> * */ struct mdpw { void *md_address; int md_condition; int md_length; }; - I put md_address and others field to MD part as it's purely MD specific. I wanted to leave room for possible watchpoints of types without specified address. 3. Do not expose CPU Debug Registers to userland. I finally decided to restrict the underlying hardware implementation (based on CPU Debug Registers) to kernel only. In FreeBSD these registers are a part of machine context (mcontext), I think it's a misdesign as it should be limited to the tracer only and it has no use-case in the tracee. CPU Debug Registers can expose privileged gates and in theory can try to alter watchpoints set by a debugger on the fly.. AMD64 Debug Registers are rather part of the tracer context observing a tracee. 4. Do not set watchpoints globally per process, limit them to threads (LWP). In general kernel hardware watchpoints must be set for all CPUs and userland watchpoints must be limited to CPU running a thread. Adding process-wide management in the ptrace(2) interface calls adds extra complexity that should be pushed away to user-land code in debuggers. 5. Do not allow to mix PT_STEP and hardware watchpoint, in case of single-stepping the code, disable (it means: don't set) hardware watchpoints for threads. Some platforms might implement single-step with hardware watchpoints and managing both at the same time is generating extra pointless complexity. 6. I have no strong opinions on si_code, on amd64 we set TRAP_TRACE for hardware watchpoints. POSIX specifies two types: TRAP_BRKPT and TRAP_TRACE. I don't want to introduce new third type as: it might be unportable across ports, its usability is questionable (I would limit it myself to curiosity without significant real-life impact). 7. Linux has interfaces to allocate (reserve) watchpoints for in-kernel usage and user-land one... I think it's unnecessary complexity for little gain. In the case that someone would use in-kernel hardware watchpoints I just recommend to stop setting them for userland (it's doable with a single if() condition...). 8. The design for the amd64 port is as follows: - track watchpoints in a private LWP structure, within a table, - all threads before entering userland call userret() - at the end of this function check if a thread has active watchpoints and isn't going to single-step -- if so set hardware watchpoint - never reset hardware watchpoints after entering the kernel, traps must be specified for memory in the user-level range and mustn't trigger trap within kernel code 9. I was trying to leave room for in-kernel x86 watchpoints in future. Currently only the amd64 part is finished and tested. The i386 and XEN configurations aren't tested, while both might work out of the box. compat32 isn't fully implemented. Tomorrow, I plan to add dedicated ATF tests for this interface, currently a draft that I used is available at: http://netbsd.org/~kamil/t_ptrace_wait-hw-watchpoint-draft.c The 32-bit work is planned to be started once LLDB will be fully functional on amd64 first, currently it's out of the scope.
Attachment:
signature.asc
Description: OpenPGP digital signature