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.
16 // A LineTable is a data structure mapping program counters to line numbers.
18 // In Go 1.1 and earlier, each function (represented by a Func) had its own LineTable,
19 // and the line number corresponded to a numbering of all source lines in the
20 // program, across all files. That absolute line number would then have to be
21 // converted separately to a file name and line number within the file.
23 // In Go 1.2, the format of the data changed so that there is a single LineTable
24 // for the entire program, shared by all Funcs, and there are no absolute line
25 // numbers, just line numbers within specific files.
27 // For the most part, LineTable's methods should be treated as an internal
28 // detail of the package; callers should use the methods on Table instead.
29 type LineTable
struct {
36 go12
int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes
37 binary binary
.ByteOrder
44 fileMap
map[string]uint32
47 // NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4,
48 // but we have no idea whether we're using arm or not. This only
49 // matters in the old (pre-Go 1.2) symbol table format, so it's not worth
53 func (t
*LineTable
) parse(targetPC
uint64, targetLine
int) (b
[]byte, pc
uint64, line
int) {
54 // The PC/line table can be thought of as a sequence of
55 // <pc update>* <line update>
56 // batches. Each update batch results in a (pc, line) pair,
57 // where line applies to every PC from pc up to but not
58 // including the pc of the next pair.
60 // Here we process each update individually, which simplifies
61 // the code, but makes the corner cases more confusing.
62 b
, pc
, line
= t
.Data
, t
.PC
, t
.Line
63 for pc
<= targetPC
&& line
!= targetLine
&& len(b
) > 0 {
72 val
:= binary
.BigEndian
.Uint32(b
)
78 line
-= int(code
- 64)
80 pc
+= oldQuantum
* uint64(code
-128)
88 func (t
*LineTable
) slice(pc
uint64) *LineTable
{
89 data
, pc
, line
:= t
.parse(pc
, -1)
90 return &LineTable
{Data
: data
, PC
: pc
, Line
: line
}
93 // PCToLine returns the line number for the given program counter.
94 // Callers should use Table's PCToLine method instead.
95 func (t
*LineTable
) PCToLine(pc
uint64) int {
97 return t
.go12PCToLine(pc
)
99 _
, _
, line
:= t
.parse(pc
, -1)
103 // LineToPC returns the program counter for the given line number,
104 // considering only program counters before maxpc.
105 // Callers should use Table's LineToPC method instead.
106 func (t
*LineTable
) LineToPC(line
int, maxpc
uint64) uint64 {
110 _
, pc
, line1
:= t
.parse(maxpc
, line
)
114 // Subtract quantum from PC to account for post-line increment
115 return pc
- oldQuantum
118 // NewLineTable returns a new PC/line table
119 // corresponding to the encoded data.
120 // Text must be the start address of the
121 // corresponding text segment.
122 func NewLineTable(data
[]byte, text
uint64) *LineTable
{
123 return &LineTable
{Data
: data
, PC
: text
, Line
: 0}
126 // Go 1.2 symbol table format.
127 // See golang.org/s/go12symtab.
129 // A general note about the methods here: rather than try to avoid
130 // index out of bounds errors, we trust Go to detect them, and then
131 // we recover from the panics and treat them as indicative of a malformed
132 // or incomplete table.
134 // The methods called by symtab.go, which begin with "go12" prefixes,
135 // are expected to have that recovery logic.
137 // isGo12 reports whether this is a Go 1.2 (or later) symbol table.
138 func (t
*LineTable
) isGo12() bool {
143 const go12magic
= 0xfffffffb
145 // uintptr returns the pointer-sized value encoded at b.
146 // The pointer size is dictated by the table being read.
147 func (t
*LineTable
) uintptr(b
[]byte) uint64 {
149 return uint64(t
.binary
.Uint32(b
))
151 return t
.binary
.Uint64(b
)
154 // go12init initializes the Go 1.2 metadata if t is a Go 1.2 symbol table.
155 func (t
*LineTable
) go12Init() {
163 // If we panic parsing, assume it's not a Go 1.2 symbol table.
167 // Check header: 4-byte magic, two zeros, pc quantum, pointer size.
168 t
.go12
= -1 // not Go 1.2 until proven otherwise
169 if len(t
.Data
) < 16 || t
.Data
[4] != 0 || t
.Data
[5] != 0 ||
170 (t
.Data
[6] != 1 && t
.Data
[6] != 4) ||
// pc quantum
171 (t
.Data
[7] != 4 && t
.Data
[7] != 8) { // pointer size
175 switch uint32(go12magic
) {
176 case binary
.LittleEndian
.Uint32(t
.Data
):
177 t
.binary
= binary
.LittleEndian
178 case binary
.BigEndian
.Uint32(t
.Data
):
179 t
.binary
= binary
.BigEndian
184 t
.quantum
= uint32(t
.Data
[6])
185 t
.ptrsize
= uint32(t
.Data
[7])
187 t
.nfunctab
= uint32(t
.uintptr(t
.Data
[8:]))
188 t
.functab
= t
.Data
[8+t
.ptrsize
:]
189 functabsize
:= t
.nfunctab
*2*t
.ptrsize
+ t
.ptrsize
190 fileoff
:= t
.binary
.Uint32(t
.functab
[functabsize
:])
191 t
.functab
= t
.functab
[:functabsize
]
192 t
.filetab
= t
.Data
[fileoff
:]
193 t
.nfiletab
= t
.binary
.Uint32(t
.filetab
)
194 t
.filetab
= t
.filetab
[:t
.nfiletab
*4]
196 t
.go12
= 1 // so far so good
199 // go12Funcs returns a slice of Funcs derived from the Go 1.2 pcln table.
200 func (t
*LineTable
) go12Funcs() []Func
{
201 // Assume it is malformed and return nil on error.
206 n
:= len(t
.functab
) / int(t
.ptrsize
) / 2
207 funcs
:= make([]Func
, n
)
208 for i
:= range funcs
{
210 f
.Entry
= uint64(t
.uintptr(t
.functab
[2*i
*int(t
.ptrsize
):]))
211 f
.End
= uint64(t
.uintptr(t
.functab
[(2*i
+2)*int(t
.ptrsize
):]))
212 info
:= t
.Data
[t
.uintptr(t
.functab
[(2*i
+1)*int(t
.ptrsize
):]):]
214 f
.FrameSize
= int(t
.binary
.Uint32(info
[t
.ptrsize
+2*4:]))
218 Name
: t
.string(t
.binary
.Uint32(info
[t
.ptrsize
:])),
226 // findFunc returns the func corresponding to the given program counter.
227 func (t
*LineTable
) findFunc(pc
uint64) []byte {
228 if pc
< t
.uintptr(t
.functab
) || pc
>= t
.uintptr(t
.functab
[len(t
.functab
)-int(t
.ptrsize
):]) {
232 // The function table is a list of 2*nfunctab+1 uintptrs,
233 // alternating program counters and offsets to func structures.
238 fm
:= f
[2*t
.ptrsize
*m
:]
239 if t
.uintptr(fm
) <= pc
&& pc
< t
.uintptr(fm
[2*t
.ptrsize
:]) {
240 return t
.Data
[t
.uintptr(fm
[t
.ptrsize
:]):]
241 } else if pc
< t
.uintptr(fm
) {
244 f
= f
[(m
+1)*2*t
.ptrsize
:]
251 // readvarint reads, removes, and returns a varint from *pp.
252 func (t
*LineTable
) readvarint(pp
*[]byte) uint32 {
255 for shift
= 0; ; shift
+= 7 {
258 v |
= (uint32(b
) & 0x7F) << shift
267 // string returns a Go string found at off.
268 func (t
*LineTable
) string(off
uint32) string {
269 for i
:= off
; ; i
++ {
271 return string(t
.Data
[off
:i
])
276 // step advances to the next pc, value pair in the encoded table.
277 func (t
*LineTable
) step(p
*[]byte, pc
*uint64, val
*int32, first
bool) bool {
278 uvdelta
:= t
.readvarint(p
)
279 if uvdelta
== 0 && !first
{
283 uvdelta
= ^(uvdelta
>> 1)
287 vdelta
:= int32(uvdelta
)
288 pcdelta
:= t
.readvarint(p
) * t
.quantum
289 *pc
+= uint64(pcdelta
)
294 // pcvalue reports the value associated with the target pc.
295 // off is the offset to the beginning of the pc-value table,
296 // and entry is the start PC for the corresponding function.
297 func (t
*LineTable
) pcvalue(off
uint32, entry
, targetpc
uint64) int32 {
305 for t
.step(&p
, &pc
, &val
, pc
== entry
) {
313 // findFileLine scans one function in the binary looking for a
314 // program counter in the given file on the given line.
315 // It does so by running the pc-value tables mapping program counter
316 // to file number. Since most functions come from a single file, these
317 // are usually short and quick to scan. If a file match is found, then the
318 // code goes to the expense of looking for a simultaneous line number match.
319 func (t
*LineTable
) findFileLine(entry
uint64, filetab
, linetab
uint32, filenum
, line
int32) uint64 {
320 if filetab
== 0 || linetab
== 0 {
324 fp
:= t
.Data
[filetab
:]
325 fl
:= t
.Data
[linetab
:]
330 fileStartPC
:= filePC
331 for t
.step(&fp
, &filePC
, &fileVal
, filePC
== entry
) {
332 if fileVal
== filenum
&& fileStartPC
< filePC
{
333 // fileVal is in effect starting at fileStartPC up to
334 // but not including filePC, and it's the file we want.
335 // Run the PC table looking for a matching line number
336 // or until we reach filePC.
337 lineStartPC
:= linePC
338 for linePC
< filePC
&& t
.step(&fl
, &linePC
, &lineVal
, linePC
== entry
) {
339 // lineVal is in effect until linePC, and lineStartPC < filePC.
341 if fileStartPC
<= lineStartPC
{
344 if fileStartPC
< linePC
{
356 // go12PCToLine maps program counter to line number for the Go 1.2 pcln table.
357 func (t
*LineTable
) go12PCToLine(pc
uint64) (line
int) {
359 if recover() != nil {
368 entry
:= t
.uintptr(f
)
369 linetab
:= t
.binary
.Uint32(f
[t
.ptrsize
+5*4:])
370 return int(t
.pcvalue(linetab
, entry
, pc
))
373 // go12PCToFile maps program counter to file name for the Go 1.2 pcln table.
374 func (t
*LineTable
) go12PCToFile(pc
uint64) (file
string) {
376 if recover() != nil {
385 entry
:= t
.uintptr(f
)
386 filetab
:= t
.binary
.Uint32(f
[t
.ptrsize
+4*4:])
387 fno
:= t
.pcvalue(filetab
, entry
, pc
)
391 return t
.string(t
.binary
.Uint32(t
.filetab
[4*fno
:]))
394 // go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table.
395 func (t
*LineTable
) go12LineToPC(file
string, line
int) (pc
uint64) {
397 if recover() != nil {
403 filenum
:= t
.fileMap
[file
]
408 // Scan all functions.
409 // If this turns out to be a bottleneck, we could build a map[int32][]int32
410 // mapping file number to a list of functions with code from that file.
411 for i
:= uint32(0); i
< t
.nfunctab
; i
++ {
412 f
:= t
.Data
[t
.uintptr(t
.functab
[2*t
.ptrsize
*i
+t
.ptrsize
:]):]
413 entry
:= t
.uintptr(f
)
414 filetab
:= t
.binary
.Uint32(f
[t
.ptrsize
+4*4:])
415 linetab
:= t
.binary
.Uint32(f
[t
.ptrsize
+5*4:])
416 pc
:= t
.findFileLine(entry
, filetab
, linetab
, int32(filenum
), int32(line
))
424 // initFileMap initializes the map from file name to file number.
425 func (t
*LineTable
) initFileMap() {
429 if t
.fileMap
!= nil {
432 m
:= make(map[string]uint32)
434 for i
:= uint32(1); i
< t
.nfiletab
; i
++ {
435 s
:= t
.string(t
.binary
.Uint32(t
.filetab
[4*i
:]))
441 // go12MapFiles adds to m a key for every file in the Go 1.2 LineTable.
442 // Every key maps to obj. That's not a very interesting map, but it provides
443 // a way for callers to obtain the list of files in the program.
444 func (t
*LineTable
) go12MapFiles(m
map[string]*Obj
, obj
*Obj
) {
450 for file
:= range t
.fileMap
{