1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
package native
import (
"debug/elf"
"errors"
"fmt"
"syscall"
"unsafe"
sys "golang.org/x/sys/unix"
"github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/linutil"
)
func (thread *nativeThread) fpRegisters() ([]proc.Register, []byte, error) {
var err error
var arm_fpregs linutil.ARM64PtraceFpRegs
thread.dbp.execPtraceFunc(func() { arm_fpregs.Vregs, err = ptraceGetFpRegset(thread.ID) })
fpregs := arm_fpregs.Decode()
if err != nil {
err = fmt.Errorf("could not get floating point registers: %v", err.Error())
}
return fpregs, arm_fpregs.Vregs, err
}
func (t *nativeThread) restoreRegisters(savedRegs proc.Registers) error {
sr := savedRegs.(*linutil.ARM64Registers)
var restoreRegistersErr error
t.dbp.execPtraceFunc(func() {
restoreRegistersErr = ptraceSetGRegs(t.ID, sr.Regs)
if restoreRegistersErr != syscall.Errno(0) && restoreRegistersErr != nil {
return
}
if sr.Fpregset != nil {
iov := sys.Iovec{Base: &sr.Fpregset[0], Len: uint64(len(sr.Fpregset))}
_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(t.ID), uintptr(elf.NT_FPREGSET), uintptr(unsafe.Pointer(&iov)), 0, 0)
}
})
if restoreRegistersErr == syscall.Errno(0) {
restoreRegistersErr = nil
}
return restoreRegistersErr
}
const (
_MAX_ARM64_WATCH = 16
_NT_ARM_HW_WATCH = 0x403
_TRAP_HWBKPT = 0x4
)
type watchpointState struct {
num uint8
debugVer uint8
words []uint64
}
func (wpstate *watchpointState) set(idx uint8, addr, ctrl uint64) {
wpstate.words[1+idx*2] = addr
wpstate.words[1+idx*2+1] = ctrl
}
// getWatchpoints reads the NT_ARM_HW_WATCH ptrace register set.
// The format of this register set is described by user_hwdebug_state in
// arch/arm64/include/uapi/asm/ptrace.h.
// It consists of one 64bit word containing:
// - 1byte number of watchpoints
// - 1byte debug architecture version (the 4 least significant bits of ID_AA64DFR0_EL1)
// - 6bytes padding
//
// Followed by 2 64bit words for each watchpoint, up to a maximum of 16
// watchpoints. The first word contains the address at which the watchpoint
// is set (DBGWVRn_EL1), the second word is the control register for the
// watchpoint (DBGWCRn_EL1), as described by
// ARM - Architecture Reference Manual Armv8, for A-profile architectures
// section D13.3.11
// where only the BAS, LSC, PAC and E fields are accessible.
func (t *nativeThread) getWatchpoints() (*watchpointState, error) {
words := make([]uint64, _MAX_ARM64_WATCH*2+1)
iov := sys.Iovec{Base: (*byte)(unsafe.Pointer(&words[0])), Len: uint64(len(words)) * uint64(unsafe.Sizeof(words[0]))}
var err error
t.dbp.execPtraceFunc(func() {
_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETREGSET, uintptr(t.ID), _NT_ARM_HW_WATCH, uintptr(unsafe.Pointer(&iov)), 0, 0)
})
if err != syscall.Errno(0) {
return nil, err
}
wpstate := &watchpointState{num: uint8(words[0] & 0xff), debugVer: uint8((words[0] >> 8) & 0xff), words: words}
if wpstate.num > _MAX_ARM64_WATCH {
// According to the specification this should never be more than 16 but
// the code here will not work if this limit ever gets relaxed.
wpstate.num = _MAX_ARM64_WATCH
}
// remove the watchpoint registers that don't exist from the words slice so
// that we won't try later to write them (which would cause an ENOSPC
// error).
wpstate.words = wpstate.words[:wpstate.num*2+1]
return wpstate, nil
}
// setWatchpoints saves the watchpoint state of the given thread.
// See (*nativeThread).getWatchpoints for a description of how this works.
func (t *nativeThread) setWatchpoints(wpstate *watchpointState) error {
iov := sys.Iovec{Base: (*byte)(unsafe.Pointer(&(wpstate.words[0]))), Len: uint64(len(wpstate.words)) * uint64(unsafe.Sizeof(wpstate.words[0]))}
var err error
t.dbp.execPtraceFunc(func() {
_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(t.ID), _NT_ARM_HW_WATCH, uintptr(unsafe.Pointer(&iov)), 0, 0)
})
if err != syscall.Errno(0) {
return err
}
return nil
}
type ptraceSiginfoArm64 struct {
signo uint32
errno uint32
code uint32
addr uint64 // only valid if Signo is SIGTRAP, SIGFPE, SIGILL, SIGBUS or SIGEMT
pad [128]byte // the total size of siginfo_t on ARM64 is 128 bytes so this is more than enough padding for all the fields we don't care about
}
func (t *nativeThread) findHardwareBreakpoint() (*proc.Breakpoint, error) {
var siginfo ptraceSiginfoArm64
var err error
t.dbp.execPtraceFunc(func() {
_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETSIGINFO, uintptr(t.ID), 0, uintptr(unsafe.Pointer(&siginfo)), 0, 0)
})
if err != syscall.Errno(0) {
return nil, err
}
if siginfo.signo != uint32(sys.SIGTRAP) || (siginfo.code&0xffff) != _TRAP_HWBKPT {
return nil, nil
}
for _, bp := range t.dbp.Breakpoints().M {
if bp.WatchType != 0 && siginfo.addr >= bp.Addr && siginfo.addr < bp.Addr+uint64(bp.WatchType.Size()) {
return bp, nil
}
}
return nil, fmt.Errorf("could not find hardware breakpoint for address %#x", siginfo.addr)
}
func (t *nativeThread) writeHardwareBreakpoint(addr uint64, wtype proc.WatchType, idx uint8) error {
wpstate, err := t.getWatchpoints()
if err != nil {
return err
}
if idx >= wpstate.num {
return errors.New("hardware breakpoints exhausted")
}
const (
readBreakpoint = 0x1
writeBreakpoint = 0x2
lenBitOffset = 5
typeBitOffset = 3
privBitOffset = 1
)
var typ uint64
if wtype.Read() {
typ |= readBreakpoint
}
if wtype.Write() {
typ |= writeBreakpoint
}
len := uint64((1 << wtype.Size()) - 1) // arm wants the length expressed as address bitmask
priv := uint64(3)
ctrl := (len << lenBitOffset) | (typ << typeBitOffset) | (priv << privBitOffset) | 1
wpstate.set(idx, addr, ctrl)
return t.setWatchpoints(wpstate)
}
func (t *nativeThread) clearHardwareBreakpoint(addr uint64, wtype proc.WatchType, idx uint8) error {
wpstate, err := t.getWatchpoints()
if err != nil {
return err
}
if idx >= wpstate.num {
return errors.New("hardware breakpoints exhausted")
}
wpstate.set(idx, 0, 0)
return t.setWatchpoints(wpstate)
}
|