
Typedefs that resolve to slices are not recorded in DWARF as typedefs but instead as structs in a way that there is no way to know they are really slices using debug/dwarf. Using golang.org/x/debug/dwarf instead this problem is solved and as a bonus some types are printed with a nicer names: (struct string → string, struct []int → []int, etc) Fixes #356 and #293
156 lines
4.2 KiB
Go
156 lines
4.2 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package dwarf
|
|
|
|
// This file provides simple methods to access the symbol table by name and address.
|
|
|
|
import "fmt"
|
|
|
|
// lookupEntry returns the Entry for the name. If tag is non-zero, only entries
|
|
// with that tag are considered.
|
|
func (d *Data) lookupEntry(name string, tag Tag) (*Entry, error) {
|
|
r := d.Reader()
|
|
for {
|
|
entry, err := r.Next()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if entry == nil {
|
|
// TODO: why don't we get an error here?
|
|
break
|
|
}
|
|
if tag != 0 && tag != entry.Tag {
|
|
continue
|
|
}
|
|
nameAttr := entry.Val(AttrName)
|
|
if nameAttr == nil {
|
|
continue
|
|
}
|
|
if nameAttr.(string) == name {
|
|
return entry, nil
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("DWARF entry for %q not found", name)
|
|
}
|
|
|
|
// LookupEntry returns the Entry for the named symbol.
|
|
func (d *Data) LookupEntry(name string) (*Entry, error) {
|
|
return d.lookupEntry(name, 0)
|
|
}
|
|
|
|
// LookupFunction returns the address of the named symbol, a function.
|
|
func (d *Data) LookupFunction(name string) (uint64, error) {
|
|
entry, err := d.lookupEntry(name, TagSubprogram)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
addrAttr := entry.Val(AttrLowpc)
|
|
if addrAttr == nil {
|
|
return 0, fmt.Errorf("symbol %q has no LowPC attribute", name)
|
|
}
|
|
addr, ok := addrAttr.(uint64)
|
|
if !ok {
|
|
return 0, fmt.Errorf("symbol %q has non-uint64 LowPC attribute", name)
|
|
}
|
|
return addr, nil
|
|
}
|
|
|
|
// TODO: should LookupVariable handle both globals and locals? Locals don't
|
|
// necessarily have a fixed address. They may be in a register, or otherwise
|
|
// move around.
|
|
|
|
// LookupVariable returns the location of a named symbol, a variable.
|
|
func (d *Data) LookupVariable(name string) (uint64, error) {
|
|
entry, err := d.lookupEntry(name, TagVariable)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("variable %s: %s", name, err)
|
|
}
|
|
loc, err := d.EntryLocation(entry)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("variable %s: %s", name, err)
|
|
}
|
|
return loc, nil
|
|
}
|
|
|
|
// EntryLocation returns the address of the object referred to by the given Entry.
|
|
func (d *Data) EntryLocation(e *Entry) (uint64, error) {
|
|
loc, _ := e.Val(AttrLocation).([]byte)
|
|
if len(loc) == 0 {
|
|
return 0, fmt.Errorf("DWARF entry has no Location attribute")
|
|
}
|
|
// TODO: implement the DWARF Location bytecode. What we have here only
|
|
// recognizes a program with a single literal opAddr bytecode.
|
|
if asize := d.unit[0].asize; loc[0] == opAddr && len(loc) == 1+asize {
|
|
switch asize {
|
|
case 1:
|
|
return uint64(loc[1]), nil
|
|
case 2:
|
|
return uint64(d.order.Uint16(loc[1:])), nil
|
|
case 4:
|
|
return uint64(d.order.Uint32(loc[1:])), nil
|
|
case 8:
|
|
return d.order.Uint64(loc[1:]), nil
|
|
}
|
|
}
|
|
return 0, fmt.Errorf("DWARF entry has an unimplemented Location op")
|
|
}
|
|
|
|
// EntryTypeOffset returns the offset in the given Entry's type attribute.
|
|
func (d *Data) EntryTypeOffset(e *Entry) (Offset, error) {
|
|
v := e.Val(AttrType)
|
|
if v == nil {
|
|
return 0, fmt.Errorf("DWARF entry has no Type attribute")
|
|
}
|
|
off, ok := v.(Offset)
|
|
if !ok {
|
|
return 0, fmt.Errorf("DWARF entry has an invalid Type attribute")
|
|
}
|
|
return off, nil
|
|
}
|
|
|
|
// LookupPC returns the name of a symbol at the specified PC.
|
|
func (d *Data) LookupPC(pc uint64) (string, error) {
|
|
entry, _, err := d.EntryForPC(pc)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
nameAttr := entry.Val(AttrName)
|
|
if nameAttr == nil {
|
|
// TODO: this shouldn't be possible.
|
|
return "", fmt.Errorf("LookupPC: TODO")
|
|
}
|
|
name, ok := nameAttr.(string)
|
|
if !ok {
|
|
return "", fmt.Errorf("name for PC %#x is not a string", pc)
|
|
}
|
|
return name, nil
|
|
}
|
|
|
|
// EntryForPC returns the entry and address for a symbol at the specified PC.
|
|
func (d *Data) EntryForPC(pc uint64) (entry *Entry, lowpc uint64, err error) {
|
|
// TODO: do something better than a linear scan?
|
|
r := d.Reader()
|
|
for {
|
|
entry, err := r.Next()
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if entry == nil {
|
|
// TODO: why don't we get an error here.
|
|
break
|
|
}
|
|
if entry.Tag != TagSubprogram {
|
|
continue
|
|
}
|
|
lowpc, lok := entry.Val(AttrLowpc).(uint64)
|
|
highpc, hok := entry.Val(AttrHighpc).(uint64)
|
|
if !lok || !hok || pc < lowpc || highpc <= pc {
|
|
continue
|
|
}
|
|
return entry, lowpc, nil
|
|
}
|
|
return nil, 0, fmt.Errorf("PC %#x not found", pc)
|
|
}
|