diff --git a/pkg/dwarf/reader/reader.go b/pkg/dwarf/reader/reader.go index 36d2f402..39bff123 100755 --- a/pkg/dwarf/reader/reader.go +++ b/pkg/dwarf/reader/reader.go @@ -319,3 +319,49 @@ func (reader *Reader) NextCompileUnit() (*dwarf.Entry, error) { return nil, nil } + +// Entry represents a debug_info entry. +// When calling Val, if the entry does not have the specified attribute, the +// entry specified by DW_AT_abstract_origin will be searched recursively. +type Entry interface { + Val(dwarf.Attr) interface{} +} + +type compositeEntry []*dwarf.Entry + +func (ce compositeEntry) Val(attr dwarf.Attr) interface{} { + for _, e := range ce { + if r := e.Val(attr); r != nil { + return r + } + } + return nil +} + +// LoadAbstractOrigin loads the entry corresponding to the +// DW_AT_abstract_origin of entry and returns a combination of entry and its +// abstract origin. +func LoadAbstractOrigin(entry *dwarf.Entry, aordr *dwarf.Reader) (Entry, dwarf.Offset) { + ao, ok := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) + if !ok { + return entry, entry.Offset + } + + r := []*dwarf.Entry{entry} + + for { + aordr.Seek(ao) + e, _ := aordr.Next() + if e == nil { + break + } + r = append(r, e) + + ao, ok = e.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) + if !ok { + break + } + } + + return compositeEntry(r), entry.Offset +} diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go index 4f0fea01..0bc95898 100644 --- a/pkg/proc/bininfo.go +++ b/pkg/proc/bininfo.go @@ -59,6 +59,8 @@ type BinaryInfo struct { loadErrMu sync.Mutex loadErr error + + dwarfReader *dwarf.Reader } var UnsupportedLinuxArchErr = errors.New("unsupported architecture - only linux/amd64 is supported") @@ -342,7 +344,7 @@ func (bi *BinaryInfo) loclistInit(data []byte) { // This will either be an int64 address or a slice of Pieces for locations // that don't correspond to a single memory address (registers, composite // locations). -func (bi *BinaryInfo) Location(entry *dwarf.Entry, attr dwarf.Attr, pc uint64, regs op.DwarfRegisters) (int64, []op.Piece, string, error) { +func (bi *BinaryInfo) Location(entry reader.Entry, attr dwarf.Attr, pc uint64, regs op.DwarfRegisters) (int64, []op.Piece, string, error) { a := entry.Val(attr) if a == nil { return 0, nil, "", fmt.Errorf("no location attribute %s", attr) @@ -425,6 +427,8 @@ func (bi *BinaryInfo) LoadBinaryInfoElf(path string, wg *sync.WaitGroup) error { return err } + bi.dwarfReader = bi.dwarf.Reader() + debugLineBytes, err := getDebugLineInfoElf(elfFile) if err != nil { return err @@ -535,6 +539,8 @@ func (bi *BinaryInfo) LoadBinaryInfoPE(path string, wg *sync.WaitGroup) error { return err } + bi.dwarfReader = bi.dwarf.Reader() + debugLineBytes, err := getDebugLineInfoPE(peFile) if err != nil { return err @@ -701,6 +707,8 @@ func (bi *BinaryInfo) LoadBinaryInfoMacho(path string, wg *sync.WaitGroup) error return err } + bi.dwarfReader = bi.dwarf.Reader() + debugLineBytes, err := getDebugLineInfoMacho(exe) if err != nil { return err diff --git a/pkg/proc/core/core_test.go b/pkg/proc/core/core_test.go index 0abfd478..5f0977a5 100644 --- a/pkg/proc/core/core_test.go +++ b/pkg/proc/core/core_test.go @@ -197,7 +197,7 @@ func TestCore(t *testing.T) { if mainFrame == nil { t.Fatalf("Couldn't find main in stack %v", panickingStack) } - msg, err := proc.FrameToScope(p, *mainFrame).EvalVariable("msg", proc.LoadConfig{MaxStringLen: 64}) + msg, err := proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, *mainFrame).EvalVariable("msg", proc.LoadConfig{MaxStringLen: 64}) if err != nil { t.Fatalf("Couldn't EvalVariable(msg, ...): %v", err) } diff --git a/pkg/proc/moduledata.go b/pkg/proc/moduledata.go index ec56f5d2..c38f7787 100644 --- a/pkg/proc/moduledata.go +++ b/pkg/proc/moduledata.go @@ -3,8 +3,6 @@ package proc import ( "go/constant" "unsafe" - - "github.com/derekparker/delve/pkg/dwarf/op" ) // delve counterpart to runtime.moduledata @@ -15,7 +13,7 @@ type moduleData struct { func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) (err error) { bi.loadModuleDataOnce.Do(func() { - scope := &EvalScope{0, op.DwarfRegisters{}, mem, nil, bi, 0} + scope := globalScope(bi, mem) var md *Variable md, err = scope.findGlobal("runtime.firstmoduledata") if err != nil { @@ -121,7 +119,7 @@ func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryRea } func reflectOffsMapAccess(bi *BinaryInfo, off uintptr, mem MemoryReadWriter) (*Variable, error) { - scope := &EvalScope{0, op.DwarfRegisters{}, mem, nil, bi, 0} + scope := globalScope(bi, mem) reflectOffs, err := scope.findGlobal("runtime.reflectOffs") if err != nil { return nil, err diff --git a/pkg/proc/proc.go b/pkg/proc/proc.go index ce3b08e4..0263f6c1 100644 --- a/pkg/proc/proc.go +++ b/pkg/proc/proc.go @@ -482,10 +482,15 @@ func ConvertEvalScope(dbp Process, gid, frame int) (*EvalScope, error) { return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid) } - return &EvalScope{locs[frame].Current.PC, locs[frame].Regs, thread, g.variable, dbp.BinInfo(), locs[frame].FrameOffset()}, nil + return FrameToScope(dbp.BinInfo(), thread, g, locs[frame]), nil } // FrameToScope returns a new EvalScope for this frame -func FrameToScope(p Process, frame Stackframe) *EvalScope { - return &EvalScope{frame.Current.PC, frame.Regs, p.CurrentThread(), nil, p.BinInfo(), frame.FrameOffset()} +func FrameToScope(bi *BinaryInfo, thread MemoryReadWriter, g *G, frame Stackframe) *EvalScope { + var gvar *Variable + if g != nil { + gvar = g.variable + } + s := &EvalScope{PC: frame.Call.PC, Regs: frame.Regs, Mem: thread, Gvar: gvar, BinInfo: bi, frameOffset: frame.FrameOffset()} + return s } diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index 362cc9d3..938a4993 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -1119,7 +1119,7 @@ func evalVariableOrError(p proc.Process, symbol string) (*proc.Variable, error) var frame proc.Stackframe frame, err = findFirstNonRuntimeFrame(p) if err == nil { - scope = proc.FrameToScope(p, frame) + scope = proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frame) } } else { scope, err = proc.GoroutineScope(p.CurrentThread()) @@ -2931,7 +2931,7 @@ func TestIssue871(t *testing.T) { var frame proc.Stackframe frame, err = findFirstNonRuntimeFrame(p) if err == nil { - scope = proc.FrameToScope(p, frame) + scope = proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frame) } } else { scope, err = proc.GoroutineScope(p.CurrentThread()) @@ -3345,7 +3345,7 @@ func TestIssue1034(t *testing.T) { assertNoError(proc.Continue(p), t, "Continue()") frames, err := p.SelectedGoroutine().Stacktrace(10) assertNoError(err, t, "Stacktrace") - scope := proc.FrameToScope(p, frames[2]) + scope := proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frames[2]) args, _ := scope.FunctionArguments(normalLoadConfig) assertNoError(err, t, "FunctionArguments()") if len(args) > 0 { diff --git a/pkg/proc/threads.go b/pkg/proc/threads.go index 4950187b..0f37f89e 100644 --- a/pkg/proc/threads.go +++ b/pkg/proc/threads.go @@ -398,7 +398,7 @@ func ThreadScope(thread Thread) (*EvalScope, error) { if len(locations) < 1 { return nil, errors.New("could not decode first frame") } - return &EvalScope{locations[0].Current.PC, locations[0].Regs, thread, nil, thread.BinInfo(), 0}, nil + return FrameToScope(thread.BinInfo(), thread, nil, locations[0]), nil } // GoroutineScope returns an EvalScope for the goroutine running on this thread. @@ -414,7 +414,7 @@ func GoroutineScope(thread Thread) (*EvalScope, error) { if err != nil { return nil, err } - return &EvalScope{locations[0].Current.PC, locations[0].Regs, thread, g.variable, thread.BinInfo(), locations[0].FrameOffset()}, nil + return FrameToScope(thread.BinInfo(), thread, g, locations[0]), nil } func onRuntimeBreakpoint(thread Thread) bool { diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index 51b4b6ee..ac3298da 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -174,6 +174,10 @@ func (err *IsNilErr) Error() string { return fmt.Sprintf("%s is nil", err.name) } +func globalScope(bi *BinaryInfo, mem MemoryReadWriter) *EvalScope { + return &EvalScope{PC: 0, Regs: op.DwarfRegisters{}, Mem: mem, Gvar: nil, BinInfo: bi, frameOffset: 0} +} + func (scope *EvalScope) newVariable(name string, addr uintptr, dwarfType godwarf.Type, mem MemoryReadWriter) *Variable { return newVariable(name, addr, dwarfType, scope.BinInfo, mem) } @@ -749,15 +753,17 @@ func (v *Variable) structMember(memberName string) (*Variable, error) { // Extracts the name and type of a variable from a dwarf entry // then executes the instructions given in the DW_AT_location attribute to grab the variable's address -func (scope *EvalScope) extractVarInfoFromEntry(entry *dwarf.Entry) (*Variable, error) { - if entry == nil { +func (scope *EvalScope) extractVarInfoFromEntry(varEntry *dwarf.Entry) (*Variable, error) { + if varEntry == nil { return nil, fmt.Errorf("invalid entry") } - if entry.Tag != dwarf.TagFormalParameter && entry.Tag != dwarf.TagVariable { - return nil, fmt.Errorf("invalid entry tag, only supports FormalParameter and Variable, got %s", entry.Tag.String()) + if varEntry.Tag != dwarf.TagFormalParameter && varEntry.Tag != dwarf.TagVariable { + return nil, fmt.Errorf("invalid entry tag, only supports FormalParameter and Variable, got %s", varEntry.Tag.String()) } + entry, _ := reader.LoadAbstractOrigin(varEntry, scope.BinInfo.dwarfReader) + n, ok := entry.Val(dwarf.AttrName).(string) if !ok { return nil, fmt.Errorf("type assertion failed") diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go index f8d3f5aa..31e240d0 100644 --- a/service/debugger/debugger.go +++ b/service/debugger/debugger.go @@ -868,7 +868,7 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadCo } if cfg != nil && rawlocs[i].Current.Fn != nil { var err error - scope := proc.FrameToScope(d.target, rawlocs[i]) + scope := proc.FrameToScope(d.target.BinInfo(), d.target.CurrentThread(), nil, rawlocs[i]) locals, err := scope.LocalVariables(*cfg) if err != nil { return nil, err diff --git a/service/test/variables_test.go b/service/test/variables_test.go index 0ef00789..7d4b7fb2 100644 --- a/service/test/variables_test.go +++ b/service/test/variables_test.go @@ -78,7 +78,7 @@ func evalVariable(p proc.Process, symbol string, cfg proc.LoadConfig) (*proc.Var var frame proc.Stackframe frame, err = findFirstNonRuntimeFrame(p) if err == nil { - scope = proc.FrameToScope(p, frame) + scope = proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frame) } } else { scope, err = proc.GoroutineScope(p.CurrentThread()) @@ -405,7 +405,7 @@ func TestLocalVariables(t *testing.T) { var frame proc.Stackframe frame, err = findFirstNonRuntimeFrame(p) if err == nil { - scope = proc.FrameToScope(p, frame) + scope = proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frame) } } else { scope, err = proc.GoroutineScope(p.CurrentThread())