Source file src/pkg/debug/gosym/symtab.go
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package gosym implements access to the Go symbol
6 // and line number tables embedded in Go binaries generated
7 // by the gc compilers.
8 package gosym
9
10 // The table format is a variant of the format used in Plan 9's a.out
11 // format, documented at http://plan9.bell-labs.com/magic/man2html/6/a.out.
12 // The best reference for the differences between the Plan 9 format
13 // and the Go format is the runtime source, specifically ../../runtime/symtab.c.
14
15 import (
16 "encoding/binary"
17 "fmt"
18 "strconv"
19 "strings"
20 )
21
22 /*
23 * Symbols
24 */
25
26 // A Sym represents a single symbol table entry.
27 type Sym struct {
28 Value uint64
29 Type byte
30 Name string
31 GoType uint64
32 // If this symbol if a function symbol, the corresponding Func
33 Func *Func
34 }
35
36 // Static returns whether this symbol is static (not visible outside its file).
37 func (s *Sym) Static() bool { return s.Type >= 'a' }
38
39 // PackageName returns the package part of the symbol name,
40 // or the empty string if there is none.
41 func (s *Sym) PackageName() string {
42 if i := strings.Index(s.Name, "."); i != -1 {
43 return s.Name[0:i]
44 }
45 return ""
46 }
47
48 // ReceiverName returns the receiver type name of this symbol,
49 // or the empty string if there is none.
50 func (s *Sym) ReceiverName() string {
51 l := strings.Index(s.Name, ".")
52 r := strings.LastIndex(s.Name, ".")
53 if l == -1 || r == -1 || l == r {
54 return ""
55 }
56 return s.Name[l+1 : r]
57 }
58
59 // BaseName returns the symbol name without the package or receiver name.
60 func (s *Sym) BaseName() string {
61 if i := strings.LastIndex(s.Name, "."); i != -1 {
62 return s.Name[i+1:]
63 }
64 return s.Name
65 }
66
67 // A Func collects information about a single function.
68 type Func struct {
69 Entry uint64
70 *Sym
71 End uint64
72 Params []*Sym
73 Locals []*Sym
74 FrameSize int
75 LineTable *LineTable
76 Obj *Obj
77 }
78
79 // An Obj represents a single object file.
80 type Obj struct {
81 Funcs []Func
82 Paths []Sym
83 }
84
85 /*
86 * Symbol tables
87 */
88
89 // Table represents a Go symbol table. It stores all of the
90 // symbols decoded from the program and provides methods to translate
91 // between symbols, names, and addresses.
92 type Table struct {
93 Syms []Sym
94 Funcs []Func
95 Files map[string]*Obj
96 Objs []Obj
97 // textEnd uint64;
98 }
99
100 type sym struct {
101 value uint32
102 gotype uint32
103 typ byte
104 name []byte
105 }
106
107 func walksymtab(data []byte, fn func(sym) error) error {
108 var s sym
109 p := data
110 for len(p) >= 6 {
111 s.value = binary.BigEndian.Uint32(p[0:4])
112 typ := p[4]
113 if typ&0x80 == 0 {
114 return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ}
115 }
116 typ &^= 0x80
117 s.typ = typ
118 p = p[5:]
119 var i int
120 var nnul int
121 for i = 0; i < len(p); i++ {
122 if p[i] == 0 {
123 nnul = 1
124 break
125 }
126 }
127 switch typ {
128 case 'z', 'Z':
129 p = p[i+nnul:]
130 for i = 0; i+2 <= len(p); i += 2 {
131 if p[i] == 0 && p[i+1] == 0 {
132 nnul = 2
133 break
134 }
135 }
136 }
137 if i+nnul+4 > len(p) {
138 return &DecodingError{len(data), "unexpected EOF", nil}
139 }
140 s.name = p[0:i]
141 i += nnul
142 s.gotype = binary.BigEndian.Uint32(p[i : i+4])
143 p = p[i+4:]
144 fn(s)
145 }
146 return nil
147 }
148
149 // NewTable decodes the Go symbol table in data,
150 // returning an in-memory representation.
151 func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
152 var n int
153 err := walksymtab(symtab, func(s sym) error {
154 n++
155 return nil
156 })
157 if err != nil {
158 return nil, err
159 }
160
161 var t Table
162 fname := make(map[uint16]string)
163 t.Syms = make([]Sym, 0, n)
164 nf := 0
165 nz := 0
166 lasttyp := uint8(0)
167 err = walksymtab(symtab, func(s sym) error {
168 n := len(t.Syms)
169 t.Syms = t.Syms[0 : n+1]
170 ts := &t.Syms[n]
171 ts.Type = s.typ
172 ts.Value = uint64(s.value)
173 ts.GoType = uint64(s.gotype)
174 switch s.typ {
175 default:
176 // rewrite name to use . instead of ยท (c2 b7)
177 w := 0
178 b := s.name
179 for i := 0; i < len(b); i++ {
180 if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 {
181 i++
182 b[i] = '.'
183 }
184 b[w] = b[i]
185 w++
186 }
187 ts.Name = string(s.name[0:w])
188 case 'z', 'Z':
189 if lasttyp != 'z' && lasttyp != 'Z' {
190 nz++
191 }
192 for i := 0; i < len(s.name); i += 2 {
193 eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])
194 elt, ok := fname[eltIdx]
195 if !ok {
196 return &DecodingError{-1, "bad filename code", eltIdx}
197 }
198 if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
199 ts.Name += "/"
200 }
201 ts.Name += elt
202 }
203 }
204 switch s.typ {
205 case 'T', 't', 'L', 'l':
206 nf++
207 case 'f':
208 fname[uint16(s.value)] = ts.Name
209 }
210 lasttyp = s.typ
211 return nil
212 })
213 if err != nil {
214 return nil, err
215 }
216
217 t.Funcs = make([]Func, 0, nf)
218 t.Objs = make([]Obj, 0, nz)
219 t.Files = make(map[string]*Obj)
220
221 // Count text symbols and attach frame sizes, parameters, and
222 // locals to them. Also, find object file boundaries.
223 var obj *Obj
224 lastf := 0
225 for i := 0; i < len(t.Syms); i++ {
226 sym := &t.Syms[i]
227 switch sym.Type {
228 case 'Z', 'z': // path symbol
229 // Finish the current object
230 if obj != nil {
231 obj.Funcs = t.Funcs[lastf:]
232 }
233 lastf = len(t.Funcs)
234
235 // Start new object
236 n := len(t.Objs)
237 t.Objs = t.Objs[0 : n+1]
238 obj = &t.Objs[n]
239
240 // Count & copy path symbols
241 var end int
242 for end = i + 1; end < len(t.Syms); end++ {
243 if c := t.Syms[end].Type; c != 'Z' && c != 'z' {
244 break
245 }
246 }
247 obj.Paths = t.Syms[i:end]
248 i = end - 1 // loop will i++
249
250 // Record file names
251 depth := 0
252 for j := range obj.Paths {
253 s := &obj.Paths[j]
254 if s.Name == "" {
255 depth--
256 } else {
257 if depth == 0 {
258 t.Files[s.Name] = obj
259 }
260 depth++
261 }
262 }
263
264 case 'T', 't', 'L', 'l': // text symbol
265 if n := len(t.Funcs); n > 0 {
266 t.Funcs[n-1].End = sym.Value
267 }
268 if sym.Name == "etext" {
269 continue
270 }
271
272 // Count parameter and local (auto) syms
273 var np, na int
274 var end int
275 countloop:
276 for end = i + 1; end < len(t.Syms); end++ {
277 switch t.Syms[end].Type {
278 case 'T', 't', 'L', 'l', 'Z', 'z':
279 break countloop
280 case 'p':
281 np++
282 case 'a':
283 na++
284 }
285 }
286
287 // Fill in the function symbol
288 n := len(t.Funcs)
289 t.Funcs = t.Funcs[0 : n+1]
290 fn := &t.Funcs[n]
291 sym.Func = fn
292 fn.Params = make([]*Sym, 0, np)
293 fn.Locals = make([]*Sym, 0, na)
294 fn.Sym = sym
295 fn.Entry = sym.Value
296 fn.Obj = obj
297 if pcln != nil {
298 fn.LineTable = pcln.slice(fn.Entry)
299 pcln = fn.LineTable
300 }
301 for j := i; j < end; j++ {
302 s := &t.Syms[j]
303 switch s.Type {
304 case 'm':
305 fn.FrameSize = int(s.Value)
306 case 'p':
307 n := len(fn.Params)
308 fn.Params = fn.Params[0 : n+1]
309 fn.Params[n] = s
310 case 'a':
311 n := len(fn.Locals)
312 fn.Locals = fn.Locals[0 : n+1]
313 fn.Locals[n] = s
314 }
315 }
316 i = end - 1 // loop will i++
317 }
318 }
319 if obj != nil {
320 obj.Funcs = t.Funcs[lastf:]
321 }
322 return &t, nil
323 }
324
325 // PCToFunc returns the function containing the program counter pc,
326 // or nil if there is no such function.
327 func (t *Table) PCToFunc(pc uint64) *Func {
328 funcs := t.Funcs
329 for len(funcs) > 0 {
330 m := len(funcs) / 2
331 fn := &funcs[m]
332 switch {
333 case pc < fn.Entry:
334 funcs = funcs[0:m]
335 case fn.Entry <= pc && pc < fn.End:
336 return fn
337 default:
338 funcs = funcs[m+1:]
339 }
340 }
341 return nil
342 }
343
344 // PCToLine looks up line number information for a program counter.
345 // If there is no information, it returns fn == nil.
346 func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) {
347 if fn = t.PCToFunc(pc); fn == nil {
348 return
349 }
350 file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc))
351 return
352 }
353
354 // LineToPC looks up the first program counter on the given line in
355 // the named file. Returns UnknownPathError or UnknownLineError if
356 // there is an error looking up this line.
357 func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) {
358 obj, ok := t.Files[file]
359 if !ok {
360 return 0, nil, UnknownFileError(file)
361 }
362 abs, err := obj.alineFromLine(file, line)
363 if err != nil {
364 return
365 }
366 for i := range obj.Funcs {
367 f := &obj.Funcs[i]
368 pc := f.LineTable.LineToPC(abs, f.End)
369 if pc != 0 {
370 return pc, f, nil
371 }
372 }
373 return 0, nil, &UnknownLineError{file, line}
374 }
375
376 // LookupSym returns the text, data, or bss symbol with the given name,
377 // or nil if no such symbol is found.
378 func (t *Table) LookupSym(name string) *Sym {
379 // TODO(austin) Maybe make a map
380 for i := range t.Syms {
381 s := &t.Syms[i]
382 switch s.Type {
383 case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
384 if s.Name == name {
385 return s
386 }
387 }
388 }
389 return nil
390 }
391
392 // LookupFunc returns the text, data, or bss symbol with the given name,
393 // or nil if no such symbol is found.
394 func (t *Table) LookupFunc(name string) *Func {
395 for i := range t.Funcs {
396 f := &t.Funcs[i]
397 if f.Sym.Name == name {
398 return f
399 }
400 }
401 return nil
402 }
403
404 // SymByAddr returns the text, data, or bss symbol starting at the given address.
405 // TODO(rsc): Allow lookup by any address within the symbol.
406 func (t *Table) SymByAddr(addr uint64) *Sym {
407 // TODO(austin) Maybe make a map
408 for i := range t.Syms {
409 s := &t.Syms[i]
410 switch s.Type {
411 case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
412 if s.Value == addr {
413 return s
414 }
415 }
416 }
417 return nil
418 }
419
420 /*
421 * Object files
422 */
423
424 func (o *Obj) lineFromAline(aline int) (string, int) {
425 type stackEnt struct {
426 path string
427 start int
428 offset int
429 prev *stackEnt
430 }
431
432 noPath := &stackEnt{"", 0, 0, nil}
433 tos := noPath
434
435 // TODO(austin) I have no idea how 'Z' symbols work, except
436 // that they pop the stack.
437 pathloop:
438 for _, s := range o.Paths {
439 val := int(s.Value)
440 switch {
441 case val > aline:
442 break pathloop
443
444 case val == 1:
445 // Start a new stack
446 tos = &stackEnt{s.Name, val, 0, noPath}
447
448 case s.Name == "":
449 // Pop
450 if tos == noPath {
451 return "<malformed symbol table>", 0
452 }
453 tos.prev.offset += val - tos.start
454 tos = tos.prev
455
456 default:
457 // Push
458 tos = &stackEnt{s.Name, val, 0, tos}
459 }
460 }
461
462 if tos == noPath {
463 return "", 0
464 }
465 return tos.path, aline - tos.start - tos.offset + 1
466 }
467
468 func (o *Obj) alineFromLine(path string, line int) (int, error) {
469 if line < 1 {
470 return 0, &UnknownLineError{path, line}
471 }
472
473 for i, s := range o.Paths {
474 // Find this path
475 if s.Name != path {
476 continue
477 }
478
479 // Find this line at this stack level
480 depth := 0
481 var incstart int
482 line += int(s.Value)
483 pathloop:
484 for _, s := range o.Paths[i:] {
485 val := int(s.Value)
486 switch {
487 case depth == 1 && val >= line:
488 return line - 1, nil
489
490 case s.Name == "":
491 depth--
492 if depth == 0 {
493 break pathloop
494 } else if depth == 1 {
495 line += val - incstart
496 }
497
498 default:
499 if depth == 1 {
500 incstart = val
501 }
502 depth++
503 }
504 }
505 return 0, &UnknownLineError{path, line}
506 }
507 return 0, UnknownFileError(path)
508 }
509
510 /*
511 * Errors
512 */
513
514 // UnknownFileError represents a failure to find the specific file in
515 // the symbol table.
516 type UnknownFileError string
517
518 func (e UnknownFileError) Error() string { return "unknown file: " + string(e) }
519
520 // UnknownLineError represents a failure to map a line to a program
521 // counter, either because the line is beyond the bounds of the file
522 // or because there is no code on the given line.
523 type UnknownLineError struct {
524 File string
525 Line int
526 }
527
528 func (e *UnknownLineError) Error() string {
529 return "no code at " + e.File + ":" + strconv.Itoa(e.Line)
530 }
531
532 // DecodingError represents an error during the decoding of
533 // the symbol table.
534 type DecodingError struct {
535 off int
536 msg string
537 val interface{}
538 }
539
540 func (e *DecodingError) Error() string {
541 msg := e.msg
542 if e.val != nil {
543 msg += fmt.Sprintf(" '%v'", e.val)
544 }
545 msg += fmt.Sprintf(" at byte %#x", e.off)
546 return msg
547 }