URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [debug/] [gosym/] [symtab.go] - Rev 747
Compare with Previous | Blame | View Log
// Copyright 2009 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// Package gosym implements access to the Go symbol// and line number tables embedded in Go binaries generated// by the gc compilers.package gosym// The table format is a variant of the format used in Plan 9's a.out// format, documented at http://plan9.bell-labs.com/magic/man2html/6/a.out.// The best reference for the differences between the Plan 9 format// and the Go format is the runtime source, specifically ../../runtime/symtab.c.import ("encoding/binary""fmt""strconv""strings")/** Symbols*/// A Sym represents a single symbol table entry.type Sym struct {Value uint64Type byteName stringGoType uint64// If this symbol if a function symbol, the corresponding FuncFunc *Func}// Static returns whether this symbol is static (not visible outside its file).func (s *Sym) Static() bool { return s.Type >= 'a' }// PackageName returns the package part of the symbol name,// or the empty string if there is none.func (s *Sym) PackageName() string {if i := strings.Index(s.Name, "."); i != -1 {return s.Name[0:i]}return ""}// ReceiverName returns the receiver type name of this symbol,// or the empty string if there is none.func (s *Sym) ReceiverName() string {l := strings.Index(s.Name, ".")r := strings.LastIndex(s.Name, ".")if l == -1 || r == -1 || l == r {return ""}return s.Name[l+1 : r]}// BaseName returns the symbol name without the package or receiver name.func (s *Sym) BaseName() string {if i := strings.LastIndex(s.Name, "."); i != -1 {return s.Name[i+1:]}return s.Name}// A Func collects information about a single function.type Func struct {Entry uint64*SymEnd uint64Params []*SymLocals []*SymFrameSize intLineTable *LineTableObj *Obj}// An Obj represents a single object file.type Obj struct {Funcs []FuncPaths []Sym}/** Symbol tables*/// Table represents a Go symbol table. It stores all of the// symbols decoded from the program and provides methods to translate// between symbols, names, and addresses.type Table struct {Syms []SymFuncs []FuncFiles map[string]*ObjObjs []Obj// textEnd uint64;}type sym struct {value uint32gotype uint32typ bytename []byte}func walksymtab(data []byte, fn func(sym) error) error {var s symp := datafor len(p) >= 6 {s.value = binary.BigEndian.Uint32(p[0:4])typ := p[4]if typ&0x80 == 0 {return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ}}typ &^= 0x80s.typ = typp = p[5:]var i intvar nnul intfor i = 0; i < len(p); i++ {if p[i] == 0 {nnul = 1break}}switch typ {case 'z', 'Z':p = p[i+nnul:]for i = 0; i+2 <= len(p); i += 2 {if p[i] == 0 && p[i+1] == 0 {nnul = 2break}}}if i+nnul+4 > len(p) {return &DecodingError{len(data), "unexpected EOF", nil}}s.name = p[0:i]i += nnuls.gotype = binary.BigEndian.Uint32(p[i : i+4])p = p[i+4:]fn(s)}return nil}// NewTable decodes the Go symbol table in data,// returning an in-memory representation.func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {var n interr := walksymtab(symtab, func(s sym) error {n++return nil})if err != nil {return nil, err}var t Tablefname := make(map[uint16]string)t.Syms = make([]Sym, 0, n)nf := 0nz := 0lasttyp := uint8(0)err = walksymtab(symtab, func(s sym) error {n := len(t.Syms)t.Syms = t.Syms[0 : n+1]ts := &t.Syms[n]ts.Type = s.typts.Value = uint64(s.value)ts.GoType = uint64(s.gotype)switch s.typ {default:// rewrite name to use . instead of · (c2 b7)w := 0b := s.namefor i := 0; i < len(b); i++ {if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 {i++b[i] = '.'}b[w] = b[i]w++}ts.Name = string(s.name[0:w])case 'z', 'Z':if lasttyp != 'z' && lasttyp != 'Z' {nz++}for i := 0; i < len(s.name); i += 2 {eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])elt, ok := fname[eltIdx]if !ok {return &DecodingError{-1, "bad filename code", eltIdx}}if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {ts.Name += "/"}ts.Name += elt}}switch s.typ {case 'T', 't', 'L', 'l':nf++case 'f':fname[uint16(s.value)] = ts.Name}lasttyp = s.typreturn nil})if err != nil {return nil, err}t.Funcs = make([]Func, 0, nf)t.Objs = make([]Obj, 0, nz)t.Files = make(map[string]*Obj)// Count text symbols and attach frame sizes, parameters, and// locals to them. Also, find object file boundaries.var obj *Objlastf := 0for i := 0; i < len(t.Syms); i++ {sym := &t.Syms[i]switch sym.Type {case 'Z', 'z': // path symbol// Finish the current objectif obj != nil {obj.Funcs = t.Funcs[lastf:]}lastf = len(t.Funcs)// Start new objectn := len(t.Objs)t.Objs = t.Objs[0 : n+1]obj = &t.Objs[n]// Count & copy path symbolsvar end intfor end = i + 1; end < len(t.Syms); end++ {if c := t.Syms[end].Type; c != 'Z' && c != 'z' {break}}obj.Paths = t.Syms[i:end]i = end - 1 // loop will i++// Record file namesdepth := 0for j := range obj.Paths {s := &obj.Paths[j]if s.Name == "" {depth--} else {if depth == 0 {t.Files[s.Name] = obj}depth++}}case 'T', 't', 'L', 'l': // text symbolif n := len(t.Funcs); n > 0 {t.Funcs[n-1].End = sym.Value}if sym.Name == "etext" {continue}// Count parameter and local (auto) symsvar np, na intvar end intcountloop:for end = i + 1; end < len(t.Syms); end++ {switch t.Syms[end].Type {case 'T', 't', 'L', 'l', 'Z', 'z':break countloopcase 'p':np++case 'a':na++}}// Fill in the function symboln := len(t.Funcs)t.Funcs = t.Funcs[0 : n+1]fn := &t.Funcs[n]sym.Func = fnfn.Params = make([]*Sym, 0, np)fn.Locals = make([]*Sym, 0, na)fn.Sym = symfn.Entry = sym.Valuefn.Obj = objif pcln != nil {fn.LineTable = pcln.slice(fn.Entry)pcln = fn.LineTable}for j := i; j < end; j++ {s := &t.Syms[j]switch s.Type {case 'm':fn.FrameSize = int(s.Value)case 'p':n := len(fn.Params)fn.Params = fn.Params[0 : n+1]fn.Params[n] = scase 'a':n := len(fn.Locals)fn.Locals = fn.Locals[0 : n+1]fn.Locals[n] = s}}i = end - 1 // loop will i++}}if obj != nil {obj.Funcs = t.Funcs[lastf:]}return &t, nil}// PCToFunc returns the function containing the program counter pc,// or nil if there is no such function.func (t *Table) PCToFunc(pc uint64) *Func {funcs := t.Funcsfor len(funcs) > 0 {m := len(funcs) / 2fn := &funcs[m]switch {case pc < fn.Entry:funcs = funcs[0:m]case fn.Entry <= pc && pc < fn.End:return fndefault:funcs = funcs[m+1:]}}return nil}// PCToLine looks up line number information for a program counter.// If there is no information, it returns fn == nil.func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) {if fn = t.PCToFunc(pc); fn == nil {return}file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc))return}// LineToPC looks up the first program counter on the given line in// the named file. Returns UnknownPathError or UnknownLineError if// there is an error looking up this line.func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) {obj, ok := t.Files[file]if !ok {return 0, nil, UnknownFileError(file)}abs, err := obj.alineFromLine(file, line)if err != nil {return}for i := range obj.Funcs {f := &obj.Funcs[i]pc := f.LineTable.LineToPC(abs, f.End)if pc != 0 {return pc, f, nil}}return 0, nil, &UnknownLineError{file, line}}// LookupSym returns the text, data, or bss symbol with the given name,// or nil if no such symbol is found.func (t *Table) LookupSym(name string) *Sym {// TODO(austin) Maybe make a mapfor i := range t.Syms {s := &t.Syms[i]switch s.Type {case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':if s.Name == name {return s}}}return nil}// LookupFunc returns the text, data, or bss symbol with the given name,// or nil if no such symbol is found.func (t *Table) LookupFunc(name string) *Func {for i := range t.Funcs {f := &t.Funcs[i]if f.Sym.Name == name {return f}}return nil}// SymByAddr returns the text, data, or bss symbol starting at the given address.// TODO(rsc): Allow lookup by any address within the symbol.func (t *Table) SymByAddr(addr uint64) *Sym {// TODO(austin) Maybe make a mapfor i := range t.Syms {s := &t.Syms[i]switch s.Type {case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':if s.Value == addr {return s}}}return nil}/** Object files*/func (o *Obj) lineFromAline(aline int) (string, int) {type stackEnt struct {path stringstart intoffset intprev *stackEnt}noPath := &stackEnt{"", 0, 0, nil}tos := noPath// TODO(austin) I have no idea how 'Z' symbols work, except// that they pop the stack.pathloop:for _, s := range o.Paths {val := int(s.Value)switch {case val > aline:break pathloopcase val == 1:// Start a new stacktos = &stackEnt{s.Name, val, 0, noPath}case s.Name == "":// Popif tos == noPath {return "<malformed symbol table>", 0}tos.prev.offset += val - tos.starttos = tos.prevdefault:// Pushtos = &stackEnt{s.Name, val, 0, tos}}}if tos == noPath {return "", 0}return tos.path, aline - tos.start - tos.offset + 1}func (o *Obj) alineFromLine(path string, line int) (int, error) {if line < 1 {return 0, &UnknownLineError{path, line}}for i, s := range o.Paths {// Find this pathif s.Name != path {continue}// Find this line at this stack leveldepth := 0var incstart intline += int(s.Value)pathloop:for _, s := range o.Paths[i:] {val := int(s.Value)switch {case depth == 1 && val >= line:return line - 1, nilcase s.Name == "":depth--if depth == 0 {break pathloop} else if depth == 1 {line += val - incstart}default:if depth == 1 {incstart = val}depth++}}return 0, &UnknownLineError{path, line}}return 0, UnknownFileError(path)}/** Errors*/// UnknownFileError represents a failure to find the specific file in// the symbol table.type UnknownFileError stringfunc (e UnknownFileError) Error() string { return "unknown file: " + string(e) }// UnknownLineError represents a failure to map a line to a program// counter, either because the line is beyond the bounds of the file// or because there is no code on the given line.type UnknownLineError struct {File stringLine int}func (e *UnknownLineError) Error() string {return "no code at " + e.File + ":" + strconv.Itoa(e.Line)}// DecodingError represents an error during the decoding of// the symbol table.type DecodingError struct {off intmsg stringval interface{}}func (e *DecodingError) Error() string {msg := e.msgif e.val != nil {msg += fmt.Sprintf(" '%v'", e.val)}msg += fmt.Sprintf(" at byte %#x", e.off)return msg}
