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.
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.
27 // A Sym represents a single symbol table entry.
33 // If this symbol is a function symbol, the corresponding 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
, "/")
48 if i
:= strings
.Index(s
.Name
[pathend
:], "."); i
!= -1 {
49 return s
.Name
[:pathend
+i
]
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
, "/")
61 l
:= strings
.Index(s
.Name
[pathend
:], ".")
62 r
:= strings
.LastIndex(s
.Name
[pathend
:], ".")
63 if l
== -1 || r
== -1 || l
== r
{
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 {
77 // A Func collects information about a single function.
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.
101 // Funcs is a list of functions in the Obj.
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.
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.
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
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
144 var order binary
.ByteOrder
= binary
.BigEndian
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.
152 order
= binary
.LittleEndian
153 case bytes
.HasPrefix(data
, bigEndianSymtab
):
155 case bytes
.HasPrefix(data
, littleEndianSymtab
):
157 order
= binary
.LittleEndian
162 return &DecodingError
{len(data
), "unexpected EOF", nil}
165 if ptrsz
!= 4 && ptrsz
!= 8 {
166 return &DecodingError
{7, "invalid pointer size", ptrsz
}
175 // Symbol type, value, Go type.
177 wideValue
:= p
[0]&0x40 != 0
178 goType
:= p
[0]&0x80 != 0
188 return &DecodingError
{len(data
), "unexpected EOF", nil}
192 s
.value
= order
.Uint64(p
[0:8])
195 s
.value
= uint64(order
.Uint32(p
[0:4]))
202 for len(p
) > 0 && p
[0]&0x80 != 0 {
203 s
.value |
= uint64(p
[0]&0x7F) << shift
208 return &DecodingError
{len(data
), "unexpected EOF", nil}
210 s
.value |
= uint64(p
[0]) << shift
215 return &DecodingError
{len(data
), "unexpected EOF", nil}
217 // fixed-width go type
219 s
.gotype
= order
.Uint64(p
[0:8])
222 s
.gotype
= uint64(order
.Uint32(p
[0:4]))
227 // Value, symbol type.
228 s
.value
= uint64(order
.Uint32(p
[0:4]))
230 return &DecodingError
{len(data
), "unexpected EOF", nil}
234 return &DecodingError
{len(data
) - len(p
) + 4, "bad symbol type", typ
}
244 for i
= 0; i
< len(p
); i
++ {
253 for i
= 0; i
+2 <= len(p
); i
+= 2 {
254 if p
[i
] == 0 && p
[i
+1] == 0 {
261 return &DecodingError
{len(data
), "unexpected EOF", nil}
269 return &DecodingError
{len(data
), "unexpected EOF", nil}
272 s
.gotype
= uint64(order
.Uint32(p
[:4]))
280 // NewTable decodes the Go symbol table in data,
281 // returning an in-memory representation.
282 func NewTable(symtab
[]byte, pcln
*LineTable
) (*Table
, error
) {
284 err
:= walksymtab(symtab
, func(s sym
) error
{
296 fname
:= make(map[uint16]string)
297 t
.Syms
= make([]Sym
, 0, n
)
301 err
= walksymtab(symtab
, func(s sym
) error
{
303 t
.Syms
= t
.Syms
[0 : n
+1]
310 // rewrite name to use . instead of · (c2 b7)
313 for i
:= 0; i
< len(b
); i
++ {
314 if b
[i
] == 0xc2 && i
+1 < len(b
) && b
[i
+1] == 0xb7 {
321 ts
.Name
= string(s
.name
[0:w
])
323 if lasttyp
!= 'z' && lasttyp
!= 'Z' {
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
]
330 return &DecodingError
{-1, "bad filename code", eltIdx
}
332 if n
:= len(ts
.Name
); n
> 0 && ts
.Name
[n
-1] != '/' {
339 case 'T', 't', 'L', 'l':
342 fname
[uint16(s
.value
)] = ts
.Name
351 t
.Funcs
= make([]Func
, 0, nf
)
352 t
.Files
= make(map[string]*Obj
)
355 if t
.go12line
!= nil {
356 // Put all functions into one Obj.
357 t
.Objs
= make([]Obj
, 1)
359 t
.go12line
.go12MapFiles(t
.Files
, obj
)
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.
367 for i
:= 0; i
< len(t
.Syms
); i
++ {
370 case 'Z', 'z': // path symbol
371 if t
.go12line
!= nil {
372 // Go 1.2 binaries have the file information elsewhere. Ignore.
375 // Finish the current object
377 obj
.Funcs
= t
.Funcs
[lastf
:]
383 t
.Objs
= t
.Objs
[0 : n
+1]
386 // Count & copy path symbols
388 for end
= i
+ 1; end
< len(t
.Syms
); end
++ {
389 if c
:= t
.Syms
[end
].Type
; c
!= 'Z' && c
!= 'z' {
393 obj
.Paths
= t
.Syms
[i
:end
]
394 i
= end
- 1 // loop will i++
398 for j
:= range obj
.Paths
{
404 t
.Files
[s
.Name
] = obj
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" {
418 // Count parameter and local (auto) syms
422 for end
= i
+ 1; end
< len(t
.Syms
); end
++ {
423 switch t
.Syms
[end
].Type
{
424 case 'T', 't', 'L', 'l', 'Z', 'z':
433 // Fill in the function symbol
435 t
.Funcs
= t
.Funcs
[0 : n
+1]
438 fn
.Params
= make([]*Sym
, 0, np
)
439 fn
.Locals
= make([]*Sym
, 0, na
)
443 if t
.go12line
!= nil {
444 // All functions share the same line table.
445 // It knows how to narrow down to a specific
447 fn
.LineTable
= t
.go12line
448 } else if pcln
!= nil {
449 fn
.LineTable
= pcln
.slice(fn
.Entry
)
452 for j
:= i
; j
< end
; j
++ {
456 fn
.FrameSize
= int(s
.Value
)
459 fn
.Params
= fn
.Params
[0 : n
+1]
463 fn
.Locals
= fn
.Locals
[0 : n
+1]
467 i
= end
- 1 // loop will i++
471 if t
.go12line
!= nil && nf
== 0 {
472 t
.Funcs
= t
.go12line
.go12Funcs()
475 obj
.Funcs
= t
.Funcs
[lastf
:]
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
{
490 case fn
.Entry
<= pc
&& pc
< fn
.End
:
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 {
505 if t
.go12line
!= nil {
506 file
= t
.go12line
.go12PCToFile(pc
)
507 line
= t
.go12line
.go12PCToLine(pc
)
509 file
, line
= fn
.Obj
.lineFromAline(fn
.LineTable
.PCToLine(pc
))
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
]
520 return 0, nil, UnknownFileError(file
)
523 if t
.go12line
!= nil {
524 pc
:= t
.go12line
.go12LineToPC(file
, line
)
526 return 0, nil, &UnknownLineError
{file
, line
}
528 return pc
, t
.PCToFunc(pc
), nil
531 abs
, err
:= obj
.alineFromLine(file
, line
)
535 for i
:= range obj
.Funcs
{
537 pc
:= f
.LineTable
.LineToPC(abs
, f
.End
)
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
{
552 case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
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
{
566 if f
.Sym
.Name
== name
{
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
{
578 case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
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 {
606 noPath
:= &stackEnt
{"", 0, 0, nil}
610 for _
, s
:= range o
.Paths
{
618 tos
= &stackEnt
{s
.Name
, val
, 0, noPath
}
623 return "<malformed symbol table>", 0
625 tos
.prev
.offset
+= val
- tos
.start
630 tos
= &stackEnt
{s
.Name
, val
, 0, tos
}
637 return tos
.path
, aline
- tos
.start
- tos
.offset
+ 1
640 func (o
*Obj
) alineFromLine(path
string, line
int) (int, error
) {
642 return 0, &UnknownLineError
{path
, line
}
645 for i
, s
:= range o
.Paths
{
651 // Find this line at this stack level
656 for _
, s
:= range o
.Paths
[i
:] {
659 case depth
== 1 && val
>= line
:
666 } else if depth
== 1 {
667 line
+= val
- incstart
677 return 0, &UnknownLineError
{path
, line
}
679 return 0, UnknownFileError(path
)
686 // UnknownFileError represents a failure to find the specific file in
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 {
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
706 type DecodingError
struct {
712 func (e
*DecodingError
) Error() string {
715 msg
+= fmt
.Sprintf(" '%v'", e
.val
)
717 msg
+= fmt
.Sprintf(" at byte %#x", e
.off
)