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