proc/gdbserial: Added support for darwin/arm64 using gdbserver (#2285)

* Added support for reading darwin/arm64 using gdbserver

* Trying to fix test failures

* Addressing review comments
This commit is contained in:
Christian Banse 2021-01-04 17:52:04 +01:00 committed by GitHub
parent 6328edaa1b
commit 57f033e4bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 478 additions and 186 deletions

@ -129,6 +129,7 @@ var (
supportedDarwinArch = map[macho.Cpu]bool{
macho.CpuAmd64: true,
macho.CpuArm64: true,
}
)
@ -1368,9 +1369,19 @@ func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
// loadBinaryInfoMacho specifically loads information from a Mach-O binary.
func loadBinaryInfoMacho(bi *BinaryInfo, image *Image, path string, entryPoint uint64, wg *sync.WaitGroup) error {
exe, err := macho.Open(path)
if err != nil {
return err
}
if entryPoint != 0 {
// This is a little bit hacky. We use the entryPoint variable, but it
// actually holds the address of the mach-o header. We can use this
// to calculate the offset to the non-aslr location of the mach-o header
// (which is 0x100000000)
image.StaticBase = entryPoint - 0x100000000
}
image.closer = exe
if !supportedDarwinArch[exe.Cpu] {
return &ErrUnsupportedArch{os: "darwin", cpuArch: exe.Cpu}

@ -77,6 +77,7 @@ import (
"sync"
"time"
"golang.org/x/arch/arm64/arm64asm"
"golang.org/x/arch/x86/x86asm"
"github.com/go-delve/delve/pkg/logflags"
@ -192,6 +193,7 @@ type gdbRegisters struct {
gaddr uint64
hasgaddr bool
buf []byte
arch *proc.Arch
}
type gdbRegister struct {
@ -292,6 +294,13 @@ func (p *gdbProcess) Connect(conn net.Conn, path string, pid int, debugInfoDirs
p.gcmdok = false
break
}
// Workaround for darwin arm64. Apple's debugserver seems to have a problem
// with not selecting the correct thread in the 'g' command and the returned
// registers are empty / an E74 error is thrown.
if v[len("version:"):] == "1200" && p.bi.Arch.Name == "arm64" {
p.gcmdok = false
}
}
}
}
@ -301,17 +310,19 @@ func (p *gdbProcess) Connect(conn net.Conn, path string, pid int, debugInfoDirs
return nil, err
}
// None of the stubs we support returns the value of fs_base or gs_base
// along with the registers, therefore we have to resort to executing a MOV
// instruction on the inferior to find out where the G struct of a given
// thread is located.
// Here we try to allocate some memory on the inferior which we will use to
// store the MOV instruction.
// If the stub doesn't support memory allocation reloadRegisters will
// overwrite some existing memory to store the MOV.
if addr, err := p.conn.allocMemory(256); err == nil {
if _, err := p.conn.writeMemory(addr, p.loadGInstr()); err == nil {
p.loadGInstrAddr = addr
if p.bi.Arch.Name != "arm64" {
// None of the stubs we support returns the value of fs_base or gs_base
// along with the registers, therefore we have to resort to executing a MOV
// instruction on the inferior to find out where the G struct of a given
// thread is located.
// Here we try to allocate some memory on the inferior which we will use to
// store the MOV instruction.
// If the stub doesn't support memory allocation reloadRegisters will
// overwrite some existing memory to store the MOV.
if addr, err := p.conn.allocMemory(256); err == nil {
if _, err := p.conn.writeMemory(addr, p.loadGInstr()); err == nil {
p.loadGInstrAddr = addr
}
}
}
@ -557,12 +568,27 @@ func LLDBAttach(pid int, path string, debugInfoDirs []string) (*proc.Target, err
// debugging PIEs.
func (p *gdbProcess) EntryPoint() (uint64, error) {
var entryPoint uint64
if auxv, err := p.conn.readAuxv(); err == nil {
if p.bi.GOOS == "darwin" && p.bi.Arch.Name == "arm64" {
// There is no auxv on darwin, however, we can get the location of the mach-o
// header from the debugserver by going through the loaded libraries, which includes
// the exe itself
images, _ := p.conn.getLoadedDynamicLibraries()
for _, image := range images {
if image.MachHeader.FileType == macho.TypeExec {
// This is a bit hacky. This is technically not the entrypoint,
// but rather we use the variable to points at the mach-o header,
// so we can get the offset in bininfo
entryPoint = image.LoadAddress
break
}
}
} else if auxv, err := p.conn.readAuxv(); err == nil {
// If we can't read the auxiliary vector it just means it's not supported
// by the OS or by the stub. If we are debugging a PIE and the entry point
// is needed proc.LoadBinaryInfo will complain about it.
entryPoint = linutil.EntryPointFromAuxv(auxv, p.BinInfo().Arch.PtrSize())
}
return entryPoint, nil
}
@ -1436,7 +1462,8 @@ func (p *gdbProcess) loadGInstr() []byte {
return buf.Bytes()
}
func (regs *gdbRegisters) init(regsInfo []gdbRegisterInfo) {
func (regs *gdbRegisters) init(regsInfo []gdbRegisterInfo, arch *proc.Arch) {
regs.arch = arch
regs.regs = make(map[string]gdbRegister)
regs.regsInfo = regsInfo
@ -1458,7 +1485,7 @@ func (regs *gdbRegisters) init(regsInfo []gdbRegisterInfo) {
// the stub can allocate memory, or reloadGAtPC, if the stub can't.
func (t *gdbThread) reloadRegisters() error {
if t.regs.regs == nil {
t.regs.init(t.p.conn.regsInfo)
t.regs.init(t.p.conn.regsInfo, t.p.bi.Arch)
}
if t.p.gcmdok {
@ -1488,10 +1515,21 @@ func (t *gdbThread) reloadRegisters() error {
}
}
if t.p.loadGInstrAddr > 0 {
return t.reloadGAlloc()
if t.p.bi.Arch.Name == "arm64" {
// no need to play around with the GInstr on ARM64 because
// the G addr is stored in a register
t.regs.gaddr = t.regs.byName("x28")
t.regs.hasgaddr = true
t.regs.tls = 0
} else {
if t.p.loadGInstrAddr > 0 {
return t.reloadGAlloc()
}
return t.reloadGAtPC()
}
return t.reloadGAtPC()
return nil
}
func (t *gdbThread) writeSomeRegisters(regNames ...string) error {
@ -1590,7 +1628,7 @@ func (t *gdbThread) reloadGAtPC() error {
err = t.p.conn.step(t.strID, nil, true)
if err != nil {
if err == threadBlockedError {
if err == errThreadBlocked {
t.regs.tls = 0
t.regs.gaddr = 0
t.regs.hasgaddr = true
@ -1643,7 +1681,7 @@ func (t *gdbThread) reloadGAlloc() error {
err = t.p.conn.step(t.strID, nil, true)
if err != nil {
if err == threadBlockedError {
if err == errThreadBlocked {
t.regs.tls = 0
t.regs.gaddr = 0
t.regs.hasgaddr = true
@ -1742,157 +1780,369 @@ func (regs *gdbRegisters) byName(name string) uint64 {
}
func (regs *gdbRegisters) Get(n int) (uint64, error) {
reg := x86asm.Reg(n)
const (
mask8 = 0x000f
mask16 = 0x00ff
mask32 = 0xffff
mask8 = 0xff
mask16 = 0xffff
mask32 = 0xffffffff
)
switch reg {
// 8-bit
case x86asm.AL:
return regs.byName("rax") & mask8, nil
case x86asm.CL:
return regs.byName("rcx") & mask8, nil
case x86asm.DL:
return regs.byName("rdx") & mask8, nil
case x86asm.BL:
return regs.byName("rbx") & mask8, nil
case x86asm.AH:
return (regs.byName("rax") >> 8) & mask8, nil
case x86asm.CH:
return (regs.byName("rcx") >> 8) & mask8, nil
case x86asm.DH:
return (regs.byName("rdx") >> 8) & mask8, nil
case x86asm.BH:
return (regs.byName("rbx") >> 8) & mask8, nil
case x86asm.SPB:
return regs.byName("rsp") & mask8, nil
case x86asm.BPB:
return regs.byName("rbp") & mask8, nil
case x86asm.SIB:
return regs.byName("rsi") & mask8, nil
case x86asm.DIB:
return regs.byName("rdi") & mask8, nil
case x86asm.R8B:
return regs.byName("r8") & mask8, nil
case x86asm.R9B:
return regs.byName("r9") & mask8, nil
case x86asm.R10B:
return regs.byName("r10") & mask8, nil
case x86asm.R11B:
return regs.byName("r11") & mask8, nil
case x86asm.R12B:
return regs.byName("r12") & mask8, nil
case x86asm.R13B:
return regs.byName("r13") & mask8, nil
case x86asm.R14B:
return regs.byName("r14") & mask8, nil
case x86asm.R15B:
return regs.byName("r15") & mask8, nil
if regs.arch.Name == "arm64" {
reg := arm64asm.Reg(n)
// 16-bit
case x86asm.AX:
return regs.byName("rax") & mask16, nil
case x86asm.CX:
return regs.byName("rcx") & mask16, nil
case x86asm.DX:
return regs.byName("rdx") & mask16, nil
case x86asm.BX:
return regs.byName("rbx") & mask16, nil
case x86asm.SP:
return regs.byName("rsp") & mask16, nil
case x86asm.BP:
return regs.byName("rbp") & mask16, nil
case x86asm.SI:
return regs.byName("rsi") & mask16, nil
case x86asm.DI:
return regs.byName("rdi") & mask16, nil
case x86asm.R8W:
return regs.byName("r8") & mask16, nil
case x86asm.R9W:
return regs.byName("r9") & mask16, nil
case x86asm.R10W:
return regs.byName("r10") & mask16, nil
case x86asm.R11W:
return regs.byName("r11") & mask16, nil
case x86asm.R12W:
return regs.byName("r12") & mask16, nil
case x86asm.R13W:
return regs.byName("r13") & mask16, nil
case x86asm.R14W:
return regs.byName("r14") & mask16, nil
case x86asm.R15W:
return regs.byName("r15") & mask16, nil
switch reg {
// 64-bit
case arm64asm.X0:
return regs.byName("x0"), nil
case arm64asm.X1:
return regs.byName("x1"), nil
case arm64asm.X2:
return regs.byName("x2"), nil
case arm64asm.X3:
return regs.byName("x3"), nil
case arm64asm.X4:
return regs.byName("x4"), nil
case arm64asm.X5:
return regs.byName("x5"), nil
case arm64asm.X6:
return regs.byName("x6"), nil
case arm64asm.X7:
return regs.byName("x7"), nil
case arm64asm.X8:
return regs.byName("x8"), nil
case arm64asm.X9:
return regs.byName("x9"), nil
case arm64asm.X10:
return regs.byName("x10"), nil
case arm64asm.X11:
return regs.byName("x11"), nil
case arm64asm.X12:
return regs.byName("x12"), nil
case arm64asm.X13:
return regs.byName("x13"), nil
case arm64asm.X14:
return regs.byName("x14"), nil
case arm64asm.X15:
return regs.byName("x15"), nil
case arm64asm.X16:
return regs.byName("x16"), nil
case arm64asm.X17:
return regs.byName("x17"), nil
case arm64asm.X18:
return regs.byName("x18"), nil
case arm64asm.X19:
return regs.byName("x19"), nil
case arm64asm.X20:
return regs.byName("x20"), nil
case arm64asm.X21:
return regs.byName("x21"), nil
case arm64asm.X22:
return regs.byName("x22"), nil
case arm64asm.X23:
return regs.byName("x23"), nil
case arm64asm.X24:
return regs.byName("x24"), nil
case arm64asm.X25:
return regs.byName("x25"), nil
case arm64asm.X26:
return regs.byName("x26"), nil
case arm64asm.X27:
return regs.byName("x27"), nil
case arm64asm.X28:
return regs.byName("x28"), nil
case arm64asm.X29:
return regs.byName("fp"), nil
case arm64asm.X30:
return regs.byName("lr"), nil
case arm64asm.SP:
return regs.byName("sp"), nil
}
// 32-bit
case x86asm.EAX:
return regs.byName("rax") & mask32, nil
case x86asm.ECX:
return regs.byName("rcx") & mask32, nil
case x86asm.EDX:
return regs.byName("rdx") & mask32, nil
case x86asm.EBX:
return regs.byName("rbx") & mask32, nil
case x86asm.ESP:
return regs.byName("rsp") & mask32, nil
case x86asm.EBP:
return regs.byName("rbp") & mask32, nil
case x86asm.ESI:
return regs.byName("rsi") & mask32, nil
case x86asm.EDI:
return regs.byName("rdi") & mask32, nil
case x86asm.R8L:
return regs.byName("r8") & mask32, nil
case x86asm.R9L:
return regs.byName("r9") & mask32, nil
case x86asm.R10L:
return regs.byName("r10") & mask32, nil
case x86asm.R11L:
return regs.byName("r11") & mask32, nil
case x86asm.R12L:
return regs.byName("r12") & mask32, nil
case x86asm.R13L:
return regs.byName("r13") & mask32, nil
case x86asm.R14L:
return regs.byName("r14") & mask32, nil
case x86asm.R15L:
return regs.byName("r15") & mask32, nil
// 64-bit
switch reg {
case arm64asm.W0:
return regs.byName("w0"), nil
case arm64asm.W1:
return regs.byName("w1"), nil
case arm64asm.W2:
return regs.byName("w2"), nil
case arm64asm.W3:
return regs.byName("w3"), nil
case arm64asm.W4:
return regs.byName("w4"), nil
case arm64asm.W5:
return regs.byName("w5"), nil
case arm64asm.W6:
return regs.byName("w6"), nil
case arm64asm.W7:
return regs.byName("w7"), nil
case arm64asm.W8:
return regs.byName("w8"), nil
case arm64asm.W9:
return regs.byName("w9"), nil
case arm64asm.W10:
return regs.byName("w10"), nil
case arm64asm.W11:
return regs.byName("w11"), nil
case arm64asm.W12:
return regs.byName("w12"), nil
case arm64asm.W13:
return regs.byName("w13"), nil
case arm64asm.W14:
return regs.byName("w14"), nil
case arm64asm.W15:
return regs.byName("w15"), nil
case arm64asm.W16:
return regs.byName("w16"), nil
case arm64asm.W17:
return regs.byName("w17"), nil
case arm64asm.W18:
return regs.byName("w18"), nil
case arm64asm.W19:
return regs.byName("w19"), nil
case arm64asm.W20:
return regs.byName("w20"), nil
case arm64asm.W21:
return regs.byName("w21"), nil
case arm64asm.W22:
return regs.byName("w22"), nil
case arm64asm.W23:
return regs.byName("w23"), nil
case arm64asm.W24:
return regs.byName("w24"), nil
case arm64asm.W25:
return regs.byName("w25"), nil
case arm64asm.W26:
return regs.byName("w26"), nil
case arm64asm.W27:
return regs.byName("w27"), nil
case arm64asm.W28:
return regs.byName("w28"), nil
// TODO: not sure about these ones, they are not returned by debugserver
// probably need to take the x-register and bitmask them
/*case arm64asm.W29:
return regs.byName("w29"), nil
case arm64asm.W30:
return regs.byName("w30"), nil
/*case arm64asm.WSP:
return regs.byName("wsp"), nil*/
}
// 64-bit
case x86asm.RAX:
return regs.byName("rax"), nil
case x86asm.RCX:
return regs.byName("rcx"), nil
case x86asm.RDX:
return regs.byName("rdx"), nil
case x86asm.RBX:
return regs.byName("rbx"), nil
case x86asm.RSP:
return regs.byName("rsp"), nil
case x86asm.RBP:
return regs.byName("rbp"), nil
case x86asm.RSI:
return regs.byName("rsi"), nil
case x86asm.RDI:
return regs.byName("rdi"), nil
case x86asm.R8:
return regs.byName("r8"), nil
case x86asm.R9:
return regs.byName("r9"), nil
case x86asm.R10:
return regs.byName("r10"), nil
case x86asm.R11:
return regs.byName("r11"), nil
case x86asm.R12:
return regs.byName("r12"), nil
case x86asm.R13:
return regs.byName("r13"), nil
case x86asm.R14:
return regs.byName("r14"), nil
case x86asm.R15:
return regs.byName("r15"), nil
// vector registers
// 64-bit
switch reg {
case arm64asm.V0:
return regs.byName("v0"), nil
case arm64asm.V1:
return regs.byName("v1"), nil
case arm64asm.V2:
return regs.byName("v2"), nil
case arm64asm.V3:
return regs.byName("v3"), nil
case arm64asm.V4:
return regs.byName("v4"), nil
case arm64asm.V5:
return regs.byName("v5"), nil
case arm64asm.V6:
return regs.byName("v6"), nil
case arm64asm.V7:
return regs.byName("v7"), nil
case arm64asm.V8:
return regs.byName("v8"), nil
case arm64asm.V9:
return regs.byName("v9"), nil
case arm64asm.V10:
return regs.byName("v10"), nil
case arm64asm.V11:
return regs.byName("v11"), nil
case arm64asm.V12:
return regs.byName("v12"), nil
case arm64asm.V13:
return regs.byName("v13"), nil
case arm64asm.V14:
return regs.byName("v14"), nil
case arm64asm.V15:
return regs.byName("v15"), nil
case arm64asm.V16:
return regs.byName("v16"), nil
case arm64asm.V17:
return regs.byName("v17"), nil
case arm64asm.V18:
return regs.byName("v18"), nil
case arm64asm.V19:
return regs.byName("v19"), nil
case arm64asm.V20:
return regs.byName("v20"), nil
case arm64asm.V21:
return regs.byName("v21"), nil
case arm64asm.V22:
return regs.byName("v22"), nil
case arm64asm.V23:
return regs.byName("v23"), nil
case arm64asm.V24:
return regs.byName("v24"), nil
case arm64asm.V25:
return regs.byName("v25"), nil
case arm64asm.V26:
return regs.byName("v26"), nil
case arm64asm.V27:
return regs.byName("v27"), nil
case arm64asm.V28:
return regs.byName("v28"), nil
case arm64asm.V29:
return regs.byName("v29"), nil
case arm64asm.V30:
return regs.byName("v30"), nil
case arm64asm.V31:
return regs.byName("v31"), nil
}
} else {
reg := x86asm.Reg(n)
switch reg {
// 8-bit
case x86asm.AL:
return regs.byName("rax") & mask8, nil
case x86asm.CL:
return regs.byName("rcx") & mask8, nil
case x86asm.DL:
return regs.byName("rdx") & mask8, nil
case x86asm.BL:
return regs.byName("rbx") & mask8, nil
case x86asm.AH:
return (regs.byName("rax") >> 8) & mask8, nil
case x86asm.CH:
return (regs.byName("rcx") >> 8) & mask8, nil
case x86asm.DH:
return (regs.byName("rdx") >> 8) & mask8, nil
case x86asm.BH:
return (regs.byName("rbx") >> 8) & mask8, nil
case x86asm.SPB:
return regs.byName("rsp") & mask8, nil
case x86asm.BPB:
return regs.byName("rbp") & mask8, nil
case x86asm.SIB:
return regs.byName("rsi") & mask8, nil
case x86asm.DIB:
return regs.byName("rdi") & mask8, nil
case x86asm.R8B:
return regs.byName("r8") & mask8, nil
case x86asm.R9B:
return regs.byName("r9") & mask8, nil
case x86asm.R10B:
return regs.byName("r10") & mask8, nil
case x86asm.R11B:
return regs.byName("r11") & mask8, nil
case x86asm.R12B:
return regs.byName("r12") & mask8, nil
case x86asm.R13B:
return regs.byName("r13") & mask8, nil
case x86asm.R14B:
return regs.byName("r14") & mask8, nil
case x86asm.R15B:
return regs.byName("r15") & mask8, nil
// 16-bit
case x86asm.AX:
return regs.byName("rax") & mask16, nil
case x86asm.CX:
return regs.byName("rcx") & mask16, nil
case x86asm.DX:
return regs.byName("rdx") & mask16, nil
case x86asm.BX:
return regs.byName("rbx") & mask16, nil
case x86asm.SP:
return regs.byName("rsp") & mask16, nil
case x86asm.BP:
return regs.byName("rbp") & mask16, nil
case x86asm.SI:
return regs.byName("rsi") & mask16, nil
case x86asm.DI:
return regs.byName("rdi") & mask16, nil
case x86asm.R8W:
return regs.byName("r8") & mask16, nil
case x86asm.R9W:
return regs.byName("r9") & mask16, nil
case x86asm.R10W:
return regs.byName("r10") & mask16, nil
case x86asm.R11W:
return regs.byName("r11") & mask16, nil
case x86asm.R12W:
return regs.byName("r12") & mask16, nil
case x86asm.R13W:
return regs.byName("r13") & mask16, nil
case x86asm.R14W:
return regs.byName("r14") & mask16, nil
case x86asm.R15W:
return regs.byName("r15") & mask16, nil
// 32-bit
case x86asm.EAX:
return regs.byName("rax") & mask32, nil
case x86asm.ECX:
return regs.byName("rcx") & mask32, nil
case x86asm.EDX:
return regs.byName("rdx") & mask32, nil
case x86asm.EBX:
return regs.byName("rbx") & mask32, nil
case x86asm.ESP:
return regs.byName("rsp") & mask32, nil
case x86asm.EBP:
return regs.byName("rbp") & mask32, nil
case x86asm.ESI:
return regs.byName("rsi") & mask32, nil
case x86asm.EDI:
return regs.byName("rdi") & mask32, nil
case x86asm.R8L:
return regs.byName("r8") & mask32, nil
case x86asm.R9L:
return regs.byName("r9") & mask32, nil
case x86asm.R10L:
return regs.byName("r10") & mask32, nil
case x86asm.R11L:
return regs.byName("r11") & mask32, nil
case x86asm.R12L:
return regs.byName("r12") & mask32, nil
case x86asm.R13L:
return regs.byName("r13") & mask32, nil
case x86asm.R14L:
return regs.byName("r14") & mask32, nil
case x86asm.R15L:
return regs.byName("r15") & mask32, nil
// 64-bit
case x86asm.RAX:
return regs.byName("rax"), nil
case x86asm.RCX:
return regs.byName("rcx"), nil
case x86asm.RDX:
return regs.byName("rdx"), nil
case x86asm.RBX:
return regs.byName("rbx"), nil
case x86asm.RSP:
return regs.byName("rsp"), nil
case x86asm.RBP:
return regs.byName("rbp"), nil
case x86asm.RSI:
return regs.byName("rsi"), nil
case x86asm.RDI:
return regs.byName("rdi"), nil
case x86asm.R8:
return regs.byName("r8"), nil
case x86asm.R9:
return regs.byName("r9"), nil
case x86asm.R10:
return regs.byName("r10"), nil
case x86asm.R11:
return regs.byName("r11"), nil
case x86asm.R12:
return regs.byName("r12"), nil
case x86asm.R13:
return regs.byName("r13"), nil
case x86asm.R14:
return regs.byName("r14"), nil
case x86asm.R15:
return regs.byName("r15"), nil
}
}
return 0, proc.ErrUnknownRegister
@ -1926,6 +2176,10 @@ func (t *gdbThread) SetSP(sp uint64) error {
// SetDX will set the value of the DX register to the given value.
func (t *gdbThread) SetDX(dx uint64) error {
if t.p.bi.Arch.Name == "arm64" {
return fmt.Errorf("not supported")
}
_, _ = t.Registers() // Registes must be loaded first
t.regs.setDX(dx)
if t.p.gcmdok {
@ -1990,7 +2244,7 @@ func (regs *gdbRegisters) Slice(floatingPoint bool) ([]proc.Register, error) {
func (regs *gdbRegisters) Copy() (proc.Registers, error) {
savedRegs := &gdbRegisters{}
savedRegs.init(regs.regsInfo)
savedRegs.init(regs.regsInfo, regs.arch)
copy(savedRegs.buf, regs.buf)
return savedRegs, nil
}

@ -48,16 +48,6 @@ type gdbConn struct {
log *logrus.Entry
}
const (
regnamePC = "rip"
regnameCX = "rcx"
regnameSP = "rsp"
regnameDX = "rdx"
regnameBP = "rbp"
regnameFsBase = "fs_base"
regnameGsBase = "gs_base"
)
var ErrTooManyAttempts = errors.New("too many transmit attempts")
// GdbProtocolError is an error response (Exx) of Gdb Remote Serial Protocol
@ -261,6 +251,7 @@ func (conn *gdbConn) readTargetXml() (err error) {
}
regnum++
}
if !pcFound {
return errors.New("could not find RIP register")
}
@ -270,6 +261,7 @@ func (conn *gdbConn) readTargetXml() (err error) {
if !cxFound {
return errors.New("could not find RCX register")
}
return nil
}
@ -412,18 +404,18 @@ func (conn *gdbConn) qXfer(kind, annex string, binary bool) ([]byte, error) {
return out, nil
}
// setBreakpoint executes a 'Z' (insert breakpoint) command of type '0' and kind '1'
// setBreakpoint executes a 'Z' (insert breakpoint) command of type '0' and kind '1' or '4'
func (conn *gdbConn) setBreakpoint(addr uint64) error {
conn.outbuf.Reset()
fmt.Fprintf(&conn.outbuf, "$Z0,%x,1", addr)
fmt.Fprintf(&conn.outbuf, "$Z0,%x,%d", addr, breakpointKind)
_, err := conn.exec(conn.outbuf.Bytes(), "set breakpoint")
return err
}
// clearBreakpoint executes a 'z' (remove breakpoint) command of type '0' and kind '1'
// clearBreakpoint executes a 'z' (remove breakpoint) command of type '0' and kind '1' or '4'
func (conn *gdbConn) clearBreakpoint(addr uint64) error {
conn.outbuf.Reset()
fmt.Fprintf(&conn.outbuf, "$z0,%x,1", addr)
fmt.Fprintf(&conn.outbuf, "$z0,%x,%d", addr, breakpointKind)
_, err := conn.exec(conn.outbuf.Bytes(), "clear breakpoint")
return err
}
@ -632,7 +624,7 @@ func (conn *gdbConn) step(threadID string, tu *threadUpdater, ignoreFaultSignal
}
}
var threadBlockedError = errors.New("thread blocked")
var errThreadBlocked = errors.New("thread blocked")
func (conn *gdbConn) waitForvContStop(context string, threadID string, tu *threadUpdater) (string, uint8, error) {
count := 0
@ -655,7 +647,7 @@ func (conn *gdbConn) waitForvContStop(context string, threadID string, tu *threa
}
count++
} else if failed {
return "", 0, threadBlockedError
return "", 0, errThreadBlocked
} else if err != nil {
return "", 0, err
} else {
@ -999,8 +991,9 @@ type imageList struct {
}
type imageDescription struct {
Pathname string `json:"pathname"`
MachHeader machHeader `json:"mach_header"`
LoadAddress uint64 `json:"load_address"`
Pathname string `json:"pathname"`
MachHeader machHeader `json:"mach_header"`
}
type machHeader struct {
@ -1267,7 +1260,10 @@ func checksumok(packet, checksumBuf []byte) bool {
if err != nil {
return false
}
return sum == uint8(tgt)
tgt8 := uint8(tgt)
return sum == tgt8
}
func checksum(packet []byte) (sum uint8) {

@ -0,0 +1,15 @@
//+build amd64
package gdbserial
const (
regnamePC = "rip"
regnameCX = "rcx"
regnameSP = "rsp"
regnameDX = "rdx"
regnameBP = "rbp"
regnameFsBase = "fs_base"
regnameGsBase = "gs_base"
breakpointKind = 1
)

@ -0,0 +1,16 @@
//+build arm64
package gdbserial
const (
regnamePC = "pc"
regnameCX = "x0"
regnameSP = "sp"
regnameBP = "fp"
// not needed but needs to be declared
regnameFsBase = ""
regnameDX = ""
breakpointKind = 4
)

@ -1,5 +1,5 @@
// This file is used to detect build on unsupported GOOS/GOARCH combinations.
//+build !linux,!darwin,!windows,!freebsd linux,!amd64,!arm64,!386 darwin,!amd64 windows,!amd64 freebsd,!amd64
//+build !linux,!darwin,!windows,!freebsd linux,!amd64,!arm64,!386 darwin,!amd64,!arm64 windows,!amd64 freebsd,!amd64
package your_operating_system_and_architecture_combination_is_not_supported_by_delve

@ -1467,7 +1467,7 @@ func TestClientServer_FpRegisters(t *testing.T) {
func TestClientServer_RestartBreakpointPosition(t *testing.T) {
protest.AllowRecording(t)
if buildMode == "pie" {
if buildMode == "pie" || (runtime.GOOS == "darwin" && runtime.GOARCH == "arm64") {
t.Skip("not meaningful in PIE mode")
}
withTestClient2("locationsprog2", t, func(c service.Client) {