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 }