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