delve/pkg/proc/native/threads_linux_arm64.go
Alessandro Arzilli c412dcdc4f
*: run go1.19 'go fmt' on everything and fix problems (#3031)
Go 1.19 also formats doc comments according to the new godoc syntax.
Some of our comments, especially unexported symbols did not conform to
the godoc syntax and therefore are mangled by 'go fmt'.

This PR runs 'go fmt' from go1.19 on everything and manually fixes the
problems.

See also:
	https://github.com/golang/proposal/blob/master/design/51082-godocfmt.md
2022-06-17 10:08:11 -07:00

192 lines
6.0 KiB
Go

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)
}