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