src/pkg/debug/gosym/symtab.go - The Go Programming Language

Golang

Source file src/pkg/debug/gosym/symtab.go

     1	// 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 "<malformed symbol table>", 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	}