OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [debug/] [gosym/] [symtab.go] - Blame information for rev 750

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 747 jeremybenn
// Copyright 2009 The Go Authors.  All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
4
 
5
// Package gosym implements access to the Go symbol
6
// and line number tables embedded in Go binaries generated
7
// by the gc compilers.
8
package gosym
9
 
10
// The table format is a variant of the format used in Plan 9's a.out
11
// format, documented at http://plan9.bell-labs.com/magic/man2html/6/a.out.
12
// The best reference for the differences between the Plan 9 format
13
// and the Go format is the runtime source, specifically ../../runtime/symtab.c.
14
 
15
import (
16
        "encoding/binary"
17
        "fmt"
18
        "strconv"
19
        "strings"
20
)
21
 
22
/*
23
 * Symbols
24
 */
25
 
26
// A Sym represents a single symbol table entry.
27
type Sym struct {
28
        Value  uint64
29
        Type   byte
30
        Name   string
31
        GoType uint64
32
        // If this symbol if a function symbol, the corresponding Func
33
        Func *Func
34
}
35
 
36
// Static returns whether this symbol is static (not visible outside its file).
37
func (s *Sym) Static() bool { return s.Type >= 'a' }
38
 
39
// PackageName returns the package part of the symbol name,
40
// or the empty string if there is none.
41
func (s *Sym) PackageName() string {
42
        if i := strings.Index(s.Name, "."); i != -1 {
43
                return s.Name[0:i]
44
        }
45
        return ""
46
}
47
 
48
// ReceiverName returns the receiver type name of this symbol,
49
// or the empty string if there is none.
50
func (s *Sym) ReceiverName() string {
51
        l := strings.Index(s.Name, ".")
52
        r := strings.LastIndex(s.Name, ".")
53
        if l == -1 || r == -1 || l == r {
54
                return ""
55
        }
56
        return s.Name[l+1 : r]
57
}
58
 
59
// BaseName returns the symbol name without the package or receiver name.
60
func (s *Sym) BaseName() string {
61
        if i := strings.LastIndex(s.Name, "."); i != -1 {
62
                return s.Name[i+1:]
63
        }
64
        return s.Name
65
}
66
 
67
// A Func collects information about a single function.
68
type Func struct {
69
        Entry uint64
70
        *Sym
71
        End       uint64
72
        Params    []*Sym
73
        Locals    []*Sym
74
        FrameSize int
75
        LineTable *LineTable
76
        Obj       *Obj
77
}
78
 
79
// An Obj represents a single object file.
80
type Obj struct {
81
        Funcs []Func
82
        Paths []Sym
83
}
84
 
85
/*
86
 * Symbol tables
87
 */
88
 
89
// Table represents a Go symbol table.  It stores all of the
90
// symbols decoded from the program and provides methods to translate
91
// between symbols, names, and addresses.
92
type Table struct {
93
        Syms  []Sym
94
        Funcs []Func
95
        Files map[string]*Obj
96
        Objs  []Obj
97
        //      textEnd uint64;
98
}
99
 
100
type sym struct {
101
        value  uint32
102
        gotype uint32
103
        typ    byte
104
        name   []byte
105
}
106
 
107
func walksymtab(data []byte, fn func(sym) error) error {
108
        var s sym
109
        p := data
110
        for len(p) >= 6 {
111
                s.value = binary.BigEndian.Uint32(p[0:4])
112
                typ := p[4]
113
                if typ&0x80 == 0 {
114
                        return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ}
115
                }
116
                typ &^= 0x80
117
                s.typ = typ
118
                p = p[5:]
119
                var i int
120
                var nnul int
121
                for i = 0; i < len(p); i++ {
122
                        if p[i] == 0 {
123
                                nnul = 1
124
                                break
125
                        }
126
                }
127
                switch typ {
128
                case 'z', 'Z':
129
                        p = p[i+nnul:]
130
                        for i = 0; i+2 <= len(p); i += 2 {
131
                                if p[i] == 0 && p[i+1] == 0 {
132
                                        nnul = 2
133
                                        break
134
                                }
135
                        }
136
                }
137
                if i+nnul+4 > len(p) {
138
                        return &DecodingError{len(data), "unexpected EOF", nil}
139
                }
140
                s.name = p[0:i]
141
                i += nnul
142
                s.gotype = binary.BigEndian.Uint32(p[i : i+4])
143
                p = p[i+4:]
144
                fn(s)
145
        }
