From 2a66ecb10bf3e400ec031b636c7e99be8ea28220 Mon Sep 17 00:00:00 2001 From: Michael Gehring Date: Sat, 24 Jan 2015 02:58:03 +0100 Subject: [PATCH] Some hw breakpoint fixes Enable usage of dr1-dr3. Clear control bits when a breakpoint is disabled. Use DR_LEN_1 instead of DR_LEN_8 so breakpoint work on unaligned adresses. Fixes #51. --- proctl/breakpoints_linux_amd64.go | 47 +++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/proctl/breakpoints_linux_amd64.go b/proctl/breakpoints_linux_amd64.go index 85ac45c1..44446b26 100644 --- a/proctl/breakpoints_linux_amd64.go +++ b/proctl/breakpoints_linux_amd64.go @@ -17,6 +17,7 @@ import "C" import ( "fmt" "syscall" + "unsafe" ) // Represents a single breakpoint. Stores information on the break @@ -50,6 +51,15 @@ func PtracePokeUser(tid int, off, addr uintptr) error { return nil } +func PtracePeekUser(tid int, off uintptr) (uintptr, error) { + var x uintptr // XXX: this should not be necessary + ret, _, err := syscall.Syscall6(syscall.SYS_PTRACE, syscall.PTRACE_PEEKUSR, uintptr(tid), uintptr(off), uintptr(unsafe.Pointer(&x)), 0, 0) + if err != syscall.Errno(0) { + return 0, err + } + return ret, nil +} + func (dbp *DebuggedProcess) BreakpointExists(addr uint64) bool { for _, bp := range dbp.HWBreakPoints { if bp != nil && bp.Addr == addr { @@ -133,24 +143,49 @@ func (dbp *DebuggedProcess) newBreakpoint(fn, f string, l int, addr uint64, data // that we want to break at. There are only 4 debug registers // DR0-DR3. Debug register 7 is the control register. func setHardwareBreakpoint(reg, tid int, addr uint64) error { - if reg < 0 || reg > 7 { + if reg < 0 || reg > 3 { return fmt.Errorf("invalid register value") } var ( - off = uintptr(C.offset(C.int(reg))) - dr7 = uintptr(0x1 | C.DR_RW_EXECUTE | C.DR_LEN_8) - dr7addr = uintptr(C.offset(C.DR_CONTROL)) + dr7off = uintptr(C.offset(C.DR_CONTROL)) + drxoff = uintptr(C.offset(C.int(reg))) + drxmask = uintptr((((1 << C.DR_CONTROL_SIZE) - 1) << uintptr(reg*C.DR_CONTROL_SIZE)) | (((1 << C.DR_ENABLE_SIZE) - 1) << uintptr(reg*C.DR_ENABLE_SIZE))) + drxenable = uintptr(0x1) << uintptr(reg*C.DR_ENABLE_SIZE) + drxctl = uintptr(C.DR_RW_EXECUTE|C.DR_LEN_1) << uintptr(reg*C.DR_CONTROL_SIZE) ) + // Get current state + dr7, err := PtracePeekUser(tid, dr7off) + if err != nil { + return err + } + + // If addr == 0 we are expected to disable the breakpoint + if addr == 0 { + dr7 &= ^drxmask + return PtracePokeUser(tid, dr7off, dr7) + } + + // Error out if dr`reg` is already used + if dr7&(0x3<