github.com/undoio/delve@v1.9.0/pkg/proc/native/threads_linux_arm64.go (about)

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