* tree-vect-loop-manip.c (vect_do_peeling): Do not use
[official-gcc.git] / libgo / go / debug / gosym / symtab.go
blobf5f996309509167bb8c40380c1510058eddc88c3
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.
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
10 // The table format is a variant of the format used in Plan 9's a.out
11 // format, documented at https://9p.io/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.
15 import (
16 "bytes"
17 "encoding/binary"
18 "fmt"
19 "strconv"
20 "strings"
24 * Symbols
27 // A Sym represents a single symbol table entry.
28 type Sym struct {
29 Value uint64
30 Type byte
31 Name string
32 GoType uint64
33 // If this symbol is a function symbol, the corresponding Func
34 Func *Func
37 // Static reports whether this symbol is static (not visible outside its file).
38 func (s *Sym) Static() bool { return s.Type >= 'a' }
40 // PackageName returns the package part of the symbol name,
41 // or the empty string if there is none.
42 func (s *Sym) PackageName() string {
43 pathend := strings.LastIndex(s.Name, "/")
44 if pathend < 0 {
45 pathend = 0
48 if i := strings.Index(s.Name[pathend:], "."); i != -1 {
49 return s.Name[:pathend+i]
51 return ""
54 // ReceiverName returns the receiver type name of this symbol,
55 // or the empty string if there is none.
56 func (s *Sym) ReceiverName() string {
57 pathend := strings.LastIndex(s.Name, "/")
58 if pathend < 0 {
59 pathend = 0
61 l := strings.Index(s.Name[pathend:], ".")
62 r := strings.LastIndex(s.Name[pathend:], ".")
63 if l == -1 || r == -1 || l == r {
64 return ""
66 return s.Name[pathend+l+1 : pathend+r]
69 // BaseName returns the symbol name without the package or receiver name.
70 func (s *Sym) BaseName() string {
71 if i := strings.LastIndex(s.Name, "."); i != -1 {
72 return s.Name[i+1:]
74 return s.Name
77 // A Func collects information about a single function.
78 type Func struct {
79 Entry uint64
80 *Sym
81 End uint64
82 Params []*Sym
83 Locals []*Sym
84 FrameSize int
85 LineTable *LineTable
86 Obj *Obj
89 // An Obj represents a collection of functions in a symbol table.
91 // The exact method of division of a binary into separate Objs is an internal detail
92 // of the symbol table format.
94 // In early versions of Go each source file became a different Obj.
96 // In Go 1 and Go 1.1, each package produced one Obj for all Go sources
97 // and one Obj per C source file.
99 // In Go 1.2, there is a single Obj for the entire program.
100 type Obj struct {
101 // Funcs is a list of functions in the Obj.
102 Funcs []Func
104 // In Go 1.1 and earlier, Paths is a list of symbols corresponding
105 // to the source file names that produced the Obj.
106 // In Go 1.2, Paths is nil.
107 // Use the keys of Table.Files to obtain a list of source files.
108 Paths []Sym // meta
112 * Symbol tables
115 // Table represents a Go symbol table. It stores all of the
116 // symbols decoded from the program and provides methods to translate
117 // between symbols, names, and addresses.
118 type Table struct {
119 Syms []Sym
120 Funcs []Func
121 Files map[string]*Obj // nil for Go 1.2 and later binaries
122 Objs []Obj // nil for Go 1.2 and later binaries
124 go12line *LineTable // Go 1.2 line number table
127 type sym struct {
128 value uint64
129 gotype uint64
130 typ byte
131 name []byte
134 var (
135 littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}
136 bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00}
137 oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00}
140 func walksymtab(data []byte, fn func(sym) error) error {
141 if len(data) == 0 { // missing symtab is okay
142 return nil
144 var order binary.ByteOrder = binary.BigEndian
145 newTable := false
146 switch {
147 case bytes.HasPrefix(data, oldLittleEndianSymtab):
148 // Same as Go 1.0, but little endian.
149 // Format was used during interim development between Go 1.0 and Go 1.1.
150 // Should not be widespread, but easy to support.
151 data = data[6:]
152 order = binary.LittleEndian
153 case bytes.HasPrefix(data, bigEndianSymtab):
154 newTable = true
155 case bytes.HasPrefix(data, littleEndianSymtab):
156 newTable = true
157 order = binary.LittleEndian
159 var ptrsz int
160 if newTable {
161 if len(data) < 8 {
162 return &DecodingError{len(data), "unexpected EOF", nil}
164 ptrsz = int(data[7])
165 if ptrsz != 4 && ptrsz != 8 {
166 return &DecodingError{7, "invalid pointer size", ptrsz}
168 data = data[8:]
170 var s sym
171 p := data
172 for len(p) >= 4 {
173 var typ byte
174 if newTable {
175 // Symbol type, value, Go type.
176 typ = p[0] & 0x3F
177 wideValue := p[0]&0x40 != 0
178 goType := p[0]&0x80 != 0
179 if typ < 26 {
180 typ += 'A'
181 } else {
182 typ += 'a' - 26
184 s.typ = typ
185 p = p[1:]
186 if wideValue {
187 if len(p) < ptrsz {
188 return &DecodingError{len(data), "unexpected EOF", nil}
190 // fixed-width value
191 if ptrsz == 8 {
192 s.value = order.Uint64(p[0:8])
193 p = p[8:]
194 } else {
195 s.value = uint64(order.Uint32(p[0:4]))
196 p = p[4:]
198 } else {
199 // varint value
200 s.value = 0
201 shift := uint(0)
202 for len(p) > 0 && p[0]&0x80 != 0 {
203 s.value |= uint64(p[0]&0x7F) << shift
204 shift += 7
205 p = p[1:]
207 if len(p) == 0 {
208 return &DecodingError{len(data), "unexpected EOF", nil}
210 s.value |= uint64(p[0]) << shift
211 p = p[1:]
213 if goType {
214 if len(p) < ptrsz {
215 return &DecodingError{len(data), "unexpected EOF", nil}
217 // fixed-width go type
218 if ptrsz == 8 {
219 s.gotype = order.Uint64(p[0:8])
220 p = p[8:]
221 } else {
222 s.gotype = uint64(order.Uint32(p[0:4]))
223 p = p[4:]
226 } else {
227 // Value, symbol type.
228 s.value = uint64(order.Uint32(p[0:4]))
229 if len(p) < 5 {
230 return &DecodingError{len(data), "unexpected EOF", nil}
232 typ = p[4]
233 if typ&0x80 == 0 {
234 return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ}
236 typ &^= 0x80
237 s.typ = typ
238 p = p[5:]
241 // Name.
242 var i int
243 var nnul int
244 for i = 0; i < len(p); i++ {
245 if p[i] == 0 {
246 nnul = 1
247 break
250 switch typ {
251 case 'z', 'Z':
252 p = p[i+nnul:]
253 for i = 0; i+2 <= len(p); i += 2 {
254 if p[i] == 0 && p[i+1] == 0 {
255 nnul = 2
256 break
260 if len(p) < i+nnul {
261 return &DecodingError{len(data), "unexpected EOF", nil}
263 s.name = p[0:i]
264 i += nnul
265 p = p[i:]
267 if !newTable {
268 if len(p) < 4 {
269 return &DecodingError{len(data), "unexpected EOF", nil}
271 // Go type.
272 s.gotype = uint64(order.Uint32(p[:4]))
273 p = p[4:]
275 fn(s)
277 return nil
280 // NewTable decodes the Go symbol table in data,
281 // returning an in-memory representation.
282 func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
283 var n int
284 err := walksymtab(symtab, func(s sym) error {
286 return nil
288 if err != nil {
289 return nil, err
292 var t Table
293 if pcln.isGo12() {
294 t.go12line = pcln
296 fname := make(map[uint16]string)
297 t.Syms = make([]Sym, 0, n)
298 nf := 0
299 nz := 0
300 lasttyp := uint8(0)
301 err = walksymtab(symtab, func(s sym) error {
302 n := len(t.Syms)
303 t.Syms = t.Syms[0 : n+1]
304 ts := &t.Syms[n]
305 ts.Type = s.typ
306 ts.Value = s.value
307 ts.GoType = s.gotype
308 switch s.typ {
309 default:
310 // rewrite name to use . instead of · (c2 b7)
311 w := 0
312 b := s.name
313 for i := 0; i < len(b); i++ {
314 if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 {
316 b[i] = '.'
318 b[w] = b[i]
321 ts.Name = string(s.name[0:w])
322 case 'z', 'Z':
323 if lasttyp != 'z' && lasttyp != 'Z' {
324 nz++
326 for i := 0; i < len(s.name); i += 2 {
327 eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])
328 elt, ok := fname[eltIdx]
329 if !ok {
330 return &DecodingError{-1, "bad filename code", eltIdx}
332 if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
333 ts.Name += "/"
335 ts.Name += elt
338 switch s.typ {
339 case 'T', 't', 'L', 'l':
340 nf++
341 case 'f':
342 fname[uint16(s.value)] = ts.Name
344 lasttyp = s.typ
345 return nil
347 if err != nil {
348 return nil, err
351 t.Funcs = make([]Func, 0, nf)
352 t.Files = make(map[string]*Obj)
354 var obj *Obj
355 if t.go12line != nil {
356 // Put all functions into one Obj.
357 t.Objs = make([]Obj, 1)
358 obj = &t.Objs[0]
359 t.go12line.go12MapFiles(t.Files, obj)
360 } else {
361 t.Objs = make([]Obj, 0, nz)
364 // Count text symbols and attach frame sizes, parameters, and
365 // locals to them. Also, find object file boundaries.
366 lastf := 0
367 for i := 0; i < len(t.Syms); i++ {
368 sym := &t.Syms[i]
369 switch sym.Type {
370 case 'Z', 'z': // path symbol
371 if t.go12line != nil {
372 // Go 1.2 binaries have the file information elsewhere. Ignore.
373 break
375 // Finish the current object
376 if obj != nil {
377 obj.Funcs = t.Funcs[lastf:]
379 lastf = len(t.Funcs)
381 // Start new object
382 n := len(t.Objs)
383 t.Objs = t.Objs[0 : n+1]
384 obj = &t.Objs[n]
386 // Count & copy path symbols
387 var end int
388 for end = i + 1; end < len(t.Syms); end++ {
389 if c := t.Syms[end].Type; c != 'Z' && c != 'z' {
390 break
393 obj.Paths = t.Syms[i:end]
394 i = end - 1 // loop will i++
396 // Record file names
397 depth := 0
398 for j := range obj.Paths {
399 s := &obj.Paths[j]
400 if s.Name == "" {
401 depth--
402 } else {
403 if depth == 0 {
404 t.Files[s.Name] = obj
406 depth++
410 case 'T', 't', 'L', 'l': // text symbol
411 if n := len(t.Funcs); n > 0 {
412 t.Funcs[n-1].End = sym.Value
414 if sym.Name == "runtime.etext" || sym.Name == "etext" {
415 continue
418 // Count parameter and local (auto) syms
419 var np, na int
420 var end int
421 countloop:
422 for end = i + 1; end < len(t.Syms); end++ {
423 switch t.Syms[end].Type {
424 case 'T', 't', 'L', 'l', 'Z', 'z':
425 break countloop
426 case 'p':
427 np++
428 case 'a':
429 na++
433 // Fill in the function symbol
434 n := len(t.Funcs)
435 t.Funcs = t.Funcs[0 : n+1]
436 fn := &t.Funcs[n]
437 sym.Func = fn
438 fn.Params = make([]*Sym, 0, np)
439 fn.Locals = make([]*Sym, 0, na)
440 fn.Sym = sym
441 fn.Entry = sym.Value
442 fn.Obj = obj
443 if t.go12line != nil {
444 // All functions share the same line table.
445 // It knows how to narrow down to a specific
446 // function quickly.
447 fn.LineTable = t.go12line
448 } else if pcln != nil {
449 fn.LineTable = pcln.slice(fn.Entry)
450 pcln = fn.LineTable
452 for j := i; j < end; j++ {
453 s := &t.Syms[j]
454 switch s.Type {
455 case 'm':
456 fn.FrameSize = int(s.Value)
457 case 'p':
458 n := len(fn.Params)
459 fn.Params = fn.Params[0 : n+1]
460 fn.Params[n] = s
461 case 'a':
462 n := len(fn.Locals)
463 fn.Locals = fn.Locals[0 : n+1]
464 fn.Locals[n] = s
467 i = end - 1 // loop will i++
471 if t.go12line != nil && nf == 0 {
472 t.Funcs = t.go12line.go12Funcs()
474 if obj != nil {
475 obj.Funcs = t.Funcs[lastf:]
477 return &t, nil
480 // PCToFunc returns the function containing the program counter pc,
481 // or nil if there is no such function.
482 func (t *Table) PCToFunc(pc uint64) *Func {
483 funcs := t.Funcs
484 for len(funcs) > 0 {
485 m := len(funcs) / 2
486 fn := &funcs[m]
487 switch {
488 case pc < fn.Entry:
489 funcs = funcs[0:m]
490 case fn.Entry <= pc && pc < fn.End:
491 return fn
492 default:
493 funcs = funcs[m+1:]
496 return nil
499 // PCToLine looks up line number information for a program counter.
500 // If there is no information, it returns fn == nil.
501 func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) {
502 if fn = t.PCToFunc(pc); fn == nil {
503 return
505 if t.go12line != nil {
506 file = t.go12line.go12PCToFile(pc)
507 line = t.go12line.go12PCToLine(pc)
508 } else {
509 file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc))
511 return
514 // LineToPC looks up the first program counter on the given line in
515 // the named file. It returns UnknownPathError or UnknownLineError if
516 // there is an error looking up this line.
517 func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) {
518 obj, ok := t.Files[file]
519 if !ok {
520 return 0, nil, UnknownFileError(file)
523 if t.go12line != nil {
524 pc := t.go12line.go12LineToPC(file, line)
525 if pc == 0 {
526 return 0, nil, &UnknownLineError{file, line}
528 return pc, t.PCToFunc(pc), nil
531 abs, err := obj.alineFromLine(file, line)
532 if err != nil {
533 return
535 for i := range obj.Funcs {
536 f := &obj.Funcs[i]
537 pc := f.LineTable.LineToPC(abs, f.End)
538 if pc != 0 {
539 return pc, f, nil
542 return 0, nil, &UnknownLineError{file, line}
545 // LookupSym returns the text, data, or bss symbol with the given name,
546 // or nil if no such symbol is found.
547 func (t *Table) LookupSym(name string) *Sym {
548 // TODO(austin) Maybe make a map
549 for i := range t.Syms {
550 s := &t.Syms[i]
551 switch s.Type {
552 case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
553 if s.Name == name {
554 return s
558 return nil
561 // LookupFunc returns the text, data, or bss symbol with the given name,
562 // or nil if no such symbol is found.
563 func (t *Table) LookupFunc(name string) *Func {
564 for i := range t.Funcs {
565 f := &t.Funcs[i]
566 if f.Sym.Name == name {
567 return f
570 return nil
573 // SymByAddr returns the text, data, or bss symbol starting at the given address.
574 func (t *Table) SymByAddr(addr uint64) *Sym {
575 for i := range t.Syms {
576 s := &t.Syms[i]
577 switch s.Type {
578 case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
579 if s.Value == addr {
580 return s
584 return nil
588 * Object files
591 // This is legacy code for Go 1.1 and earlier, which used the
592 // Plan 9 format for pc-line tables. This code was never quite
593 // correct. It's probably very close, and it's usually correct, but
594 // we never quite found all the corner cases.
596 // Go 1.2 and later use a simpler format, documented at golang.org/s/go12symtab.
598 func (o *Obj) lineFromAline(aline int) (string, int) {
599 type stackEnt struct {
600 path string
601 start int
602 offset int
603 prev *stackEnt
606 noPath := &stackEnt{"", 0, 0, nil}
607 tos := noPath
609 pathloop:
610 for _, s := range o.Paths {
611 val := int(s.Value)
612 switch {
613 case val > aline:
614 break pathloop
616 case val == 1:
617 // Start a new stack
618 tos = &stackEnt{s.Name, val, 0, noPath}
620 case s.Name == "":
621 // Pop
622 if tos == noPath {
623 return "<malformed symbol table>", 0
625 tos.prev.offset += val - tos.start
626 tos = tos.prev
628 default:
629 // Push
630 tos = &stackEnt{s.Name, val, 0, tos}
634 if tos == noPath {
635 return "", 0
637 return tos.path, aline - tos.start - tos.offset + 1
640 func (o *Obj) alineFromLine(path string, line int) (int, error) {
641 if line < 1 {
642 return 0, &UnknownLineError{path, line}
645 for i, s := range o.Paths {
646 // Find this path
647 if s.Name != path {
648 continue
651 // Find this line at this stack level
652 depth := 0
653 var incstart int
654 line += int(s.Value)
655 pathloop:
656 for _, s := range o.Paths[i:] {
657 val := int(s.Value)
658 switch {
659 case depth == 1 && val >= line:
660 return line - 1, nil
662 case s.Name == "":
663 depth--
664 if depth == 0 {
665 break pathloop
666 } else if depth == 1 {
667 line += val - incstart
670 default:
671 if depth == 1 {
672 incstart = val
674 depth++
677 return 0, &UnknownLineError{path, line}
679 return 0, UnknownFileError(path)
683 * Errors
686 // UnknownFileError represents a failure to find the specific file in
687 // the symbol table.
688 type UnknownFileError string
690 func (e UnknownFileError) Error() string { return "unknown file: " + string(e) }
692 // UnknownLineError represents a failure to map a line to a program
693 // counter, either because the line is beyond the bounds of the file
694 // or because there is no code on the given line.
695 type UnknownLineError struct {
696 File string
697 Line int
700 func (e *UnknownLineError) Error() string {
701 return "no code at " + e.File + ":" + strconv.Itoa(e.Line)
704 // DecodingError represents an error during the decoding of
705 // the symbol table.
706 type DecodingError struct {
707 off int
708 msg string
709 val interface{}
712 func (e *DecodingError) Error() string {
713 msg := e.msg
714 if e.val != nil {
715 msg += fmt.Sprintf(" '%v'", e.val)
717 msg += fmt.Sprintf(" at byte %#x", e.off)
718 return msg