| // 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) |
| } |