146
        return nil
147
}
148
 
149
// NewTable decodes the Go symbol table in data,
150
// returning an in-memory representation.
151
func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
152
        var n int
153
        err := walksymtab(symtab, func(s sym) error {
154
                n++
155
                return nil
156
        })
157
        if err != nil {
158
                return nil, err
159
        }
160
 
161
        var t Table
162
        fname := make(map[uint16]string)
163
        t.Syms = make([]Sym, 0, n)
164
        nf := 0
165
        nz := 0
166
        lasttyp := uint8(0)
167
        err = walksymtab(symtab, func(s sym) error {
168
                n := len(t.Syms)
169
                t.Syms = t.Syms[0 : n+1]
170
                ts := &t.Syms[n]
171
                ts.Type = s.typ
172
                ts.Value = uint64(s.value)
173
                ts.GoType = uint64(s.gotype)
174
                switch s.typ {
175
                default:
176
                        // rewrite name to use . instead of · (c2 b7)
177
                        w := 0
178
                        b := s.name
179
                        for i := 0; i < len(b); i++ {
180
                                if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 {
181
                                        i++
182
                                        b[i] = '.'
183
                                }
184
                                b[w] = b[i]
185
                                w++
186
                        }
187
                        ts.Name = string(s.name[0:w])
188
                case 'z', 'Z':
189
                        if lasttyp != 'z' && lasttyp != 'Z' {
190
                                nz++
191
                        }
192
                        for i := 0; i < len(s.name); i += 2 {
193
                                eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])
194
                                elt, ok := fname[eltIdx]
195
                                if !ok {
196
                                        return &DecodingError{-1, "bad filename code", eltIdx}
197
                                }
198
                                if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
199
                                        ts.Name += "/"
200
                                }
201
                                ts.Name += elt
202
                        }
203
                }
204
                switch s.typ {
205
                case 'T', 't', 'L', 'l':
206
                        nf++
207
                case 'f':
208
                        fname[uint16(s.value)] = ts.Name
209
                }
210
                lasttyp = s.typ
211
                return nil
212
        })
213
        if err != nil {
214
                return nil, err
215
        }
216
 
217
        t.Funcs = make([]Func, 0, nf)
218
        t.Objs = make([]Obj, 0, nz)
219
        t.Files = make(map[string]*Obj)
220
 
221
        // Count text symbols and attach frame sizes, parameters, and
222
        // locals to them.  Also, find object file boundaries.
223
        var obj *Obj
224
        lastf := 0
