dwarf/reader,proc: support DW_AT_abstract_origin (#1111)
debug_info entries can use DW_AT_abstract_origin to inherit the attributes of another entry, supporting this attribute is necessary to support DW_TAG_inlined_subroutine. Go, starting with 1.10, emits DW_TAG_inlined_subroutine entries when inlining is enabled.
This commit is contained in:
parent
be62813261
commit
0c40a8f52a
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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())
|
||||
|
||||
Loading…
Reference in New Issue
Block a user