1
2
3
4
5
6
7
8 package gosym
9
10
11
12
13
14
15 import (
16 "encoding/binary"
17 "fmt"
18 "os"
19 "strconv"
20 "strings"
21 )
22
23 24 25
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 if i := strings.Index(s.Name, "."); i != -1 {
44 return s.Name[0:i]
45 }
46 return ""
47 }
48
49
50
51 func (s *Sym) ReceiverName() string {
52 l := strings.Index(s.Name, ".")
53 r := strings.LastIndex(s.Name, ".")
54 if l == -1 || r == -1 || l == r {
55 return ""
56 }
57 return s.Name[l+1 : r]
58 }
59
60
61 func (s *Sym) BaseName() string {
62 if i := strings.LastIndex(s.Name, "."); i != -1 {
63 return s.Name[i+1:]
64 }
65 return s.Name
66 }
67
68
69 type Func struct {
70 Entry uint64
71 *Sym
72 End uint64
73 Params []*Sym
74 Locals []*Sym
75 FrameSize int
76 LineTable *LineTable
77 Obj *Obj
78 }
79
80
81 type Obj struct {
82 Funcs []Func
83 Paths []Sym
84 }
85
86 87 88
89
90
91
92
93 type Table struct {
94 Syms []Sym
95 Funcs []Func
96 Files map[string]*Obj
97 Objs []Obj
98
99 }
100
101 type sym struct {
102 value uint32
103 gotype uint32
104 typ byte
105 name []byte
106 }
107
108 func walksymtab(data []byte, fn func(sym) os.Error) os.Error {
109 var s sym
110 p := data
111 for len(p) >= 6 {
112 s.value = binary.BigEndian.Uint32(p[0:4])
113 typ := p[4]
114 if typ&0x80 == 0 {
115 return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ}
116 }
117 typ &^= 0x80
118 s.typ = typ
119 p = p[5:]
120 var i int
121 var nnul int
122 for i = 0; i < len(p); i++ {
123 if p[i] == 0 {
124 nnul = 1
125 break
126 }
127 }
128 switch typ {
129 case 'z', 'Z':
130 p = p[i+nnul:]
131 for i = 0; i+2 <= len(p); i += 2 {
132 if p[i] == 0 && p[i+1] == 0 {
133 nnul = 2
134 break
135 }
136 }
137 }
138 if i+nnul+4 > len(p) {
139 return &DecodingError{len(data), "unexpected EOF", nil}
140 }
141 s.name = p[0:i]
142 i += nnul
143 s.gotype = binary.BigEndian.Uint32(p[i : i+4])
144 p = p[i+4:]
145 fn(s)
146 }
147 return nil
148 }
149
150
151
152 func NewTable(symtab []byte, pcln *LineTable) (*Table, os.Error) {
153 var n int
154 err := walksymtab(symtab, func(s sym) os.Error {
155 n++
156 return nil
157 })
158 if err != nil {
159 return nil, err
160 }
161
162 var t Table
163 fname := make(map[uint16]string)
164 t.Syms = make([]Sym, 0, n)
165 nf := 0
166 nz := 0
167 lasttyp := uint8(0)
168 err = walksymtab(symtab, func(s sym) os.Error {
169 n := len(t.Syms)
170 t.Syms = t.Syms[0 : n+1]
171 ts := &t.Syms[n]
172 ts.Type = s.typ
173 ts.Value = uint64(s.value)
174 ts.GoType = uint64(s.gotype)
175 switch s.typ {
176 default:
177
178 w := 0
179 b := s.name
180 for i := 0; i < len(b); i++ {
181 if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 {
182 i++
183 b[i] = '.'
184 }
185 b[w] = b[i]
186 w++
187 }
188 ts.Name = string(s.name[0:w])
189 case 'z', 'Z':
190 if lasttyp != 'z' && lasttyp != 'Z' {
191 nz++
192 }
193 for i := 0; i < len(s.name); i += 2 {
194 eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])
195 elt, ok := fname[eltIdx]
196 if !ok {
197 return &DecodingError{-1, "bad filename code", eltIdx}
198 }
199 if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
200 ts.Name += "/"
201 }
202 ts.Name += elt
203 }
204 }
205 switch s.typ {
206 case 'T', 't', 'L', 'l':
207 nf++
208 case 'f':
209 fname[uint16(s.value)] = ts.Name
210 }
211 lasttyp = s.typ
212 return nil
213 })
214 if err != nil {
215 return nil, err
216 }
217
218 t.Funcs = make([]Func, 0, nf)
219 t.Objs = make([]Obj, 0, nz)
220 t.Files = make(map[string]*Obj)
221
222
223
224 var obj *Obj
225 lastf := 0
226 for i := 0; i < len(t.Syms); i++ {
227 sym := &t.Syms[i]
228 switch sym.Type {
229 case 'Z', 'z':
230
231 if obj != nil {
232 obj.Funcs = t.Funcs[lastf:]
233 }
234 lastf = len(t.Funcs)
235
236
237 n := len(t.Objs)
238 t.Objs = t.Objs[0 : n+1]
239 obj = &t.Objs[n]
240
241
242 var end int
243 for end = i + 1; end < len(t.Syms); end++ {
244 if c := t.Syms[end].Type; c != 'Z' && c != 'z' {
245 break
246 }
247 }
248 obj.Paths = t.Syms[i:end]
249 i = end - 1
250
251
252 depth := 0
253 for j := range obj.Paths {
254 s := &obj.Paths[j]
255 if s.Name == "" {
256 depth--
257 } else {
258 if depth == 0 {
259 t.Files[s.Name] = obj
260 }
261 depth++
262 }
263 }
264
265 case 'T', 't', 'L', 'l':
266 if n := len(t.Funcs); n > 0 {
267 t.Funcs[n-1].End = sym.Value
268 }
269 if sym.Name == "etext" {
270 continue
271 }
272
273
274 var np, na int
275 var end int
276 countloop:
277 for end = i + 1; end < len(t.Syms); end++ {
278 switch t.Syms[end].Type {
279 case 'T', 't', 'L', 'l', 'Z', 'z':
280 break countloop
281 case 'p':
282 np++
283 case 'a':
284 na++
285 }
286 }
287
288
289 n := len(t.Funcs)
290 t.Funcs = t.Funcs[0 : n+1]
291 fn := &t.Funcs[n]
292 sym.Func = fn
293 fn.Params = make([]*Sym, 0, np)
294 fn.Locals = make([]*Sym, 0, na)
295 fn.Sym = sym
296 fn.Entry = sym.Value
297 fn.Obj = obj
298 if pcln != nil {
299 fn.LineTable = pcln.slice(fn.Entry)
300 pcln = fn.LineTable
301 }
302 for j := i; j < end; j++ {
303 s := &t.Syms[j]
304 switch s.Type {
305 case 'm':
306 fn.FrameSize = int(s.Value)
307 case 'p':
308 n := len(fn.Params)
309 fn.Params = fn.Params[0 : n+1]
310 fn.Params[n] = s
311 case 'a':
312 n := len(fn.Locals)
313 fn.Locals = fn.Locals[0 : n+1]
314 fn.Locals[n] = s
315 }
316 }
317 i = end - 1
318 }
319 }
320 if obj != nil {
321 obj.Funcs = t.Funcs[lastf:]
322 }
323 return &t, nil
324 }
325
326
327
328 func (t *Table) PCToFunc(pc uint64) *Func {
329 funcs := t.Funcs
330 for len(funcs) > 0 {
331 m := len(funcs) / 2
332 fn := &funcs[m]
333 switch {
334 case pc < fn.Entry:
335 funcs = funcs[0:m]
336 case fn.Entry <= pc && pc < fn.End:
337 return fn
338 default:
339 funcs = funcs[m+1:]
340 }
341 }
342 return nil
343 }
344
345
346
347 func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) {
348 if fn = t.PCToFunc(pc); fn == nil {
349 return
350 }
351 file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc))
352 return
353 }
354
355
356
357
358 func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err os.Error) {
359 obj, ok := t.Files[file]
360 if !ok {
361 return 0, nil, UnknownFileError(file)
362 }
363 abs, err := obj.alineFromLine(file, line)
364 if err != nil {
365 return
366 }
367 for i := range obj.Funcs {
368 f := &obj.Funcs[i]
369 pc := f.LineTable.LineToPC(abs, f.End)
370 if pc != 0 {
371 return pc, f, nil
372 }
373 }
374 return 0, nil, &UnknownLineError{file, line}
375 }
376
377
378
379 func (t *Table) LookupSym(name string) *Sym {
380
381 for i := range t.Syms {
382 s := &t.Syms[i]
383 switch s.Type {
384 case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
385 if s.Name == name {
386 return s
387 }
388 }
389 }
390 return nil
391 }
392
393
394
395 func (t *Table) LookupFunc(name string) *Func {
396 for i := range t.Funcs {
397 f := &t.Funcs[i]
398 if f.Sym.Name == name {
399 return f
400 }
401 }
402 return nil
403 }
404
405
406
407 func (t *Table) SymByAddr(addr uint64) *Sym {
408
409 for i := range t.Syms {
410 s := &t.Syms[i]
411 switch s.Type {
412 case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
413 if s.Value == addr {
414 return s
415 }
416 }
417 }
418 return nil
419 }
420
421 422 423
424
425 func (o *Obj) lineFromAline(aline int) (string, int) {
426 type stackEnt struct {
427 path string
428 start int
429 offset int
430 prev *stackEnt
431 }
432
433 noPath := &stackEnt{"", 0, 0, nil}
434 tos := noPath
435
436
437
438 pathloop:
439 for _, s := range o.Paths {
440 val := int(s.Value)
441 switch {
442 case val > aline:
443 break pathloop
444
445 case val == 1:
446
447 tos = &stackEnt{s.Name, val, 0, noPath}
448
449 case s.Name == "":
450
451 if tos == noPath {
452 return "<malformed symbol table>", 0
453 }
454 tos.prev.offset += val - tos.start
455 tos = tos.prev
456
457 default:
458
459 tos = &stackEnt{s.Name, val, 0, tos}
460 }
461 }
462
463 if tos == noPath {
464 return "", 0
465 }
466 return tos.path, aline - tos.start - tos.offset + 1
467 }
468
469 func (o *Obj) alineFromLine(path string, line int) (int, os.Error) {
470 if line < 1 {
471 return 0, &UnknownLineError{path, line}
472 }
473
474 for i, s := range o.Paths {
475
476 if s.Name != path {
477 continue
478 }
479
480
481 depth := 0
482 var incstart int
483 line += int(s.Value)
484 pathloop:
485 for _, s := range o.Paths[i:] {
486 val := int(s.Value)
487 switch {
488 case depth == 1 && val >= line:
489 return line - 1, nil
490
491 case s.Name == "":
492 depth--
493 if depth == 0 {
494 break pathloop
495 } else if depth == 1 {
496 line += val - incstart
497 }
498
499 default:
500 if depth == 1 {
501 incstart = val
502 }
503 depth++
504 }
505 }
506 return 0, &UnknownLineError{path, line}
507 }
508 return 0, UnknownFileError(path)
509 }
510
511 512 513
514
515
516
517 type UnknownFileError string
518
519 func (e UnknownFileError) String() string { return "unknown file: " + string(e) }
520
521
522
523
524 type UnknownLineError struct {
525 File string
526 Line int
527 }
528
529 func (e *UnknownLineError) String() string {
530 return "no code at " + e.File + ":" + strconv.Itoa(e.Line)
531 }
532
533
534
535 type DecodingError struct {
536 off int
537 msg string
538 val interface{}
539 }
540
541 func (e *DecodingError) String() string {
542 msg := e.msg
543 if e.val != nil {
544 msg += fmt.Sprintf(" '%v'", e.val)
545 }
546 msg += fmt.Sprintf(" at byte %#x", e.off)
547 return msg
548 }