225
        for i := 0; i < len(t.Syms); i++ {
226
                sym := &t.Syms[i]
227
                switch sym.Type {
228
                case 'Z', 'z': // path symbol
229
                        // Finish the current object
230
                        if obj != nil {
231
                                obj.Funcs = t.Funcs[lastf:]
232
                        }
233
                        lastf = len(t.Funcs)
234
 
235
                        // Start new object
236
                        n := len(t.Objs)
237
                        t.Objs = t.Objs[0 : n+1]
238
                        obj = &t.Objs[n]
239
 
240
                        // Count & copy path symbols
241
                        var end int
242
                        for end = i + 1; end < len(t.Syms); end++ {
243
                                if c := t.Syms[end].Type; c != 'Z' && c != 'z' {
244
                                        break
245
                                }
246
                        }
247
                        obj.Paths = t.Syms[i:end]
248
                        i = end - 1 // loop will i++
249
 
250
                        // Record file names
251
                        depth := 0
252
                        for j := range obj.Paths {
253
                                s := &obj.Paths[j]
254
                                if s.Name == "" {
255
                                        depth--
256
                                } else {
257
                                        if depth == 0 {
258
                                                t.Files[s.Name] = obj
259
                                        }
260
                                        depth++
261
                                }
262
                        }
263
 
264
                case 'T', 't', 'L', 'l': // text symbol
265
                        if n := len(t.Funcs); n > 0 {
266
                                t.Funcs[n-1].End = sym.Value
267
                        }
268
                        if sym.Name == "etext" {
269
                                continue
270
                        }
271
 
272
                        // Count parameter and local (auto) syms
273
                        var np, na int
274
                        var end int
275
                countloop:
276
                        for end = i + 1; end < len(t.Syms); end++ {
277
                                switch t.Syms[end].Type {
278
                                case 'T', 't', 'L', 'l', 'Z', 'z':
279
                                        break countloop
280
                                case 'p':
281
                                        np++
282
                                case 'a':
283
                                        na++
284
                                }
285
                        }
286
 
287
                        // Fill in the function symbol
288
                        n := len(t.Funcs)
289
                        t.Funcs = t.Funcs[0 : n+1]
290
                        fn := &t.Funcs[n]
291
                        sym.Func = fn
292
                        fn.Params = make([]*Sym, 0, np)
293
                        fn.Locals = make([]*Sym, 0, na)
294
                        fn.Sym = sym
295
                        fn.Entry = sym.Value
296
                        fn.Obj = obj
297
                        if pcln != nil {
298
                                fn.LineTable = pcln.slice(fn.Entry)
299
                                pcln = fn.LineTable
300
                        }
301
                        for j := i; j < end; j++ {
302
                                s := &t.Syms[j]
303
                                switch s.Type {
304
                                case 'm':
305
                                        fn.FrameSize = int(s.Value)
306
                                case 'p':
307
                                        n := len(fn.Params)
308
                                        fn.Params = fn.Params[0 : n+1]
309
                                        fn.Params[n] = s
310
                                case 'a':
311
                                        n := len(fn.Locals)
312
                                        fn.Locals = fn.Locals[0 : n+1]
313
                                        fn.Locals[n] = s
314
                                }
315
                        }
316
                        i = end - 1 // loop will i++
317
                }
318
        }
319
        if obj != nil {
320
                obj.Funcs = t.Funcs[lastf:]
321
        }
322
        return &t, nil
323
}
324
 
325
// PCToFunc returns the function containing the program counter pc,
326
// or nil if there is no such function.
327
func (t *Table) PCToFunc(pc uint64) *Func {
328
        funcs := t.Funcs
329
        for len(funcs) > 0 {
330
                m := len(funcs) / 2
331
                fn := &funcs[m]
332
                switch {
333
                case pc < fn.Entry:
334
                        funcs = funcs[0:m]
335
                case fn.Entry <= pc && pc < fn.End:
336
                        return fn
337
                default:
338
                        funcs = funcs[m+1:]
339
                }
340
        }
341
        return nil
342
}
343
 
344
// PCToLine looks up line number information for a program counter.
345
// If there is no information, it returns fn == nil.
346
func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) {
347
        if fn = t.PCToFunc(pc); fn == nil {
348
                return
349
        }
350
        file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc))
351
        return
352
}
353
 
354
// LineToPC looks up the first program counter on the given line in
355
// the named file.  Returns UnknownPathError or UnknownLineError if
356
// there is an error looking up this line.
357
func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) {
358
        obj, ok := t.Files[file]
359
        if !ok {
360
                return 0, nil, UnknownFileError(file)
361
        }
362
        abs, err := obj.alineFromLine(file, line)
363
        if err != nil {
364
                return
365
        }
366
        for i := range obj.Funcs {
367
                f := &obj.Funcs[i]
368
                pc := f.LineTable.LineToPC(abs, f.End)
369
                if pc != 0 {
370
                        return pc, f, nil
371
                }
372
        }
373
        return 0, nil, &UnknownLineError{file, line}
374
}
375
 
376
// LookupSym returns the text, data, or bss symbol with the given name,
377
// or nil if no such symbol is found.
378
func (t *Table) LookupSym(name string) *Sym {
379
        // TODO(austin) Maybe make a map
380
        for i := range t.Syms {
381
                s := &t.Syms[i]
382
                switch s.Type {
383
                case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
384
                        if s.Name == name {
385
                                return s
386
                        }
387
                }
388
        }
389
        return nil
390
}
391
 
392
// LookupFunc returns the text, data, or bss symbol with the given name,
393
// or nil if no such symbol is found.
394
func (t *Table) LookupFunc(name string) *Func {
395
        for i := range t.Funcs {
396
                f := &t.Funcs[i]
397
                if f.Sym.Name == name {
398
                        return f
399
                }
400
        }
401
        return nil
402
}
403
 
