1
2
3
4
5
6
7
8 package gosym
9
10 import (
11 "bytes"
12 "encoding/binary"
13 "fmt"
14 "strconv"
15 "strings"
16 )
17
18
21
22
23 type Sym struct {
24 Value uint64
25 Type byte
26 Name string
27 GoType uint64
28
29 Func *Func
30
31 goVersion version
32 }
33
34
35 func (s *Sym) Static() bool { return s.Type >= 'a' }
36
37
38
39
40
41
42
43 func (s *Sym) nameWithoutInst() string {
44 n := 0
45 b := []byte(s.Name)
46 for i, r := range b {
47 switch r {
48 case '[':
49 n++
50 case ']':
51 n--
52 if n < 0 {
53 return s.Name
54 }
55 default:
56 if n > 0 {
57 b[i] = '_'
58 }
59 }
60 }
61 if n > 0 {
62 return s.Name
63 }
64 return string(b)
65 }
66
67
68
69 func (s *Sym) PackageName() string {
70 name := s.nameWithoutInst()
71
72
73
74
75
76 if s.goVersion >= ver120 && (strings.HasPrefix(name, "go:") || strings.HasPrefix(name, "type:")) {
77 return ""
78 }
79
80
81 if s.goVersion <= ver118 && (strings.HasPrefix(name, "go.") || strings.HasPrefix(name, "type.")) {
82 return ""
83 }
84
85 pathend := strings.LastIndex(name, "/")
86 if pathend < 0 {
87 pathend = 0
88 }
89
90 if i := strings.Index(name[pathend:], "."); i != -1 {
91 return s.Name[:pathend+i]
92 }
93 return ""
94 }
95
96
97
98
99 func (s *Sym) ReceiverName() string {
100 name := s.nameWithoutInst()
101 pathend := strings.LastIndex(name, "/")
102 if pathend < 0 {
103 pathend = 0
104 }
105
106
107 l := strings.Index(name[pathend:], ".")
108
109 r := strings.LastIndex(name[pathend:], ".")
110 if l == -1 || r == -1 || l == r {
111
112 return ""
113 }
114 return s.Name[pathend+l+1 : pathend+r]
115 }
116
117
118 func (s *Sym) BaseName() string {
119 name := s.nameWithoutInst()
120 if i := strings.LastIndex(name, "."); i != -1 {
121 return s.Name[i+1:]
122 }
123 return s.Name
124 }
125
126
127 type Func struct {
128 Entry uint64
129 *Sym
130 End uint64
131 Params []*Sym
132 Locals []*Sym
133 FrameSize int
134 LineTable *LineTable
135 Obj *Obj
136 }
137
138
139
140
141
142
143
144
145
146
147
148
149 type Obj struct {
150
151 Funcs []Func
152
153
154
155
156
157 Paths []Sym
158 }
159
160
163
164
165
166
167 type Table struct {
168 Syms []Sym
169 Funcs []Func
170 Files map[string]*Obj
171 Objs []Obj
172
173 go12line *LineTable
174 }
175
176 type sym struct {
177 value uint64
178 gotype uint64
179 typ byte
180 name []byte
181 }
182
183 var (
184 littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}
185 bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00}
186 oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00}
187 )
188
189 func walksymtab(data []byte, fn func(sym) error) error {
190 if len(data) == 0 {
191 return nil
192 }
193 var order binary.ByteOrder = binary.BigEndian
194 newTable := false
195 switch {
196 case bytes.HasPrefix(data, oldLittleEndianSymtab):
197
198
199
200 data = data[6:]
201 order = binary.LittleEndian
202 case bytes.HasPrefix(data, bigEndianSymtab):
203 newTable = true
204 case bytes.HasPrefix(data, littleEndianSymtab):
205 newTable = true
206 order = binary.LittleEndian
207 }
208 var ptrsz int
209 if newTable {
210 if len(data) < 8 {
211 return &DecodingError{len(data), "unexpected EOF", nil}
212 }
213 ptrsz = int(data[7])
214 if ptrsz != 4 && ptrsz != 8 {
215 return &DecodingError{7, "invalid pointer size", ptrsz}
216 }
217 data = data[8:]
218 }
219 var s sym
220 p := data
221 for len(p) >= 4 {
222 var typ byte
223 if newTable {
224
225 typ = p[0] & 0x3F
226 wideValue := p[0]&0x40 != 0
227 goType := p[0]&0x80 != 0
228 if typ < 26 {
229 typ += 'A'
230 } else {
231 typ += 'a' - 26
232 }
233 s.typ = typ
234 p = p[1:]
235 if wideValue {
236 if len(p) < ptrsz {
237 return &DecodingError{len(data), "unexpected EOF", nil}
238 }
239
240 if ptrsz == 8 {
241 s.value = order.Uint64(p[0:8])
242 p = p[8:]
243 } else {
244 s.value = uint64(order.Uint32(p[0:4]))
245 p = p[4:]
246 }
247 } else {
248
249 s.value = 0
250 shift := uint(0)
251 for len(p) > 0 && p[0]&0x80 != 0 {
252 s.value |= uint64(p[0]&0x7F) << shift
253 shift += 7
254 p = p[1:]
255 }
256 if len(p) == 0 {
257 return &DecodingError{len(data), "unexpected EOF", nil}
258 }
259 s.value |= uint64(p[0]) << shift
260 p = p[1:]
261 }
262 if goType {
263 if len(p) < ptrsz {
264 return &DecodingError{len(data), "unexpected EOF", nil}
265 }
266
267 if ptrsz == 8 {
268 s.gotype = order.Uint64(p[0:8])
269 p = p[8:]
270 } else {
271 s.gotype = uint64(order.Uint32(p[0:4]))
272 p = p[4:]
273 }
274 }
275 } else {
276
277 s.value = uint64(order.Uint32(p[0:4]))
278 if len(p) < 5 {
279 return &DecodingError{len(data), "unexpected EOF", nil}
280 }
281 typ = p[4]
282 if typ&0x80 == 0 {
283 return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ}
284 }
285 typ &^= 0x80
286 s.typ = typ
287 p = p[5:]
288 }
289
290
291 var i int
292 var nnul int
293 for i = 0; i < len(p); i++ {
294 if p[i] == 0 {
295 nnul = 1
296 break
297 }
298 }
299 switch typ {
300 case 'z', 'Z':
301 p = p[i+nnul:]
302 for i = 0; i+2 <= len(p); i += 2 {
303 if p[i] == 0 && p[i+1] == 0 {
304 nnul = 2
305 break
306 }
307 }
308 }
309 if len(p) < i+nnul {
310 return &DecodingError{len(data), "unexpected EOF", nil}
311 }
312 s.name = p[0:i]
313 i += nnul
314 p = p[i:]
315
316 if !newTable {
317 if len(p) < 4 {
318 return &DecodingError{len(data), "unexpected EOF", nil}
319 }
320
321 s.gotype = uint64(order.Uint32(p[:4]))
322 p = p[4:]
323 }
324 fn(s)
325 }
326 return nil
327 }
328
329
330
331
332
333 func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
334 var n int
335 err := walksymtab(symtab, func(s sym) error {
336 n++
337 return nil
338 })
339 if err != nil {
340 return nil, err
341 }
342
343 var t Table
344 if pcln.isGo12() {
345 t.go12line = pcln
346 }
347 fname := make(map[uint16]string)
348 t.Syms = make([]Sym, 0, n)
349 nf := 0
350 nz := 0
351 lasttyp := uint8(0)
352 err = walksymtab(symtab, func(s sym) error {
353 n := len(t.Syms)
354 t.Syms = t.Syms[0 : n+1]
355 ts := &t.Syms[n]
356 ts.Type = s.typ
357 ts.Value = s.value
358 ts.GoType = s.gotype
359 ts.goVersion = pcln.version
360 switch s.typ {
361 default:
362
363 w := 0
364 b := s.name
365 for i := 0; i < len(b); i++ {
366 if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 {
367 i++
368 b[i] = '.'
369 }
370 b[w] = b[i]
371 w++
372 }
373 ts.Name = string(s.name[0:w])
374 case 'z', 'Z':
375 if lasttyp != 'z' && lasttyp != 'Z' {
376 nz++
377 }
378 for i := 0; i < len(s.name); i += 2 {
379 eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])
380 elt, ok := fname[eltIdx]
381 if !ok {
382 return &DecodingError{-1, "bad filename code", eltIdx}
383 }
384 if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
385 ts.Name += "/"
386 }
387 ts.Name += elt
388 }
389 }
390 switch s.typ {
391 case 'T', 't', 'L', 'l':
392 nf++
393 case 'f':
394 fname[uint16(s.value)] = ts.Name
395 }
396 lasttyp = s.typ
397 return nil
398 })
399 if err != nil {
400 return nil, err
401 }
402
403 t.Funcs = make([]Func, 0, nf)
404 t.Files = make(map[string]*Obj)
405
406 var obj *Obj
407 if t.go12line != nil {
408
409 t.Objs = make([]Obj, 1)
410 obj = &t.Objs[0]
411 t.go12line.go12MapFiles(t.Files, obj)
412 } else {
413 t.Objs = make([]Obj, 0, nz)
414 }
415
416
417
418 lastf := 0
419 for i := 0; i < len(t.Syms); i++ {
420 sym := &t.Syms[i]
421 switch sym.Type {
422 case 'Z', 'z':
423 if t.go12line != nil {
424
425 break
426 }
427
428 if obj != nil {
429 obj.Funcs = t.Funcs[lastf:]
430 }
431 lastf = len(t.Funcs)
432
433
434 n := len(t.Objs)
435 t.Objs = t.Objs[0 : n+1]
436 obj = &t.Objs[n]
437
438
439 var end int
440 for end = i + 1; end < len(t.Syms); end++ {
441 if c := t.Syms[end].Type; c != 'Z' && c != 'z' {
442 break
443 }
444 }
445 obj.Paths = t.Syms[i:end]
446 i = end - 1
447
448
449 depth := 0
450 for j := range obj.Paths {
451 s := &obj.Paths[j]
452 if s.Name == "" {
453 depth--
454 } else {
455 if depth == 0 {
456 t.Files[s.Name] = obj
457 }
458 depth++
459 }
460 }
461
462 case 'T', 't', 'L', 'l':
463 if n := len(t.Funcs); n > 0 {
464 t.Funcs[n-1].End = sym.Value
465 }
466 if sym.Name == "runtime.etext" || sym.Name == "etext" {
467 continue
468 }
469
470
471 var np, na int
472 var end int
473 countloop:
474 for end = i + 1; end < len(t.Syms); end++ {
475 switch t.Syms[end].Type {
476 case 'T', 't', 'L', 'l', 'Z', 'z':
477 break countloop
478 case 'p':
479 np++
480 case 'a':
481 na++
482 }
483 }
484
485
486 n := len(t.Funcs)
487 t.Funcs = t.Funcs[0 : n+1]
488 fn := &t.Funcs[n]
489 sym.Func = fn
490 fn.Params = make([]*Sym, 0, np)
491 fn.Locals = make([]*Sym, 0, na)
492 fn.Sym = sym
493 fn.Entry = sym.Value
494 fn.Obj = obj
495 if t.go12line != nil {
496
497
498
499 fn.LineTable = t.go12line
500 } else if pcln != nil {
501 fn.LineTable = pcln.slice(fn.Entry)
502 pcln = fn.LineTable
503 }
504 for j := i; j < end; j++ {
505 s := &t.Syms[j]
506 switch s.Type {
507 case 'm':
508 fn.FrameSize = int(s.Value)
509 case 'p':
510 n := len(fn.Params)
511 fn.Params = fn.Params[0 : n+1]
512 fn.Params[n] = s
513 case 'a':
514 n := len(fn.Locals)
515 fn.Locals = fn.Locals[0 : n+1]
516 fn.Locals[n] = s
517 }
518 }
519 i = end - 1
520 }
521 }
522
523 if t.go12line != nil && nf == 0 {
524 t.Funcs = t.go12line.go12Funcs()
525 }
526 if obj != nil {
527 obj.Funcs = t.Funcs[lastf:]
528 }
529 return &t, nil
530 }
531
532
533
534 func (t *Table) PCToFunc(pc uint64) *Func {
535 funcs := t.Funcs
536 for len(funcs) > 0 {
537 m := len(funcs) / 2
538 fn := &funcs[m]
539 switch {
540 case pc < fn.Entry:
541 funcs = funcs[0:m]
542 case fn.Entry <= pc && pc < fn.End:
543 return fn
544 default:
545 funcs = funcs[m+1:]
546 }
547 }
548 return nil
549 }
550
551
552
553 func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) {
554 if fn = t.PCToFunc(pc); fn == nil {
555 return
556 }
557 if t.go12line != nil {
558 file = t.go12line.go12PCToFile(pc)
559 line = t.go12line.go12PCToLine(pc)
560 } else {
561 file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc))
562 }
563 return
564 }
565
566
567
568
569 func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) {
570 obj, ok := t.Files[file]
571 if !ok {
572 return 0, nil, UnknownFileError(file)
573 }
574
575 if t.go12line != nil {
576 pc := t.go12line.go12LineToPC(file, line)
577 if pc == 0 {
578 return 0, nil, &UnknownLineError{file, line}
579 }
580 return pc, t.PCToFunc(pc), nil
581 }
582
583 abs, err := obj.alineFromLine(file, line)
584 if err != nil {
585 return
586 }
587 for i := range obj.Funcs {
588 f := &obj.Funcs[i]
589 pc := f.LineTable.LineToPC(abs, f.End)
590 if pc != 0 {
591 return pc, f, nil
592 }
593 }
594 return 0, nil, &UnknownLineError{file, line}
595 }
596
597
598
599 func (t *Table) LookupSym(name string) *Sym {
600
601 for i := range t.Syms {
602 s := &t.Syms[i]
603 switch s.Type {
604 case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
605 if s.Name == name {
606 return s
607 }
608 }
609 }
610 return nil
611 }
612
613
614
615 func (t *Table) LookupFunc(name string) *Func {
616 for i := range t.Funcs {
617 f := &t.Funcs[i]
618 if f.Sym.Name == name {
619 return f
620 }
621 }
622 return nil
623 }
624
625
626 func (t *Table) SymByAddr(addr uint64) *Sym {
627 for i := range t.Syms {
628 s := &t.Syms[i]
629 switch s.Type {
630 case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
631 if s.Value == addr {
632 return s
633 }
634 }
635 }
636 return nil
637 }
638
639
642
643
644
645
646
647
648
649
650 func (o *Obj) lineFromAline(aline int) (string, int) {
651 type stackEnt struct {
652 path string
653 start int
654 offset int
655 prev *stackEnt
656 }
657
658 noPath := &stackEnt{"", 0, 0, nil}
659 tos := noPath
660
661 pathloop:
662 for _, s := range o.Paths {
663 val := int(s.Value)
664 switch {
665 case val > aline:
666 break pathloop
667
668 case val == 1:
669
670 tos = &stackEnt{s.Name, val, 0, noPath}
671
672 case s.Name == "":
673
674 if tos == noPath {
675 return "<malformed symbol table>", 0
676 }
677 tos.prev.offset += val - tos.start
678 tos = tos.prev
679
680 default:
681
682 tos = &stackEnt{s.Name, val, 0, tos}
683 }
684 }
685
686 if tos == noPath {
687 return "", 0
688 }
689 return tos.path, aline - tos.start - tos.offset + 1
690 }
691
692 func (o *Obj) alineFromLine(path string, line int) (int, error) {
693 if line < 1 {
694 return 0, &UnknownLineError{path, line}
695 }
696
697 for i, s := range o.Paths {
698
699 if s.Name != path {
700 continue
701 }
702
703
704 depth := 0
705 var incstart int
706 line += int(s.Value)
707 pathloop:
708 for _, s := range o.Paths[i:] {
709 val := int(s.Value)
710 switch {
711 case depth == 1 && val >= line:
712 return line - 1, nil
713
714 case s.Name == "":
715 depth--
716 if depth == 0 {
717 break pathloop
718 } else if depth == 1 {
719 line += val - incstart
720 }
721
722 default:
723 if depth == 1 {
724 incstart = val
725 }
726 depth++
727 }
728 }
729 return 0, &UnknownLineError{path, line}
730 }
731 return 0, UnknownFileError(path)
732 }
733
734
737
738
739
740 type UnknownFileError string
741
742 func (e UnknownFileError) Error() string { return "unknown file: " + string(e) }
743
744
745
746
747 type UnknownLineError struct {
748 File string
749 Line int
750 }
751
752 func (e *UnknownLineError) Error() string {
753 return "no code at " + e.File + ":" + strconv.Itoa(e.Line)
754 }
755
756
757
758 type DecodingError struct {
759 off int
760 msg string
761 val any
762 }
763
764 func (e *DecodingError) Error() string {
765 msg := e.msg
766 if e.val != nil {
767 msg += fmt.Sprintf(" '%v'", e.val)
768 }
769 msg += fmt.Sprintf(" at byte %#x", e.off)
770 return msg
771 }
772
View as plain text