1
2
3
4
5
8
9 package gosym
10
11 import (
12 "bytes"
13 "encoding/binary"
14 "sync"
15 )
16
17
18 type version int
19
20 const (
21 verUnknown version = iota
22 ver11
23 ver12
24 ver116
25 )
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 type LineTable struct {
41 Data []byte
42 PC uint64
43 Line int
44
45
46 mu sync.Mutex
47
48
49 version version
50
51
52 binary binary.ByteOrder
53 quantum uint32
54 ptrsize uint32
55 funcnametab []byte
56 cutab []byte
57 funcdata []byte
58 functab []byte
59 nfunctab uint32
60 filetab []byte
61 pctab []byte
62 nfiletab uint32
63 funcNames map[uint32]string
64 strings map[uint32]string
65
66
67
68 fileMap map[string]uint32
69 }
70
71
72
73
74
75 const oldQuantum = 1
76
77 func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64, line int) {
78
79
80
81
82
83
84
85
86 b, pc, line = t.Data, t.PC, t.Line
87 for pc <= targetPC && line != targetLine && len(b) > 0 {
88 code := b[0]
89 b = b[1:]
90 switch {
91 case code == 0:
92 if len(b) < 4 {
93 b = b[0:0]
94 break
95 }
96 val := binary.BigEndian.Uint32(b)
97 b = b[4:]
98 line += int(val)
99 case code <= 64:
100 line += int(code)
101 case code <= 128:
102 line -= int(code - 64)
103 default:
104 pc += oldQuantum * uint64(code-128)
105 continue
106 }
107 pc += oldQuantum
108 }
109 return b, pc, line
110 }
111
112 func (t *LineTable) slice(pc uint64) *LineTable {
113 data, pc, line := t.parse(pc, -1)
114 return &LineTable{Data: data, PC: pc, Line: line}
115 }
116
117
118
119
120 func (t *LineTable) PCToLine(pc uint64) int {
121 if t.isGo12() {
122 return t.go12PCToLine(pc)
123 }
124 _, _, line := t.parse(pc, -1)
125 return line
126 }
127
128
129
130
131
132 func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 {
133 if t.isGo12() {
134 return 0
135 }
136 _, pc, line1 := t.parse(maxpc, line)
137 if line1 != line {
138 return 0
139 }
140
141 return pc - oldQuantum
142 }
143
144
145
146
147
148 func NewLineTable(data []byte, text uint64) *LineTable {
149 return &LineTable{Data: data, PC: text, Line: 0, funcNames: make(map[uint32]string), strings: make(map[uint32]string)}
150 }
151
152
153
154
155
156
157
158
159
160
161
162
163
164 func (t *LineTable) isGo12() bool {
165 t.parsePclnTab()
166 return t.version >= ver12
167 }
168
169 const go12magic = 0xfffffffb
170 const go116magic = 0xfffffffa
171
172
173
174 func (t *LineTable) uintptr(b []byte) uint64 {
175 if t.ptrsize == 4 {
176 return uint64(t.binary.Uint32(b))
177 }
178 return t.binary.Uint64(b)
179 }
180
181
182 func (t *LineTable) parsePclnTab() {
183 t.mu.Lock()
184 defer t.mu.Unlock()
185 if t.version != verUnknown {
186 return
187 }
188
189
190
191
192
193
194 t.version = ver11
195
196 defer func() {
197
198 recover()
199 }()
200
201
202 if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 ||
203 (t.Data[6] != 1 && t.Data[6] != 2 && t.Data[6] != 4) ||
204 (t.Data[7] != 4 && t.Data[7] != 8) {
205 return
206 }
207
208 var possibleVersion version
209 leMagic := binary.LittleEndian.Uint32(t.Data)
210 beMagic := binary.BigEndian.Uint32(t.Data)
211 switch {
212 case leMagic == go12magic:
213 t.binary, possibleVersion = binary.LittleEndian, ver12
214 case beMagic == go12magic:
215 t.binary, possibleVersion = binary.BigEndian, ver12
216 case leMagic == go116magic:
217 t.binary, possibleVersion = binary.LittleEndian, ver116
218 case beMagic == go116magic:
219 t.binary, possibleVersion = binary.BigEndian, ver116
220 default:
221 return
222 }
223
224
225 t.quantum = uint32(t.Data[6])
226 t.ptrsize = uint32(t.Data[7])
227
228 switch possibleVersion {
229 case ver116:
230 t.nfunctab = uint32(t.uintptr(t.Data[8:]))
231 t.nfiletab = uint32(t.uintptr(t.Data[8+t.ptrsize:]))
232 offset := t.uintptr(t.Data[8+2*t.ptrsize:])
233 t.funcnametab = t.Data[offset:]
234 offset = t.uintptr(t.Data[8+3*t.ptrsize:])
235 t.cutab = t.Data[offset:]
236 offset = t.uintptr(t.Data[8+4*t.ptrsize:])
237 t.filetab = t.Data[offset:]
238 offset = t.uintptr(t.Data[8+5*t.ptrsize:])
239 t.pctab = t.Data[offset:]
240 offset = t.uintptr(t.Data[8+6*t.ptrsize:])
241 t.funcdata = t.Data[offset:]
242 t.functab = t.Data[offset:]
243 functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
244 t.functab = t.functab[:functabsize]
245 case ver12:
246 t.nfunctab = uint32(t.uintptr(t.Data[8:]))
247 t.funcdata = t.Data
248 t.funcnametab = t.Data
249 t.functab = t.Data[8+t.ptrsize:]
250 t.pctab = t.Data
251 functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
252 fileoff := t.binary.Uint32(t.functab[functabsize:])
253 t.functab = t.functab[:functabsize]
254 t.filetab = t.Data[fileoff:]
255 t.nfiletab = t.binary.Uint32(t.filetab)
256 t.filetab = t.filetab[:t.nfiletab*4]
257 default:
258 panic("unreachable")
259 }
260 t.version = possibleVersion
261 }
262
263
264 func (t *LineTable) go12Funcs() []Func {
265
266 defer func() {
267 recover()
268 }()
269
270 n := len(t.functab) / int(t.ptrsize) / 2
271 funcs := make([]Func, n)
272 for i := range funcs {
273 f := &funcs[i]
274 f.Entry = t.uintptr(t.functab[2*i*int(t.ptrsize):])
275 f.End = t.uintptr(t.functab[(2*i+2)*int(t.ptrsize):])
276 info := t.funcdata[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):]
277 f.LineTable = t
278 f.FrameSize = int(t.binary.Uint32(info[t.ptrsize+2*4:]))
279 f.Sym = &Sym{
280 Value: f.Entry,
281 Type: 'T',
282 Name: t.funcName(t.binary.Uint32(info[t.ptrsize:])),
283 GoType: 0,
284 Func: f,
285 }
286 }
287 return funcs
288 }
289
290
291 func (t *LineTable) findFunc(pc uint64) []byte {
292 if pc < t.uintptr(t.functab) || pc >= t.uintptr(t.functab[len(t.functab)-int(t.ptrsize):]) {
293 return nil
294 }
295
296
297
298 f := t.functab
299 nf := t.nfunctab
300 for nf > 0 {
301 m := nf / 2
302 fm := f[2*t.ptrsize*m:]
303 if t.uintptr(fm) <= pc && pc < t.uintptr(fm[2*t.ptrsize:]) {
304 return t.funcdata[t.uintptr(fm[t.ptrsize:]):]
305 } else if pc < t.uintptr(fm) {
306 nf = m
307 } else {
308 f = f[(m+1)*2*t.ptrsize:]
309 nf -= m + 1
310 }
311 }
312 return nil
313 }
314
315
316 func (t *LineTable) readvarint(pp *[]byte) uint32 {
317 var v, shift uint32
318 p := *pp
319 for shift = 0; ; shift += 7 {
320 b := p[0]
321 p = p[1:]
322 v |= (uint32(b) & 0x7F) << shift
323 if b&0x80 == 0 {
324 break
325 }
326 }
327 *pp = p
328 return v
329 }
330
331
332 func (t *LineTable) funcName(off uint32) string {
333 if s, ok := t.funcNames[off]; ok {
334 return s
335 }
336 i := bytes.IndexByte(t.funcnametab[off:], 0)
337 s := string(t.funcnametab[off : off+uint32(i)])
338 t.funcNames[off] = s
339 return s
340 }
341
342
343 func (t *LineTable) stringFrom(arr []byte, off uint32) string {
344 if s, ok := t.strings[off]; ok {
345 return s
346 }
347 i := bytes.IndexByte(arr[off:], 0)
348 s := string(arr[off : off+uint32(i)])
349 t.strings[off] = s
350 return s
351 }
352
353
354 func (t *LineTable) string(off uint32) string {
355 return t.stringFrom(t.funcdata, off)
356 }
357
358
359 func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool {
360 uvdelta := t.readvarint(p)
361 if uvdelta == 0 && !first {
362 return false
363 }
364 if uvdelta&1 != 0 {
365 uvdelta = ^(uvdelta >> 1)
366 } else {
367 uvdelta >>= 1
368 }
369 vdelta := int32(uvdelta)
370 pcdelta := t.readvarint(p) * t.quantum
371 *pc += uint64(pcdelta)
372 *val += vdelta
373 return true
374 }
375
376
377
378
379 func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 {
380 p := t.pctab[off:]
381
382 val := int32(-1)
383 pc := entry
384 for t.step(&p, &pc, &val, pc == entry) {
385 if targetpc < pc {
386 return val
387 }
388 }
389 return -1
390 }
391
392
393
394
395
396
397
398 func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32, cutab []byte) uint64 {
399 if filetab == 0 || linetab == 0 {
400 return 0
401 }
402
403 fp := t.pctab[filetab:]
404 fl := t.pctab[linetab:]
405 fileVal := int32(-1)
406 filePC := entry
407 lineVal := int32(-1)
408 linePC := entry
409 fileStartPC := filePC
410 for t.step(&fp, &filePC, &fileVal, filePC == entry) {
411 fileIndex := fileVal
412 if t.version == ver116 {
413 fileIndex = int32(t.binary.Uint32(cutab[fileVal*4:]))
414 }
415 if fileIndex == filenum && fileStartPC < filePC {
416
417
418
419
420 lineStartPC := linePC
421 for linePC < filePC && t.step(&fl, &linePC, &lineVal, linePC == entry) {
422
423 if lineVal == line {
424 if fileStartPC <= lineStartPC {
425 return lineStartPC
426 }
427 if fileStartPC < linePC {
428 return fileStartPC
429 }
430 }
431 lineStartPC = linePC
432 }
433 }
434 fileStartPC = filePC
435 }
436 return 0
437 }
438
439
440 func (t *LineTable) go12PCToLine(pc uint64) (line int) {
441 defer func() {
442 if recover() != nil {
443 line = -1
444 }
445 }()
446
447 f := t.findFunc(pc)
448 if f == nil {
449 return -1
450 }
451 entry := t.uintptr(f)
452 linetab := t.binary.Uint32(f[t.ptrsize+5*4:])
453 return int(t.pcvalue(linetab, entry, pc))
454 }
455
456
457 func (t *LineTable) go12PCToFile(pc uint64) (file string) {
458 defer func() {
459 if recover() != nil {
460 file = ""
461 }
462 }()
463
464 f := t.findFunc(pc)
465 if f == nil {
466 return ""
467 }
468 entry := t.uintptr(f)
469 filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
470 fno := t.pcvalue(filetab, entry, pc)
471 if t.version == ver12 {
472 if fno <= 0 {
473 return ""
474 }
475 return t.string(t.binary.Uint32(t.filetab[4*fno:]))
476 }
477
478 if fno < 0 {
479 return ""
480 }
481 cuoff := t.binary.Uint32(f[t.ptrsize+7*4:])
482 if fnoff := t.binary.Uint32(t.cutab[(cuoff+uint32(fno))*4:]); fnoff != ^uint32(0) {
483 return t.stringFrom(t.filetab, fnoff)
484 }
485 return ""
486 }
487
488
489 func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) {
490 defer func() {
491 if recover() != nil {
492 pc = 0
493 }
494 }()
495
496 t.initFileMap()
497 filenum, ok := t.fileMap[file]
498 if !ok {
499 return 0
500 }
501
502
503
504
505 var cutab []byte
506 for i := uint32(0); i < t.nfunctab; i++ {
507 f := t.funcdata[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):]
508 entry := t.uintptr(f)
509 filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
510 linetab := t.binary.Uint32(f[t.ptrsize+5*4:])
511 if t.version == ver116 {
512 cuoff := t.binary.Uint32(f[t.ptrsize+7*4:]) * 4
513 cutab = t.cutab[cuoff:]
514 }
515 pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line), cutab)
516 if pc != 0 {
517 return pc
518 }
519 }
520 return 0
521 }
522
523
524 func (t *LineTable) initFileMap() {
525 t.mu.Lock()
526 defer t.mu.Unlock()
527
528 if t.fileMap != nil {
529 return
530 }
531 m := make(map[string]uint32)
532
533 if t.version == ver12 {
534 for i := uint32(1); i < t.nfiletab; i++ {
535 s := t.string(t.binary.Uint32(t.filetab[4*i:]))
536 m[s] = i
537 }
538 } else {
539 var pos uint32
540 for i := uint32(0); i < t.nfiletab; i++ {
541 s := t.stringFrom(t.filetab, pos)
542 m[s] = pos
543 pos += uint32(len(s) + 1)
544 }
545 }
546 t.fileMap = m
547 }
548
549
550
551
552 func (t *LineTable) go12MapFiles(m map[string]*Obj, obj *Obj) {
553 defer func() {
554 recover()
555 }()
556
557 t.initFileMap()
558 for file := range t.fileMap {
559 m[file] = obj
560 }
561 }
562
View as plain text