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