diff --git a/pkg/dwarf/op/regs.go b/pkg/dwarf/op/regs.go index ee3b4573..3e4b6b6b 100644 --- a/pkg/dwarf/op/regs.go +++ b/pkg/dwarf/op/regs.go @@ -12,13 +12,16 @@ type DwarfRegisters struct { CFA int64 FrameBase int64 ObjBase int64 - Regs []*DwarfRegister + regs []*DwarfRegister ByteOrder binary.ByteOrder PCRegNum uint64 SPRegNum uint64 BPRegNum uint64 LRRegNum uint64 + + FloatLoadError error // error produced when loading floating point registers + loadMoreCallback func() } type DwarfRegister struct { @@ -26,13 +29,38 @@ type DwarfRegister struct { Bytes []byte } +// NewDwarfRegisters returns a new DwarfRegisters object. +func NewDwarfRegisters(staticBase uint64, regs []*DwarfRegister, byteOrder binary.ByteOrder, pcRegNum, spRegNum, bpRegNum, lrRegNum uint64) *DwarfRegisters { + return &DwarfRegisters{ + StaticBase: staticBase, + regs: regs, + ByteOrder: byteOrder, + PCRegNum: pcRegNum, + SPRegNum: spRegNum, + BPRegNum: bpRegNum, + LRRegNum: lrRegNum, + } +} + +// SetLoadMoreCallback sets a callback function that will be called the +// first time the user of regs tries to access an undefined register. +func (regs *DwarfRegisters) SetLoadMoreCallback(fn func()) { + regs.loadMoreCallback = fn +} + +// CurrentSize returns the current number of known registers. This number might be +// wrong if loadMoreCallback has been set. +func (regs *DwarfRegisters) CurrentSize() int { + return len(regs.regs) +} + // Uint64Val returns the uint64 value of register idx. func (regs *DwarfRegisters) Uint64Val(idx uint64) uint64 { reg := regs.Reg(idx) if reg == nil { return 0 } - return regs.Regs[idx].Uint64Val + return regs.regs[idx].Uint64Val } // Bytes returns the bytes value of register idx, nil if the register is not @@ -50,12 +78,26 @@ func (regs *DwarfRegisters) Bytes(idx uint64) []byte { return reg.Bytes } +func (regs *DwarfRegisters) loadMore() { + if regs.loadMoreCallback == nil { + return + } + regs.loadMoreCallback() + regs.loadMoreCallback = nil +} + // Reg returns register idx or nil if the register is not defined. func (regs *DwarfRegisters) Reg(idx uint64) *DwarfRegister { - if idx >= uint64(len(regs.Regs)) { - return nil + if idx >= uint64(len(regs.regs)) { + regs.loadMore() + if idx >= uint64(len(regs.regs)) { + return nil + } } - return regs.Regs[idx] + if regs.regs[idx] == nil { + regs.loadMore() + } + return regs.regs[idx] } func (regs *DwarfRegisters) PC() uint64 { @@ -72,12 +114,20 @@ func (regs *DwarfRegisters) BP() uint64 { // AddReg adds register idx to regs. func (regs *DwarfRegisters) AddReg(idx uint64, reg *DwarfRegister) { - if idx >= uint64(len(regs.Regs)) { + if idx >= uint64(len(regs.regs)) { newRegs := make([]*DwarfRegister, idx+1) - copy(newRegs, regs.Regs) - regs.Regs = newRegs + copy(newRegs, regs.regs) + regs.regs = newRegs + } + regs.regs[idx] = reg +} + +// ClearRegisters clears all registers. +func (regs *DwarfRegisters) ClearRegisters() { + regs.loadMoreCallback = nil + for regnum := range regs.regs { + regs.regs[regnum] = nil } - regs.Regs[idx] = reg } func DwarfRegisterFromUint64(v uint64) *DwarfRegister { diff --git a/pkg/proc/amd64_arch.go b/pkg/proc/amd64_arch.go index f09a3d17..3be49db8 100644 --- a/pkg/proc/amd64_arch.go +++ b/pkg/proc/amd64_arch.go @@ -345,21 +345,32 @@ func maxAmd64DwarfRegister() int { } func amd64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters { - dregs := make([]*op.DwarfRegister, maxAmd64DwarfRegister()+1) + dregs := initDwarfRegistersFromSlice(maxAmd64DwarfRegister(), regs, amd64NameToDwarf) + dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, amd64DwarfIPRegNum, amd64DwarfSPRegNum, amd64DwarfBPRegNum, 0) + dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, amd64NameToDwarf)) + return *dr +} - for _, reg := range regs.Slice(true) { - if dwarfReg, ok := amd64NameToDwarf[strings.ToLower(reg.Name)]; ok { +func initDwarfRegistersFromSlice(maxRegs int, regs Registers, nameToDwarf map[string]int) []*op.DwarfRegister { + dregs := make([]*op.DwarfRegister, maxRegs+1) + regslice, _ := regs.Slice(false) + for _, reg := range regslice { + if dwarfReg, ok := nameToDwarf[strings.ToLower(reg.Name)]; ok { dregs[dwarfReg] = reg.Reg } } + return dregs +} - return op.DwarfRegisters{ - StaticBase: staticBase, - Regs: dregs, - ByteOrder: binary.LittleEndian, - PCRegNum: amd64DwarfIPRegNum, - SPRegNum: amd64DwarfSPRegNum, - BPRegNum: amd64DwarfBPRegNum, +func loadMoreDwarfRegistersFromSliceFunc(dr *op.DwarfRegisters, regs Registers, nameToDwarf map[string]int) func() { + return func() { + regslice, err := regs.Slice(true) + dr.FloatLoadError = err + for _, reg := range regslice { + if dwarfReg, ok := nameToDwarf[strings.ToLower(reg.Name)]; ok { + dr.AddReg(uint64(dwarfReg), reg.Reg) + } + } } } @@ -369,14 +380,7 @@ func amd64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp) dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp) - return op.DwarfRegisters{ - StaticBase: staticBase, - Regs: dregs, - ByteOrder: binary.LittleEndian, - PCRegNum: amd64DwarfIPRegNum, - SPRegNum: amd64DwarfSPRegNum, - BPRegNum: amd64DwarfBPRegNum, - } + return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, amd64DwarfIPRegNum, amd64DwarfSPRegNum, amd64DwarfBPRegNum, 0) } func amd64DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { diff --git a/pkg/proc/arm64_arch.go b/pkg/proc/arm64_arch.go index 62085973..5f2bb165 100644 --- a/pkg/proc/arm64_arch.go +++ b/pkg/proc/arm64_arch.go @@ -312,6 +312,20 @@ var arm64DwarfToHardware = map[int]arm64asm.Reg{ 95: arm64asm.V31, } +var arm64NameToDwarf = func() map[string]int { + r := make(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["sp"] = 31 + for i := 0; i <= 31; i++ { + r[fmt.Sprintf("v%d", i)] = i + 64 + } + return r +}() + func maxArm64DwarfRegister() int { max := int(arm64DwarfIPRegNum) for i := range arm64DwarfToHardware { @@ -339,15 +353,9 @@ func arm64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfR } } - return op.DwarfRegisters{ - StaticBase: staticBase, - Regs: dregs, - ByteOrder: binary.LittleEndian, - PCRegNum: arm64DwarfIPRegNum, - SPRegNum: arm64DwarfSPRegNum, - BPRegNum: arm64DwarfBPRegNum, - LRRegNum: arm64DwarfLRRegNum, - } + dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, arm64DwarfIPRegNum, arm64DwarfSPRegNum, arm64DwarfBPRegNum, arm64DwarfLRRegNum) + dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, arm64NameToDwarf)) + return *dr } func arm64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { @@ -357,15 +365,7 @@ func arm64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op dregs[arm64DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp) dregs[arm64DwarfLRRegNum] = op.DwarfRegisterFromUint64(lr) - return op.DwarfRegisters{ - StaticBase: staticBase, - Regs: dregs, - ByteOrder: binary.LittleEndian, - PCRegNum: arm64DwarfIPRegNum, - SPRegNum: arm64DwarfSPRegNum, - BPRegNum: arm64DwarfBPRegNum, - LRRegNum: arm64DwarfLRRegNum, - } + return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, arm64DwarfIPRegNum, arm64DwarfSPRegNum, arm64DwarfBPRegNum, arm64DwarfLRRegNum) } func arm64DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { diff --git a/pkg/proc/core/core.go b/pkg/proc/core/core.go index 3e6aa1bb..41c7df79 100644 --- a/pkg/proc/core/core.go +++ b/pkg/proc/core/core.go @@ -170,7 +170,7 @@ type thread struct { } type osThread interface { - registers(floatingPoint bool) (proc.Registers, error) + registers() (proc.Registers, error) pid() int } @@ -281,7 +281,7 @@ func (t *thread) WriteMemory(addr uintptr, data []byte) (int, error) { // Location returns the location of this thread based on // the value of the instruction pointer register. func (t *thread) Location() (*proc.Location, error) { - regs, err := t.th.registers(false) + regs, err := t.th.registers() if err != nil { return nil, err } @@ -303,8 +303,8 @@ func (t *thread) ThreadID() int { } // Registers returns the current value of the registers for this thread. -func (t *thread) Registers(floatingPoint bool) (proc.Registers, error) { - return t.th.registers(floatingPoint) +func (t *thread) Registers() (proc.Registers, error) { + return t.th.registers() } // RestoreRegisters will only return an error for core files, diff --git a/pkg/proc/core/core_test.go b/pkg/proc/core/core_test.go index 7c3a250e..297da186 100644 --- a/pkg/proc/core/core_test.go +++ b/pkg/proc/core/core_test.go @@ -187,7 +187,9 @@ func withCoreFile(t *testing.T, name, args string) *proc.Target { func logRegisters(t *testing.T, regs proc.Registers, arch *proc.Arch) { dregs := arch.RegistersToDwarfRegisters(0, regs) - for i, reg := range dregs.Regs { + dregs.Reg(^uint64(0)) + for i := 0; i < dregs.CurrentSize(); i++ { + reg := dregs.Reg(uint64(i)) if reg == nil { continue } @@ -250,7 +252,7 @@ func TestCore(t *testing.T) { t.Errorf("main.msg = %q, want %q", msg.Value, "BOOM!") } - regs, err := p.CurrentThread().Registers(true) + regs, err := p.CurrentThread().Registers() if err != nil { t.Fatalf("Couldn't get current thread registers: %v", err) } @@ -286,7 +288,7 @@ func TestCoreFpRegisters(t *testing.T) { continue } if frames[i].Current.Fn.Name == "main.main" { - regs, err = thread.Registers(true) + regs, err = thread.Registers() if err != nil { t.Fatalf("Could not get registers for thread %x, %v", thread.ThreadID(), err) } @@ -326,7 +328,9 @@ func TestCoreFpRegisters(t *testing.T) { for _, regtest := range regtests { found := false - for i, reg := range dregs.Regs { + dregs.Reg(^uint64(0)) + for i := 0; i < dregs.CurrentSize(); i++ { + reg := dregs.Reg(uint64(i)) regname, _, regval := arch.DwarfRegisterToString(i, reg) if reg != nil && regname == regtest.name { found = true diff --git a/pkg/proc/core/linux_core.go b/pkg/proc/core/linux_core.go index 48099431..35f6f6b6 100644 --- a/pkg/proc/core/linux_core.go +++ b/pkg/proc/core/linux_core.go @@ -154,21 +154,17 @@ type linuxARM64Thread struct { t *linuxPrStatusARM64 } -func (t *linuxAMD64Thread) registers(floatingPoint bool) (proc.Registers, error) { +func (t *linuxAMD64Thread) registers() (proc.Registers, error) { var r linutil.AMD64Registers r.Regs = t.regs.Regs - if floatingPoint { - r.Fpregs = t.regs.Fpregs - } + r.Fpregs = t.regs.Fpregs return &r, nil } -func (t *linuxARM64Thread) registers(floatingPoint bool) (proc.Registers, error) { +func (t *linuxARM64Thread) registers() (proc.Registers, error) { var r linutil.ARM64Registers r.Regs = t.regs.Regs - if floatingPoint { - r.Fpregs = t.regs.Fpregs - } + r.Fpregs = t.regs.Fpregs return &r, nil } diff --git a/pkg/proc/core/windows_amd64_minidump.go b/pkg/proc/core/windows_amd64_minidump.go index 1f94f4f7..4a67ef44 100644 --- a/pkg/proc/core/windows_amd64_minidump.go +++ b/pkg/proc/core/windows_amd64_minidump.go @@ -54,6 +54,6 @@ func (th *windowsAMD64Thread) pid() int { return int(th.th.ID) } -func (th *windowsAMD64Thread) registers(floatingPoint bool) (proc.Registers, error) { - return winutil.NewAMD64Registers(&th.th.Context, th.th.TEB, floatingPoint), nil +func (th *windowsAMD64Thread) registers() (proc.Registers, error) { + return winutil.NewAMD64Registers(&th.th.Context, th.th.TEB), nil } diff --git a/pkg/proc/dwarf_expr_test.go b/pkg/proc/dwarf_expr_test.go index cbfe812d..73abafd8 100644 --- a/pkg/proc/dwarf_expr_test.go +++ b/pkg/proc/dwarf_expr_test.go @@ -232,7 +232,7 @@ func TestDwarfExprLoclist(t *testing.T) { uintExprCheck(t, scope, "a", before) scope.PC = 0x40800 - scope.Regs.Regs[scope.Regs.PCRegNum].Uint64Val = scope.PC + scope.Regs.Reg(scope.Regs.PCRegNum).Uint64Val = scope.PC uintExprCheck(t, scope, "a", after) } diff --git a/pkg/proc/fbsdutil/regs.go b/pkg/proc/fbsdutil/regs.go index d00bec95..cf65a122 100644 --- a/pkg/proc/fbsdutil/regs.go +++ b/pkg/proc/fbsdutil/regs.go @@ -14,6 +14,12 @@ type AMD64Registers struct { Fpregs []proc.Register Fpregset *AMD64Xstate Fsbase uint64 + + loadFpRegs func(*AMD64Registers) error +} + +func NewAMD64Registers(regs *AMD64PtraceRegs, fsbase uint64, loadFpRegs func(*AMD64Registers) error) *AMD64Registers { + return &AMD64Registers{Regs: regs, Fsbase: fsbase, loadFpRegs: loadFpRegs} } // AMD64PtraceRegs is the struct used by the freebsd kernel to return the @@ -49,7 +55,7 @@ type AMD64PtraceRegs struct { } // Slice returns the registers as a list of (name, value) pairs. -func (r *AMD64Registers) Slice(floatingPoint bool) []proc.Register { +func (r *AMD64Registers) Slice(floatingPoint bool) ([]proc.Register, error) { var regs64 = []struct { k string v int64 @@ -112,10 +118,15 @@ func (r *AMD64Registers) Slice(floatingPoint bool) []proc.Register { // x86 called this register "Eflags". amd64 extended it and renamed it // "Rflags", but Linux still uses the old name. out = proc.AppendUint64Register(out, "Rflags", uint64(r.Regs.Rflags)) + var floatLoadError error if floatingPoint { + if r.loadFpRegs != nil { + floatLoadError = r.loadFpRegs(r) + r.loadFpRegs = nil + } out = append(out, r.Fpregs...) } - return out + return out, floatLoadError } // PC returns the value of RIP register. @@ -302,7 +313,14 @@ func (r *AMD64Registers) Get(n int) (uint64, error) { } // Copy returns a copy of these registers that is guarenteed not to change. -func (r *AMD64Registers) Copy() proc.Registers { +func (r *AMD64Registers) Copy() (proc.Registers, error) { + if r.loadFpRegs != nil { + err := r.loadFpRegs(r) + r.loadFpRegs = nil + if err != nil { + return nil, err + } + } var rr AMD64Registers rr.Regs = &AMD64PtraceRegs{} rr.Fpregset = &AMD64Xstate{} @@ -314,7 +332,7 @@ func (r *AMD64Registers) Copy() proc.Registers { rr.Fpregs = make([]proc.Register, len(r.Fpregs)) copy(rr.Fpregs, r.Fpregs) } - return &rr + return &rr, nil } type AMD64Xstate linutil.AMD64Xstate diff --git a/pkg/proc/fncall.go b/pkg/proc/fncall.go index 26d0a756..ffeb9d27 100644 --- a/pkg/proc/fncall.go +++ b/pkg/proc/fncall.go @@ -252,11 +252,14 @@ func evalFunctionCall(scope *EvalScope, node *ast.CallExpr) (*Variable, error) { } // check that there are at least 256 bytes free on the stack - regs, err := scope.g.Thread.Registers(true) + regs, err := scope.g.Thread.Registers() + if err != nil { + return nil, err + } + regs, err = regs.Copy() if err != nil { return nil, err } - regs = regs.Copy() if regs.SP()-256 <= scope.g.stack.lo { return nil, errNotEnoughStack } @@ -293,18 +296,13 @@ func evalFunctionCall(scope *EvalScope, node *ast.CallExpr) (*Variable, error) { scope.g = scope.callCtx.doContinue() // adjust the value of registers inside scope - for regnum := range scope.Regs.Regs { - switch uint64(regnum) { - case scope.Regs.PCRegNum, scope.Regs.SPRegNum, scope.Regs.BPRegNum: - // leave these alone - default: - // every other register is dirty and unrecoverable - scope.Regs.Regs[regnum] = nil - } - } - - scope.Regs.Regs[scope.Regs.SPRegNum].Uint64Val = uint64(spoff + int64(scope.g.stack.hi)) - scope.Regs.Regs[scope.Regs.BPRegNum].Uint64Val = uint64(bpoff + int64(scope.g.stack.hi)) + pcreg, bpreg, spreg := scope.Regs.Reg(scope.Regs.PCRegNum), scope.Regs.Reg(scope.Regs.BPRegNum), scope.Regs.Reg(scope.Regs.SPRegNum) + scope.Regs.ClearRegisters() + scope.Regs.AddReg(scope.Regs.PCRegNum, pcreg) + scope.Regs.AddReg(scope.Regs.BPRegNum, bpreg) + scope.Regs.AddReg(scope.Regs.SPRegNum, spreg) + scope.Regs.Reg(scope.Regs.SPRegNum).Uint64Val = uint64(spoff + int64(scope.g.stack.hi)) + scope.Regs.Reg(scope.Regs.BPRegNum).Uint64Val = uint64(bpoff + int64(scope.g.stack.hi)) scope.Regs.FrameBase = fboff + int64(scope.g.stack.hi) scope.Regs.CFA = scope.frameOffset + int64(scope.g.stack.hi) @@ -655,12 +653,16 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState) bool { bi := p.BinInfo() thread := callScope.g.Thread - regs, err := thread.Registers(false) + regs, err := thread.Registers() + if err != nil { + fncall.err = err + return true + } + regs, err = regs.Copy() if err != nil { fncall.err = err return true } - regs = regs.Copy() rax, _ := regs.Get(int(x86asm.RAX)) @@ -823,7 +825,7 @@ func fakeFunctionEntryScope(scope *EvalScope, fn *Function, cfa int64, sp uint64 scope.File, scope.Line, _ = scope.BinInfo.PCToLine(fn.Entry) scope.Regs.CFA = cfa - scope.Regs.Regs[scope.Regs.SPRegNum].Uint64Val = sp + scope.Regs.Reg(scope.Regs.SPRegNum).Uint64Val = sp fn.cu.image.dwarfReader.Seek(fn.offset) e, err := fn.cu.image.dwarfReader.Next() diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go index 07fcb7f8..c88ed334 100644 --- a/pkg/proc/gdbserial/gdbserver.go +++ b/pkg/proc/gdbserial/gdbserver.go @@ -1286,7 +1286,7 @@ func (t *gdbThread) WriteMemory(addr uintptr, data []byte) (written int, err err // Location returns the current location of this thread. func (t *gdbThread) Location() (*proc.Location, error) { - regs, err := t.Registers(false) + regs, err := t.Registers() if err != nil { return nil, err } @@ -1311,7 +1311,7 @@ func (t *gdbThread) ThreadID() int { } // Registers returns the CPU registers for this thread. -func (t *gdbThread) Registers(floatingPoint bool) (proc.Registers, error) { +func (t *gdbThread) Registers() (proc.Registers, error) { if t.regs.regs == nil { if err := t.reloadRegisters(); err != nil { return nil, err @@ -1358,7 +1358,7 @@ func (t *gdbThread) StepInstruction() error { // Blocked returns true if the thread is blocked in runtime or kernel code. func (t *gdbThread) Blocked() bool { - regs, err := t.Registers(false) + regs, err := t.Registers() if err != nil { return false } @@ -1639,7 +1639,7 @@ func (t *gdbThread) SetCurrentBreakpoint(adjustPC bool) error { // adjustPC is ignored, it is the stub's responsibiility to set the PC // address correctly after hitting a breakpoint. t.clearBreakpointState() - regs, err := t.Registers(false) + regs, err := t.Registers() if err != nil { return err } @@ -1865,6 +1865,10 @@ func (regs *gdbRegisters) Get(n int) (uint64, error) { return 0, proc.ErrUnknownRegister } +func (r *gdbRegisters) FloatLoadError() error { + return nil +} + // SetPC will set the value of the PC register to the given value. func (t *gdbThread) SetPC(pc uint64) error { t.regs.setPC(pc) @@ -1895,7 +1899,7 @@ func (t *gdbThread) SetDX(dx uint64) error { return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value) } -func (regs *gdbRegisters) Slice(floatingPoint bool) []proc.Register { +func (regs *gdbRegisters) Slice(floatingPoint bool) ([]proc.Register, error) { r := make([]proc.Register, 0, len(regs.regsInfo)) for _, reginfo := range regs.regsInfo { if reginfo.Group == "float" && !floatingPoint { @@ -1941,12 +1945,12 @@ func (regs *gdbRegisters) Slice(floatingPoint bool) []proc.Register { r = proc.AppendBytesRegister(r, strings.ToUpper(reginfo.Name), value[16:]) } } - return r + return r, nil } -func (regs *gdbRegisters) Copy() proc.Registers { +func (regs *gdbRegisters) Copy() (proc.Registers, error) { savedRegs := &gdbRegisters{} savedRegs.init(regs.regsInfo) copy(savedRegs.buf, regs.buf) - return savedRegs + return savedRegs, nil } diff --git a/pkg/proc/i386_arch.go b/pkg/proc/i386_arch.go index dcb6950e..dbfeb0d5 100644 --- a/pkg/proc/i386_arch.go +++ b/pkg/proc/i386_arch.go @@ -253,22 +253,11 @@ func maxI386DwarfRegister() int { } func i386RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters { - dregs := make([]*op.DwarfRegister, maxI386DwarfRegister()+1) + dregs := initDwarfRegistersFromSlice(maxI386DwarfRegister(), regs, i386NameToDwarf) + dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, i386DwarfIPRegNum, i386DwarfSPRegNum, i386DwarfBPRegNum, 0) + dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, i386NameToDwarf)) - for _, reg := range regs.Slice(true) { - if dwarfReg, ok := i386NameToDwarf[strings.ToLower(reg.Name)]; ok { - dregs[dwarfReg] = reg.Reg - } - } - - return op.DwarfRegisters{ - StaticBase: staticBase, - Regs: dregs, - ByteOrder: binary.LittleEndian, - PCRegNum: i386DwarfIPRegNum, - SPRegNum: i386DwarfSPRegNum, - BPRegNum: i386DwarfBPRegNum, - } + return *dr } func i386AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { @@ -277,14 +266,7 @@ func i386AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op. dregs[i386DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp) dregs[i386DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp) - return op.DwarfRegisters{ - StaticBase: staticBase, - Regs: dregs, - ByteOrder: binary.LittleEndian, - PCRegNum: i386DwarfIPRegNum, - SPRegNum: i386DwarfSPRegNum, - BPRegNum: i386DwarfBPRegNum, - } + return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, i386DwarfIPRegNum, i386DwarfSPRegNum, i386DwarfBPRegNum, 0) } func i386DwarfRegisterToString(j int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { @@ -301,9 +283,9 @@ func i386DwarfRegisterToString(j int, reg *op.DwarfRegister) (name string, float return name, true, fmt.Sprintf("%#04x", reg.Uint64Val) default: - if reg.Bytes != nil && strings.HasPrefix(name, "xmm") { + if reg.Bytes != nil && strings.HasPrefix(n, "xmm") { return name, true, formatSSEReg(reg.Bytes) - } else if reg.Bytes != nil && strings.HasPrefix(name, "st(") { + } else if reg.Bytes != nil && strings.HasPrefix(n, "st(") { return name, true, formatX87Reg(reg.Bytes) } else if reg.Bytes == nil || (reg.Bytes != nil && len(reg.Bytes) <= 8) { return name, false, fmt.Sprintf("%#016x", reg.Uint64Val) diff --git a/pkg/proc/linutil/regs_amd64_arch.go b/pkg/proc/linutil/regs_amd64_arch.go index be1781bf..911a9f44 100644 --- a/pkg/proc/linutil/regs_amd64_arch.go +++ b/pkg/proc/linutil/regs_amd64_arch.go @@ -16,6 +16,12 @@ type AMD64Registers struct { Regs *AMD64PtraceRegs Fpregs []proc.Register Fpregset *AMD64Xstate + + loadFpRegs func(*AMD64Registers) error +} + +func NewAMD64Registers(regs *AMD64PtraceRegs, loadFpRegs func(*AMD64Registers) error) *AMD64Registers { + return &AMD64Registers{Regs: regs, loadFpRegs: loadFpRegs} } // AMD64PtraceRegs is the struct used by the linux kernel to return the @@ -51,7 +57,7 @@ type AMD64PtraceRegs struct { } // Slice returns the registers as a list of (name, value) pairs. -func (r *AMD64Registers) Slice(floatingPoint bool) []proc.Register { +func (r *AMD64Registers) Slice(floatingPoint bool) ([]proc.Register, error) { var regs = []struct { k string v uint64 @@ -88,10 +94,15 @@ func (r *AMD64Registers) Slice(floatingPoint bool) []proc.Register { for _, reg := range regs { out = proc.AppendUint64Register(out, reg.k, reg.v) } + var floatLoadError error if floatingPoint { + if r.loadFpRegs != nil { + floatLoadError = r.loadFpRegs(r) + r.loadFpRegs = nil + } out = append(out, r.Fpregs...) } - return out + return out, floatLoadError } // PC returns the value of RIP register. @@ -278,7 +289,14 @@ func (r *AMD64Registers) Get(n int) (uint64, error) { } // Copy returns a copy of these registers that is guarenteed not to change. -func (r *AMD64Registers) Copy() proc.Registers { +func (r *AMD64Registers) Copy() (proc.Registers, error) { + if r.loadFpRegs != nil { + err := r.loadFpRegs(r) + r.loadFpRegs = nil + if err != nil { + return nil, err + } + } var rr AMD64Registers rr.Regs = &AMD64PtraceRegs{} rr.Fpregset = &AMD64Xstate{} @@ -290,7 +308,7 @@ func (r *AMD64Registers) Copy() proc.Registers { rr.Fpregs = make([]proc.Register, len(r.Fpregs)) copy(rr.Fpregs, r.Fpregs) } - return &rr + return &rr, nil } // AMD64PtraceFpRegs tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h diff --git a/pkg/proc/linutil/regs_arm64_arch.go b/pkg/proc/linutil/regs_arm64_arch.go index bce0d52d..51ea3df3 100644 --- a/pkg/proc/linutil/regs_arm64_arch.go +++ b/pkg/proc/linutil/regs_arm64_arch.go @@ -12,6 +12,12 @@ type ARM64Registers struct { Regs *ARM64PtraceRegs //general-purpose registers Fpregs []proc.Register //Formatted floating point registers Fpregset []byte //holding all floating point register values + + loadFpRegs func(*ARM64Registers) error +} + +func NewARM64Registers(regs *ARM64PtraceRegs, loadFpRegs func(*ARM64Registers) error) *ARM64Registers { + return &ARM64Registers{Regs: regs, loadFpRegs: loadFpRegs} } // ARM64PtraceRegs is the struct used by the linux kernel to return the @@ -25,7 +31,7 @@ type ARM64PtraceRegs struct { } // Slice returns the registers as a list of (name, value) pairs. -func (r *ARM64Registers) Slice(floatingPoint bool) []proc.Register { +func (r *ARM64Registers) Slice(floatingPoint bool) ([]proc.Register, error) { var regs64 = []struct { k string v uint64 @@ -69,8 +75,15 @@ func (r *ARM64Registers) Slice(floatingPoint bool) []proc.Register { for _, reg := range regs64 { out = proc.AppendUint64Register(out, reg.k, reg.v) } - out = append(out, r.Fpregs...) - return out + var floatLoadError error + if floatingPoint { + if r.loadFpRegs != nil { + floatLoadError = r.loadFpRegs(r) + r.loadFpRegs = nil + } + out = append(out, r.Fpregs...) + } + return out, floatLoadError } // PC returns the value of RIP register. @@ -110,7 +123,14 @@ func (r *ARM64Registers) Get(n int) (uint64, error) { } // Copy returns a copy of these registers that is guarenteed not to change. -func (r *ARM64Registers) Copy() proc.Registers { +func (r *ARM64Registers) Copy() (proc.Registers, error) { + if r.loadFpRegs != nil { + err := r.loadFpRegs(r) + r.loadFpRegs = nil + if err != nil { + return nil, err + } + } var rr ARM64Registers rr.Regs = &ARM64PtraceRegs{} *(rr.Regs) = *(r.Regs) @@ -122,7 +142,7 @@ func (r *ARM64Registers) Copy() proc.Registers { rr.Fpregset = make([]byte, len(r.Fpregset)) copy(rr.Fpregset, r.Fpregset) } - return &rr + return &rr, nil } type ARM64PtraceFpRegs struct { diff --git a/pkg/proc/linutil/regs_i386_arch.go b/pkg/proc/linutil/regs_i386_arch.go index 6ec04d23..eb8e179a 100644 --- a/pkg/proc/linutil/regs_i386_arch.go +++ b/pkg/proc/linutil/regs_i386_arch.go @@ -15,6 +15,12 @@ type I386Registers struct { Fpregs []proc.Register Fpregset *I386Xstate Tls uint64 + + loadFpRegs func(*I386Registers) error +} + +func NewI386Registers(regs *I386PtraceRegs, loadFpRegs func(*I386Registers) error) *I386Registers { + return &I386Registers{Regs: regs, Fpregs: nil, Fpregset: nil, Tls: 0, loadFpRegs: loadFpRegs} } // I386PtraceRegs is the struct used by the linux kernel to return the @@ -40,7 +46,7 @@ type I386PtraceRegs struct { } // Slice returns the registers as a list of (name, value) pairs. -func (r *I386Registers) Slice(floatingPoint bool) []proc.Register { +func (r *I386Registers) Slice(floatingPoint bool) ([]proc.Register, error) { var regs = []struct { k string v int32 @@ -67,10 +73,15 @@ func (r *I386Registers) Slice(floatingPoint bool) []proc.Register { for _, reg := range regs { out = proc.AppendUint64Register(out, reg.k, uint64(uint32(reg.v))) } + var floatLoadError error if floatingPoint { + if r.loadFpRegs != nil { + floatLoadError = r.loadFpRegs(r) + r.loadFpRegs = nil + } out = append(out, r.Fpregs...) } - return out + return out, floatLoadError } // PC returns the value of EIP register. @@ -178,7 +189,14 @@ func (r *I386Registers) Get(n int) (uint64, error) { } // Copy returns a copy of these registers that is guarenteed not to change. -func (r *I386Registers) Copy() proc.Registers { +func (r *I386Registers) Copy() (proc.Registers, error) { + if r.loadFpRegs != nil { + err := r.loadFpRegs(r) + r.loadFpRegs = nil + if err != nil { + return nil, err + } + } var rr I386Registers rr.Regs = &I386PtraceRegs{} rr.Fpregset = &I386Xstate{} @@ -190,7 +208,7 @@ func (r *I386Registers) Copy() proc.Registers { rr.Fpregs = make([]proc.Register, len(r.Fpregs)) copy(rr.Fpregs, r.Fpregs) } - return &rr + return &rr, nil } // I386PtraceFpRegs tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h diff --git a/pkg/proc/mem.go b/pkg/proc/mem.go index 6dee7c95..d7891f48 100644 --- a/pkg/proc/mem.go +++ b/pkg/proc/mem.go @@ -104,6 +104,9 @@ func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []o sz = len(reg) } if sz > 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)", sz, piece.RegNum, len(reg)) } cmem.data = append(cmem.data, reg[:sz]...) diff --git a/pkg/proc/native/nonative_darwin.go b/pkg/proc/native/nonative_darwin.go index 5ea4dc8a..ef27a302 100644 --- a/pkg/proc/native/nonative_darwin.go +++ b/pkg/proc/native/nonative_darwin.go @@ -39,7 +39,7 @@ func killProcess(pid int) error { panic(ErrNativeBackendDisabled) } -func registers(thread *nativeThread, floatingPoint bool) (proc.Registers, error) { +func registers(thread *nativeThread) (proc.Registers, error) { panic(ErrNativeBackendDisabled) } diff --git a/pkg/proc/native/register_linux_386.go b/pkg/proc/native/register_linux_386.go index b8aa0128..e3fb1916 100644 --- a/pkg/proc/native/register_linux_386.go +++ b/pkg/proc/native/register_linux_386.go @@ -11,7 +11,7 @@ import ( // SetPC sets EIP to the value specified by 'pc'. func (thread *nativeThread) SetPC(pc uint64) error { - ir, err := registers(thread, false) + ir, err := registers(thread) if err != nil { return err } @@ -24,7 +24,7 @@ func (thread *nativeThread) SetPC(pc uint64) error { // 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, false) + ir, err = registers(thread) if err != nil { return err } @@ -36,7 +36,7 @@ func (thread *nativeThread) SetSP(sp uint64) (err error) { func (thread *nativeThread) SetDX(dx uint64) (err error) { var ir proc.Registers - ir, err = registers(thread, false) + ir, err = registers(thread) if err != nil { return err } @@ -46,7 +46,7 @@ func (thread *nativeThread) SetDX(dx uint64) (err error) { return } -func registers(thread *nativeThread, floatingPoint bool) (proc.Registers, error) { +func registers(thread *nativeThread) (proc.Registers, error) { var ( regs linutil.I386PtraceRegs err error @@ -55,15 +55,13 @@ func registers(thread *nativeThread, floatingPoint bool) (proc.Registers, error) if err != nil { return nil, err } - r := &linutil.I386Registers{®s, nil, nil, 0} - if floatingPoint { + r := linutil.NewI386Registers(®s, func(r *linutil.I386Registers) error { var fpregset linutil.I386Xstate - r.Fpregs, fpregset, err = thread.fpRegisters() + var floatLoadError error + r.Fpregs, fpregset, floatLoadError = thread.fpRegisters() r.Fpregset = &fpregset - if err != nil { - return nil, err - } - } + return floatLoadError + }) thread.dbp.execPtraceFunc(func() { tls, _ := ptraceGetTls(regs.Xgs, thread.ThreadID()) r.Tls = uint64(tls) diff --git a/pkg/proc/native/registers_darwin_amd64.go b/pkg/proc/native/registers_darwin_amd64.go index e3006a0a..8923441a 100644 --- a/pkg/proc/native/registers_darwin_amd64.go +++ b/pkg/proc/native/registers_darwin_amd64.go @@ -41,7 +41,7 @@ type Regs struct { fpregs []proc.Register } -func (r *Regs) Slice(floatingPoint bool) []proc.Register { +func (r *Regs) Slice(floatingPoint bool) ([]proc.Register, error) { var regs = []struct { k string v uint64 @@ -80,7 +80,7 @@ func (r *Regs) Slice(floatingPoint bool) []proc.Register { if floatingPoint { out = append(out, r.fpregs...) } - return out + return out, nil } // PC returns the current program counter @@ -285,7 +285,7 @@ func (r *Regs) Get(n int) (uint64, error) { return 0, proc.ErrUnknownRegister } -func registers(thread *nativeThread, floatingPoint bool) (proc.Registers, error) { +func registers(thread *nativeThread) (proc.Registers, error) { var state C.x86_thread_state64_t var identity C.thread_identifier_info_data_t kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &state) @@ -333,37 +333,36 @@ func registers(thread *nativeThread, floatingPoint bool) (proc.Registers, error) gsBase: uint64(identity.thread_handle), } - if floatingPoint { - // https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/thread_status.h?txt - var fpstate C.x86_float_state64_t - kret = C.get_fpu_registers(C.mach_port_name_t(thread.os.threadAct), &fpstate) - if kret != C.KERN_SUCCESS { - return nil, fmt.Errorf("could not get floating point registers") - } - - regs.fpregs = proc.AppendUint64Register(regs.fpregs, "CW", uint64(*((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw))))) - regs.fpregs = proc.AppendUint64Register(regs.fpregs, "SW", uint64(*((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw))))) - regs.fpregs = proc.AppendUint64Register(regs.fpregs, "TW", uint64(fpstate.__fpu_ftw)) - regs.fpregs = proc.AppendUint64Register(regs.fpregs, "FOP", uint64(fpstate.__fpu_fop)) - regs.fpregs = proc.AppendUint64Register(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip)) - regs.fpregs = proc.AppendUint64Register(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp)) - - for i, st := range []*C.char{&fpstate.__fpu_stmm0.__mmst_reg[0], &fpstate.__fpu_stmm1.__mmst_reg[0], &fpstate.__fpu_stmm2.__mmst_reg[0], &fpstate.__fpu_stmm3.__mmst_reg[0], &fpstate.__fpu_stmm4.__mmst_reg[0], &fpstate.__fpu_stmm5.__mmst_reg[0], &fpstate.__fpu_stmm6.__mmst_reg[0], &fpstate.__fpu_stmm7.__mmst_reg[0]} { - stb := C.GoBytes(unsafe.Pointer(st), 10) - regs.fpregs = proc.AppendBytesRegister(regs.fpregs, fmt.Sprintf("ST(%d)", i), stb) - } - - regs.fpregs = proc.AppendUint64Register(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr)) - regs.fpregs = proc.AppendUint64Register(regs.fpregs, "MXCSR_MASK", uint64(fpstate.__fpu_mxcsrmask)) - - for i, xmm := range []*C.char{&fpstate.__fpu_xmm0.__xmm_reg[0], &fpstate.__fpu_xmm1.__xmm_reg[0], &fpstate.__fpu_xmm2.__xmm_reg[0], &fpstate.__fpu_xmm3.__xmm_reg[0], &fpstate.__fpu_xmm4.__xmm_reg[0], &fpstate.__fpu_xmm5.__xmm_reg[0], &fpstate.__fpu_xmm6.__xmm_reg[0], &fpstate.__fpu_xmm7.__xmm_reg[0], &fpstate.__fpu_xmm8.__xmm_reg[0], &fpstate.__fpu_xmm9.__xmm_reg[0], &fpstate.__fpu_xmm10.__xmm_reg[0], &fpstate.__fpu_xmm11.__xmm_reg[0], &fpstate.__fpu_xmm12.__xmm_reg[0], &fpstate.__fpu_xmm13.__xmm_reg[0], &fpstate.__fpu_xmm14.__xmm_reg[0], &fpstate.__fpu_xmm15.__xmm_reg[0]} { - regs.fpregs = proc.AppendBytesRegister(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16)) - } + // https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/thread_status.h?txt + var fpstate C.x86_float_state64_t + kret = C.get_fpu_registers(C.mach_port_name_t(thread.os.threadAct), &fpstate) + if kret != C.KERN_SUCCESS { + return nil, fmt.Errorf("could not get floating point registers") } + + regs.fpregs = proc.AppendUint64Register(regs.fpregs, "CW", uint64(*((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw))))) + regs.fpregs = proc.AppendUint64Register(regs.fpregs, "SW", uint64(*((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw))))) + regs.fpregs = proc.AppendUint64Register(regs.fpregs, "TW", uint64(fpstate.__fpu_ftw)) + regs.fpregs = proc.AppendUint64Register(regs.fpregs, "FOP", uint64(fpstate.__fpu_fop)) + regs.fpregs = proc.AppendUint64Register(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip)) + regs.fpregs = proc.AppendUint64Register(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp)) + + for i, st := range []*C.char{&fpstate.__fpu_stmm0.__mmst_reg[0], &fpstate.__fpu_stmm1.__mmst_reg[0], &fpstate.__fpu_stmm2.__mmst_reg[0], &fpstate.__fpu_stmm3.__mmst_reg[0], &fpstate.__fpu_stmm4.__mmst_reg[0], &fpstate.__fpu_stmm5.__mmst_reg[0], &fpstate.__fpu_stmm6.__mmst_reg[0], &fpstate.__fpu_stmm7.__mmst_reg[0]} { + stb := C.GoBytes(unsafe.Pointer(st), 10) + regs.fpregs = proc.AppendBytesRegister(regs.fpregs, fmt.Sprintf("ST(%d)", i), stb) + } + + regs.fpregs = proc.AppendUint64Register(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr)) + regs.fpregs = proc.AppendUint64Register(regs.fpregs, "MXCSR_MASK", uint64(fpstate.__fpu_mxcsrmask)) + + for i, xmm := range []*C.char{&fpstate.__fpu_xmm0.__xmm_reg[0], &fpstate.__fpu_xmm1.__xmm_reg[0], &fpstate.__fpu_xmm2.__xmm_reg[0], &fpstate.__fpu_xmm3.__xmm_reg[0], &fpstate.__fpu_xmm4.__xmm_reg[0], &fpstate.__fpu_xmm5.__xmm_reg[0], &fpstate.__fpu_xmm6.__xmm_reg[0], &fpstate.__fpu_xmm7.__xmm_reg[0], &fpstate.__fpu_xmm8.__xmm_reg[0], &fpstate.__fpu_xmm9.__xmm_reg[0], &fpstate.__fpu_xmm10.__xmm_reg[0], &fpstate.__fpu_xmm11.__xmm_reg[0], &fpstate.__fpu_xmm12.__xmm_reg[0], &fpstate.__fpu_xmm13.__xmm_reg[0], &fpstate.__fpu_xmm14.__xmm_reg[0], &fpstate.__fpu_xmm15.__xmm_reg[0]} { + regs.fpregs = proc.AppendBytesRegister(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16)) + } + return regs, nil } -func (r *Regs) Copy() proc.Registers { +func (r *Regs) Copy() (proc.Registers, error) { //TODO(aarzilli): implement this to support function calls - return nil + return nil, nil } diff --git a/pkg/proc/native/registers_freebsd_amd64.go b/pkg/proc/native/registers_freebsd_amd64.go index 1ee17fa4..7e7de291 100644 --- a/pkg/proc/native/registers_freebsd_amd64.go +++ b/pkg/proc/native/registers_freebsd_amd64.go @@ -11,7 +11,7 @@ import ( // SetPC sets RIP to the value specified by 'pc'. func (thread *nativeThread) SetPC(pc uint64) error { - ir, err := registers(thread, false) + ir, err := registers(thread) if err != nil { return err } @@ -24,7 +24,7 @@ func (thread *nativeThread) SetPC(pc uint64) error { // 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, false) + ir, err = registers(thread) if err != nil { return err } @@ -36,7 +36,7 @@ func (thread *nativeThread) SetSP(sp uint64) (err error) { func (thread *nativeThread) SetDX(dx uint64) (err error) { var ir proc.Registers - ir, err = registers(thread, false) + ir, err = registers(thread) if err != nil { return err } @@ -46,7 +46,7 @@ func (thread *nativeThread) SetDX(dx uint64) (err error) { return } -func registers(thread *nativeThread, floatingPoint bool) (proc.Registers, error) { +func registers(thread *nativeThread) (proc.Registers, error) { var ( regs fbsdutil.AMD64PtraceRegs err error @@ -60,15 +60,13 @@ func registers(thread *nativeThread, floatingPoint bool) (proc.Registers, error) if err != nil { return nil, err } - r := &fbsdutil.AMD64Registers{®s, nil, nil, uint64(fsbase)} - if floatingPoint { + r := fbsdutil.NewAMD64Registers(®s, uint64(fsbase), func(r *fbsdutil.AMD64Registers) error { var fpregset fbsdutil.AMD64Xstate - r.Fpregs, fpregset, err = thread.fpRegisters() + var floatLoadError error + r.Fpregs, fpregset, floatLoadError = thread.fpRegisters() r.Fpregset = &fpregset - if err != nil { - return nil, err - } - } + return floatLoadError + }) return r, nil } diff --git a/pkg/proc/native/registers_linux_amd64.go b/pkg/proc/native/registers_linux_amd64.go index 9a1607da..5066af01 100644 --- a/pkg/proc/native/registers_linux_amd64.go +++ b/pkg/proc/native/registers_linux_amd64.go @@ -11,7 +11,7 @@ import ( // SetPC sets RIP to the value specified by 'pc'. func (thread *nativeThread) SetPC(pc uint64) error { - ir, err := registers(thread, false) + ir, err := registers(thread) if err != nil { return err } @@ -24,7 +24,7 @@ func (thread *nativeThread) SetPC(pc uint64) error { // 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, false) + ir, err = registers(thread) if err != nil { return err } @@ -36,7 +36,7 @@ func (thread *nativeThread) SetSP(sp uint64) (err error) { func (thread *nativeThread) SetDX(dx uint64) (err error) { var ir proc.Registers - ir, err = registers(thread, false) + ir, err = registers(thread) if err != nil { return err } @@ -46,7 +46,7 @@ func (thread *nativeThread) SetDX(dx uint64) (err error) { return } -func registers(thread *nativeThread, floatingPoint bool) (proc.Registers, error) { +func registers(thread *nativeThread) (proc.Registers, error) { var ( regs linutil.AMD64PtraceRegs err error @@ -55,15 +55,13 @@ func registers(thread *nativeThread, floatingPoint bool) (proc.Registers, error) if err != nil { return nil, err } - r := &linutil.AMD64Registers{Regs: ®s} - if floatingPoint { + r := linutil.NewAMD64Registers(®s, func(r *linutil.AMD64Registers) error { var fpregset linutil.AMD64Xstate - r.Fpregs, fpregset, err = thread.fpRegisters() + var floatLoadError error + r.Fpregs, fpregset, floatLoadError = thread.fpRegisters() r.Fpregset = &fpregset - if err != nil { - return nil, err - } - } + return floatLoadError + }) return r, nil } diff --git a/pkg/proc/native/registers_linux_arm64.go b/pkg/proc/native/registers_linux_arm64.go index 7c349254..19ee8aba 100644 --- a/pkg/proc/native/registers_linux_arm64.go +++ b/pkg/proc/native/registers_linux_arm64.go @@ -56,7 +56,7 @@ func ptraceGetFpRegset(tid int) (fpregset []byte, err error) { // SetPC sets PC to the value specified by 'pc'. func (thread *nativeThread) SetPC(pc uint64) error { - ir, err := registers(thread, false) + ir, err := registers(thread) if err != nil { return err } @@ -69,7 +69,7 @@ func (thread *nativeThread) SetPC(pc uint64) error { // 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, false) + ir, err = registers(thread) if err != nil { return err } @@ -83,7 +83,7 @@ func (thread *nativeThread) SetDX(dx uint64) (err error) { return fmt.Errorf("not supported") } -func registers(thread *nativeThread, floatingPoint bool) (proc.Registers, error) { +func registers(thread *nativeThread) (proc.Registers, error) { var ( regs linutil.ARM64PtraceRegs err error @@ -92,12 +92,10 @@ func registers(thread *nativeThread, floatingPoint bool) (proc.Registers, error) if err != nil { return nil, err } - r := &linutil.ARM64Registers{®s, nil, nil} - if floatingPoint { - r.Fpregs, r.Fpregset, err = thread.fpRegisters() - if err != nil { - return nil, err - } - } + r := linutil.NewARM64Registers(®s, func(r *linutil.ARM64Registers) error { + var floatLoadError error + r.Fpregs, r.Fpregset, floatLoadError = thread.fpRegisters() + return floatLoadError + }) return r, nil } diff --git a/pkg/proc/native/registers_windows_amd64.go b/pkg/proc/native/registers_windows_amd64.go index cc702269..f3669e40 100644 --- a/pkg/proc/native/registers_windows_amd64.go +++ b/pkg/proc/native/registers_windows_amd64.go @@ -52,7 +52,7 @@ func (thread *nativeThread) SetDX(dx uint64) error { return _SetThreadContext(thread.os.hThread, context) } -func registers(thread *nativeThread, floatingPoint bool) (proc.Registers, error) { +func registers(thread *nativeThread) (proc.Registers, error) { context := winutil.NewCONTEXT() context.ContextFlags = _CONTEXT_ALL @@ -67,5 +67,5 @@ func registers(thread *nativeThread, floatingPoint bool) (proc.Registers, error) return nil, fmt.Errorf("NtQueryInformationThread failed: it returns 0x%x", status) } - return winutil.NewAMD64Registers(context, uint64(threadInfo.TebBaseAddress), floatingPoint), nil + return winutil.NewAMD64Registers(context, uint64(threadInfo.TebBaseAddress)), nil } diff --git a/pkg/proc/native/threads.go b/pkg/proc/native/threads.go index daf5d4f6..780f3bc8 100644 --- a/pkg/proc/native/threads.go +++ b/pkg/proc/native/threads.go @@ -157,8 +157,8 @@ func (t *nativeThread) ClearBreakpoint(bp *proc.Breakpoint) error { } // Registers obtains register values from the debugged process. -func (t *nativeThread) Registers(floatingPoint bool) (proc.Registers, error) { - return registers(t, floatingPoint) +func (t *nativeThread) Registers() (proc.Registers, error) { + return registers(t) } // RestoreRegisters will set the value of the CPU registers to those @@ -169,7 +169,7 @@ func (t *nativeThread) RestoreRegisters(savedRegs proc.Registers) error { // PC returns the current program counter value for this thread. func (t *nativeThread) PC() (uint64, error) { - regs, err := t.Registers(false) + regs, err := t.Registers() if err != nil { return 0, err } diff --git a/pkg/proc/native/threads_darwin.go b/pkg/proc/native/threads_darwin.go index 68fe01da..1ce8902d 100644 --- a/pkg/proc/native/threads_darwin.go +++ b/pkg/proc/native/threads_darwin.go @@ -87,7 +87,7 @@ func (t *nativeThread) resume() error { func (t *nativeThread) Blocked() bool { // TODO(dp) cache the func pc to remove this lookup - regs, err := t.Registers(false) + regs, err := t.Registers() if err != nil { return false } diff --git a/pkg/proc/native/threads_linux.go b/pkg/proc/native/threads_linux.go index 20c62fe4..0f6dfa79 100644 --- a/pkg/proc/native/threads_linux.go +++ b/pkg/proc/native/threads_linux.go @@ -72,7 +72,7 @@ func (t *nativeThread) singleStep() (err error) { } func (t *nativeThread) Blocked() bool { - regs, err := t.Registers(false) + regs, err := t.Registers() if err != nil { return false } diff --git a/pkg/proc/native/threads_windows.go b/pkg/proc/native/threads_windows.go index 9c729a66..fb429377 100644 --- a/pkg/proc/native/threads_windows.go +++ b/pkg/proc/native/threads_windows.go @@ -114,7 +114,7 @@ func (t *nativeThread) resume() error { func (t *nativeThread) Blocked() bool { // TODO: Probably incorrect - what are the runtime functions that // indicate blocking on Windows? - regs, err := t.Registers(false) + regs, err := t.Registers() if err != nil { return false } diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index d0b7ad44..9e16093a 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -90,7 +90,7 @@ func withTestProcessArgs(name string, t testing.TB, wd string, args []string, bu } func getRegisters(p *proc.Target, t *testing.T) proc.Registers { - regs, err := p.CurrentThread().Registers(false) + regs, err := p.CurrentThread().Registers() if err != nil { t.Fatal("Registers():", err) } @@ -113,7 +113,7 @@ func assertNoError(err error, t testing.TB, s string) { } func currentPC(p *proc.Target, t *testing.T) uint64 { - regs, err := p.CurrentThread().Registers(false) + regs, err := p.CurrentThread().Registers() if err != nil { t.Fatal(err) } @@ -280,7 +280,7 @@ func TestBreakpoint(t *testing.T) { bp := setFunctionBreakpoint(p, t, "main.helloworld") assertNoError(p.Continue(), t, "Continue()") - regs, err := p.CurrentThread().Registers(false) + regs, err := p.CurrentThread().Registers() assertNoError(err, t, "Registers") pc := regs.PC() @@ -302,7 +302,7 @@ func TestBreakpointInSeparateGoRoutine(t *testing.T) { assertNoError(p.Continue(), t, "Continue") - regs, err := p.CurrentThread().Registers(false) + regs, err := p.CurrentThread().Registers() assertNoError(err, t, "Registers") pc := regs.PC() @@ -409,7 +409,7 @@ func testseq2Args(wd string, args []string, buildFlags protest.BuildFlags, t *te if traceTestseq2 { t.Logf("initial breakpoint %v", bp) } - regs, err := p.CurrentThread().Registers(false) + regs, err := p.CurrentThread().Registers() assertNoError(err, t, "Registers") f, ln := currentLineNumber(p, t) @@ -474,7 +474,7 @@ func testseq2Args(wd string, args []string, buildFlags protest.BuildFlags, t *te } f, ln = currentLineNumber(p, t) - regs, _ = p.CurrentThread().Registers(false) + regs, _ = p.CurrentThread().Registers() pc := regs.PC() if traceTestseq2 { @@ -710,7 +710,7 @@ func TestRuntimeBreakpoint(t *testing.T) { if err != nil { t.Fatal(err) } - regs, err := p.CurrentThread().Registers(false) + regs, err := p.CurrentThread().Registers() assertNoError(err, t, "Registers") pc := regs.PC() f, l, _ := p.BinInfo().PCToLine(pc) @@ -1770,7 +1770,7 @@ func TestIssue332_Part2(t *testing.T) { } } - regs, err := p.CurrentThread().Registers(false) + regs, err := p.CurrentThread().Registers() assertNoError(err, t, "Registers()") pc := regs.PC() pcAfterPrologue := findFunctionLocation(p, t, "main.changeMe") @@ -2502,7 +2502,7 @@ func TestStepOnCallPtrInstr(t *testing.T) { if ln != 10 { break } - regs, err := p.CurrentThread().Registers(false) + regs, err := p.CurrentThread().Registers() assertNoError(err, t, "Registers()") pc := regs.PC() text, err := proc.Disassemble(p.CurrentThread(), regs, p.Breakpoints(), p.BinInfo(), pc, pc+uint64(p.BinInfo().Arch.MaxInstructionLength())) @@ -3611,7 +3611,7 @@ func TestDisassembleGlobalVars(t *testing.T) { } withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) { mainfn := p.BinInfo().LookupFunc["main.main"] - regs, _ := p.CurrentThread().Registers(false) + regs, _ := p.CurrentThread().Registers() text, err := proc.Disassemble(p.CurrentThread(), regs, p.Breakpoints(), p.BinInfo(), mainfn.Entry, mainfn.End) assertNoError(err, t, "Disassemble") found := false @@ -4168,7 +4168,7 @@ func TestIssue1374(t *testing.T) { assertNoError(proc.EvalExpressionWithCalls(p, p.SelectedGoroutine(), "getNum()", normalLoadConfig, true), t, "Call") err := p.Continue() if _, isexited := err.(proc.ErrProcessExited); !isexited { - regs, _ := p.CurrentThread().Registers(false) + regs, _ := p.CurrentThread().Registers() f, l, _ := p.BinInfo().PCToLine(regs.PC()) t.Fatalf("expected process exited error got %v at %s:%d", err, f, l) } diff --git a/pkg/proc/registers.go b/pkg/proc/registers.go index b60f3438..22c79f6f 100644 --- a/pkg/proc/registers.go +++ b/pkg/proc/registers.go @@ -21,10 +21,10 @@ type Registers interface { // GAddr returns the address of the G variable if it is known, 0 and false otherwise GAddr() (uint64, bool) Get(int) (uint64, error) - Slice(floatingPoint bool) []Register + Slice(floatingPoint bool) ([]Register, error) // Copy returns a copy of the registers that is guaranteed not to change // when the registers of the associated thread change. - Copy() Registers + Copy() (Registers, error) } // Register represents a CPU register. diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go index 907d2d56..12fc2753 100644 --- a/pkg/proc/stack.go +++ b/pkg/proc/stack.go @@ -95,7 +95,7 @@ func (frame *Stackframe) FramePointerOffset() int64 { func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) { g, _ := GetG(thread) if g == nil { - regs, err := thread.Registers(true) + regs, err := thread.Registers() if err != nil { return nil, err } @@ -114,7 +114,7 @@ func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) { bi := g.variable.bi if g.Thread != nil { - regs, err := g.Thread.Registers(true) + regs, err := g.Thread.Registers() if err != nil { return nil, err } @@ -456,8 +456,8 @@ func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uin } if it.bi.Arch.Name == "arm64" { - if ret == 0 && it.regs.Regs[it.regs.LRRegNum] != nil { - ret = it.regs.Regs[it.regs.LRRegNum].Uint64Val + if ret == 0 && it.regs.Reg(it.regs.LRRegNum) != nil { + ret = it.regs.Reg(it.regs.LRRegNum).Uint64Val } } @@ -666,7 +666,7 @@ func (d *Defer) EvalScope(thread Thread) (*EvalScope, error) { // the space occupied by pushing the return address on the stack during the // CALL. scope.Regs.CFA = (int64(d.variable.Addr) + d.variable.RealType.Common().ByteSize) - scope.Regs.Regs[scope.Regs.SPRegNum].Uint64Val = uint64(scope.Regs.CFA - int64(bi.Arch.PtrSize())) + scope.Regs.Reg(scope.Regs.SPRegNum).Uint64Val = uint64(scope.Regs.CFA - int64(bi.Arch.PtrSize())) rdr := scope.Fn.cu.image.dwarfReader rdr.Seek(scope.Fn.offset) diff --git a/pkg/proc/target_exec.go b/pkg/proc/target_exec.go index b0016173..792a66ca 100644 --- a/pkg/proc/target_exec.go +++ b/pkg/proc/target_exec.go @@ -230,7 +230,7 @@ func pickCurrentThread(dbp *Target, trapthread Thread, threads []Thread) error { } func disassembleCurrentInstruction(p Process, thread Thread) ([]AsmInstruction, error) { - regs, err := thread.Registers(false) + regs, err := thread.Registers() if err != nil { return nil, err } @@ -474,7 +474,7 @@ func next(dbp *Target, stepInto, inlinedStepOut bool) error { var regs Registers if selg != nil && selg.Thread != nil { thread = selg.Thread - regs, err = selg.Thread.Registers(false) + regs, err = selg.Thread.Registers() if err != nil { return err } diff --git a/pkg/proc/threads.go b/pkg/proc/threads.go index 1d2068f7..8ca17ff3 100644 --- a/pkg/proc/threads.go +++ b/pkg/proc/threads.go @@ -19,7 +19,7 @@ type Thread interface { // SetPC/SetSP/etc. // To insure that the the returned variable won't change call the Copy // method of Registers. - Registers(floatingPoint bool) (Registers, error) + Registers() (Registers, error) // RestoreRegisters restores saved registers RestoreRegisters(Registers) error diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index b8ad1af5..51a695bf 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -422,7 +422,7 @@ func FindGoroutine(dbp *Target, gid int) (*G, error) { } func getGVariable(thread Thread) (*Variable, error) { - regs, err := thread.Registers(false) + regs, err := thread.Registers() if err != nil { return nil, err } diff --git a/pkg/proc/winutil/regs.go b/pkg/proc/winutil/regs.go index 6ea2baa2..3a5acabd 100644 --- a/pkg/proc/winutil/regs.go +++ b/pkg/proc/winutil/regs.go @@ -41,7 +41,7 @@ type AMD64Registers struct { // NewAMD64Registers creates a new AMD64Registers struct from a CONTEXT // struct and the TEB base address of the thread. -func NewAMD64Registers(context *CONTEXT, TebBaseAddress uint64, floatingPoint bool) *AMD64Registers { +func NewAMD64Registers(context *CONTEXT, TebBaseAddress uint64) *AMD64Registers { regs := &AMD64Registers{ rax: uint64(context.Rax), rbx: uint64(context.Rbx), @@ -67,16 +67,14 @@ func NewAMD64Registers(context *CONTEXT, TebBaseAddress uint64, floatingPoint bo tls: TebBaseAddress, } - if floatingPoint { - regs.fltSave = &context.FltSave - } + regs.fltSave = &context.FltSave regs.Context = context return regs } // Slice returns the registers as a list of (name, value) pairs. -func (r *AMD64Registers) Slice(floatingPoint bool) []proc.Register { +func (r *AMD64Registers) Slice(floatingPoint bool) ([]proc.Register, error) { var regs = []struct { k string v uint64 @@ -134,7 +132,7 @@ func (r *AMD64Registers) Slice(floatingPoint bool) []proc.Register { out = proc.AppendBytesRegister(out, fmt.Sprintf("XMM%d", i/16), r.fltSave.XmmRegisters[i:i+16]) } } - return out + return out, nil } // PC returns the current program counter @@ -325,13 +323,13 @@ func (r *AMD64Registers) Get(n int) (uint64, error) { } // Copy returns a copy of these registers that is guarenteed not to change. -func (r *AMD64Registers) Copy() proc.Registers { +func (r *AMD64Registers) Copy() (proc.Registers, error) { var rr AMD64Registers rr = *r rr.Context = NewCONTEXT() *(rr.Context) = *(r.Context) rr.fltSave = &rr.Context.FltSave - return &rr + return &rr, nil } // M128A tracks the _M128A windows struct. diff --git a/service/api/conversions.go b/service/api/conversions.go index 25470633..321b7a4e 100644 --- a/service/api/conversions.go +++ b/service/api/conversions.go @@ -330,8 +330,11 @@ func LoadConfigFromProc(cfg *proc.LoadConfig) *LoadConfig { // ConvertRegisters converts proc.Register to api.Register for a slice. func ConvertRegisters(in op.DwarfRegisters, arch *proc.Arch, floatingPoint bool) (out []Register) { - out = make([]Register, 0, len(in.Regs)) - for i := range in.Regs { + if floatingPoint { + in.Reg(^uint64(0)) // force loading all registers + } + out = make([]Register, 0, in.CurrentSize()) + for i := 0; i < in.CurrentSize(); i++ { reg := in.Reg(uint64(i)) if reg == nil { continue diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go index ec6c3ff3..2bdcb8ca 100644 --- a/service/debugger/debugger.go +++ b/service/debugger/debugger.go @@ -350,7 +350,7 @@ func (d *Debugger) FunctionReturnLocations(fnName string) ([]uint64, error) { var mem proc.MemoryReadWriter = p.CurrentThread() if g != nil && g.Thread != nil { mem = g.Thread - regs, _ = g.Thread.Registers(false) + regs, _ = g.Thread.Registers() } instructions, err := proc.Disassemble(mem, regs, p.Breakpoints(), p.BinInfo(), fn.Entry, fn.End) if err != nil { @@ -1159,13 +1159,16 @@ func (d *Debugger) Registers(threadID int, scope *api.EvalScope, floatingPoint b if !found { return nil, fmt.Errorf("couldn't find thread %d", threadID) } - regs, err := thread.Registers(floatingPoint) + regs, err := thread.Registers() if err != nil { return nil, err } dregs = d.target.BinInfo().Arch.RegistersToDwarfRegisters(0, regs) } r := api.ConvertRegisters(dregs, d.target.BinInfo().Arch, floatingPoint) + if floatingPoint && dregs.FloatLoadError != nil { + return nil, dregs.FloatLoadError + } // Sort the registers in a canonical order we prefer, this is mostly // because the DWARF register numbering for AMD64 is weird. sort.Slice(r, func(i, j int) bool { @@ -1488,7 +1491,7 @@ func (d *Debugger) Disassemble(goroutineID int, addr1, addr2 uint64, flavour api if g != nil && g.Thread != nil { curthread = g.Thread } - regs, _ := curthread.Registers(false) + regs, _ := curthread.Registers() insts, err := proc.Disassemble(curthread, regs, d.target.Breakpoints(), d.target.BinInfo(), addr1, addr2) if err != nil { diff --git a/service/test/variables_test.go b/service/test/variables_test.go index 5d000e4a..61b544b6 100644 --- a/service/test/variables_test.go +++ b/service/test/variables_test.go @@ -1439,7 +1439,7 @@ func setFileBreakpoint(p *proc.Target, t *testing.T, fixture protest.Fixture, li } func currentLocation(p *proc.Target, t *testing.T) (pc uint64, f string, ln int, fn *proc.Function) { - regs, err := p.CurrentThread().Registers(false) + regs, err := p.CurrentThread().Registers() if err != nil { t.Fatalf("Registers error: %v", err) }