diff --git a/Documentation/backend_test_health.md b/Documentation/backend_test_health.md index 5807bfd6..400a9588 100644 --- a/Documentation/backend_test_health.md +++ b/Documentation/backend_test_health.md @@ -1,20 +1,21 @@ Tests skipped by each supported backend: -* 386 skipped = 2.7% (4/147) +* 386 skipped = 2.7% (4/148) * 1 broken * 3 broken - cgo stacktraces -* arm64 skipped = 2.7% (4/147) +* arm64 skipped = 2.7% (4/148) * 2 broken * 1 broken - cgo stacktraces * 1 broken - global variable symbolication -* darwin/lldb skipped = 0.68% (1/147) +* darwin/lldb skipped = 0.68% (1/148) * 1 upstream issue -* freebsd skipped = 7.5% (11/147) +* freebsd skipped = 8.1% (12/148) * 11 broken -* linux/386/pie skipped = 0.68% (1/147) + * 1 not implemented +* linux/386/pie skipped = 0.68% (1/148) * 1 broken -* pie skipped = 0.68% (1/147) +* pie skipped = 0.68% (1/148) * 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 upstream issue diff --git a/pkg/dwarf/op/regs.go b/pkg/dwarf/op/regs.go index 3e4b6b6b..88dcf23f 100644 --- a/pkg/dwarf/op/regs.go +++ b/pkg/dwarf/op/regs.go @@ -14,11 +14,12 @@ type DwarfRegisters struct { ObjBase int64 regs []*DwarfRegister - ByteOrder binary.ByteOrder - PCRegNum uint64 - SPRegNum uint64 - BPRegNum uint64 - LRRegNum uint64 + ByteOrder binary.ByteOrder + PCRegNum uint64 + SPRegNum uint64 + BPRegNum uint64 + LRRegNum uint64 + ChangeFunc RegisterChangeFunc FloatLoadError error // error produced when loading floating point registers loadMoreCallback func() @@ -29,6 +30,8 @@ type DwarfRegister struct { Bytes []byte } +type RegisterChangeFunc func(regNum uint64, reg *DwarfRegister) error + // NewDwarfRegisters returns a new DwarfRegisters object. func NewDwarfRegisters(staticBase uint64, regs []*DwarfRegister, byteOrder binary.ByteOrder, pcRegNum, spRegNum, bpRegNum, lrRegNum uint64) *DwarfRegisters { return &DwarfRegisters{ @@ -152,3 +155,12 @@ func DwarfRegisterFromBytes(bytes []byte) *DwarfRegister { } 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) +} diff --git a/pkg/dwarf/regnum/amd64.go b/pkg/dwarf/regnum/amd64.go new file mode 100644 index 00000000..2adfb5c6 --- /dev/null +++ b/pkg/dwarf/regnum/amd64.go @@ -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) +} diff --git a/pkg/dwarf/regnum/arm64.go b/pkg/dwarf/regnum/arm64.go new file mode 100644 index 00000000..13b15c1e --- /dev/null +++ b/pkg/dwarf/regnum/arm64.go @@ -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) + } +} diff --git a/pkg/dwarf/regnum/i386.go b/pkg/dwarf/regnum/i386.go new file mode 100644 index 00000000..ab722700 --- /dev/null +++ b/pkg/dwarf/regnum/i386.go @@ -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) +} diff --git a/pkg/proc/amd64_arch.go b/pkg/proc/amd64_arch.go index 21805c5a..6a20bbfe 100644 --- a/pkg/proc/amd64_arch.go +++ b/pkg/proc/amd64_arch.go @@ -10,12 +10,7 @@ import ( "github.com/go-delve/delve/pkg/dwarf/frame" "github.com/go-delve/delve/pkg/dwarf/op" -) - -const ( - amd64DwarfIPRegNum uint64 = 16 - amd64DwarfSPRegNum uint64 = 7 - amd64DwarfBPRegNum uint64 = 6 + "github.com/go-delve/delve/pkg/dwarf/regnum" ) var amd64BreakInstruction = []byte{0xCC} @@ -39,6 +34,10 @@ func AMD64Arch(goos string) *Arch { DwarfRegisterToString: amd64DwarfRegisterToString, inhibitStepInto: func(*BinaryInfo, uint64) bool { return false }, 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). return &frame.FrameContext{ - RetAddrReg: amd64DwarfIPRegNum, + RetAddrReg: regnum.AMD64_Rip, Regs: map[uint64]frame.DWRule{ - amd64DwarfIPRegNum: { + regnum.AMD64_Rip: { Rule: frame.RuleOffset, Offset: int64(-a.PtrSize()), }, - amd64DwarfBPRegNum: { + regnum.AMD64_Rbp: { Rule: frame.RuleOffset, Offset: int64(-2 * a.PtrSize()), }, - amd64DwarfSPRegNum: { + regnum.AMD64_Rsp: { Rule: frame.RuleValOffset, Offset: 0, }, }, CFA: frame.DWRule{ Rule: frame.RuleCFA, - Reg: amd64DwarfBPRegNum, + Reg: regnum.AMD64_Rbp, 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 // without descriptor entries. // If there isn't a rule already we emit one. - if fctxt.Regs[amd64DwarfBPRegNum].Rule == frame.RuleUndefined { - fctxt.Regs[amd64DwarfBPRegNum] = frame.DWRule{ + if fctxt.Regs[regnum.AMD64_Rbp].Rule == frame.RuleUndefined { + fctxt.Regs[regnum.AMD64_Rbp] = frame.DWRule{ Rule: frame.RuleFramePointer, - Reg: amd64DwarfBPRegNum, + Reg: regnum.AMD64_Rbp, Offset: 0, } } @@ -244,110 +243,22 @@ func amd64SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool { // in the System V ABI AMD64 Architecture Processor Supplement page 57, // figure 3.36 // https://www.uclibc.org/docs/psABI-x86_64.pdf -func amd64RegSize(regnum uint64) int { +func amd64RegSize(rn uint64) int { // XMM registers - if regnum > amd64DwarfIPRegNum && regnum <= 32 { + if rn > regnum.AMD64_Rip && rn <= 32 { return 16 } // x87 registers - if regnum >= 33 && regnum <= 40 { + if rn >= 33 && rn <= 40 { return 10 } 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 { - dregs := initDwarfRegistersFromSlice(maxAmd64DwarfRegister(), regs, amd64NameToDwarf) - dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, amd64DwarfIPRegNum, amd64DwarfSPRegNum, amd64DwarfBPRegNum, 0) - dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, amd64NameToDwarf)) + dregs := initDwarfRegistersFromSlice(int(regnum.AMD64MaxRegNum()), regs, regnum.AMD64NameToDwarf) + dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.AMD64_Rip, regnum.AMD64_Rsp, regnum.AMD64_Rbp, 0) + dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, regnum.AMD64NameToDwarf)) return *dr } @@ -389,18 +300,19 @@ func loadMoreDwarfRegistersFromSliceFunc(dr *op.DwarfRegisters, regs Registers, } func amd64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { - dregs := make([]*op.DwarfRegister, amd64DwarfIPRegNum+1) - dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc) - dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp) - dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp) + dregs := make([]*op.DwarfRegister, regnum.AMD64_Rip+1) + dregs[regnum.AMD64_Rip] = op.DwarfRegisterFromUint64(pc) + dregs[regnum.AMD64_Rsp] = op.DwarfRegisterFromUint64(sp) + 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) { - name, ok := amd64DwarfToName[i] - if !ok { - name = fmt.Sprintf("unknown%d", i) + name = regnum.AMD64ToName(uint64(i)) + + if reg == nil { + return name, false, "" } switch n := strings.ToLower(name); n { diff --git a/pkg/proc/amd64util/xsave.go b/pkg/proc/amd64util/xsave.go index b90bb3e4..16e621fd 100644 --- a/pkg/proc/amd64util/xsave.go +++ b/pkg/proc/amd64util/xsave.go @@ -74,6 +74,7 @@ func (xsave *AMD64Xstate) Decode() []proc.Register { const ( _XSTATE_MAX_KNOWN_SIZE = 2969 + _XSAVE_XMM_REGION_START = 160 _XSAVE_HEADER_START = 512 _XSAVE_HEADER_LEN = 64 _XSAVE_EXTENDED_REGION_START = 576 @@ -129,3 +130,61 @@ func AMD64XstateRead(xstateargs []byte, readLegacy bool, regset *AMD64Xstate) er 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 +} diff --git a/pkg/proc/arch.go b/pkg/proc/arch.go index 732b8704..39d2ec23 100644 --- a/pkg/proc/arch.go +++ b/pkg/proc/arch.go @@ -17,6 +17,10 @@ type Arch struct { breakInstrMovesPC bool derefTLS bool 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. // 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 // PC, SP, and BP registers in the format used by the DWARF expression interpreter. 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) // inhibitStepInto returns whether StepBreakpoint can be set at pc. inhibitStepInto func(bi *BinaryInfo, pc uint64) bool diff --git a/pkg/proc/arm64_arch.go b/pkg/proc/arm64_arch.go index 49b9395a..7952ab04 100644 --- a/pkg/proc/arm64_arch.go +++ b/pkg/proc/arm64_arch.go @@ -8,16 +8,10 @@ import ( "github.com/go-delve/delve/pkg/dwarf/frame" "github.com/go-delve/delve/pkg/dwarf/op" + "github.com/go-delve/delve/pkg/dwarf/regnum" "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} // ARM64Arch returns an initialized ARM64 @@ -40,6 +34,8 @@ func ARM64Arch(goos string) *Arch { inhibitStepInto: func(*BinaryInfo, uint64) bool { return false }, asmDecode: arm64AsmDecode, usesLR: true, + PCRegNum: regnum.ARM64_PC, + SPRegNum: regnum.ARM64_SP, } } @@ -69,24 +65,24 @@ func arm64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary // here). return &frame.FrameContext{ - RetAddrReg: arm64DwarfIPRegNum, + RetAddrReg: regnum.ARM64_PC, Regs: map[uint64]frame.DWRule{ - arm64DwarfIPRegNum: frame.DWRule{ + regnum.ARM64_PC: frame.DWRule{ Rule: frame.RuleOffset, Offset: int64(-a.PtrSize()), }, - arm64DwarfBPRegNum: frame.DWRule{ + regnum.ARM64_BP: frame.DWRule{ Rule: frame.RuleOffset, Offset: int64(-2 * a.PtrSize()), }, - arm64DwarfSPRegNum: frame.DWRule{ + regnum.ARM64_SP: frame.DWRule{ Rule: frame.RuleValOffset, Offset: 0, }, }, CFA: frame.DWRule{ Rule: frame.RuleCFA, - Reg: arm64DwarfBPRegNum, + Reg: regnum.ARM64_BP, 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 // without descriptor entries. // If there isn't a rule already we emit one. - if fctxt.Regs[arm64DwarfBPRegNum].Rule == frame.RuleUndefined { - fctxt.Regs[arm64DwarfBPRegNum] = frame.DWRule{ + if fctxt.Regs[regnum.ARM64_BP].Rule == frame.RuleUndefined { + fctxt.Regs[regnum.ARM64_BP] = frame.DWRule{ Rule: frame.RuleFramePointer, - Reg: arm64DwarfBPRegNum, + Reg: regnum.ARM64_BP, Offset: 0, } } - if fctxt.Regs[arm64DwarfLRRegNum].Rule == frame.RuleUndefined { - fctxt.Regs[arm64DwarfLRRegNum] = frame.DWRule{ + if fctxt.Regs[regnum.ARM64_LR].Rule == frame.RuleUndefined { + fctxt.Regs[regnum.ARM64_LR] = frame.DWRule{ Rule: frame.RuleFramePointer, - Reg: arm64DwarfLRRegNum, + Reg: regnum.ARM64_LR, Offset: 0, } } @@ -317,8 +313,8 @@ var arm64NameToDwarf = func() map[string]int { for i := 0; i <= 30; i++ { r[fmt.Sprintf("x%d", i)] = i } - r["pc"] = int(arm64DwarfIPRegNum) - r["lr"] = int(arm64DwarfLRRegNum) + r["pc"] = int(regnum.ARM64_PC) + r["lr"] = int(regnum.ARM64_LR) r["sp"] = 31 for i := 0; i <= 31; i++ { r[fmt.Sprintf("v%d", i)] = i + 64 @@ -327,7 +323,7 @@ var arm64NameToDwarf = func() map[string]int { }() func maxArm64DwarfRegister() int { - max := int(arm64DwarfIPRegNum) + max := int(regnum.ARM64_PC) for i := range arm64DwarfToHardware { if i > max { max = i @@ -339,11 +335,11 @@ func maxArm64DwarfRegister() int { func arm64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters { dregs := make([]*op.DwarfRegister, maxArm64DwarfRegister()+1) - dregs[arm64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC()) - dregs[arm64DwarfSPRegNum] = op.DwarfRegisterFromUint64(regs.SP()) - dregs[arm64DwarfBPRegNum] = op.DwarfRegisterFromUint64(regs.BP()) + dregs[regnum.ARM64_PC] = op.DwarfRegisterFromUint64(regs.PC()) + dregs[regnum.ARM64_SP] = op.DwarfRegisterFromUint64(regs.SP()) + dregs[regnum.ARM64_BP] = op.DwarfRegisterFromUint64(regs.BP()) 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 { @@ -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)) return *dr } func arm64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { - dregs := make([]*op.DwarfRegister, arm64DwarfIPRegNum+1) - dregs[arm64DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc) - dregs[arm64DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp) - dregs[arm64DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp) - dregs[arm64DwarfLRRegNum] = op.DwarfRegisterFromUint64(lr) + dregs := make([]*op.DwarfRegister, regnum.ARM64_PC+1) + dregs[regnum.ARM64_PC] = op.DwarfRegisterFromUint64(pc) + dregs[regnum.ARM64_SP] = op.DwarfRegisterFromUint64(sp) + dregs[regnum.ARM64_BP] = op.DwarfRegisterFromUint64(bp) + 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) { - // see arm64DwarfToHardware table for explanation - switch { - case i <= 30: - name = fmt.Sprintf("X%d", i) - 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) + name = regnum.ARM64ToName(uint64(i)) + + if reg == nil { + return name, false, "" } if reg.Bytes != nil && name[0] == 'V' { diff --git a/pkg/proc/core/core.go b/pkg/proc/core/core.go index 4855dbb7..c586daae 100644 --- a/pkg/proc/core/core.go +++ b/pkg/proc/core/core.go @@ -6,6 +6,7 @@ import ( "io" "github.com/go-delve/delve/pkg/elfwriter" + "github.com/go-delve/delve/pkg/dwarf/op" "github.com/go-delve/delve/pkg/proc" ) @@ -372,6 +373,12 @@ func (t *thread) SetDX(uint64) error { 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. func (p *process) Breakpoints() *proc.BreakpointMap { return &p.breakpoints diff --git a/pkg/proc/dwarf_export_test.go b/pkg/proc/dwarf_export_test.go index de8f505a..57f723fc 100644 --- a/pkg/proc/dwarf_export_test.go +++ b/pkg/proc/dwarf_export_test.go @@ -1,6 +1,21 @@ package proc +import "github.com/go-delve/delve/pkg/dwarf/op" + // PackageVars returns bi.packageVars (for tests) func (bi *BinaryInfo) PackageVars() []packageVar { 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) +} diff --git a/pkg/proc/dwarf_expr_test.go b/pkg/proc/dwarf_expr_test.go index 0ce6c83e..c7935dac 100644 --- a/pkg/proc/dwarf_expr_test.go +++ b/pkg/proc/dwarf_expr_test.go @@ -77,8 +77,17 @@ func (mem *fakeMemory) ReadMemory(data []byte, addr uint64) (int, error) { return len(data), nil } -func (mem *fakeMemory) WriteMemory(uint64, []byte) (int, error) { - return 0, fmt.Errorf("not implemented") +func (mem *fakeMemory) WriteMemory(addr uint64, data []byte) (int, error) { + 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) { @@ -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 { - scope := &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: fn}, Regs: regs, Mem: mem, BinInfo: bi} +func fakeScope(mem proc.MemoryReadWriter, regs op.DwarfRegisters, bi *proc.BinaryInfo, fn *proc.Function) *proc.EvalScope { + 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 { uintExprCheck(t, scope, name, uint64(value)) } - - return scope } 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.Rdx = uint64(testCases["c"]) - dwarfExprCheck(t, mem, dwarfRegisters(bi, ®s), bi, testCases, mainfn) + dwarfExprCheck(t, fakeScope(mem, dwarfRegisters(bi, ®s), bi, mainfn), testCases) } func TestDwarfExprComposite(t *testing.T) { @@ -192,7 +202,17 @@ func TestDwarfExprComposite(t *testing.T) { regs.Regs.Rcx = uint64(testCases["pair.k"]) regs.Regs.Rbx = uint64(testCases["n"]) - scope := dwarfExprCheck(t, mem, dwarfRegisters(bi, ®s), bi, testCases, mainfn) + dwarfRegs := dwarfRegisters(bi, ®s) + 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) 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) } } + + // 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) { diff --git a/pkg/proc/fncall.go b/pkg/proc/fncall.go index e5422a46..026ea39d 100644 --- a/pkg/proc/fncall.go +++ b/pkg/proc/fncall.go @@ -421,13 +421,13 @@ func callOP(bi *BinaryInfo, thread Thread, regs Registers, callAddr uint64) erro sp := regs.SP() // push PC on the stack sp -= uint64(bi.Arch.PtrSize()) - if err := thread.SetSP(sp); err != nil { + if err := setSP(thread, sp); err != nil { return err } if err := writePointer(bi, thread.ProcessMemory(), sp, regs.PC()); err != nil { return err } - return thread.SetPC(callAddr) + return setPC(thread, callAddr) } // 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 { // When calling a function pointer we must set the DX register to the // address of the function pointer itself. - thread.SetDX(fncall.closureAddr) + setClosureReg(thread, fncall.closureAddr) } cfa := regs.SP() oldpc := regs.PC() @@ -770,8 +770,8 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread err := funcCallEvalArgs(callScope, fncall, cfa) if err != nil { // rolling back the call, note: this works because we called regs.Copy() above - thread.SetSP(cfa) - thread.SetPC(oldpc) + setSP(thread, cfa) + setPC(thread, oldpc) fncall.err = err fncall.lateCallFailure = true break @@ -784,10 +784,10 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread if err := thread.RestoreRegisters(fncall.savedRegs); err != nil { 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) } - if err := thread.SetSP(sp); err != nil { + if err := setSP(thread, sp); err != nil { fncall.err = fmt.Errorf("could not restore SP: %v", err) } if err := stepInstructionOut(p, thread, debugCallFunctionName, debugCallFunctionName); err != nil { diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go index b997e478..d7c800bf 100644 --- a/pkg/proc/gdbserial/gdbserver.go +++ b/pkg/proc/gdbserial/gdbserver.go @@ -80,6 +80,7 @@ import ( "golang.org/x/arch/arm64/arm64asm" "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/logflags" "github.com/go-delve/delve/pkg/proc" @@ -1744,7 +1745,7 @@ func (t *gdbThread) SetCurrentBreakpoint(adjustPC bool) error { pc := regs.PC() if bp, ok := t.p.FindBreakpoint(pc); ok { if t.regs.PC() != bp.Addr { - if err := t.SetPC(bp.Addr); err != nil { + if err := t.setPC(bp.Addr); err != nil { return err } } @@ -1770,13 +1771,6 @@ func (regs *gdbRegisters) setPC(value uint64) { func (regs *gdbRegisters) SP() uint64 { 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 { 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. -func (t *gdbThread) SetPC(pc uint64) error { +func (t *gdbThread) setPC(pc uint64) error { _, _ = t.Registers() // Registes must be loaded first t.regs.setPC(pc) 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) } -// SetSP will set the value of the SP register to the given value. -func (t *gdbThread) SetSP(sp uint64) error { - _, _ = t.Registers() // Registes must be loaded first - t.regs.setSP(sp) - if t.p.gcmdok { - return t.p.conn.writeRegisters(t.strID, t.regs.buf) +// SetReg will change the value of a list of registers +func (t *gdbThread) SetReg(regNum uint64, reg *op.DwarfRegister) error { + regName, _, _ := t.p.bi.Arch.DwarfRegisterToString(int(regNum), nil) + regName = strings.ToLower(regName) + _, _ = t.Registers() // Registers must be loaded first + 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] - return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value) -} - -// 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") + if !ok { + return fmt.Errorf("could not set register %s: not found", regName) } - - _, _ = t.Registers() // Registes must be loaded first - t.regs.setDX(dx) - if t.p.gcmdok { - return t.p.conn.writeRegisters(t.strID, t.regs.buf) + reg.FillBytes() + if len(reg.Bytes) != len(gdbreg.value) { + return fmt.Errorf("could not set register %s: wrong size, expected %d got %d", regName, len(gdbreg.value), len(reg.Bytes)) } - reg := t.regs.regs[regnameDX] - return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value) + copy(gdbreg.value, reg.Bytes) + return t.p.conn.writeRegister(t.strID, gdbreg.regnum, gdbreg.value) } func (regs *gdbRegisters) Slice(floatingPoint bool) ([]proc.Register, error) { diff --git a/pkg/proc/i386_arch.go b/pkg/proc/i386_arch.go index f22787d1..bf22d359 100644 --- a/pkg/proc/i386_arch.go +++ b/pkg/proc/i386_arch.go @@ -3,15 +3,11 @@ package proc import ( "encoding/binary" "fmt" + "strings" + "github.com/go-delve/delve/pkg/dwarf/frame" "github.com/go-delve/delve/pkg/dwarf/op" - "strings" -) - -const ( - i386DwarfIPRegNum uint64 = 8 - i386DwarfSPRegNum uint64 = 4 - i386DwarfBPRegNum uint64 = 5 + "github.com/go-delve/delve/pkg/dwarf/regnum" ) var i386BreakInstruction = []byte{0xCC} @@ -36,6 +32,8 @@ func I386Arch(goos string) *Arch { DwarfRegisterToString: i386DwarfRegisterToString, inhibitStepInto: i386InhibitStepInto, asmDecode: i386AsmDecode, + PCRegNum: regnum.I386_Eip, + SPRegNum: regnum.I386_Esp, } } @@ -65,24 +63,24 @@ func i386FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryI // here). return &frame.FrameContext{ - RetAddrReg: i386DwarfIPRegNum, + RetAddrReg: regnum.I386_Eip, Regs: map[uint64]frame.DWRule{ - i386DwarfIPRegNum: frame.DWRule{ + regnum.I386_Eip: frame.DWRule{ Rule: frame.RuleOffset, Offset: int64(-i.PtrSize()), }, - i386DwarfBPRegNum: frame.DWRule{ + regnum.I386_Ebp: frame.DWRule{ Rule: frame.RuleOffset, Offset: int64(-2 * i.PtrSize()), }, - i386DwarfSPRegNum: frame.DWRule{ + regnum.I386_Esp: frame.DWRule{ Rule: frame.RuleValOffset, Offset: 0, }, }, CFA: frame.DWRule{ Rule: frame.RuleCFA, - Reg: i386DwarfBPRegNum, + Reg: regnum.I386_Ebp, 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 // without descriptor entries. // If there isn't i rule already we emit one. - if fctxt.Regs[i386DwarfBPRegNum].Rule == frame.RuleUndefined { - fctxt.Regs[i386DwarfBPRegNum] = frame.DWRule{ + if fctxt.Regs[regnum.I386_Ebp].Rule == frame.RuleUndefined { + fctxt.Regs[regnum.I386_Ebp] = frame.DWRule{ Rule: frame.RuleFramePointer, - Reg: i386DwarfBPRegNum, + Reg: regnum.I386_Ebp, Offset: 0, } } @@ -186,94 +184,28 @@ func i386RegSize(regnum uint64) int { 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 { - dregs := initDwarfRegistersFromSlice(maxI386DwarfRegister(), regs, i386NameToDwarf) - dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, i386DwarfIPRegNum, i386DwarfSPRegNum, i386DwarfBPRegNum, 0) - dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, i386NameToDwarf)) + dregs := initDwarfRegistersFromSlice(regnum.I386MaxRegNum(), regs, regnum.I386NameToDwarf) + dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.I386_Eip, regnum.I386_Esp, regnum.I386_Ebp, 0) + dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, regnum.I386NameToDwarf)) return *dr } func i386AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { - dregs := make([]*op.DwarfRegister, i386DwarfIPRegNum+1) - dregs[i386DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc) - dregs[i386DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp) - dregs[i386DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp) + dregs := make([]*op.DwarfRegister, regnum.I386_Eip+1) + dregs[regnum.I386_Eip] = op.DwarfRegisterFromUint64(pc) + dregs[regnum.I386_Esp] = op.DwarfRegisterFromUint64(sp) + 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) { - name, ok := i386DwarfToName[j] - if !ok { - name = fmt.Sprintf("unknown%d", j) + name = regnum.I386ToName(j) + + if reg == nil { + return name, false, "" } switch n := strings.ToLower(name); n { diff --git a/pkg/proc/linutil/regs_amd64_arch.go b/pkg/proc/linutil/regs_amd64_arch.go index 6ea4bbc9..6d6f0cf4 100644 --- a/pkg/proc/linutil/regs_amd64_arch.go +++ b/pkg/proc/linutil/regs_amd64_arch.go @@ -1,8 +1,12 @@ package linutil import ( + "fmt" + "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/amd64util" ) @@ -307,3 +311,71 @@ func (r *AMD64Registers) Copy() (proc.Registers, error) { } 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 +} diff --git a/pkg/proc/mem.go b/pkg/proc/mem.go index 4a795cbf..70ff9434 100644 --- a/pkg/proc/mem.go +++ b/pkg/proc/mem.go @@ -89,27 +89,28 @@ const fakeAddress = 0xbeef0000 // memory. type compositeMemory struct { realmem MemoryReadWriter + arch *Arch regs op.DwarfRegisters pieces []op.Piece data []byte } -func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []op.Piece) (*compositeMemory, error) { - cmem := &compositeMemory{realmem: mem, regs: regs, pieces: pieces, data: []byte{}} - for _, piece := range pieces { +func newCompositeMemory(mem MemoryReadWriter, arch *Arch, regs op.DwarfRegisters, pieces []op.Piece) (*compositeMemory, error) { + cmem := &compositeMemory{realmem: mem, arch: arch, regs: regs, pieces: pieces, data: []byte{}} + for i := range pieces { + piece := &pieces[i] if piece.IsRegister { reg := regs.Bytes(piece.RegNum) - sz := piece.Size - if sz == 0 && len(pieces) == 1 { - sz = len(reg) + if piece.Size == 0 && len(pieces) == 1 { + piece.Size = len(reg) } - if sz > len(reg) { + if piece.Size > len(reg) { 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 { buf := make([]byte, piece.Size) 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) { - //TODO(aarzilli): implement - return 0, errors.New("can't write composite memory") + addr -= fakeAddress + 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 diff --git a/pkg/proc/native/nonative_darwin.go b/pkg/proc/native/nonative_darwin.go index 9531239c..95614cc3 100644 --- a/pkg/proc/native/nonative_darwin.go +++ b/pkg/proc/native/nonative_darwin.go @@ -6,6 +6,7 @@ import ( "errors" "sync" + "github.com/go-delve/delve/pkg/dwarf/op" "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. -func (t *nativeThread) SetPC(pc uint64) error { +func (t *nativeThread) setPC(pc uint64) error { panic(ErrNativeBackendDisabled) } -// SetSP sets the value of the SP register. -func (t *nativeThread) SetSP(sp uint64) error { - panic(ErrNativeBackendDisabled) -} - -// SetDX sets the value of the DX register. -func (t *nativeThread) SetDX(dx uint64) error { +// SetReg changes the value of the specified register. +func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error { panic(ErrNativeBackendDisabled) } diff --git a/pkg/proc/native/proc_linux.go b/pkg/proc/native/proc_linux.go index 78e92344..3bed3a9a 100644 --- a/pkg/proc/native/proc_linux.go +++ b/pkg/proc/native/proc_linux.go @@ -594,7 +594,7 @@ func (dbp *nativeProcess) stop(trapthread *nativeThread) (*nativeThread, error) } if !isHardcodedBreakpoint { // 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 if trapthread.ThreadID() == th.ThreadID() { // Will switch to a different thread for trapthread because we don't diff --git a/pkg/proc/native/proc_windows.go b/pkg/proc/native/proc_windows.go index baba658b..dbad2370 100644 --- a/pkg/proc/native/proc_windows.go +++ b/pkg/proc/native/proc_windows.go @@ -325,7 +325,7 @@ func (dbp *nativeProcess) waitForDebugEvent(flags waitForDebugEventFlags) (threa } } if !atbp { - thread.SetPC(uint64(exception.ExceptionRecord.ExceptionAddress)) + thread.setPC(uint64(exception.ExceptionRecord.ExceptionAddress)) } } diff --git a/pkg/proc/native/register_linux_386.go b/pkg/proc/native/register_linux_386.go index 72bfd90e..906b34fc 100644 --- a/pkg/proc/native/register_linux_386.go +++ b/pkg/proc/native/register_linux_386.go @@ -5,13 +5,15 @@ import ( 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/amd64util" "github.com/go-delve/delve/pkg/proc/linutil" ) -// SetPC sets EIP to the value specified by 'pc'. -func (thread *nativeThread) SetPC(pc uint64) error { +// setPC sets EIP to the value specified by 'pc'. +func (thread *nativeThread) setPC(pc uint64) error { ir, err := registers(thread) if err != nil { return err @@ -22,29 +24,26 @@ func (thread *nativeThread) SetPC(pc uint64) error { return err } -// SetSP sets ESP to the value specified by 'sp' -func (thread *nativeThread) SetSP(sp uint64) (err error) { - var ir proc.Registers - ir, err = registers(thread) +func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error { + ir, err := registers(thread) if err != nil { return err } r := ir.(*linutil.I386Registers) - r.Regs.Esp = int32(sp) - 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 { - return err + switch regNum { + case regnum.I386_Eip: + r.Regs.Eip = int32(reg.Uint64Val) + case regnum.I386_Esp: + r.Regs.Esp = int32(reg.Uint64Val) + case regnum.I386_Edx: + r.Regs.Edx = int32(reg.Uint64Val) + default: + //TODO(aarzilli): when the register calling convention is adopted by Go on + // 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)) }) - return + return err } func registers(thread *nativeThread) (proc.Registers, error) { diff --git a/pkg/proc/native/registers_darwin_amd64.go b/pkg/proc/native/registers_darwin_amd64.go index 8923441a..e036494c 100644 --- a/pkg/proc/native/registers_darwin_amd64.go +++ b/pkg/proc/native/registers_darwin_amd64.go @@ -5,12 +5,13 @@ package native // #include "threads_darwin.h" import "C" import ( - "errors" "fmt" "unsafe" "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" ) @@ -111,7 +112,7 @@ func (r *Regs) GAddr() (uint64, bool) { } // 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)) if kret != C.KERN_SUCCESS { return fmt.Errorf("could not set pc") @@ -119,13 +120,12 @@ func (thread *nativeThread) SetPC(pc uint64) error { return nil } -// SetSP sets the RSP register to the value specified by `pc`. -func (thread *nativeThread) SetSP(sp uint64) error { - return errors.New("not implemented") -} - -func (thread *nativeThread) SetDX(dx uint64) error { - return errors.New("not implemented") +// SetReg changes the value of the specified register. +func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error { + if regNum != regnum.AMD64_Rip { + return fmt.Errorf("changing register %d not implemented", regNum) + } + return thread.setPC(reg.Uint64Val) } func (r *Regs) Get(n int) (uint64, error) { diff --git a/pkg/proc/native/registers_freebsd_amd64.go b/pkg/proc/native/registers_freebsd_amd64.go index 98e09912..7b3ffd9a 100644 --- a/pkg/proc/native/registers_freebsd_amd64.go +++ b/pkg/proc/native/registers_freebsd_amd64.go @@ -5,13 +5,15 @@ import ( 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/amd64util" "github.com/go-delve/delve/pkg/proc/fbsdutil" ) // 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) if err != nil { return err @@ -22,27 +24,23 @@ func (thread *nativeThread) SetPC(pc uint64) error { return err } -// SetSP sets RSP to the value specified by 'sp' -func (thread *nativeThread) SetSP(sp uint64) (err error) { - var ir proc.Registers - ir, err = registers(thread) +// SetReg changes the value of the specified register. +func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) (err error) { + ir, err := registers(thread) if err != nil { return err } r := ir.(*fbsdutil.AMD64Registers) - r.Regs.Rsp = int64(sp) - thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.Reg)(r.Regs)) }) - return -} - -func (thread *nativeThread) SetDX(dx uint64) (err error) { - var ir proc.Registers - ir, err = registers(thread) - if err != nil { - return err + switch regNum { + case regnum.AMD64_Rip: + r.Regs.Rip = int64(reg.Uint64Val) + case regnum.AMD64_Rsp: + r.Regs.Rsp = int64(reg.Uint64Val) + case regnum.AMD64_Rdx: + r.Regs.Rdx = int64(reg.Uint64Val) + default: + return fmt.Errorf("changing register %d not implemented", regNum) } - r := ir.(*fbsdutil.AMD64Registers) - r.Regs.Rdx = int64(dx) thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.Reg)(r.Regs)) }) return } diff --git a/pkg/proc/native/registers_linux_amd64.go b/pkg/proc/native/registers_linux_amd64.go index c189de2b..676d8964 100644 --- a/pkg/proc/native/registers_linux_amd64.go +++ b/pkg/proc/native/registers_linux_amd64.go @@ -2,16 +2,19 @@ package native import ( "fmt" + "syscall" + "unsafe" 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/amd64util" "github.com/go-delve/delve/pkg/proc/linutil" ) // 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) if err != nil { return err @@ -22,29 +25,31 @@ func (thread *nativeThread) SetPC(pc uint64) error { return err } -// SetSP sets RSP to the value specified by 'sp' -func (thread *nativeThread) SetSP(sp uint64) (err error) { - var ir proc.Registers - ir, err = registers(thread) +// SetReg changes the value of the specified register. +func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error { + ir, err := registers(thread) if err != nil { return err } r := ir.(*linutil.AMD64Registers) - r.Regs.Rsp = sp - 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) + fpchanged, err := r.SetReg(regNum, reg) if err != nil { return err } - r := ir.(*linutil.AMD64Registers) - r.Regs.Rdx = dx - thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) }) - return + thread.dbp.execPtraceFunc(func() { + err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) + if err != nil { + 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) { diff --git a/pkg/proc/native/registers_linux_arm64.go b/pkg/proc/native/registers_linux_arm64.go index 19971298..cebbd175 100644 --- a/pkg/proc/native/registers_linux_arm64.go +++ b/pkg/proc/native/registers_linux_arm64.go @@ -8,6 +8,8 @@ import ( 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/linutil" ) @@ -64,8 +66,8 @@ func ptraceGetFpRegset(tid int) (fpregset []byte, err error) { return fpregset, err } -// SetPC sets PC to the value specified by 'pc'. -func (thread *nativeThread) SetPC(pc uint64) error { +// setPC sets PC to the value specified by 'pc'. +func (thread *nativeThread) setPC(pc uint64) error { ir, err := registers(thread) if err != nil { return err @@ -76,21 +78,26 @@ func (thread *nativeThread) SetPC(pc uint64) error { return err } -// SetSP sets RSP to the value specified by 'sp' -func (thread *nativeThread) SetSP(sp uint64) (err error) { - var ir proc.Registers - ir, err = registers(thread) +func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error { + ir, err := registers(thread) if err != nil { return err } 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) { - return fmt.Errorf("not supported") + switch regNum { + 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) { diff --git a/pkg/proc/native/registers_windows_amd64.go b/pkg/proc/native/registers_windows_amd64.go index f3669e40..2f44bc33 100644 --- a/pkg/proc/native/registers_windows_amd64.go +++ b/pkg/proc/native/registers_windows_amd64.go @@ -4,12 +4,14 @@ import ( "fmt" "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/winutil" ) // 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.ContextFlags = _CONTEXT_ALL @@ -23,8 +25,8 @@ func (thread *nativeThread) SetPC(pc uint64) error { return _SetThreadContext(thread.os.hThread, context) } -// SetSP sets the RSP register to the value specified by `sp`. -func (thread *nativeThread) SetSP(sp uint64) error { +// SetReg changes the value of the specified register. +func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error { context := winutil.NewCONTEXT() context.ContextFlags = _CONTEXT_ALL @@ -33,21 +35,60 @@ func (thread *nativeThread) SetSP(sp uint64) error { return err } - context.Rsp = sp + var p *uint64 - return _SetThreadContext(thread.os.hThread, context) -} - -func (thread *nativeThread) SetDX(dx uint64) error { - context := winutil.NewCONTEXT() - context.ContextFlags = _CONTEXT_ALL - - err := _GetThreadContext(thread.os.hThread, context) - if err != nil { - return err + switch regNum { + case regnum.AMD64_Rax: + p = &context.Rax + case regnum.AMD64_Rbx: + p = &context.Rbx + case regnum.AMD64_Rcx: + p = &context.Rcx + case regnum.AMD64_Rdx: + p = &context.Rdx + 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) } diff --git a/pkg/proc/native/threads.go b/pkg/proc/native/threads.go index c15d63bd..286318cd 100644 --- a/pkg/proc/native/threads.go +++ b/pkg/proc/native/threads.go @@ -122,7 +122,7 @@ func (t *nativeThread) SetCurrentBreakpoint(adjustPC bool) error { if bp, ok := t.dbp.FindBreakpoint(pc, adjustPC); ok { if adjustPC { - if err = t.SetPC(bp.Addr); err != nil { + if err = t.setPC(bp.Addr); err != nil { return err } } diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index 9149ccab..7d3593b2 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -2,6 +2,7 @@ package proc_test import ( "bytes" + "encoding/binary" "flag" "fmt" "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) + } + }) +} diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go index aa1b3861..0e05d474 100644 --- a/pkg/proc/stack.go +++ b/pkg/proc/stack.go @@ -100,7 +100,9 @@ func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) { return nil, err } 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 g.Stacktrace(depth, 0) @@ -119,9 +121,11 @@ func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) { return nil, err } so := bi.PCToImage(regs.PC()) + dwarfRegs := bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs) + dwarfRegs.ChangeFunc = g.Thread.SetReg return newStackIterator( bi, g.variable.mem, - bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs), + dwarfRegs, g.stack.hi, stkbar, g.stkbarPos, g, opts), nil } so := g.variable.bi.PCToImage(g.PC) diff --git a/pkg/proc/target_exec.go b/pkg/proc/target_exec.go index ba4aa835..385ce9e2 100644 --- a/pkg/proc/target_exec.go +++ b/pkg/proc/target_exec.go @@ -127,7 +127,7 @@ func (dbp *Target) Continue() error { // In linux-arm64, PtraceSingleStep seems cannot step over BRK instruction // (linux-arm64 feature or kernel bug maybe). 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 // runtime.Breakpoint. @@ -145,7 +145,7 @@ func (dbp *Target) Continue() error { bp := make([]byte, bpsize) _, err = dbp.Memory().ReadMemory(bp, loc.PC) if bytes.Equal(bp, arch.BreakpointInstruction()) { - curthread.SetPC(loc.PC + uint64(bpsize)) + setPC(curthread, loc.PC+uint64(bpsize)) } } return conditionErrors(threads) diff --git a/pkg/proc/threads.go b/pkg/proc/threads.go index edc36e78..35b33670 100644 --- a/pkg/proc/threads.go +++ b/pkg/proc/threads.go @@ -2,6 +2,8 @@ package proc import ( "errors" + + "github.com/go-delve/delve/pkg/dwarf/op" ) // Thread represents a thread. @@ -31,9 +33,10 @@ type Thread interface { // Common returns the CommonThread structure for this thread Common() *CommonThread - SetPC(uint64) error - SetSP(uint64) error - SetDX(uint64) error + // SetReg changes the value of the specified register. A minimal + // implementation of this interface can support just setting the PC + // register. + SetReg(uint64, *op.DwarfRegister) error } // 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 } } + +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)) +} diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index 82dfd81b..3ff8e5f4 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -1147,7 +1147,7 @@ func extractVarInfoFromEntry(bi *BinaryInfo, image *Image, regs op.DwarfRegister if pieces != nil { addr = fakeAddress var cmem *compositeMemory - cmem, err = newCompositeMemory(mem, regs, pieces) + cmem, err = newCompositeMemory(mem, bi.Arch, regs, pieces) if cmem != nil { mem = cmem }