From 200994bc8f6108cc441fdf110ddb44e0184c1fe0 Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Wed, 13 May 2020 20:56:50 +0200 Subject: [PATCH] proc/*: only load floating point registers when needed (#1981) Changes implementations of proc.Registers interface and the op.DwarfRegisters struct so that floating point registers can be loaded only when they are needed. Removes the floatingPoint parameter from proc.Thread.Registers. This accomplishes three things: 1. it simplifies the proc.Thread.Registers interface 2. it makes it impossible to accidentally create a broken set of saved registers or of op.DwarfRegisters by accidentally calling Registers(false) 3. it improves general performance of Delve by avoiding to load floating point registers as much as possible Floating point registers are loaded under two circumstances: 1. When the Slice method is called with floatingPoint == true 2. When the Copy method is called Benchmark before: BenchmarkConditionalBreakpoints-4 1 4327350142 ns/op Benchmark after: BenchmarkConditionalBreakpoints-4 1 3852642917 ns/op Updates #1549 --- pkg/dwarf/op/regs.go | 68 +++++++++++++++++++--- pkg/proc/amd64_arch.go | 40 +++++++------ pkg/proc/arm64_arch.go | 36 ++++++------ pkg/proc/core/core.go | 8 +-- pkg/proc/core/core_test.go | 12 ++-- pkg/proc/core/linux_core.go | 12 ++-- pkg/proc/core/windows_amd64_minidump.go | 4 +- pkg/proc/dwarf_expr_test.go | 2 +- pkg/proc/fbsdutil/regs.go | 26 +++++++-- pkg/proc/fncall.go | 36 ++++++------ pkg/proc/gdbserial/gdbserver.go | 20 ++++--- pkg/proc/i386_arch.go | 32 +++------- pkg/proc/linutil/regs_amd64_arch.go | 26 +++++++-- pkg/proc/linutil/regs_arm64_arch.go | 30 ++++++++-- pkg/proc/linutil/regs_i386_arch.go | 26 +++++++-- pkg/proc/mem.go | 3 + pkg/proc/native/nonative_darwin.go | 2 +- pkg/proc/native/register_linux_386.go | 20 +++---- pkg/proc/native/registers_darwin_amd64.go | 61 ++++++++++--------- pkg/proc/native/registers_freebsd_amd64.go | 20 +++---- pkg/proc/native/registers_linux_amd64.go | 20 +++---- pkg/proc/native/registers_linux_arm64.go | 18 +++--- pkg/proc/native/registers_windows_amd64.go | 4 +- pkg/proc/native/threads.go | 6 +- pkg/proc/native/threads_darwin.go | 2 +- pkg/proc/native/threads_linux.go | 2 +- pkg/proc/native/threads_windows.go | 2 +- pkg/proc/proc_test.go | 22 +++---- pkg/proc/registers.go | 4 +- pkg/proc/stack.go | 10 ++-- pkg/proc/target_exec.go | 4 +- pkg/proc/threads.go | 2 +- pkg/proc/variables.go | 2 +- pkg/proc/winutil/regs.go | 14 ++--- service/api/conversions.go | 7 ++- service/debugger/debugger.go | 9 ++- service/test/variables_test.go | 2 +- 37 files changed, 364 insertions(+), 250 deletions(-) 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) }