2017-03-19 22:45:54 +00:00
|
|
|
package reader
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"golang.org/x/debug/dwarf"
|
2017-03-26 14:55:28 +00:00
|
|
|
"github.com/derekparker/delve/dwarf/op"
|
2017-03-19 22:45:54 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Reader struct {
|
|
|
|
*dwarf.Reader
|
|
|
|
depth int
|
|
|
|
}
|
|
|
|
|
|
|
|
// New returns a reader for the specified dwarf data.
|
|
|
|
func New(data *dwarf.Data) *Reader {
|
|
|
|
return &Reader{data.Reader(), 0}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Seek moves the reader to an arbitrary offset.
|
|
|
|
func (reader *Reader) Seek(off dwarf.Offset) {
|
|
|
|
reader.depth = 0
|
|
|
|
reader.Reader.Seek(off)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SeekToEntry moves the reader to an arbitrary entry.
|
|
|
|
func (reader *Reader) SeekToEntry(entry *dwarf.Entry) error {
|
|
|
|
reader.Seek(entry.Offset)
|
|
|
|
// Consume the current entry so .Next works as intended
|
|
|
|
_, err := reader.Next()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// SeekToFunctionEntry moves the reader to the function that includes the
|
|
|
|
// specified program counter.
|
|
|
|
func (reader *Reader) SeekToFunction(pc uint64) (*dwarf.Entry, error) {
|
|
|
|
reader.Seek(0)
|
|
|
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if entry.Tag != dwarf.TagSubprogram {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
lowpc, ok := entry.Val(dwarf.AttrLowpc).(uint64)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
highpc, ok := entry.Val(dwarf.AttrHighpc).(uint64)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if lowpc <= pc && highpc > pc {
|
|
|
|
return entry, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("unable to find function context")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the address for the named entry.
|
|
|
|
func (reader *Reader) AddrFor(name string) (uint64, error) {
|
|
|
|
entry, err := reader.FindEntryNamed(name, false)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
|
|
|
|
if !ok {
|
|
|
|
return 0, fmt.Errorf("type assertion failed")
|
|
|
|
}
|
|
|
|
addr, err := op.ExecuteStackProgram(0, instructions)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return uint64(addr), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the address for the named struct member. Expects the reader to be at the parent entry
|
|
|
|
// or one of the parents children, thus does not seek to parent by itself.
|
|
|
|
func (reader *Reader) AddrForMember(member string, initialInstructions []byte) (uint64, error) {
|
|
|
|
for {
|
|
|
|
entry, err := reader.NextMemberVariable()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if entry == nil {
|
|
|
|
return 0, fmt.Errorf("nil entry for member named %s", member)
|
|
|
|
}
|
|
|
|
name, ok := entry.Val(dwarf.AttrName).(string)
|
|
|
|
if !ok || name != member {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
addr, err := op.ExecuteStackProgram(0, append(initialInstructions, instructions...))
|
|
|
|
return uint64(addr), err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var TypeNotFoundErr = errors.New("no type entry found, use 'types' for a list of valid types")
|
|
|
|
|
|
|
|
// SeekToType moves the reader to the type specified by the entry,
|
|
|
|
// optionally resolving typedefs and pointer types. If the reader is set
|
|
|
|
// to a struct type the NextMemberVariable call can be used to walk all member data.
|
|
|
|
func (reader *Reader) SeekToType(entry *dwarf.Entry, resolveTypedefs bool, resolvePointerTypes bool) (*dwarf.Entry, error) {
|
|
|
|
offset, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("entry does not have a type attribute")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Seek to the first type offset
|
|
|
|
reader.Seek(offset)
|
|
|
|
|
|
|
|
// Walk the types to the base
|
|
|
|
for typeEntry, err := reader.Next(); typeEntry != nil; typeEntry, err = reader.Next() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if typeEntry.Tag == dwarf.TagTypedef && !resolveTypedefs {
|
|
|
|
return typeEntry, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if typeEntry.Tag == dwarf.TagPointerType && !resolvePointerTypes {
|
|
|
|
return typeEntry, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
offset, ok = typeEntry.Val(dwarf.AttrType).(dwarf.Offset)
|
|
|
|
if !ok {
|
|
|
|
return typeEntry, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
reader.Seek(offset)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, TypeNotFoundErr
|
|
|
|
}
|
|
|
|
|
|
|
|
func (reader *Reader) NextType() (*dwarf.Entry, error) {
|
|
|
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch entry.Tag {
|
|
|
|
case dwarf.TagArrayType, dwarf.TagBaseType, dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType, dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType, dwarf.TagEnumerationType, dwarf.TagPointerType, dwarf.TagSubroutineType, dwarf.TagTypedef, dwarf.TagUnspecifiedType:
|
|
|
|
return entry, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SeekToTypeNamed moves the reader to the type specified by the name.
|
|
|
|
// If the reader is set to a struct type the NextMemberVariable call
|
|
|
|
// can be used to walk all member data.
|
|
|
|
func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) {
|
|
|
|
// Walk the types to the base
|
|
|
|
for entry, err := reader.NextType(); entry != nil; entry, err = reader.NextType() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
n, ok := entry.Val(dwarf.AttrName).(string)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if n == name {
|
|
|
|
return entry, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, TypeNotFoundErr
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finds the entry for 'name'.
|
|
|
|
func (reader *Reader) FindEntryNamed(name string, member bool) (*dwarf.Entry, error) {
|
|
|
|
depth := 1
|
|
|
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if entry.Children {
|
|
|
|
depth++
|
|
|
|
}
|
|
|
|
|
|
|
|
if entry.Tag == 0 {
|
|
|
|
depth--
|
|
|
|
if depth <= 0 {
|
|
|
|
return nil, fmt.Errorf("could not find symbol value for %s", name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if member {
|
|
|
|
if entry.Tag != dwarf.TagMember {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if entry.Tag != dwarf.TagVariable && entry.Tag != dwarf.TagFormalParameter && entry.Tag != dwarf.TagStructType {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
n, ok := entry.Val(dwarf.AttrName).(string)
|
|
|
|
if !ok || n != name {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return entry, nil
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("could not find symbol value for %s", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (reader *Reader) InstructionsForEntryNamed(name string, member bool) ([]byte, error) {
|
|
|
|
entry, err := reader.FindEntryNamed(name, member)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var attr dwarf.Attr
|
|
|
|
if member {
|
|
|
|
attr = dwarf.AttrDataMemberLoc
|
|
|
|
} else {
|
|
|
|
attr = dwarf.AttrLocation
|
|
|
|
}
|
|
|
|
instr, ok := entry.Val(attr).([]byte)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("invalid typecast for Dwarf instructions")
|
|
|
|
}
|
|
|
|
return instr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (reader *Reader) InstructionsForEntry(entry *dwarf.Entry) ([]byte, error) {
|
|
|
|
if entry.Tag == dwarf.TagMember {
|
|
|
|
instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("member data has no data member location attribute")
|
|
|
|
}
|
|
|
|
// clone slice to prevent stomping on the dwarf data
|
|
|
|
return append([]byte{}, instructions...), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// non-member
|
|
|
|
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("entry has no location attribute")
|
|
|
|
}
|
|
|
|
|
|
|
|
// clone slice to prevent stomping on the dwarf data
|
|
|
|
return append([]byte{}, instructions...), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NextScopeVariable moves the reader to the next debug entry that describes a local variable and returns the entry.
|
|
|
|
func (reader *Reader) NextScopeVariable() (*dwarf.Entry, error) {
|
|
|
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// All scope variables will be at the same depth
|
|
|
|
reader.SkipChildren()
|
|
|
|
|
|
|
|
// End of the current depth
|
|
|
|
if entry.Tag == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if entry.Tag == dwarf.TagVariable || entry.Tag == dwarf.TagFormalParameter {
|
|
|
|
return entry, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No more items
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NextMememberVariable moves the reader to the next debug entry that describes a member variable and returns the entry.
|
|
|
|
func (reader *Reader) NextMemberVariable() (*dwarf.Entry, error) {
|
|
|
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// All member variables will be at the same depth
|
|
|
|
reader.SkipChildren()
|
|
|
|
|
|
|
|
// End of the current depth
|
|
|
|
if entry.Tag == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if entry.Tag == dwarf.TagMember {
|
|
|
|
return entry, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No more items
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NextPackageVariable moves the reader to the next debug entry that describes a package variable.
|
|
|
|
// Any TagVariable entry that is not inside a sub prgram entry and is marked external is considered a package variable.
|
|
|
|
func (reader *Reader) NextPackageVariable() (*dwarf.Entry, error) {
|
|
|
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if entry.Tag == dwarf.TagVariable {
|
|
|
|
ext, ok := entry.Val(dwarf.AttrExternal).(bool)
|
|
|
|
if ok && ext {
|
|
|
|
return entry, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ignore everything inside sub programs
|
|
|
|
if entry.Tag == dwarf.TagSubprogram {
|
|
|
|
reader.SkipChildren()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No more items
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (reader *Reader) NextCompileUnit() (*dwarf.Entry, error) {
|
|
|
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if entry.Tag == dwarf.TagCompileUnit {
|
|
|
|
return entry, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|