blob: 47e263bf5907fb2dcb4526ac46f4b577b8207b4b [file] [log] [blame]
// 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, err
}
loc, _ := entry.Val(AttrLocation).([]byte)
if len(loc) == 0 {
return 0, fmt.Errorf("name %q has no Location attribute", name)
}
// 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: unimplemented Location op")
}
// 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)
}