proc/*: implement proc.(*compositeMemory).WriteMemory (#2271)

Delve represents registerized variables (fully or partially) using
compositeMemory, implementing proc.(*compositeMemory).WriteMemory is
necessary to make SetVariable and function calls work when Go will
switch to using the register calling convention in 1.17.

This commit also makes some refactoring by moving the code that
converts between register numbers and register names out of pkg/proc
into a different package.
This commit is contained in:
Alessandro Arzilli 2021-03-04 19:28:28 +01:00 committed by GitHub
parent 3088b8b579
commit 6a70d531bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 910 additions and 431 deletions

@ -1,20 +1,21 @@
Tests skipped by each supported backend: Tests skipped by each supported backend:
* 386 skipped = 2.7% (4/147) * 386 skipped = 2.7% (4/148)
* 1 broken * 1 broken
* 3 broken - cgo stacktraces * 3 broken - cgo stacktraces
* arm64 skipped = 2.7% (4/147) * arm64 skipped = 2.7% (4/148)
* 2 broken * 2 broken
* 1 broken - cgo stacktraces * 1 broken - cgo stacktraces
* 1 broken - global variable symbolication * 1 broken - global variable symbolication
* darwin/lldb skipped = 0.68% (1/147) * darwin/lldb skipped = 0.68% (1/148)
* 1 upstream issue * 1 upstream issue
* freebsd skipped = 7.5% (11/147) * freebsd skipped = 8.1% (12/148)
* 11 broken * 11 broken
* linux/386/pie skipped = 0.68% (1/147) * 1 not implemented
* linux/386/pie skipped = 0.68% (1/148)
* 1 broken * 1 broken
* pie skipped = 0.68% (1/147) * pie skipped = 0.68% (1/148)
* 1 upstream issue - https://github.com/golang/go/issues/29322 * 1 upstream issue - https://github.com/golang/go/issues/29322
* windows skipped = 1.4% (2/147) * windows skipped = 1.4% (2/148)
* 1 broken * 1 broken
* 1 upstream issue * 1 upstream issue

@ -14,11 +14,12 @@ type DwarfRegisters struct {
ObjBase int64 ObjBase int64
regs []*DwarfRegister regs []*DwarfRegister
ByteOrder binary.ByteOrder ByteOrder binary.ByteOrder
PCRegNum uint64 PCRegNum uint64
SPRegNum uint64 SPRegNum uint64
BPRegNum uint64 BPRegNum uint64
LRRegNum uint64 LRRegNum uint64
ChangeFunc RegisterChangeFunc
FloatLoadError error // error produced when loading floating point registers FloatLoadError error // error produced when loading floating point registers
loadMoreCallback func() loadMoreCallback func()
@ -29,6 +30,8 @@ type DwarfRegister struct {
Bytes []byte Bytes []byte
} }
type RegisterChangeFunc func(regNum uint64, reg *DwarfRegister) error
// NewDwarfRegisters returns a new DwarfRegisters object. // NewDwarfRegisters returns a new DwarfRegisters object.
func NewDwarfRegisters(staticBase uint64, regs []*DwarfRegister, byteOrder binary.ByteOrder, pcRegNum, spRegNum, bpRegNum, lrRegNum uint64) *DwarfRegisters { func NewDwarfRegisters(staticBase uint64, regs []*DwarfRegister, byteOrder binary.ByteOrder, pcRegNum, spRegNum, bpRegNum, lrRegNum uint64) *DwarfRegisters {
return &DwarfRegisters{ return &DwarfRegisters{
@ -152,3 +155,12 @@ func DwarfRegisterFromBytes(bytes []byte) *DwarfRegister {
} }
return &DwarfRegister{Uint64Val: v, Bytes: bytes} return &DwarfRegister{Uint64Val: v, Bytes: bytes}
} }
// FillBytes fills the Bytes slice of reg using Uint64Val.
func (reg *DwarfRegister) FillBytes() {
if reg.Bytes != nil {
return
}
reg.Bytes = make([]byte, 8)
binary.LittleEndian.PutUint64(reg.Bytes, reg.Uint64Val)
}

136
pkg/dwarf/regnum/amd64.go Normal file

@ -0,0 +1,136 @@
package regnum
import (
"fmt"
"strings"
)
// The mapping between hardware registers and DWARF registers is specified
// in the System V ABI AMD64 Architecture Processor Supplement page 57,
// figure 3.36
// https://www.uclibc.org/docs/psABI-x86_64.pdf
const (
AMD64_Rax = 0
AMD64_Rdx = 1
AMD64_Rcx = 2
AMD64_Rbx = 3
AMD64_Rsi = 4
AMD64_Rdi = 5
AMD64_Rbp = 6
AMD64_Rsp = 7
AMD64_R8 = 8
AMD64_R9 = 9
AMD64_R10 = 10
AMD64_R11 = 11
AMD64_R12 = 12
AMD64_R13 = 13
AMD64_R14 = 14
AMD64_R15 = 15
AMD64_Rip = 16
AMD64_XMM0 = 17 // XMM1 through XMM15 follow
AMD64_ST0 = 33 // ST(1) through ST(7) follow
AMD64_Rflags = 49
AMD64_Es = 50
AMD64_Cs = 51
AMD64_Ss = 52
AMD64_Ds = 53
AMD64_Fs = 54
AMD64_Gs = 55
AMD64_Fs_base = 58
AMD64_Gs_base = 59
AMD64_MXCSR = 64
AMD64_CW = 65
AMD64_SW = 66
)
var amd64DwarfToName = map[uint64]string{
AMD64_Rax: "Rax",
AMD64_Rdx: "Rdx",
AMD64_Rcx: "Rcx",
AMD64_Rbx: "Rbx",
AMD64_Rsi: "Rsi",
AMD64_Rdi: "Rdi",
AMD64_Rbp: "Rbp",
AMD64_Rsp: "Rsp",
AMD64_R8: "R8",
AMD64_R9: "R9",
AMD64_R10: "R10",
AMD64_R11: "R11",
AMD64_R12: "R12",
AMD64_R13: "R13",
AMD64_R14: "R14",
AMD64_R15: "R15",
AMD64_Rip: "Rip",
AMD64_XMM0: "XMM0",
AMD64_XMM0 + 1: "XMM1",
AMD64_XMM0 + 2: "XMM2",
AMD64_XMM0 + 3: "XMM3",
AMD64_XMM0 + 4: "XMM4",
AMD64_XMM0 + 5: "XMM5",
AMD64_XMM0 + 6: "XMM6",
AMD64_XMM0 + 7: "XMM7",
AMD64_XMM0 + 8: "XMM8",
AMD64_XMM0 + 9: "XMM9",
AMD64_XMM0 + 10: "XMM10",
AMD64_XMM0 + 11: "XMM11",
AMD64_XMM0 + 12: "XMM12",
AMD64_XMM0 + 13: "XMM13",
AMD64_XMM0 + 14: "XMM14",
AMD64_XMM0 + 15: "XMM15",
AMD64_ST0: "ST(0)",
AMD64_ST0 + 1: "ST(1)",
AMD64_ST0 + 2: "ST(2)",
AMD64_ST0 + 3: "ST(3)",
AMD64_ST0 + 4: "ST(4)",
AMD64_ST0 + 5: "ST(5)",
AMD64_ST0 + 6: "ST(6)",
AMD64_ST0 + 7: "ST(7)",
AMD64_Rflags: "Rflags",
AMD64_Es: "Es",
AMD64_Cs: "Cs",
AMD64_Ss: "Ss",
AMD64_Ds: "Ds",
AMD64_Fs: "Fs",
AMD64_Gs: "Gs",
AMD64_Fs_base: "Fs_base",
AMD64_Gs_base: "Gs_base",
AMD64_MXCSR: "MXCSR",
AMD64_CW: "CW",
AMD64_SW: "SW",
}
var AMD64NameToDwarf = func() map[string]int {
r := make(map[string]int)
for regNum, regName := range amd64DwarfToName {
r[strings.ToLower(regName)] = int(regNum)
}
r["eflags"] = 49
r["st0"] = 33
r["st1"] = 34
r["st2"] = 35
r["st3"] = 36
r["st4"] = 37
r["st5"] = 38
r["st6"] = 39
r["st7"] = 40
return r
}()
func AMD64MaxRegNum() uint64 {
max := uint64(AMD64_Rip)
for i := range amd64DwarfToName {
if i > max {
max = i
}
}
return max
}
func AMD64ToName(num uint64) string {
name, ok := amd64DwarfToName[num]
if ok {
return name
}
return fmt.Sprintf("unknown%d", num)
}

35
pkg/dwarf/regnum/arm64.go Normal file

@ -0,0 +1,35 @@
package regnum
import (
"fmt"
)
// The mapping between hardware registers and DWARF registers is specified
// in the DWARF for the ARM® Architecture page 7,
// Table 1
// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040b/IHI0040B_aadwarf.pdf
const (
ARM64_X0 = 0 // X1 through X30 follow
ARM64_BP = 29 // also X29
ARM64_LR = 30 // also X30
ARM64_SP = 31
ARM64_PC = 32
ARM64_V0 = 64 // V1 through V31 follow
)
func ARM64ToName(num uint64) string {
switch {
case num <= 30:
return fmt.Sprintf("X%d", num)
case num == ARM64_SP:
return "SP"
case num == ARM64_PC:
return "PC"
case num >= ARM64_V0 && num <= 95:
return fmt.Sprintf("V%d", num-64)
default:
return fmt.Sprintf("unknown%d", num)
}
}

102
pkg/dwarf/regnum/i386.go Normal file

@ -0,0 +1,102 @@
package regnum
import (
"fmt"
"strings"
)
// The mapping between hardware registers and DWARF registers is specified
// in the System V ABI Intel386 Architecture Processor Supplement page 25,
// table 2.14
// https://www.uclibc.org/docs/psABI-i386.pdf
const (
I386_Eax = 0
I386_Ecx = 1
I386_Edx = 2
I386_Ebx = 3
I386_Esp = 4
I386_Ebp = 5
I386_Esi = 6
I386_Edi = 7
I386_Eip = 8
I386_Eflags = 9
I386_ST0 = 11 // ST(1) through ST(7) follow
I386_XMM0 = 21 // XMM1 through XMM7 follow
I386_Es = 40
I386_Cs = 41
I386_Ss = 42
I386_Ds = 43
I386_Fs = 44
I386_Gs = 45
)
var i386DwarfToName = map[int]string{
I386_Eax: "Eax",
I386_Ecx: "Ecx",
I386_Edx: "Edx",
I386_Ebx: "Ebx",
I386_Esp: "Esp",
I386_Ebp: "Ebp",
I386_Esi: "Esi",
I386_Edi: "Edi",
I386_Eip: "Eip",
I386_Eflags: "Eflags",
I386_ST0: "ST(0)",
I386_ST0 + 1: "ST(1)",
I386_ST0 + 2: "ST(2)",
I386_ST0 + 3: "ST(3)",
I386_ST0 + 4: "ST(4)",
I386_ST0 + 5: "ST(5)",
I386_ST0 + 6: "ST(6)",
I386_ST0 + 7: "ST(7)",
I386_XMM0: "XMM0",
I386_XMM0 + 1: "XMM1",
I386_XMM0 + 2: "XMM2",
I386_XMM0 + 3: "XMM3",
I386_XMM0 + 4: "XMM4",
I386_XMM0 + 5: "XMM5",
I386_XMM0 + 6: "XMM6",
I386_XMM0 + 7: "XMM7",
I386_Es: "Es",
I386_Cs: "Cs",
I386_Ss: "Ss",
I386_Ds: "Ds",
I386_Fs: "Fs",
I386_Gs: "Gs",
}
var I386NameToDwarf = func() map[string]int {
r := make(map[string]int)
for regNum, regName := range i386DwarfToName {
r[strings.ToLower(regName)] = regNum
}
r["eflags"] = 9
r["st0"] = 11
r["st1"] = 12
r["st2"] = 13
r["st3"] = 14
r["st4"] = 15
r["st5"] = 16
r["st6"] = 17
r["st7"] = 18
return r
}()
func I386MaxRegNum() int {
max := int(I386_Eip)
for i := range i386DwarfToName {
if i > max {
max = i
}
}
return max
}
func I386ToName(num int) string {
name, ok := i386DwarfToName[num]
if ok {
return name
}
return fmt.Sprintf("unknown%d", num)
}

@ -10,12 +10,7 @@ import (
"github.com/go-delve/delve/pkg/dwarf/frame" "github.com/go-delve/delve/pkg/dwarf/frame"
"github.com/go-delve/delve/pkg/dwarf/op" "github.com/go-delve/delve/pkg/dwarf/op"
) "github.com/go-delve/delve/pkg/dwarf/regnum"
const (
amd64DwarfIPRegNum uint64 = 16
amd64DwarfSPRegNum uint64 = 7
amd64DwarfBPRegNum uint64 = 6
) )
var amd64BreakInstruction = []byte{0xCC} var amd64BreakInstruction = []byte{0xCC}
@ -39,6 +34,10 @@ func AMD64Arch(goos string) *Arch {
DwarfRegisterToString: amd64DwarfRegisterToString, DwarfRegisterToString: amd64DwarfRegisterToString,
inhibitStepInto: func(*BinaryInfo, uint64) bool { return false }, inhibitStepInto: func(*BinaryInfo, uint64) bool { return false },
asmDecode: amd64AsmDecode, asmDecode: amd64AsmDecode,
PCRegNum: regnum.AMD64_Rip,
SPRegNum: regnum.AMD64_Rsp,
BPRegNum: regnum.AMD64_Rbp,
ContextRegNum: regnum.AMD64_Rdx,
} }
} }
@ -68,24 +67,24 @@ func amd64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
// here). // here).
return &frame.FrameContext{ return &frame.FrameContext{
RetAddrReg: amd64DwarfIPRegNum, RetAddrReg: regnum.AMD64_Rip,
Regs: map[uint64]frame.DWRule{ Regs: map[uint64]frame.DWRule{
amd64DwarfIPRegNum: { regnum.AMD64_Rip: {
Rule: frame.RuleOffset, Rule: frame.RuleOffset,
Offset: int64(-a.PtrSize()), Offset: int64(-a.PtrSize()),
}, },
amd64DwarfBPRegNum: { regnum.AMD64_Rbp: {
Rule: frame.RuleOffset, Rule: frame.RuleOffset,
Offset: int64(-2 * a.PtrSize()), Offset: int64(-2 * a.PtrSize()),
}, },
amd64DwarfSPRegNum: { regnum.AMD64_Rsp: {
Rule: frame.RuleValOffset, Rule: frame.RuleValOffset,
Offset: 0, Offset: 0,
}, },
}, },
CFA: frame.DWRule{ CFA: frame.DWRule{
Rule: frame.RuleCFA, Rule: frame.RuleCFA,
Reg: amd64DwarfBPRegNum, Reg: regnum.AMD64_Rbp,
Offset: int64(2 * a.PtrSize()), Offset: int64(2 * a.PtrSize()),
}, },
} }
@ -112,10 +111,10 @@ func amd64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
// so that we can use it to unwind the stack even when we encounter frames // so that we can use it to unwind the stack even when we encounter frames
// without descriptor entries. // without descriptor entries.
// If there isn't a rule already we emit one. // If there isn't a rule already we emit one.
if fctxt.Regs[amd64DwarfBPRegNum].Rule == frame.RuleUndefined { if fctxt.Regs[regnum.AMD64_Rbp].Rule == frame.RuleUndefined {
fctxt.Regs[amd64DwarfBPRegNum] = frame.DWRule{ fctxt.Regs[regnum.AMD64_Rbp] = frame.DWRule{
Rule: frame.RuleFramePointer, Rule: frame.RuleFramePointer,
Reg: amd64DwarfBPRegNum, Reg: regnum.AMD64_Rbp,
Offset: 0, Offset: 0,
} }
} }
@ -244,110 +243,22 @@ func amd64SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool {
// in the System V ABI AMD64 Architecture Processor Supplement page 57, // in the System V ABI AMD64 Architecture Processor Supplement page 57,
// figure 3.36 // figure 3.36
// https://www.uclibc.org/docs/psABI-x86_64.pdf // https://www.uclibc.org/docs/psABI-x86_64.pdf
func amd64RegSize(regnum uint64) int { func amd64RegSize(rn uint64) int {
// XMM registers // XMM registers
if regnum > amd64DwarfIPRegNum && regnum <= 32 { if rn > regnum.AMD64_Rip && rn <= 32 {
return 16 return 16
} }
// x87 registers // x87 registers
if regnum >= 33 && regnum <= 40 { if rn >= 33 && rn <= 40 {
return 10 return 10
} }
return 8 return 8
} }
// The mapping between hardware registers and DWARF registers is specified
// in the System V ABI AMD64 Architecture Processor Supplement page 57,
// figure 3.36
// https://www.uclibc.org/docs/psABI-x86_64.pdf
var amd64DwarfToName = map[int]string{
0: "Rax",
1: "Rdx",
2: "Rcx",
3: "Rbx",
4: "Rsi",
5: "Rdi",
6: "Rbp",
7: "Rsp",
8: "R8",
9: "R9",
10: "R10",
11: "R11",
12: "R12",
13: "R13",
14: "R14",
15: "R15",
16: "Rip",
17: "XMM0",
18: "XMM1",
19: "XMM2",
20: "XMM3",
21: "XMM4",
22: "XMM5",
23: "XMM6",
24: "XMM7",
25: "XMM8",
26: "XMM9",
27: "XMM10",
28: "XMM11",
29: "XMM12",
30: "XMM13",
31: "XMM14",
32: "XMM15",
33: "ST(0)",
34: "ST(1)",
35: "ST(2)",
36: "ST(3)",
37: "ST(4)",
38: "ST(5)",
39: "ST(6)",
40: "ST(7)",
49: "Rflags",
50: "Es",
51: "Cs",
52: "Ss",
53: "Ds",
54: "Fs",
55: "Gs",
58: "Fs_base",
59: "Gs_base",
64: "MXCSR",
65: "CW",
66: "SW",
}
var amd64NameToDwarf = func() map[string]int {
r := make(map[string]int)
for regNum, regName := range amd64DwarfToName {
r[strings.ToLower(regName)] = regNum
}
r["eflags"] = 49
r["st0"] = 33
r["st1"] = 34
r["st2"] = 35
r["st3"] = 36
r["st4"] = 37
r["st5"] = 38
r["st6"] = 39
r["st7"] = 40
return r
}()
func maxAmd64DwarfRegister() int {
max := int(amd64DwarfIPRegNum)
for i := range amd64DwarfToName {
if i > max {
max = i
}
}
return max
}
func amd64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters { func amd64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters {
dregs := initDwarfRegistersFromSlice(maxAmd64DwarfRegister(), regs, amd64NameToDwarf) dregs := initDwarfRegistersFromSlice(int(regnum.AMD64MaxRegNum()), regs, regnum.AMD64NameToDwarf)
dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, amd64DwarfIPRegNum, amd64DwarfSPRegNum, amd64DwarfBPRegNum, 0) dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.AMD64_Rip, regnum.AMD64_Rsp, regnum.AMD64_Rbp, 0)
dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, amd64NameToDwarf)) dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, regnum.AMD64NameToDwarf))
return *dr return *dr
} }
@ -389,18 +300,19 @@ func loadMoreDwarfRegistersFromSliceFunc(dr *op.DwarfRegisters, regs Registers,
} }
func amd64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { func amd64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters {
dregs := make([]*op.DwarfRegister, amd64DwarfIPRegNum+1) dregs := make([]*op.DwarfRegister, regnum.AMD64_Rip+1)
dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc) dregs[regnum.AMD64_Rip] = op.DwarfRegisterFromUint64(pc)
dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp) dregs[regnum.AMD64_Rsp] = op.DwarfRegisterFromUint64(sp)
dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp) dregs[regnum.AMD64_Rbp] = op.DwarfRegisterFromUint64(bp)
return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, amd64DwarfIPRegNum, amd64DwarfSPRegNum, amd64DwarfBPRegNum, 0) return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.AMD64_Rip, regnum.AMD64_Rsp, regnum.AMD64_Rbp, 0)
} }
func amd64DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { func amd64DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) {
name, ok := amd64DwarfToName[i] name = regnum.AMD64ToName(uint64(i))
if !ok {
name = fmt.Sprintf("unknown%d", i) if reg == nil {
return name, false, ""
} }
switch n := strings.ToLower(name); n { switch n := strings.ToLower(name); n {

@ -74,6 +74,7 @@ func (xsave *AMD64Xstate) Decode() []proc.Register {
const ( const (
_XSTATE_MAX_KNOWN_SIZE = 2969 _XSTATE_MAX_KNOWN_SIZE = 2969
_XSAVE_XMM_REGION_START = 160
_XSAVE_HEADER_START = 512 _XSAVE_HEADER_START = 512
_XSAVE_HEADER_LEN = 64 _XSAVE_HEADER_LEN = 64
_XSAVE_EXTENDED_REGION_START = 576 _XSAVE_EXTENDED_REGION_START = 576
@ -129,3 +130,61 @@ func AMD64XstateRead(xstateargs []byte, readLegacy bool, regset *AMD64Xstate) er
return nil return nil
} }
func (xstate *AMD64Xstate) SetXmmRegister(n int, value []byte) error {
if n >= 16 {
return fmt.Errorf("setting register XMM%d not supported", n)
}
if len(value) > 64 {
return fmt.Errorf("value of register XMM%d too large (%d bytes)", n, len(value))
}
// Copy least significant 16 bytes to Xsave area
xmmval := value
if len(xmmval) > 16 {
xmmval = xmmval[:16]
}
rest := value[len(xmmval):]
xmmpos := _XSAVE_XMM_REGION_START + (n * 16)
if xmmpos >= len(xstate.Xsave) {
return fmt.Errorf("could not set XMM%d: not in XSAVE area", n)
}
copy(xstate.Xsave[xmmpos:], xmmval)
if len(rest) == 0 {
return nil
}
// Copy bytes [16, 32) to Xsave area
ymmval := rest
if len(ymmval) > 16 {
ymmval = ymmval[:16]
}
rest = rest[len(ymmval):]
ymmpos := _XSAVE_EXTENDED_REGION_START + (n * 16)
if ymmpos >= len(xstate.Xsave) {
return fmt.Errorf("could not set XMM%d: bytes 16..%d not in XSAVE area", n, 16+len(ymmval))
}
copy(xstate.Xsave[ymmpos:], ymmval)
if len(rest) == 0 {
return nil
}
// Copy bytes [32, 64) to Xsave area
zmmval := rest
zmmpos := _XSAVE_AVX512_ZMM_REGION_START + (n * 32)
if zmmpos >= len(xstate.Xsave) {
return fmt.Errorf("could not set XMM%d: bytes 32..%d not in XSAVE area", n, 32+len(zmmval))
}
copy(xstate.Xsave[zmmpos:], zmmval)
return nil
}

@ -17,6 +17,10 @@ type Arch struct {
breakInstrMovesPC bool breakInstrMovesPC bool
derefTLS bool derefTLS bool
usesLR bool // architecture uses a link register, also called RA on some architectures usesLR bool // architecture uses a link register, also called RA on some architectures
PCRegNum uint64
SPRegNum uint64
BPRegNum uint64
ContextRegNum uint64 // register used to pass a closure context when calling a function pointer
// asmDecode decodes the assembly instruction starting at mem[0:] into asmInst. // asmDecode decodes the assembly instruction starting at mem[0:] into asmInst.
// It assumes that the Loc and AtPC fields of asmInst have already been filled. // It assumes that the Loc and AtPC fields of asmInst have already been filled.
@ -34,7 +38,9 @@ type Arch struct {
// addrAndStackRegsToDwarfRegisters returns DWARF registers from the passed in // addrAndStackRegsToDwarfRegisters returns DWARF registers from the passed in
// PC, SP, and BP registers in the format used by the DWARF expression interpreter. // PC, SP, and BP registers in the format used by the DWARF expression interpreter.
addrAndStackRegsToDwarfRegisters func(uint64, uint64, uint64, uint64, uint64) op.DwarfRegisters addrAndStackRegsToDwarfRegisters func(uint64, uint64, uint64, uint64, uint64) op.DwarfRegisters
// DwarfRegisterToString returns the name and value representation of the given register. // DwarfRegisterToString returns the name and value representation of the
// given register, the register value can be nil in which case only the
// register name will be returned.
DwarfRegisterToString func(int, *op.DwarfRegister) (string, bool, string) DwarfRegisterToString func(int, *op.DwarfRegister) (string, bool, string)
// inhibitStepInto returns whether StepBreakpoint can be set at pc. // inhibitStepInto returns whether StepBreakpoint can be set at pc.
inhibitStepInto func(bi *BinaryInfo, pc uint64) bool inhibitStepInto func(bi *BinaryInfo, pc uint64) bool

@ -8,16 +8,10 @@ import (
"github.com/go-delve/delve/pkg/dwarf/frame" "github.com/go-delve/delve/pkg/dwarf/frame"
"github.com/go-delve/delve/pkg/dwarf/op" "github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/dwarf/regnum"
"golang.org/x/arch/arm64/arm64asm" "golang.org/x/arch/arm64/arm64asm"
) )
const (
arm64DwarfIPRegNum uint64 = 32
arm64DwarfSPRegNum uint64 = 31
arm64DwarfLRRegNum uint64 = 30
arm64DwarfBPRegNum uint64 = 29
)
var arm64BreakInstruction = []byte{0x0, 0x0, 0x20, 0xd4} var arm64BreakInstruction = []byte{0x0, 0x0, 0x20, 0xd4}
// ARM64Arch returns an initialized ARM64 // ARM64Arch returns an initialized ARM64
@ -40,6 +34,8 @@ func ARM64Arch(goos string) *Arch {
inhibitStepInto: func(*BinaryInfo, uint64) bool { return false }, inhibitStepInto: func(*BinaryInfo, uint64) bool { return false },
asmDecode: arm64AsmDecode, asmDecode: arm64AsmDecode,
usesLR: true, usesLR: true,
PCRegNum: regnum.ARM64_PC,
SPRegNum: regnum.ARM64_SP,
} }
} }
@ -69,24 +65,24 @@ func arm64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
// here). // here).
return &frame.FrameContext{ return &frame.FrameContext{
RetAddrReg: arm64DwarfIPRegNum, RetAddrReg: regnum.ARM64_PC,
Regs: map[uint64]frame.DWRule{ Regs: map[uint64]frame.DWRule{
arm64DwarfIPRegNum: frame.DWRule{ regnum.ARM64_PC: frame.DWRule{
Rule: frame.RuleOffset, Rule: frame.RuleOffset,
Offset: int64(-a.PtrSize()), Offset: int64(-a.PtrSize()),
}, },
arm64DwarfBPRegNum: frame.DWRule{ regnum.ARM64_BP: frame.DWRule{
Rule: frame.RuleOffset, Rule: frame.RuleOffset,
Offset: int64(-2 * a.PtrSize()), Offset: int64(-2 * a.PtrSize()),
}, },
arm64DwarfSPRegNum: frame.DWRule{ regnum.ARM64_SP: frame.DWRule{
Rule: frame.RuleValOffset, Rule: frame.RuleValOffset,
Offset: 0, Offset: 0,
}, },
}, },
CFA: frame.DWRule{ CFA: frame.DWRule{
Rule: frame.RuleCFA, Rule: frame.RuleCFA,
Reg: arm64DwarfBPRegNum, Reg: regnum.ARM64_BP,
Offset: int64(2 * a.PtrSize()), Offset: int64(2 * a.PtrSize()),
}, },
} }
@ -113,17 +109,17 @@ func arm64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
// so that we can use it to unwind the stack even when we encounter frames // so that we can use it to unwind the stack even when we encounter frames
// without descriptor entries. // without descriptor entries.
// If there isn't a rule already we emit one. // If there isn't a rule already we emit one.
if fctxt.Regs[arm64DwarfBPRegNum].Rule == frame.RuleUndefined { if fctxt.Regs[regnum.ARM64_BP].Rule == frame.RuleUndefined {
fctxt.Regs[arm64DwarfBPRegNum] = frame.DWRule{ fctxt.Regs[regnum.ARM64_BP] = frame.DWRule{
Rule: frame.RuleFramePointer, Rule: frame.RuleFramePointer,
Reg: arm64DwarfBPRegNum, Reg: regnum.ARM64_BP,
Offset: 0, Offset: 0,
} }
} }
if fctxt.Regs[arm64DwarfLRRegNum].Rule == frame.RuleUndefined { if fctxt.Regs[regnum.ARM64_LR].Rule == frame.RuleUndefined {
fctxt.Regs[arm64DwarfLRRegNum] = frame.DWRule{ fctxt.Regs[regnum.ARM64_LR] = frame.DWRule{
Rule: frame.RuleFramePointer, Rule: frame.RuleFramePointer,
Reg: arm64DwarfLRRegNum, Reg: regnum.ARM64_LR,
Offset: 0, Offset: 0,
} }
} }
@ -317,8 +313,8 @@ var arm64NameToDwarf = func() map[string]int {
for i := 0; i <= 30; i++ { for i := 0; i <= 30; i++ {
r[fmt.Sprintf("x%d", i)] = i r[fmt.Sprintf("x%d", i)] = i
} }
r["pc"] = int(arm64DwarfIPRegNum) r["pc"] = int(regnum.ARM64_PC)
r["lr"] = int(arm64DwarfLRRegNum) r["lr"] = int(regnum.ARM64_LR)
r["sp"] = 31 r["sp"] = 31
for i := 0; i <= 31; i++ { for i := 0; i <= 31; i++ {
r[fmt.Sprintf("v%d", i)] = i + 64 r[fmt.Sprintf("v%d", i)] = i + 64
@ -327,7 +323,7 @@ var arm64NameToDwarf = func() map[string]int {
}() }()
func maxArm64DwarfRegister() int { func maxArm64DwarfRegister() int {
max := int(arm64DwarfIPRegNum) max := int(regnum.ARM64_PC)
for i := range arm64DwarfToHardware { for i := range arm64DwarfToHardware {
if i > max { if i > max {
max = i max = i
@ -339,11 +335,11 @@ func maxArm64DwarfRegister() int {
func arm64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters { func arm64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters {
dregs := make([]*op.DwarfRegister, maxArm64DwarfRegister()+1) dregs := make([]*op.DwarfRegister, maxArm64DwarfRegister()+1)
dregs[arm64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC()) dregs[regnum.ARM64_PC] = op.DwarfRegisterFromUint64(regs.PC())
dregs[arm64DwarfSPRegNum] = op.DwarfRegisterFromUint64(regs.SP()) dregs[regnum.ARM64_SP] = op.DwarfRegisterFromUint64(regs.SP())
dregs[arm64DwarfBPRegNum] = op.DwarfRegisterFromUint64(regs.BP()) dregs[regnum.ARM64_BP] = op.DwarfRegisterFromUint64(regs.BP())
if lr, err := regs.Get(int(arm64asm.X30)); err != nil { if lr, err := regs.Get(int(arm64asm.X30)); err != nil {
dregs[arm64DwarfLRRegNum] = op.DwarfRegisterFromUint64(lr) dregs[regnum.ARM64_LR] = op.DwarfRegisterFromUint64(lr)
} }
for dwarfReg, asmReg := range arm64DwarfToHardware { for dwarfReg, asmReg := range arm64DwarfToHardware {
@ -353,34 +349,26 @@ func arm64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfR
} }
} }
dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, arm64DwarfIPRegNum, arm64DwarfSPRegNum, arm64DwarfBPRegNum, arm64DwarfLRRegNum) dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.ARM64_PC, regnum.ARM64_SP, regnum.ARM64_BP, regnum.ARM64_LR)
dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, arm64NameToDwarf)) dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, arm64NameToDwarf))
return *dr return *dr
} }
func arm64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { func arm64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters {
dregs := make([]*op.DwarfRegister, arm64DwarfIPRegNum+1) dregs := make([]*op.DwarfRegister, regnum.ARM64_PC+1)
dregs[arm64DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc) dregs[regnum.ARM64_PC] = op.DwarfRegisterFromUint64(pc)
dregs[arm64DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp) dregs[regnum.ARM64_SP] = op.DwarfRegisterFromUint64(sp)
dregs[arm64DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp) dregs[regnum.ARM64_BP] = op.DwarfRegisterFromUint64(bp)
dregs[arm64DwarfLRRegNum] = op.DwarfRegisterFromUint64(lr) dregs[regnum.ARM64_LR] = op.DwarfRegisterFromUint64(lr)
return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, arm64DwarfIPRegNum, arm64DwarfSPRegNum, arm64DwarfBPRegNum, arm64DwarfLRRegNum) return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.ARM64_PC, regnum.ARM64_SP, regnum.ARM64_BP, regnum.ARM64_LR)
} }
func arm64DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { func arm64DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) {
// see arm64DwarfToHardware table for explanation name = regnum.ARM64ToName(uint64(i))
switch {
case i <= 30: if reg == nil {
name = fmt.Sprintf("X%d", i) return name, false, ""
case i == 31:
name = "SP"
case i == 32:
name = "PC"
case i >= 64 && i <= 95:
name = fmt.Sprintf("V%d", i-64)
default:
name = fmt.Sprintf("unknown%d", i)
} }
if reg.Bytes != nil && name[0] == 'V' { if reg.Bytes != nil && name[0] == 'V' {

@ -6,6 +6,7 @@ import (
"io" "io"
"github.com/go-delve/delve/pkg/elfwriter" "github.com/go-delve/delve/pkg/elfwriter"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/proc" "github.com/go-delve/delve/pkg/proc"
) )
@ -372,6 +373,12 @@ func (t *thread) SetDX(uint64) error {
return ErrChangeRegisterCore return ErrChangeRegisterCore
} }
// ChangeRegs will always return an error, you cannot
// change register values when debugging core files.
func (t *thread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
return ErrChangeRegisterCore
}
// Breakpoints will return all breakpoints for the process. // Breakpoints will return all breakpoints for the process.
func (p *process) Breakpoints() *proc.BreakpointMap { func (p *process) Breakpoints() *proc.BreakpointMap {
return &p.breakpoints return &p.breakpoints

@ -1,6 +1,21 @@
package proc package proc
import "github.com/go-delve/delve/pkg/dwarf/op"
// PackageVars returns bi.packageVars (for tests) // PackageVars returns bi.packageVars (for tests)
func (bi *BinaryInfo) PackageVars() []packageVar { func (bi *BinaryInfo) PackageVars() []packageVar {
return bi.packageVars return bi.packageVars
} }
func NewCompositeMemory(p *Target, pieces []op.Piece) (*compositeMemory, error) {
regs, err := p.CurrentThread().Registers()
if err != nil {
return nil, err
}
arch := p.BinInfo().Arch
dwarfregs := arch.RegistersToDwarfRegisters(0, regs)
dwarfregs.ChangeFunc = p.CurrentThread().SetReg
return newCompositeMemory(p.Memory(), arch, dwarfregs, pieces)
}

@ -77,8 +77,17 @@ func (mem *fakeMemory) ReadMemory(data []byte, addr uint64) (int, error) {
return len(data), nil return len(data), nil
} }
func (mem *fakeMemory) WriteMemory(uint64, []byte) (int, error) { func (mem *fakeMemory) WriteMemory(addr uint64, data []byte) (int, error) {
return 0, fmt.Errorf("not implemented") if uint64(addr) < mem.base {
return 0, fmt.Errorf("write out of bounds %d %#x", len(data), addr)
}
start := uint64(addr) - mem.base
end := uint64(len(data)) + start
if end > uint64(len(mem.data)) {
panic(fmt.Errorf("write out of bounds %d %#x", len(data), addr))
}
copy(mem.data[start:end], data)
return len(data), nil
} }
func uintExprCheck(t *testing.T, scope *proc.EvalScope, expr string, tgt uint64) { func uintExprCheck(t *testing.T, scope *proc.EvalScope, expr string, tgt uint64) {
@ -93,13 +102,14 @@ func uintExprCheck(t *testing.T, scope *proc.EvalScope, expr string, tgt uint64)
} }
} }
func dwarfExprCheck(t *testing.T, mem proc.MemoryReadWriter, regs op.DwarfRegisters, bi *proc.BinaryInfo, testCases map[string]uint16, fn *proc.Function) *proc.EvalScope { func fakeScope(mem proc.MemoryReadWriter, regs op.DwarfRegisters, bi *proc.BinaryInfo, fn *proc.Function) *proc.EvalScope {
scope := &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: fn}, Regs: regs, Mem: mem, BinInfo: bi} return &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: fn}, Regs: regs, Mem: mem, BinInfo: bi}
}
func dwarfExprCheck(t *testing.T, scope *proc.EvalScope, testCases map[string]uint16) {
for name, value := range testCases { for name, value := range testCases {
uintExprCheck(t, scope, name, uint64(value)) uintExprCheck(t, scope, name, uint64(value))
} }
return scope
} }
func dwarfRegisters(bi *proc.BinaryInfo, regs *linutil.AMD64Registers) op.DwarfRegisters { func dwarfRegisters(bi *proc.BinaryInfo, regs *linutil.AMD64Registers) op.DwarfRegisters {
@ -137,7 +147,7 @@ func TestDwarfExprRegisters(t *testing.T) {
regs.Regs.Rax = uint64(testCases["a"]) regs.Regs.Rax = uint64(testCases["a"])
regs.Regs.Rdx = uint64(testCases["c"]) regs.Regs.Rdx = uint64(testCases["c"])
dwarfExprCheck(t, mem, dwarfRegisters(bi, &regs), bi, testCases, mainfn) dwarfExprCheck(t, fakeScope(mem, dwarfRegisters(bi, &regs), bi, mainfn), testCases)
} }
func TestDwarfExprComposite(t *testing.T) { func TestDwarfExprComposite(t *testing.T) {
@ -192,7 +202,17 @@ func TestDwarfExprComposite(t *testing.T) {
regs.Regs.Rcx = uint64(testCases["pair.k"]) regs.Regs.Rcx = uint64(testCases["pair.k"])
regs.Regs.Rbx = uint64(testCases["n"]) regs.Regs.Rbx = uint64(testCases["n"])
scope := dwarfExprCheck(t, mem, dwarfRegisters(bi, &regs), bi, testCases, mainfn) dwarfRegs := dwarfRegisters(bi, &regs)
var changeCalls []string
dwarfRegs.ChangeFunc = func(regNum uint64, reg *op.DwarfRegister) error {
t.Logf("SetReg(%d, %x)", regNum, reg.Bytes)
changeCalls = append(changeCalls, fmt.Sprintf("%d - %x", regNum, reg.Bytes))
return nil
}
scope := fakeScope(mem, dwarfRegs, bi, mainfn)
dwarfExprCheck(t, scope, testCases)
thevar, err := scope.EvalExpression("s", normalLoadConfig) thevar, err := scope.EvalExpression("s", normalLoadConfig)
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s)", "s")) assertNoError(err, t, fmt.Sprintf("EvalExpression(%s)", "s"))
@ -203,6 +223,29 @@ func TestDwarfExprComposite(t *testing.T) {
t.Errorf("expected value %q got %q", stringVal, v) t.Errorf("expected value %q got %q", stringVal, v)
} }
} }
// Test writes to composite memory
assertNoError(scope.SetVariable("n", "47"), t, "SetVariable(n, 47)")
assertNoError(scope.SetVariable("pair.k", "12"), t, "SetVariable(pair.k, 12)")
assertNoError(scope.SetVariable("pair.v", "13"), t, "SetVariable(pair.v, 13)")
for i := range changeCalls {
t.Logf("%q\n", changeCalls[i])
}
if len(changeCalls) != 2 {
t.Errorf("wrong number of calls to SetReg")
}
if changeCalls[0] != "3 - 2f00000000000000" {
t.Errorf("wrong call to SetReg (Rbx)")
}
if changeCalls[1] != "2 - 0c00" {
t.Errorf("wrong call to SetReg (Rcx)")
}
if mem.data[0x10] != 13 || mem.data[0x11] != 0x00 {
t.Errorf("memory was not written %v", mem.data[:2])
}
} }
func TestDwarfExprLoclist(t *testing.T) { func TestDwarfExprLoclist(t *testing.T) {

@ -421,13 +421,13 @@ func callOP(bi *BinaryInfo, thread Thread, regs Registers, callAddr uint64) erro
sp := regs.SP() sp := regs.SP()
// push PC on the stack // push PC on the stack
sp -= uint64(bi.Arch.PtrSize()) sp -= uint64(bi.Arch.PtrSize())
if err := thread.SetSP(sp); err != nil { if err := setSP(thread, sp); err != nil {
return err return err
} }
if err := writePointer(bi, thread.ProcessMemory(), sp, regs.PC()); err != nil { if err := writePointer(bi, thread.ProcessMemory(), sp, regs.PC()); err != nil {
return err return err
} }
return thread.SetPC(callAddr) return setPC(thread, callAddr)
} }
// funcCallEvalFuncExpr evaluates expr.Fun and returns the function that we're trying to call. // funcCallEvalFuncExpr evaluates expr.Fun and returns the function that we're trying to call.
@ -761,7 +761,7 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread
if fncall.closureAddr != 0 { if fncall.closureAddr != 0 {
// When calling a function pointer we must set the DX register to the // When calling a function pointer we must set the DX register to the
// address of the function pointer itself. // address of the function pointer itself.
thread.SetDX(fncall.closureAddr) setClosureReg(thread, fncall.closureAddr)
} }
cfa := regs.SP() cfa := regs.SP()
oldpc := regs.PC() oldpc := regs.PC()
@ -770,8 +770,8 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread
err := funcCallEvalArgs(callScope, fncall, cfa) err := funcCallEvalArgs(callScope, fncall, cfa)
if err != nil { if err != nil {
// rolling back the call, note: this works because we called regs.Copy() above // rolling back the call, note: this works because we called regs.Copy() above
thread.SetSP(cfa) setSP(thread, cfa)
thread.SetPC(oldpc) setPC(thread, oldpc)
fncall.err = err fncall.err = err
fncall.lateCallFailure = true fncall.lateCallFailure = true
break break
@ -784,10 +784,10 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread
if err := thread.RestoreRegisters(fncall.savedRegs); err != nil { if err := thread.RestoreRegisters(fncall.savedRegs); err != nil {
fncall.err = fmt.Errorf("could not restore registers: %v", err) fncall.err = fmt.Errorf("could not restore registers: %v", err)
} }
if err := thread.SetPC(pc); err != nil { if err := setPC(thread, pc); err != nil {
fncall.err = fmt.Errorf("could not restore PC: %v", err) fncall.err = fmt.Errorf("could not restore PC: %v", err)
} }
if err := thread.SetSP(sp); err != nil { if err := setSP(thread, sp); err != nil {
fncall.err = fmt.Errorf("could not restore SP: %v", err) fncall.err = fmt.Errorf("could not restore SP: %v", err)
} }
if err := stepInstructionOut(p, thread, debugCallFunctionName, debugCallFunctionName); err != nil { if err := stepInstructionOut(p, thread, debugCallFunctionName, debugCallFunctionName); err != nil {

@ -80,6 +80,7 @@ import (
"golang.org/x/arch/arm64/arm64asm" "golang.org/x/arch/arm64/arm64asm"
"golang.org/x/arch/x86/x86asm" "golang.org/x/arch/x86/x86asm"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/elfwriter" "github.com/go-delve/delve/pkg/elfwriter"
"github.com/go-delve/delve/pkg/logflags" "github.com/go-delve/delve/pkg/logflags"
"github.com/go-delve/delve/pkg/proc" "github.com/go-delve/delve/pkg/proc"
@ -1744,7 +1745,7 @@ func (t *gdbThread) SetCurrentBreakpoint(adjustPC bool) error {
pc := regs.PC() pc := regs.PC()
if bp, ok := t.p.FindBreakpoint(pc); ok { if bp, ok := t.p.FindBreakpoint(pc); ok {
if t.regs.PC() != bp.Addr { if t.regs.PC() != bp.Addr {
if err := t.SetPC(bp.Addr); err != nil { if err := t.setPC(bp.Addr); err != nil {
return err return err
} }
} }
@ -1770,13 +1771,6 @@ func (regs *gdbRegisters) setPC(value uint64) {
func (regs *gdbRegisters) SP() uint64 { func (regs *gdbRegisters) SP() uint64 {
return binary.LittleEndian.Uint64(regs.regs[regnameSP].value) return binary.LittleEndian.Uint64(regs.regs[regnameSP].value)
} }
func (regs *gdbRegisters) setSP(value uint64) {
binary.LittleEndian.PutUint64(regs.regs[regnameSP].value, value)
}
func (regs *gdbRegisters) setDX(value uint64) {
binary.LittleEndian.PutUint64(regs.regs[regnameDX].value, value)
}
func (regs *gdbRegisters) BP() uint64 { func (regs *gdbRegisters) BP() uint64 {
return binary.LittleEndian.Uint64(regs.regs[regnameBP].value) return binary.LittleEndian.Uint64(regs.regs[regnameBP].value)
@ -2180,7 +2174,7 @@ func (r *gdbRegisters) FloatLoadError() error {
} }
// SetPC will set the value of the PC register to the given value. // SetPC will set the value of the PC register to the given value.
func (t *gdbThread) SetPC(pc uint64) error { func (t *gdbThread) setPC(pc uint64) error {
_, _ = t.Registers() // Registes must be loaded first _, _ = t.Registers() // Registes must be loaded first
t.regs.setPC(pc) t.regs.setPC(pc)
if t.p.gcmdok { if t.p.gcmdok {
@ -2190,30 +2184,26 @@ func (t *gdbThread) SetPC(pc uint64) error {
return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value) return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value)
} }
// SetSP will set the value of the SP register to the given value. // SetReg will change the value of a list of registers
func (t *gdbThread) SetSP(sp uint64) error { func (t *gdbThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
_, _ = t.Registers() // Registes must be loaded first regName, _, _ := t.p.bi.Arch.DwarfRegisterToString(int(regNum), nil)
t.regs.setSP(sp) regName = strings.ToLower(regName)
if t.p.gcmdok { _, _ = t.Registers() // Registers must be loaded first
return t.p.conn.writeRegisters(t.strID, t.regs.buf) gdbreg, ok := t.regs.regs[regName]
if !ok && strings.HasPrefix(regName, "xmm") {
// XMMn and YMMn are the same amd64 register (in different sizes), if we
// don't find XMMn try YMMn instead.
gdbreg, ok = t.regs.regs["y"+regName[1:]]
} }
reg := t.regs.regs[regnameSP] if !ok {
return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value) return fmt.Errorf("could not set register %s: not found", regName)
}
// 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")
} }
reg.FillBytes()
_, _ = t.Registers() // Registes must be loaded first if len(reg.Bytes) != len(gdbreg.value) {
t.regs.setDX(dx) return fmt.Errorf("could not set register %s: wrong size, expected %d got %d", regName, len(gdbreg.value), len(reg.Bytes))
if t.p.gcmdok {
return t.p.conn.writeRegisters(t.strID, t.regs.buf)
} }
reg := t.regs.regs[regnameDX] copy(gdbreg.value, reg.Bytes)
return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value) return t.p.conn.writeRegister(t.strID, gdbreg.regnum, gdbreg.value)
} }
func (regs *gdbRegisters) Slice(floatingPoint bool) ([]proc.Register, error) { func (regs *gdbRegisters) Slice(floatingPoint bool) ([]proc.Register, error) {

@ -3,15 +3,11 @@ package proc
import ( import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"strings"
"github.com/go-delve/delve/pkg/dwarf/frame" "github.com/go-delve/delve/pkg/dwarf/frame"
"github.com/go-delve/delve/pkg/dwarf/op" "github.com/go-delve/delve/pkg/dwarf/op"
"strings" "github.com/go-delve/delve/pkg/dwarf/regnum"
)
const (
i386DwarfIPRegNum uint64 = 8
i386DwarfSPRegNum uint64 = 4
i386DwarfBPRegNum uint64 = 5
) )
var i386BreakInstruction = []byte{0xCC} var i386BreakInstruction = []byte{0xCC}
@ -36,6 +32,8 @@ func I386Arch(goos string) *Arch {
DwarfRegisterToString: i386DwarfRegisterToString, DwarfRegisterToString: i386DwarfRegisterToString,
inhibitStepInto: i386InhibitStepInto, inhibitStepInto: i386InhibitStepInto,
asmDecode: i386AsmDecode, asmDecode: i386AsmDecode,
PCRegNum: regnum.I386_Eip,
SPRegNum: regnum.I386_Esp,
} }
} }
@ -65,24 +63,24 @@ func i386FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryI
// here). // here).
return &frame.FrameContext{ return &frame.FrameContext{
RetAddrReg: i386DwarfIPRegNum, RetAddrReg: regnum.I386_Eip,
Regs: map[uint64]frame.DWRule{ Regs: map[uint64]frame.DWRule{
i386DwarfIPRegNum: frame.DWRule{ regnum.I386_Eip: frame.DWRule{
Rule: frame.RuleOffset, Rule: frame.RuleOffset,
Offset: int64(-i.PtrSize()), Offset: int64(-i.PtrSize()),
}, },
i386DwarfBPRegNum: frame.DWRule{ regnum.I386_Ebp: frame.DWRule{
Rule: frame.RuleOffset, Rule: frame.RuleOffset,
Offset: int64(-2 * i.PtrSize()), Offset: int64(-2 * i.PtrSize()),
}, },
i386DwarfSPRegNum: frame.DWRule{ regnum.I386_Esp: frame.DWRule{
Rule: frame.RuleValOffset, Rule: frame.RuleValOffset,
Offset: 0, Offset: 0,
}, },
}, },
CFA: frame.DWRule{ CFA: frame.DWRule{
Rule: frame.RuleCFA, Rule: frame.RuleCFA,
Reg: i386DwarfBPRegNum, Reg: regnum.I386_Ebp,
Offset: int64(2 * i.PtrSize()), Offset: int64(2 * i.PtrSize()),
}, },
} }
@ -103,10 +101,10 @@ func i386FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryI
// so that we can use it to unwind the stack even when we encounter frames // so that we can use it to unwind the stack even when we encounter frames
// without descriptor entries. // without descriptor entries.
// If there isn't i rule already we emit one. // If there isn't i rule already we emit one.
if fctxt.Regs[i386DwarfBPRegNum].Rule == frame.RuleUndefined { if fctxt.Regs[regnum.I386_Ebp].Rule == frame.RuleUndefined {
fctxt.Regs[i386DwarfBPRegNum] = frame.DWRule{ fctxt.Regs[regnum.I386_Ebp] = frame.DWRule{
Rule: frame.RuleFramePointer, Rule: frame.RuleFramePointer,
Reg: i386DwarfBPRegNum, Reg: regnum.I386_Ebp,
Offset: 0, Offset: 0,
} }
} }
@ -186,94 +184,28 @@ func i386RegSize(regnum uint64) int {
return 4 return 4
} }
// The mapping between hardware registers and DWARF registers is specified
// in the System V ABI Intel386 Architecture Processor Supplement page 25,
// table 2.14
// https://www.uclibc.org/docs/psABI-i386.pdf
var i386DwarfToName = map[int]string{
0: "Eax",
1: "Ecx",
2: "Edx",
3: "Ebx",
4: "Esp",
5: "Ebp",
6: "Esi",
7: "Edi",
8: "Eip",
9: "Eflags",
11: "ST(0)",
12: "ST(1)",
13: "ST(2)",
14: "ST(3)",
15: "ST(4)",
16: "ST(5)",
17: "ST(6)",
18: "ST(7)",
21: "XMM0",
22: "XMM1",
23: "XMM2",
24: "XMM3",
25: "XMM4",
26: "XMM5",
27: "XMM6",
28: "XMM7",
40: "Es",
41: "Cs",
42: "Ss",
43: "Ds",
44: "Fs",
45: "Gs",
}
var i386NameToDwarf = func() map[string]int {
r := make(map[string]int)
for regNum, regName := range i386DwarfToName {
r[strings.ToLower(regName)] = regNum
}
r["eflags"] = 9
r["st0"] = 11
r["st1"] = 12
r["st2"] = 13
r["st3"] = 14
r["st4"] = 15
r["st5"] = 16
r["st6"] = 17
r["st7"] = 18
return r
}()
func maxI386DwarfRegister() int {
max := int(i386DwarfIPRegNum)
for i := range i386DwarfToName {
if i > max {
max = i
}
}
return max
}
func i386RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters { func i386RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters {
dregs := initDwarfRegistersFromSlice(maxI386DwarfRegister(), regs, i386NameToDwarf) dregs := initDwarfRegistersFromSlice(regnum.I386MaxRegNum(), regs, regnum.I386NameToDwarf)
dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, i386DwarfIPRegNum, i386DwarfSPRegNum, i386DwarfBPRegNum, 0) dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.I386_Eip, regnum.I386_Esp, regnum.I386_Ebp, 0)
dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, i386NameToDwarf)) dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, regnum.I386NameToDwarf))
return *dr return *dr
} }
func i386AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { func i386AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters {
dregs := make([]*op.DwarfRegister, i386DwarfIPRegNum+1) dregs := make([]*op.DwarfRegister, regnum.I386_Eip+1)
dregs[i386DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc) dregs[regnum.I386_Eip] = op.DwarfRegisterFromUint64(pc)
dregs[i386DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp) dregs[regnum.I386_Esp] = op.DwarfRegisterFromUint64(sp)
dregs[i386DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp) dregs[regnum.I386_Ebp] = op.DwarfRegisterFromUint64(bp)
return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, i386DwarfIPRegNum, i386DwarfSPRegNum, i386DwarfBPRegNum, 0) return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.I386_Eip, regnum.I386_Esp, regnum.I386_Ebp, 0)
} }
func i386DwarfRegisterToString(j int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { func i386DwarfRegisterToString(j int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) {
name, ok := i386DwarfToName[j] name = regnum.I386ToName(j)
if !ok {
name = fmt.Sprintf("unknown%d", j) if reg == nil {
return name, false, ""
} }
switch n := strings.ToLower(name); n { switch n := strings.ToLower(name); n {

@ -1,8 +1,12 @@
package linutil package linutil
import ( import (
"fmt"
"golang.org/x/arch/x86/x86asm" "golang.org/x/arch/x86/x86asm"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/dwarf/regnum"
"github.com/go-delve/delve/pkg/proc" "github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/amd64util" "github.com/go-delve/delve/pkg/proc/amd64util"
) )
@ -307,3 +311,71 @@ func (r *AMD64Registers) Copy() (proc.Registers, error) {
} }
return &rr, nil return &rr, nil
} }
func (r *AMD64Registers) SetReg(regNum uint64, reg *op.DwarfRegister) (bool, error) {
var p *uint64
switch regNum {
case regnum.AMD64_Rax:
p = &r.Regs.Rax
case regnum.AMD64_Rbx:
p = &r.Regs.Rbx
case regnum.AMD64_Rcx:
p = &r.Regs.Rcx
case regnum.AMD64_Rdx:
p = &r.Regs.Rdx
case regnum.AMD64_Rsi:
p = &r.Regs.Rsi
case regnum.AMD64_Rdi:
p = &r.Regs.Rdi
case regnum.AMD64_Rbp:
p = &r.Regs.Rbp
case regnum.AMD64_Rsp:
p = &r.Regs.Rsp
case regnum.AMD64_R8:
p = &r.Regs.R8
case regnum.AMD64_R9:
p = &r.Regs.R9
case regnum.AMD64_R10:
p = &r.Regs.R10
case regnum.AMD64_R11:
p = &r.Regs.R11
case regnum.AMD64_R12:
p = &r.Regs.R12
case regnum.AMD64_R13:
p = &r.Regs.R13
case regnum.AMD64_R14:
p = &r.Regs.R14
case regnum.AMD64_R15:
p = &r.Regs.R15
case regnum.AMD64_Rip:
p = &r.Regs.Rip
}
if p != nil {
if reg.Bytes != nil && len(reg.Bytes) != 8 {
return false, fmt.Errorf("wrong number of bytes for register %s (%d)", regnum.AMD64ToName(regNum), len(reg.Bytes))
}
*p = reg.Uint64Val
return false, nil
}
if r.loadFpRegs != nil {
err := r.loadFpRegs(r)
if err != nil {
return false, err
}
r.loadFpRegs = nil
}
if regNum < regnum.AMD64_XMM0 || regNum > regnum.AMD64_XMM0+15 {
return false, fmt.Errorf("can not set %s", regnum.AMD64ToName(regNum))
}
reg.FillBytes()
err := r.Fpregset.SetXmmRegister(int(regNum-regnum.AMD64_XMM0), reg.Bytes)
if err != nil {
return false, err
}
return true, nil
}

@ -89,27 +89,28 @@ const fakeAddress = 0xbeef0000
// memory. // memory.
type compositeMemory struct { type compositeMemory struct {
realmem MemoryReadWriter realmem MemoryReadWriter
arch *Arch
regs op.DwarfRegisters regs op.DwarfRegisters
pieces []op.Piece pieces []op.Piece
data []byte data []byte
} }
func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []op.Piece) (*compositeMemory, error) { func newCompositeMemory(mem MemoryReadWriter, arch *Arch, regs op.DwarfRegisters, pieces []op.Piece) (*compositeMemory, error) {
cmem := &compositeMemory{realmem: mem, regs: regs, pieces: pieces, data: []byte{}} cmem := &compositeMemory{realmem: mem, arch: arch, regs: regs, pieces: pieces, data: []byte{}}
for _, piece := range pieces { for i := range pieces {
piece := &pieces[i]
if piece.IsRegister { if piece.IsRegister {
reg := regs.Bytes(piece.RegNum) reg := regs.Bytes(piece.RegNum)
sz := piece.Size if piece.Size == 0 && len(pieces) == 1 {
if sz == 0 && len(pieces) == 1 { piece.Size = len(reg)
sz = len(reg)
} }
if sz > len(reg) { if piece.Size > len(reg) {
if regs.FloatLoadError != nil { if regs.FloatLoadError != nil {
return nil, fmt.Errorf("could not read %d bytes from register %d (size: %d), also error loading floating point registers: %v", sz, piece.RegNum, len(reg), regs.FloatLoadError) return nil, fmt.Errorf("could not read %d bytes from register %d (size: %d), also error loading floating point registers: %v", piece.Size, piece.RegNum, len(reg), regs.FloatLoadError)
} }
return nil, fmt.Errorf("could not read %d bytes from register %d (size: %d)", sz, piece.RegNum, len(reg)) return nil, fmt.Errorf("could not read %d bytes from register %d (size: %d)", piece.Size, piece.RegNum, len(reg))
} }
cmem.data = append(cmem.data, reg[:sz]...) cmem.data = append(cmem.data, reg[:piece.Size]...)
} else { } else {
buf := make([]byte, piece.Size) buf := make([]byte, piece.Size)
mem.ReadMemory(buf, uint64(piece.Addr)) mem.ReadMemory(buf, uint64(piece.Addr))
@ -129,8 +130,41 @@ func (mem *compositeMemory) ReadMemory(data []byte, addr uint64) (int, error) {
} }
func (mem *compositeMemory) WriteMemory(addr uint64, data []byte) (int, error) { func (mem *compositeMemory) WriteMemory(addr uint64, data []byte) (int, error) {
//TODO(aarzilli): implement addr -= fakeAddress
return 0, errors.New("can't write composite memory") if addr >= uint64(len(mem.data)) || addr+uint64(len(data)) > uint64(len(mem.data)) {
return 0, errors.New("write out of bounds")
}
if mem.regs.ChangeFunc == nil {
return 0, errors.New("can not write registers")
}
copy(mem.data[addr:], data)
curAddr := uint64(0)
donesz := 0
for _, piece := range mem.pieces {
if curAddr < (addr+uint64(len(data))) && addr < (curAddr+uint64(piece.Size)) {
// changed memory interval overlaps current piece
pieceMem := mem.data[curAddr : curAddr+uint64(piece.Size)]
if piece.IsRegister {
err := mem.regs.ChangeFunc(piece.RegNum, op.DwarfRegisterFromBytes(pieceMem))
if err != nil {
return donesz, err
}
} else {
n, err := mem.realmem.WriteMemory(uint64(piece.Addr), pieceMem)
if err != nil {
return donesz + n, err
}
}
donesz += piece.Size
}
curAddr += uint64(piece.Size)
}
return len(data), nil
} }
// DereferenceMemory returns a MemoryReadWriter that can read and write the // DereferenceMemory returns a MemoryReadWriter that can read and write the

@ -6,6 +6,7 @@ import (
"errors" "errors"
"sync" "sync"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/proc" "github.com/go-delve/delve/pkg/proc"
) )
@ -82,17 +83,12 @@ func (dbp *nativeProcess) EntryPoint() (uint64, error) {
} }
// SetPC sets the value of the PC register. // SetPC sets the value of the PC register.
func (t *nativeThread) SetPC(pc uint64) error { func (t *nativeThread) setPC(pc uint64) error {
panic(ErrNativeBackendDisabled) panic(ErrNativeBackendDisabled)
} }
// SetSP sets the value of the SP register. // SetReg changes the value of the specified register.
func (t *nativeThread) SetSP(sp uint64) error { func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
panic(ErrNativeBackendDisabled)
}
// SetDX sets the value of the DX register.
func (t *nativeThread) SetDX(dx uint64) error {
panic(ErrNativeBackendDisabled) panic(ErrNativeBackendDisabled)
} }

@ -594,7 +594,7 @@ func (dbp *nativeProcess) stop(trapthread *nativeThread) (*nativeThread, error)
} }
if !isHardcodedBreakpoint { if !isHardcodedBreakpoint {
// phantom breakpoint hit // phantom breakpoint hit
_ = th.SetPC(pc - uint64(len(dbp.BinInfo().Arch.BreakpointInstruction()))) _ = th.setPC(pc - uint64(len(dbp.BinInfo().Arch.BreakpointInstruction())))
th.os.setbp = false th.os.setbp = false
if trapthread.ThreadID() == th.ThreadID() { if trapthread.ThreadID() == th.ThreadID() {
// Will switch to a different thread for trapthread because we don't // Will switch to a different thread for trapthread because we don't

@ -325,7 +325,7 @@ func (dbp *nativeProcess) waitForDebugEvent(flags waitForDebugEventFlags) (threa
} }
} }
if !atbp { if !atbp {
thread.SetPC(uint64(exception.ExceptionRecord.ExceptionAddress)) thread.setPC(uint64(exception.ExceptionRecord.ExceptionAddress))
} }
} }

@ -5,13 +5,15 @@ import (
sys "golang.org/x/sys/unix" sys "golang.org/x/sys/unix"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/dwarf/regnum"
"github.com/go-delve/delve/pkg/proc" "github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/amd64util" "github.com/go-delve/delve/pkg/proc/amd64util"
"github.com/go-delve/delve/pkg/proc/linutil" "github.com/go-delve/delve/pkg/proc/linutil"
) )
// SetPC sets EIP to the value specified by 'pc'. // setPC sets EIP to the value specified by 'pc'.
func (thread *nativeThread) SetPC(pc uint64) error { func (thread *nativeThread) setPC(pc uint64) error {
ir, err := registers(thread) ir, err := registers(thread)
if err != nil { if err != nil {
return err return err
@ -22,29 +24,26 @@ func (thread *nativeThread) SetPC(pc uint64) error {
return err return err
} }
// SetSP sets ESP to the value specified by 'sp' func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
func (thread *nativeThread) SetSP(sp uint64) (err error) { ir, err := registers(thread)
var ir proc.Registers
ir, err = registers(thread)
if err != nil { if err != nil {
return err return err
} }
r := ir.(*linutil.I386Registers) r := ir.(*linutil.I386Registers)
r.Regs.Esp = int32(sp) switch regNum {
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) }) case regnum.I386_Eip:
return r.Regs.Eip = int32(reg.Uint64Val)
} case regnum.I386_Esp:
r.Regs.Esp = int32(reg.Uint64Val)
func (thread *nativeThread) SetDX(dx uint64) (err error) { case regnum.I386_Edx:
var ir proc.Registers r.Regs.Edx = int32(reg.Uint64Val)
ir, err = registers(thread) default:
if err != nil { //TODO(aarzilli): when the register calling convention is adopted by Go on
return err // i386 this should be implemented.
return fmt.Errorf("changing register %d not implemented", regNum)
} }
r := ir.(*linutil.I386Registers)
r.Regs.Edx = int32(dx)
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) }) thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
return return err
} }
func registers(thread *nativeThread) (proc.Registers, error) { func registers(thread *nativeThread) (proc.Registers, error) {

@ -5,12 +5,13 @@ package native
// #include "threads_darwin.h" // #include "threads_darwin.h"
import "C" import "C"
import ( import (
"errors"
"fmt" "fmt"
"unsafe" "unsafe"
"golang.org/x/arch/x86/x86asm" "golang.org/x/arch/x86/x86asm"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/dwarf/regnum"
"github.com/go-delve/delve/pkg/proc" "github.com/go-delve/delve/pkg/proc"
) )
@ -111,7 +112,7 @@ func (r *Regs) GAddr() (uint64, bool) {
} }
// SetPC sets the RIP register to the value specified by `pc`. // SetPC sets the RIP register to the value specified by `pc`.
func (thread *nativeThread) SetPC(pc uint64) error { func (thread *nativeThread) setPC(pc uint64) error {
kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc)) kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc))
if kret != C.KERN_SUCCESS { if kret != C.KERN_SUCCESS {
return fmt.Errorf("could not set pc") return fmt.Errorf("could not set pc")
@ -119,13 +120,12 @@ func (thread *nativeThread) SetPC(pc uint64) error {
return nil return nil
} }
// SetSP sets the RSP register to the value specified by `pc`. // SetReg changes the value of the specified register.
func (thread *nativeThread) SetSP(sp uint64) error { func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
return errors.New("not implemented") if regNum != regnum.AMD64_Rip {
} return fmt.Errorf("changing register %d not implemented", regNum)
}
func (thread *nativeThread) SetDX(dx uint64) error { return thread.setPC(reg.Uint64Val)
return errors.New("not implemented")
} }
func (r *Regs) Get(n int) (uint64, error) { func (r *Regs) Get(n int) (uint64, error) {

@ -5,13 +5,15 @@ import (
sys "golang.org/x/sys/unix" sys "golang.org/x/sys/unix"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/dwarf/regnum"
"github.com/go-delve/delve/pkg/proc" "github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/amd64util" "github.com/go-delve/delve/pkg/proc/amd64util"
"github.com/go-delve/delve/pkg/proc/fbsdutil" "github.com/go-delve/delve/pkg/proc/fbsdutil"
) )
// SetPC sets RIP to the value specified by 'pc'. // SetPC sets RIP to the value specified by 'pc'.
func (thread *nativeThread) SetPC(pc uint64) error { func (thread *nativeThread) setPC(pc uint64) error {
ir, err := registers(thread) ir, err := registers(thread)
if err != nil { if err != nil {
return err return err
@ -22,27 +24,23 @@ func (thread *nativeThread) SetPC(pc uint64) error {
return err return err
} }
// SetSP sets RSP to the value specified by 'sp' // SetReg changes the value of the specified register.
func (thread *nativeThread) SetSP(sp uint64) (err error) { func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) (err error) {
var ir proc.Registers ir, err := registers(thread)
ir, err = registers(thread)
if err != nil { if err != nil {
return err return err
} }
r := ir.(*fbsdutil.AMD64Registers) r := ir.(*fbsdutil.AMD64Registers)
r.Regs.Rsp = int64(sp) switch regNum {
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.Reg)(r.Regs)) }) case regnum.AMD64_Rip:
return r.Regs.Rip = int64(reg.Uint64Val)
} case regnum.AMD64_Rsp:
r.Regs.Rsp = int64(reg.Uint64Val)
func (thread *nativeThread) SetDX(dx uint64) (err error) { case regnum.AMD64_Rdx:
var ir proc.Registers r.Regs.Rdx = int64(reg.Uint64Val)
ir, err = registers(thread) default:
if err != nil { return fmt.Errorf("changing register %d not implemented", regNum)
return err
} }
r := ir.(*fbsdutil.AMD64Registers)
r.Regs.Rdx = int64(dx)
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.Reg)(r.Regs)) }) thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.Reg)(r.Regs)) })
return return
} }

@ -2,16 +2,19 @@ package native
import ( import (
"fmt" "fmt"
"syscall"
"unsafe"
sys "golang.org/x/sys/unix" sys "golang.org/x/sys/unix"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/proc" "github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/amd64util" "github.com/go-delve/delve/pkg/proc/amd64util"
"github.com/go-delve/delve/pkg/proc/linutil" "github.com/go-delve/delve/pkg/proc/linutil"
) )
// SetPC sets RIP to the value specified by 'pc'. // SetPC sets RIP to the value specified by 'pc'.
func (thread *nativeThread) SetPC(pc uint64) error { func (thread *nativeThread) setPC(pc uint64) error {
ir, err := registers(thread) ir, err := registers(thread)
if err != nil { if err != nil {
return err return err
@ -22,29 +25,31 @@ func (thread *nativeThread) SetPC(pc uint64) error {
return err return err
} }
// SetSP sets RSP to the value specified by 'sp' // SetReg changes the value of the specified register.
func (thread *nativeThread) SetSP(sp uint64) (err error) { func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
var ir proc.Registers ir, err := registers(thread)
ir, err = registers(thread)
if err != nil { if err != nil {
return err return err
} }
r := ir.(*linutil.AMD64Registers) r := ir.(*linutil.AMD64Registers)
r.Regs.Rsp = sp fpchanged, err := r.SetReg(regNum, reg)
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
return
}
func (thread *nativeThread) SetDX(dx uint64) (err error) {
var ir proc.Registers
ir, err = registers(thread)
if err != nil { if err != nil {
return err return err
} }
r := ir.(*linutil.AMD64Registers) thread.dbp.execPtraceFunc(func() {
r.Regs.Rdx = dx err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs))
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) }) if err != nil {
return return
}
if fpchanged && r.Fpregset != nil && r.Fpregset.Xsave != nil {
iov := sys.Iovec{Base: &r.Fpregset.Xsave[0], Len: uint64(len(r.Fpregset.Xsave))}
_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(thread.ID), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0)
if err == syscall.Errno(0) {
err = nil
}
}
})
return err
} }
func registers(thread *nativeThread) (proc.Registers, error) { func registers(thread *nativeThread) (proc.Registers, error) {

@ -8,6 +8,8 @@ import (
sys "golang.org/x/sys/unix" sys "golang.org/x/sys/unix"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/dwarf/regnum"
"github.com/go-delve/delve/pkg/proc" "github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/linutil" "github.com/go-delve/delve/pkg/proc/linutil"
) )
@ -64,8 +66,8 @@ func ptraceGetFpRegset(tid int) (fpregset []byte, err error) {
return fpregset, err return fpregset, err
} }
// SetPC sets PC to the value specified by 'pc'. // setPC sets PC to the value specified by 'pc'.
func (thread *nativeThread) SetPC(pc uint64) error { func (thread *nativeThread) setPC(pc uint64) error {
ir, err := registers(thread) ir, err := registers(thread)
if err != nil { if err != nil {
return err return err
@ -76,21 +78,26 @@ func (thread *nativeThread) SetPC(pc uint64) error {
return err return err
} }
// SetSP sets RSP to the value specified by 'sp' func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
func (thread *nativeThread) SetSP(sp uint64) (err error) { ir, err := registers(thread)
var ir proc.Registers
ir, err = registers(thread)
if err != nil { if err != nil {
return err return err
} }
r := ir.(*linutil.ARM64Registers) r := ir.(*linutil.ARM64Registers)
r.Regs.Sp = sp
thread.dbp.execPtraceFunc(func() { err = ptraceSetGRegs(thread.ID, r.Regs) })
return
}
func (thread *nativeThread) SetDX(dx uint64) (err error) { switch regNum {
return fmt.Errorf("not supported") case regnum.ARM64_PC:
r.Regs.Pc = reg.Uint64Val
case regnum.ARM64_SP:
r.Regs.Sp = reg.Uint64Val
default:
//TODO(aarzilli): when the register calling convention is adopted by Go on
// arm64 this should be implemented.
return fmt.Errorf("changing register %d not implemented", regNum)
}
thread.dbp.execPtraceFunc(func() { err = ptraceSetGRegs(thread.ID, r.Regs) })
return err
} }
func registers(thread *nativeThread) (proc.Registers, error) { func registers(thread *nativeThread) (proc.Registers, error) {

@ -4,12 +4,14 @@ import (
"fmt" "fmt"
"unsafe" "unsafe"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/dwarf/regnum"
"github.com/go-delve/delve/pkg/proc" "github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/winutil" "github.com/go-delve/delve/pkg/proc/winutil"
) )
// SetPC sets the RIP register to the value specified by `pc`. // SetPC sets the RIP register to the value specified by `pc`.
func (thread *nativeThread) SetPC(pc uint64) error { func (thread *nativeThread) setPC(pc uint64) error {
context := winutil.NewCONTEXT() context := winutil.NewCONTEXT()
context.ContextFlags = _CONTEXT_ALL context.ContextFlags = _CONTEXT_ALL
@ -23,8 +25,8 @@ func (thread *nativeThread) SetPC(pc uint64) error {
return _SetThreadContext(thread.os.hThread, context) return _SetThreadContext(thread.os.hThread, context)
} }
// SetSP sets the RSP register to the value specified by `sp`. // SetReg changes the value of the specified register.
func (thread *nativeThread) SetSP(sp uint64) error { func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
context := winutil.NewCONTEXT() context := winutil.NewCONTEXT()
context.ContextFlags = _CONTEXT_ALL context.ContextFlags = _CONTEXT_ALL
@ -33,21 +35,60 @@ func (thread *nativeThread) SetSP(sp uint64) error {
return err return err
} }
context.Rsp = sp var p *uint64
return _SetThreadContext(thread.os.hThread, context) switch regNum {
} case regnum.AMD64_Rax:
p = &context.Rax
func (thread *nativeThread) SetDX(dx uint64) error { case regnum.AMD64_Rbx:
context := winutil.NewCONTEXT() p = &context.Rbx
context.ContextFlags = _CONTEXT_ALL case regnum.AMD64_Rcx:
p = &context.Rcx
err := _GetThreadContext(thread.os.hThread, context) case regnum.AMD64_Rdx:
if err != nil { p = &context.Rdx
return err case regnum.AMD64_Rsi:
p = &context.Rsi
case regnum.AMD64_Rdi:
p = &context.Rdi
case regnum.AMD64_Rbp:
p = &context.Rbp
case regnum.AMD64_Rsp:
p = &context.Rsp
case regnum.AMD64_R8:
p = &context.R8
case regnum.AMD64_R9:
p = &context.R9
case regnum.AMD64_R10:
p = &context.R10
case regnum.AMD64_R11:
p = &context.R11
case regnum.AMD64_R12:
p = &context.R12
case regnum.AMD64_R13:
p = &context.R13
case regnum.AMD64_R14:
p = &context.R14
case regnum.AMD64_R15:
p = &context.R15
case regnum.AMD64_Rip:
p = &context.Rip
} }
context.Rdx = dx if p != nil {
if reg.Bytes != nil && len(reg.Bytes) != 8 {
return fmt.Errorf("wrong number of bytes for register %s (%d)", regnum.AMD64ToName(regNum), len(reg.Bytes))
}
*p = reg.Uint64Val
} else {
if regNum < regnum.AMD64_XMM0 || regNum > regnum.AMD64_XMM0+15 {
return fmt.Errorf("can not set register %s", regnum.AMD64ToName(regNum))
}
reg.FillBytes()
if len(reg.Bytes) > 16 {
return fmt.Errorf("too many bytes when setting register %s", regnum.AMD64ToName(regNum))
}
copy(context.FltSave.XmmRegisters[(regNum-regnum.AMD64_XMM0)*16:], reg.Bytes)
}
return _SetThreadContext(thread.os.hThread, context) return _SetThreadContext(thread.os.hThread, context)
} }

@ -122,7 +122,7 @@ func (t *nativeThread) SetCurrentBreakpoint(adjustPC bool) error {
if bp, ok := t.dbp.FindBreakpoint(pc, adjustPC); ok { if bp, ok := t.dbp.FindBreakpoint(pc, adjustPC); ok {
if adjustPC { if adjustPC {
if err = t.SetPC(bp.Addr); err != nil { if err = t.setPC(bp.Addr); err != nil {
return err return err
} }
} }

@ -2,6 +2,7 @@ package proc_test
import ( import (
"bytes" "bytes"
"encoding/binary"
"flag" "flag"
"fmt" "fmt"
"go/ast" "go/ast"
@ -5169,3 +5170,72 @@ func TestDump(t *testing.T) {
} }
}) })
} }
func TestCompositeMemoryWrite(t *testing.T) {
if runtime.GOARCH != "amd64" {
t.Skip("only valid on amd64")
}
skipOn(t, "not implemented", "freebsd")
withTestProcess("fputest/", t, func(p *proc.Target, fixture protest.Fixture) {
getregs := func() (pc, rax, xmm1 uint64) {
regs, err := p.CurrentThread().Registers()
assertNoError(err, t, "Registers")
fmtregs, err := regs.Slice(true)
assertNoError(err, t, "register slice")
var xmm1buf []byte
for _, reg := range fmtregs {
switch strings.ToLower(reg.Name) {
case "rax":
rax = reg.Reg.Uint64Val
case "xmm1":
xmm1buf = reg.Reg.Bytes
}
}
xmm1 = binary.LittleEndian.Uint64(xmm1buf[:8])
return regs.PC(), rax, xmm1
}
getmem := func(mem proc.MemoryReader) uint64 {
buf := make([]byte, 8)
_, err := mem.ReadMemory(buf, 0xbeef0000)
assertNoError(err, t, "ReadMemory")
return binary.LittleEndian.Uint64(buf)
}
assertNoError(p.Continue(), t, "Continue()")
oldPc, oldRax, oldXmm1 := getregs()
t.Logf("PC %#x AX %#x XMM1 %#x", oldPc, oldRax, oldXmm1)
memRax, err := proc.NewCompositeMemory(p, []op.Piece{{Size: 0, RegNum: 0, IsRegister: true}})
assertNoError(err, t, "NewCompositeMemory (rax)")
memXmm1, err := proc.NewCompositeMemory(p, []op.Piece{{Size: 0, RegNum: 18, IsRegister: true}})
assertNoError(err, t, "NewCompositeMemory (xmm1)")
if memRax := getmem(memRax); memRax != oldRax {
t.Errorf("reading rax memory, expected %#x got %#x", oldRax, memRax)
}
if memXmm1 := getmem(memXmm1); memXmm1 != oldXmm1 {
t.Errorf("reading xmm1 memory, expected %#x got %#x", oldXmm1, memXmm1)
}
_, err = memRax.WriteMemory(0xbeef0000, []byte{0xef, 0xbe, 0x0d, 0xf0, 0xef, 0xbe, 0x0d, 0xf0})
assertNoError(err, t, "WriteMemory (rax)")
_, err = memXmm1.WriteMemory(0xbeef0000, []byte{0xef, 0xbe, 0x0d, 0xf0, 0xef, 0xbe, 0x0d, 0xf0})
assertNoError(err, t, "WriteMemory (xmm1)")
newPc, newRax, newXmm1 := getregs()
t.Logf("PC %#x AX %#x XMM1 %#x", newPc, newRax, newXmm1)
const tgt = 0xf00dbeeff00dbeef
if newRax != tgt {
t.Errorf("reading rax register, expected %#x, got %#x", uint64(tgt), newRax)
}
if newXmm1 != tgt {
t.Errorf("reading xmm1 register, expected %#x, got %#x", uint64(tgt), newXmm1)
}
})
}

@ -100,7 +100,9 @@ func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
return nil, err return nil, err
} }
so := thread.BinInfo().PCToImage(regs.PC()) so := thread.BinInfo().PCToImage(regs.PC())
it := newStackIterator(thread.BinInfo(), thread.ProcessMemory(), thread.BinInfo().Arch.RegistersToDwarfRegisters(so.StaticBase, regs), 0, nil, -1, nil, 0) dwarfRegs := thread.BinInfo().Arch.RegistersToDwarfRegisters(so.StaticBase, regs)
dwarfRegs.ChangeFunc = thread.SetReg
it := newStackIterator(thread.BinInfo(), thread.ProcessMemory(), dwarfRegs, 0, nil, -1, nil, 0)
return it.stacktrace(depth) return it.stacktrace(depth)
} }
return g.Stacktrace(depth, 0) return g.Stacktrace(depth, 0)
@ -119,9 +121,11 @@ func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) {
return nil, err return nil, err
} }
so := bi.PCToImage(regs.PC()) so := bi.PCToImage(regs.PC())
dwarfRegs := bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs)
dwarfRegs.ChangeFunc = g.Thread.SetReg
return newStackIterator( return newStackIterator(
bi, g.variable.mem, bi, g.variable.mem,
bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs), dwarfRegs,
g.stack.hi, stkbar, g.stkbarPos, g, opts), nil g.stack.hi, stkbar, g.stkbarPos, g, opts), nil
} }
so := g.variable.bi.PCToImage(g.PC) so := g.variable.bi.PCToImage(g.PC)

@ -127,7 +127,7 @@ func (dbp *Target) Continue() error {
// In linux-arm64, PtraceSingleStep seems cannot step over BRK instruction // In linux-arm64, PtraceSingleStep seems cannot step over BRK instruction
// (linux-arm64 feature or kernel bug maybe). // (linux-arm64 feature or kernel bug maybe).
if !arch.BreakInstrMovesPC() { if !arch.BreakInstrMovesPC() {
curthread.SetPC(loc.PC + uint64(arch.BreakpointSize())) setPC(curthread, loc.PC+uint64(arch.BreakpointSize()))
} }
// Single-step current thread until we exit runtime.breakpoint and // Single-step current thread until we exit runtime.breakpoint and
// runtime.Breakpoint. // runtime.Breakpoint.
@ -145,7 +145,7 @@ func (dbp *Target) Continue() error {
bp := make([]byte, bpsize) bp := make([]byte, bpsize)
_, err = dbp.Memory().ReadMemory(bp, loc.PC) _, err = dbp.Memory().ReadMemory(bp, loc.PC)
if bytes.Equal(bp, arch.BreakpointInstruction()) { if bytes.Equal(bp, arch.BreakpointInstruction()) {
curthread.SetPC(loc.PC + uint64(bpsize)) setPC(curthread, loc.PC+uint64(bpsize))
} }
} }
return conditionErrors(threads) return conditionErrors(threads)

@ -2,6 +2,8 @@ package proc
import ( import (
"errors" "errors"
"github.com/go-delve/delve/pkg/dwarf/op"
) )
// Thread represents a thread. // Thread represents a thread.
@ -31,9 +33,10 @@ type Thread interface {
// Common returns the CommonThread structure for this thread // Common returns the CommonThread structure for this thread
Common() *CommonThread Common() *CommonThread
SetPC(uint64) error // SetReg changes the value of the specified register. A minimal
SetSP(uint64) error // implementation of this interface can support just setting the PC
SetDX(uint64) error // register.
SetReg(uint64, *op.DwarfRegister) error
} }
// Location represents the location of a thread. // Location represents the location of a thread.
@ -83,3 +86,15 @@ func topframe(g *G, thread Thread) (Stackframe, Stackframe, error) {
return frames[0], frames[1], nil return frames[0], frames[1], nil
} }
} }
func setPC(thread Thread, newPC uint64) error {
return thread.SetReg(thread.BinInfo().Arch.PCRegNum, op.DwarfRegisterFromUint64(newPC))
}
func setSP(thread Thread, newSP uint64) error {
return thread.SetReg(thread.BinInfo().Arch.SPRegNum, op.DwarfRegisterFromUint64(newSP))
}
func setClosureReg(thread Thread, newClosureReg uint64) error {
return thread.SetReg(thread.BinInfo().Arch.ContextRegNum, op.DwarfRegisterFromUint64(newClosureReg))
}

@ -1147,7 +1147,7 @@ func extractVarInfoFromEntry(bi *BinaryInfo, image *Image, regs op.DwarfRegister
if pieces != nil { if pieces != nil {
addr = fakeAddress addr = fakeAddress
var cmem *compositeMemory var cmem *compositeMemory
cmem, err = newCompositeMemory(mem, regs, pieces) cmem, err = newCompositeMemory(mem, bi.Arch, regs, pieces)
if cmem != nil { if cmem != nil {
mem = cmem mem = cmem
} }