breakpoints_linux_amd64.go 2.2 KB
Newer Older
1 2 3 4
package proctl

/*
#include <stddef.h>
5
#include <sys/types.h>
6 7 8 9 10 11 12 13 14 15 16 17
#include <sys/user.h>
#include <sys/debugreg.h>

// Exposes C macro `offsetof` which is needed for getting
// the offset of the debug register we want, and passing
// that offset to PTRACE_POKE_USER.
int offset(int reg) {
	return offsetof(struct user, u_debugreg[reg]);
}
*/
import "C"

D
Derek Parker 已提交
18
import "fmt"
D
Derek Parker 已提交
19

20 21 22 23 24
// Sets a hardware breakpoint by setting the contents of the
// debug register `reg` with the address of the instruction
// 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 {
M
Michael Gehring 已提交
25
	if reg < 0 || reg > 3 {
26 27 28 29
		return fmt.Errorf("invalid register value")
	}

	var (
M
Michael Gehring 已提交
30 31 32 33 34
		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)
35 36
	)

M
Michael Gehring 已提交
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
	// 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<<uint(reg*C.DR_ENABLE_SIZE)) != 0 {
		return fmt.Errorf("dr%d already enabled", reg)
	}

54 55
	// Set the debug register `reg` with the address of the
	// instruction we want to trigger a debug exception.
M
Michael Gehring 已提交
56
	if err := PtracePokeUser(tid, drxoff, uintptr(addr)); err != nil {
57 58
		return err
	}
M
Michael Gehring 已提交
59 60 61 62

	// Clear dr`reg` flags
	dr7 &= ^drxmask
	// Enable dr`reg`
63
	dr7 |= (drxctl << C.DR_CONTROL_SHIFT) | drxenable
M
Michael Gehring 已提交
64

65 66 67 68
	// Set the debug control register. This
	// instructs the cpu to raise a debug
	// exception when hitting the address of
	// an instruction stored in dr0-dr3.
M
Michael Gehring 已提交
69
	return PtracePokeUser(tid, dr7off, dr7)
70
}
D
Derek Parker 已提交
71 72 73 74 75 76 77

// Clears a hardware breakpoint. Essentially sets
// the debug reg to 0 and clears the control register
// flags for that reg.
func clearHardwareBreakpoint(reg, tid int) error {
	return setHardwareBreakpoint(reg, tid, 0)
}