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 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.
27 // A Sym represents a single symbol table entry.
33 // If this symbol if a function symbol, the corresponding Func
37 // Static returns 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 if i
:= strings
.Index(s
.Name
, "."); i
!= -1 {
49 // ReceiverName returns the receiver type name of this symbol,
50 // or the empty string if there is none.
51 func (s
*Sym
) ReceiverName() string {
52 l
:= strings
.Index(s
.Name
, ".")
53 r
:= strings
.LastIndex(s
.Name
, ".")
54 if l
== -1 || r
== -1 || l
== r
{
57 return s
.Name
[l
+1 : r
]
60 // BaseName returns the symbol name without the package or receiver name.
61 func (s
*Sym
) BaseName() string {
62 if i
:= strings
.LastIndex(s
.Name
, "."); i
!= -1 {
68 // A Func collects information about a single function.
80 // An Obj represents a single object file.
90 // Table represents a Go symbol table. It stores all of the
91 // symbols decoded from the program and provides methods to translate
92 // between symbols, names, and addresses.
108 func walksymtab(data
[]byte, fn
func(sym
) os
.Error
) os
.Error
{
112 s
.value
= binary
.BigEndian
.Uint32(p
[0:4])
115 return &DecodingError
{len(data
) - len(p
) + 4, "bad symbol type", typ
}
122 for i
= 0; i
< len(p
); i
++ {
131 for i
= 0; i
+2 <= len(p
); i
+= 2 {
132 if p
[i
] == 0 && p
[i
+1] == 0 {
138 if i
+nnul
+4 > len(p
) {
139 return &DecodingError
{len(data
), "unexpected EOF", nil}
143 s
.gotype
= binary
.BigEndian
.Uint32(p
[i
: i
+4])
150 // NewTable decodes the Go symbol table in data,
151 // returning an in-memory representation.
152 func NewTable(symtab
[]byte, pcln
*LineTable
) (*Table
, os
.Error
) {
154 err
:= walksymtab(symtab
, func(s sym
) os
.Error
{
163 fname
:= make(map[uint16]string)
164 t
.Syms
= make([]Sym
, 0, n
)
168 err
= walksymtab(symtab
, func(s sym
) os
.Error
{
170 t
.Syms
= t
.Syms
[0 : n
+1]
173 ts
.Value
= uint64(s
.value
)
174 ts
.GoType
= uint64(s
.gotype
)
177 // rewrite name to use . instead of ยท (c2 b7)
180 for i
:= 0; i
< len(b
); i
++ {
181 if b
[i
] == 0xc2 && i
+1 < len(b
) && b
[i
+1] == 0xb7 {
188 ts
.Name
= string(s
.name
[0:w
])
190 if lasttyp
!= 'z' && lasttyp
!= 'Z' {
193 for i
:= 0; i
< len(s
.name
); i
+= 2 {
194 eltIdx
:= binary
.BigEndian
.Uint16(s
.name
[i
: i
+2])
195 elt
, ok
:= fname
[eltIdx
]
197 return &DecodingError
{-1, "bad filename code", eltIdx
}
199 if n
:= len(ts
.Name
); n
> 0 && ts
.Name
[n
-1] != '/' {
206 case 'T', 't', 'L', 'l':
209 fname
[uint16(s
.value
)] = ts
.Name
218 t
.Funcs
= make([]Func
, 0, nf
)
219 t
.Objs
= make([]Obj
, 0, nz
)
220 t
.Files
= make(map[string]*Obj
)
222 // Count text symbols and attach frame sizes, parameters, and
223 // locals to them. Also, find object file boundaries.
226 for i
:= 0; i
< len(t
.Syms
); i
++ {
229 case 'Z', 'z': // path symbol
230 // Finish the current object
232 obj
.Funcs
= t
.Funcs
[lastf
:]
238 t
.Objs
= t
.Objs
[0 : n
+1]
241 // Count & copy path symbols
243 for end
= i
+ 1; end
< len(t
.Syms
); end
++ {
244 if c
:= t
.Syms
[end
].Type
; c
!= 'Z' && c
!= 'z' {
248 obj
.Paths
= t
.Syms
[i
:end
]
249 i
= end
- 1 // loop will i++
253 for j
:= range obj
.Paths
{
259 t
.Files
[s
.Name
] = obj
265 case 'T', 't', 'L', 'l': // text symbol
266 if n
:= len(t
.Funcs
); n
> 0 {
267 t
.Funcs
[n
-1].End
= sym
.Value
269 if sym
.Name
== "etext" {
273 // Count parameter and local (auto) syms
277 for end
= i
+ 1; end
< len(t
.Syms
); end
++ {
278 switch t
.Syms
[end
].Type
{
279 case 'T', 't', 'L', 'l', 'Z', 'z':
288 // Fill in the function symbol
290 t
.Funcs
= t
.Funcs
[0 : n
+1]
293 fn
.Params
= make([]*Sym
, 0, np
)
294 fn
.Locals
= make([]*Sym
, 0, na
)
299 fn
.LineTable
= pcln
.slice(fn
.Entry
)
302 for j
:= i
; j
< end
; j
++ {
306 fn
.FrameSize
= int(s
.Value
)
309 fn
.Params
= fn
.Params
[0 : n
+1]
313 fn
.Locals
= fn
.Locals
[0 : n
+1]
317 i
= end
- 1 // loop will i++
321 obj
.Funcs
= t
.Funcs
[lastf
:]
326 // PCToFunc returns the function containing the program counter pc,
327 // or nil if there is no such function.
328 func (t
*Table
) PCToFunc(pc
uint64) *Func
{
336 case fn
.Entry
<= pc
&& pc
< fn
.End
:
345 // PCToLine looks up line number information for a program counter.
346 // If there is no information, it returns fn == nil.
347 func (t
*Table
) PCToLine(pc
uint64) (file
string, line
int, fn
*Func
) {
348 if fn
= t
.PCToFunc(pc
); fn
== nil {
351 file
, line
= fn
.Obj
.lineFromAline(fn
.LineTable
.PCToLine(pc
))
355 // LineToPC looks up the first program counter on the given line in
356 // the named file. Returns UnknownPathError or UnknownLineError if
357 // there is an error looking up this line.
358 func (t
*Table
) LineToPC(file
string, line
int) (pc
uint64, fn
*Func
, err os
.Error
) {
359 obj
, ok
:= t
.Files
[file
]
361 return 0, nil, UnknownFileError(file
)
363 abs
, err
:= obj
.alineFromLine(file
, line
)
367 for i
:= range obj
.Funcs
{
369 pc
:= f
.LineTable
.LineToPC(abs
, f
.End
)
374 return 0, nil, &UnknownLineError
{file
, line
}
377 // LookupSym returns the text, data, or bss symbol with the given name,
378 // or nil if no such symbol is found.
379 func (t
*Table
) LookupSym(name
string) *Sym
{
380 // TODO(austin) Maybe make a map
381 for i
:= range t
.Syms
{
384 case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
393 // LookupFunc returns the text, data, or bss symbol with the given name,
394 // or nil if no such symbol is found.
395 func (t
*Table
) LookupFunc(name
string) *Func
{
396 for i
:= range t
.Funcs
{
398 if f
.Sym
.Name
== name
{
405 // SymByAddr returns the text, data, or bss symbol starting at the given address.
406 // TODO(rsc): Allow lookup by any address within the symbol.
407 func (t
*Table
) SymByAddr(addr
uint64) *Sym
{
408 // TODO(austin) Maybe make a map
409 for i
:= range t
.Syms
{
412 case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
425 func (o
*Obj
) lineFromAline(aline
int) (string, int) {
426 type stackEnt
struct {
433 noPath
:= &stackEnt
{"", 0, 0, nil}
436 // TODO(austin) I have no idea how 'Z' symbols work, except
437 // that they pop the stack.
439 for _
, s
:= range o
.Paths
{
447 tos
= &stackEnt
{s
.Name
, val
, 0, noPath
}
452 return "<malformed symbol table>", 0
454 tos
.prev
.offset
+= val
- tos
.start
459 tos
= &stackEnt
{s
.Name
, val
, 0, tos
}
466 return tos
.path
, aline
- tos
.start
- tos
.offset
+ 1
469 func (o
*Obj
) alineFromLine(path
string, line
int) (int, os
.Error
) {
471 return 0, &UnknownLineError
{path
, line
}
474 for i
, s
:= range o
.Paths
{
480 // Find this line at this stack level
485 for _
, s
:= range o
.Paths
[i
:] {
488 case depth
== 1 && val
>= line
:
495 } else if depth
== 1 {
496 line
+= val
- incstart
506 return 0, &UnknownLineError
{path
, line
}
508 return 0, UnknownFileError(path
)
515 // UnknownFileError represents a failure to find the specific file in
517 type UnknownFileError
string
519 func (e UnknownFileError
) String() string { return "unknown file: " + string(e
) }
521 // UnknownLineError represents a failure to map a line to a program
522 // counter, either because the line is beyond the bounds of the file
523 // or because there is no code on the given line.
524 type UnknownLineError
struct {
529 func (e
*UnknownLineError
) String() string {
530 return "no code at " + e
.File
+ ":" + strconv
.Itoa(e
.Line
)
533 // DecodingError represents an error during the decoding of
535 type DecodingError
struct {
541 func (e
*DecodingError
) String() string {
544 msg
+= fmt
.Sprintf(" '%v'", e
.val
)
546 msg
+= fmt
.Sprintf(" at byte %#x", e
.off
)