1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package dwarf
16
17// This file provides simple methods to access the symbol table by name and address.
18
19import (
20	"fmt"
21	"regexp"
22	"sort"
23)
24
25// lookupEntry returns the first Entry for the name.
26// If tag is non-zero, only entries with that tag are considered.
27func (d *Data) lookupEntry(name string, tag Tag) (*Entry, error) {
28	x, ok := d.nameCache[name]
29	if !ok {
30		return nil, fmt.Errorf("DWARF entry for %q not found", name)
31	}
32	for ; x != nil; x = x.link {
33		if tag == 0 || x.entry.Tag == tag {
34			return x.entry, nil
35		}
36	}
37	return nil, fmt.Errorf("no DWARF entry for %q with tag %s", name, tag)
38}
39
40// LookupMatchingSymbols returns the names of all top-level entries matching
41// the given regular expression.
42func (d *Data) LookupMatchingSymbols(nameRE *regexp.Regexp) (result []string, err error) {
43	for name := range d.nameCache {
44		if nameRE.MatchString(name) {
45			result = append(result, name)
46		}
47	}
48	return result, nil
49}
50
51// LookupEntry returns the Entry for the named symbol.
52func (d *Data) LookupEntry(name string) (*Entry, error) {
53	return d.lookupEntry(name, 0)
54}
55
56// LookupFunction returns the entry for a function.
57func (d *Data) LookupFunction(name string) (*Entry, error) {
58	return d.lookupEntry(name, TagSubprogram)
59}
60
61// LookupVariable returns the entry for a (global) variable.
62func (d *Data) LookupVariable(name string) (*Entry, error) {
63	return d.lookupEntry(name, TagVariable)
64}
65
66// EntryLocation returns the address of the object referred to by the given Entry.
67func (d *Data) EntryLocation(e *Entry) (uint64, error) {
68	loc, _ := e.Val(AttrLocation).([]byte)
69	if len(loc) == 0 {
70		return 0, fmt.Errorf("DWARF entry has no Location attribute")
71	}
72	// TODO: implement the DWARF Location bytecode. What we have here only
73	// recognizes a program with a single literal opAddr bytecode.
74	if asize := d.unit[0].asize; loc[0] == opAddr && len(loc) == 1+asize {
75		switch asize {
76		case 1:
77			return uint64(loc[1]), nil
78		case 2:
79			return uint64(d.order.Uint16(loc[1:])), nil
80		case 4:
81			return uint64(d.order.Uint32(loc[1:])), nil
82		case 8:
83			return d.order.Uint64(loc[1:]), nil
84		}
85	}
86	return 0, fmt.Errorf("DWARF entry has an unimplemented Location op")
87}
88
89// EntryType returns the Type for an Entry.
90func (d *Data) EntryType(e *Entry) (Type, error) {
91	off, err := d.EntryTypeOffset(e)
92	if err != nil {
93		return nil, err
94	}
95	return d.Type(off)
96}
97
98// EntryTypeOffset returns the offset in the given Entry's type attribute.
99func (d *Data) EntryTypeOffset(e *Entry) (Offset, error) {
100	v := e.Val(AttrType)
101	if v == nil {
102		return 0, fmt.Errorf("DWARF entry has no Type attribute")
103	}
104	off, ok := v.(Offset)
105	if !ok {
106		return 0, fmt.Errorf("DWARF entry has an invalid Type attribute")
107	}
108	return off, nil
109}
110
111// PCToFunction returns the entry and address for the function containing the
112// specified PC.
113func (d *Data) PCToFunction(pc uint64) (entry *Entry, lowpc uint64, err error) {
114	p := d.pcToFuncEntries
115	if len(p) == 0 {
116		return nil, 0, fmt.Errorf("no function addresses loaded")
117	}
118	i := sort.Search(len(p), func(i int) bool { return p[i].pc > pc }) - 1
119	// The search failed if:
120	// - pc was before the start of any function.
121	// - The largest function bound not larger than pc was the end of a function,
122	//   not the start of one.
123	// - The largest function bound not larger than pc was the start of a function
124	//   that we don't know the end of, and the PC is much larger than the start.
125	if i == -1 || p[i].entry == nil || (i+1 == len(p) && pc-p[i].pc >= 1<<20) {
126		return nil, 0, fmt.Errorf("no function at %x", pc)
127	}
128	return p[i].entry, p[i].pc, nil
129}
130