404
// SymByAddr returns the text, data, or bss symbol starting at the given address.
405
// TODO(rsc): Allow lookup by any address within the symbol.
406
func (t *Table) SymByAddr(addr uint64) *Sym {
407
        // TODO(austin) Maybe make a map
408
        for i := range t.Syms {
409
                s := &t.Syms[i]
410
                switch s.Type {
411
                case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
412
                        if s.Value == addr {
413
                                return s
414
                        }
415
                }
416
        }
417
        return nil
418
}
419
 
420
/*
421
 * Object files
422
 */
423
 
424
func (o *Obj) lineFromAline(aline int) (string, int) {
425
        type stackEnt struct {
426
                path   string
427
                start  int
428
                offset int
429
                prev   *stackEnt
430
        }
431
 
432
        noPath := &stackEnt{"", 0, 0, nil}
433
        tos := noPath
434
 
435
        // TODO(austin) I have no idea how 'Z' symbols work, except
436
        // that they pop the stack.
437
pathloop:
438
        for _, s := range o.Paths {
439
                val := int(s.Value)
440
                switch {
441
                case val > aline:
442
                        break pathloop
443
 
444
                case val == 1:
445
                        // Start a new stack
446
                        tos = &stackEnt{s.Name, val, 0, noPath}
447
 
448
                case s.Name == "":
449
                        // Pop
450
                        if tos == noPath {
451
                                return "", 0
452
                        }
453
                        tos.prev.offset += val - tos.start
454
                        tos = tos.prev
455
 
456
                default:
457
                        // Push
458
                        tos = &stackEnt{s.Name, val, 0, tos}
459
                }
460
        }
461
 
462
        if tos == noPath {
463
                return "", 0
464
        }
465
        return tos.path, aline - tos.start - tos.offset + 1
466
}
467
 
468
func (o *Obj) alineFromLine(path string, line int) (int, error) {
469
        if line < 1 {
470
                return 0, &UnknownLineError{path, line}
471
        }
472
 
473
        for i, s := range o.Paths {
474
                // Find this path
475
                if s.Name != path {
476
                        continue
477
                }
478
 
479
                // Find this line at this stack level
480
                depth := 0
481
                var incstart int
482
                line += int(s.Value)
483
        pathloop:
484
                for _, s := range o.Paths[i:] {
485
                        val := int(s.Value)
486
                        switch {
487
                        case depth == 1 && val >= line:
488
                                return line - 1, nil
489
 
490
                        case s.Name == "":
491
                                depth--
492
                                if depth == 0 {
493
                                        break pathloop
494
                                } else if depth == 1 {
495
                                        line += val - incstart
496
                                }
497
 
498
                        default:
499
                                if depth == 1 {
500
                                        incstart = val
501
                                }
502
                                depth++
503
                        }
504
                }
505
                return 0, &UnknownLineError{path, line}
506
        }
507
        return 0, UnknownFileError(path)
508
}
509
 
510
/*
511
 * Errors
512
 */
513
 
514
// UnknownFileError represents a failure to find the specific file in
515
// the symbol table.
516
type UnknownFileError string
517
 
518
func (e UnknownFileError) Error() string { return "unknown file: " + string(e) }
519
 
520
// UnknownLineError represents a failure to map a line to a program
521
// counter, either because the line is beyond the bounds of the file
522
// or because there is no code on the given line.
523
type UnknownLineError struct {
524
        File string
525
        Line int
526
}
527
 
528
func (e *UnknownLineError) Error() string {
529
        return "no code at " + e.File + ":" + strconv.Itoa(e.Line)
530
}
531
 
532
// DecodingError represents an error during the decoding of
533
// the symbol table.
534
type DecodingError struct {
535
        off int
536
        msg string
537
        val interface{}
538
}
539
 
540
func (e *DecodingError) Error() string {
541
        msg := e.msg
542
        if e.val != nil {
543
                msg += fmt.Sprintf(" '%v'", e.val)
544
        }
545
        msg += fmt.Sprintf(" at byte %#x", e.off)
546
        return msg
547
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.