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