github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/debug/gosym/symtab.go (about)

     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  	"bytes"
    17  	"encoding/binary"
    18  	"fmt"
    19  	"strconv"
    20  	"strings"
    21  )
    22  
    23  /*
    24   * Symbols
    25   */
    26  
    27  // A Sym represents a single symbol table entry.
    28  type Sym struct {
    29  	Value  uint64
    30  	Type   byte
    31  	Name   string
    32  	GoType uint64
    33  	// If this symbol if a function symbol, the corresponding Func
    34  	Func *Func
    35  }
    36  
    37  // Static reports whether this symbol is static (not visible outside its file).
    38  func (s *Sym) Static() bool { return s.Type >= 'a' }
    39  
    40  // PackageName returns the package part of the symbol name,
    41  // or the empty string if there is none.
    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  // ReceiverName returns the receiver type name of this symbol,
    50  // or the empty string if there is none.
    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  // BaseName returns the symbol name without the package or receiver name.
    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  // A Func collects information about a single function.
    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  // An Obj represents a collection of functions in a symbol table.
    81  //
    82  // The exact method of division of a binary into separate Objs is an internal detail
    83  // of the symbol table format.
    84  //
    85  // In early versions of Go each source file became a different Obj.
    86  //
    87  // In Go 1 and Go 1.1, each package produced one Obj for all Go sources
    88  // and one Obj per C source file.
    89  //
    90  // In Go 1.2, there is a single Obj for the entire program.
    91  type Obj struct {
    92  	// Funcs is a list of functions in the Obj.
    93  	Funcs []Func
    94  
    95  	// In Go 1.1 and earlier, Paths is a list of symbols corresponding
    96  	// to the source file names that produced the Obj.
    97  	// In Go 1.2, Paths is nil.
    98  	// Use the keys of Table.Files to obtain a list of source files.
    99  	Paths []Sym // meta
   100  }
   101  
   102  /*
   103   * Symbol tables
   104   */
   105  
   106  // Table represents a Go symbol table.  It stores all of the
   107  // symbols decoded from the program and provides methods to translate
   108  // between symbols, names, and addresses.
   109  type Table struct {
   110  	Syms  []Sym
   111  	Funcs []Func
   112  	Files map[string]*Obj // nil for Go 1.2 and later binaries
   113  	Objs  []Obj           // nil for Go 1.2 and later binaries
   114  
   115  	go12line *LineTable // Go 1.2 line number table
   116  }
   117  
   118  type sym struct {
   119  	value  uint64
   120  	gotype uint64
   121  	typ    byte
   122  	name   []byte
   123  }
   124  
   125  var (
   126  	littleEndianSymtab    = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}
   127  	bigEndianSymtab       = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00}
   128  	oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00}
   129  )
   130  
   131  func walksymtab(data []byte, fn func(sym) error) error {
   132  	var order binary.ByteOrder = binary.BigEndian
   133  	newTable := false
   134  	switch {
   135  	case bytes.HasPrefix(data, oldLittleEndianSymtab):
   136  		// Same as Go 1.0, but little endian.
   137  		// Format was used during interim development between Go 1.0 and Go 1.1.
   138  		// Should not be widespread, but easy to support.
   139  		data = data[6:]
   140  		order = binary.LittleEndian
   141  	case bytes.HasPrefix(data, bigEndianSymtab):
   142  		newTable = true
   143  	case bytes.HasPrefix(data, littleEndianSymtab):
   144  		newTable = true
   145  		order = binary.LittleEndian
   146  	}
   147  	var ptrsz int
   148  	if newTable {
   149  		if len(data) < 8 {
   150  			return &DecodingError{len(data), "unexpected EOF", nil}
   151  		}
   152  		ptrsz = int(data[7])
   153  		if ptrsz != 4 && ptrsz != 8 {
   154  			return &DecodingError{7, "invalid pointer size", ptrsz}
   155  		}
   156  		data = data[8:]
   157  	}
   158  	var s sym
   159  	p := data
   160  	for len(p) >= 4 {
   161  		var typ byte
   162  		if newTable {
   163  			// Symbol type, value, Go type.
   164  			typ = p[0] & 0x3F
   165  			wideValue := p[0]&0x40 != 0
   166  			goType := p[0]&0x80 != 0
   167  			if typ < 26 {
   168  				typ += 'A'
   169  			} else {
   170  				typ += 'a' - 26
   171  			}
   172  			s.typ = typ
   173  			p = p[1:]
   174  			if wideValue {
   175  				if len(p) < ptrsz {
   176  					return &DecodingError{len(data), "unexpected EOF", nil}
   177  				}
   178  				// fixed-width value
   179  				if ptrsz == 8 {
   180  					s.value = order.Uint64(p[0:8])
   181  					p = p[8:]
   182  				} else {
   183  					s.value = uint64(order.Uint32(p[0:4]))
   184  					p = p[4:]
   185  				}
   186  			} else {
   187  				// varint value
   188  				s.value = 0
   189  				shift := uint(0)
   190  				for len(p) > 0 && p[0]&0x80 != 0 {
   191  					s.value |= uint64(p[0]&0x7F) << shift
   192  					shift += 7
   193  					p = p[1:]
   194  				}
   195  				if len(p) == 0 {
   196  					return &DecodingError{len(data), "unexpected EOF", nil}
   197  				}
   198  				s.value |= uint64(p[0]) << shift
   199  				p = p[1:]
   200  			}
   201  			if goType {
   202  				if len(p) < ptrsz {
   203  					return &DecodingError{len(data), "unexpected EOF", nil}
   204  				}
   205  				// fixed-width go type
   206  				if ptrsz == 8 {
   207  					s.gotype = order.Uint64(p[0:8])
   208  					p = p[8:]
   209  				} else {
   210  					s.gotype = uint64(order.Uint32(p[0:4]))
   211  					p = p[4:]
   212  				}
   213  			}
   214  		} else {
   215  			// Value, symbol type.
   216  			s.value = uint64(order.Uint32(p[0:4]))
   217  			if len(p) < 5 {
   218  				return &DecodingError{len(data), "unexpected EOF", nil}
   219  			}
   220  			typ = p[4]
   221  			if typ&0x80 == 0 {
   222  				return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ}
   223  			}
   224  			typ &^= 0x80
   225  			s.typ = typ
   226  			p = p[5:]
   227  		}
   228  
   229  		// Name.
   230  		var i int
   231  		var nnul int
   232  		for i = 0; i < len(p); i++ {
   233  			if p[i] == 0 {
   234  				nnul = 1
   235  				break
   236  			}
   237  		}
   238  		switch typ {
   239  		case 'z', 'Z':
   240  			p = p[i+nnul:]
   241  			for i = 0; i+2 <= len(p); i += 2 {
   242  				if p[i] == 0 && p[i+1] == 0 {
   243  					nnul = 2
   244  					break
   245  				}
   246  			}
   247  		}
   248  		if len(p) < i+nnul {
   249  			return &DecodingError{len(data), "unexpected EOF", nil}
   250  		}
   251  		s.name = p[0:i]
   252  		i += nnul
   253  		p = p[i:]
   254  
   255  		if !newTable {
   256  			if len(p) < 4 {
   257  				return &DecodingError{len(data), "unexpected EOF", nil}
   258  			}
   259  			// Go type.
   260  			s.gotype = uint64(order.Uint32(p[:4]))
   261  			p = p[4:]
   262  		}
   263  		fn(s)
   264  	}
   265  	return nil
   266  }
   267  
   268  // NewTable decodes the Go symbol table in data,
   269  // returning an in-memory representation.
   270  func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
   271  	var n int
   272  	err := walksymtab(symtab, func(s sym) error {
   273  		n++
   274  		return nil
   275  	})
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  
   280  	var t Table
   281  	if pcln.isGo12() {
   282  		t.go12line = pcln
   283  	}
   284  	fname := make(map[uint16]string)
   285  	t.Syms = make([]Sym, 0, n)
   286  	nf := 0
   287  	nz := 0
   288  	lasttyp := uint8(0)
   289  	err = walksymtab(symtab, func(s sym) error {
   290  		n := len(t.Syms)
   291  		t.Syms = t.Syms[0 : n+1]
   292  		ts := &t.Syms[n]
   293  		ts.Type = s.typ
   294  		ts.Value = uint64(s.value)
   295  		ts.GoType = uint64(s.gotype)
   296  		switch s.typ {
   297  		default:
   298  			// rewrite name to use . instead of ยท (c2 b7)
   299  			w := 0
   300  			b := s.name
   301  			for i := 0; i < len(b); i++ {
   302  				if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 {
   303  					i++
   304  					b[i] = '.'
   305  				}
   306  				b[w] = b[i]
   307  				w++
   308  			}
   309  			ts.Name = string(s.name[0:w])
   310  		case 'z', 'Z':
   311  			if lasttyp != 'z' && lasttyp != 'Z' {
   312  				nz++
   313  			}
   314  			for i := 0; i < len(s.name); i += 2 {
   315  				eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])
   316  				elt, ok := fname[eltIdx]
   317  				if !ok {
   318  					return &DecodingError{-1, "bad filename code", eltIdx}
   319  				}
   320  				if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
   321  					ts.Name += "/"
   322  				}
   323  				ts.Name += elt
   324  			}
   325  		}
   326  		switch s.typ {
   327  		case 'T', 't', 'L', 'l':
   328  			nf++
   329  		case 'f':
   330  			fname[uint16(s.value)] = ts.Name
   331  		}
   332  		lasttyp = s.typ
   333  		return nil
   334  	})
   335  	if err != nil {
   336  		return nil, err
   337  	}
   338  
   339  	t.Funcs = make([]Func, 0, nf)
   340  	t.Files = make(map[string]*Obj)
   341  
   342  	var obj *Obj
   343  	if t.go12line != nil {
   344  		// Put all functions into one Obj.
   345  		t.Objs = make([]Obj, 1)
   346  		obj = &t.Objs[0]
   347  		t.go12line.go12MapFiles(t.Files, obj)
   348  	} else {
   349  		t.Objs = make([]Obj, 0, nz)
   350  	}
   351  
   352  	// Count text symbols and attach frame sizes, parameters, and
   353  	// locals to them.  Also, find object file boundaries.
   354  	lastf := 0
   355  	for i := 0; i < len(t.Syms); i++ {
   356  		sym := &t.Syms[i]
   357  		switch sym.Type {
   358  		case 'Z', 'z': // path symbol
   359  			if t.go12line != nil {
   360  				// Go 1.2 binaries have the file information elsewhere. Ignore.
   361  				break
   362  			}
   363  			// Finish the current object
   364  			if obj != nil {
   365  				obj.Funcs = t.Funcs[lastf:]
   366  			}
   367  			lastf = len(t.Funcs)
   368  
   369  			// Start new object
   370  			n := len(t.Objs)
   371  			t.Objs = t.Objs[0 : n+1]
   372  			obj = &t.Objs[n]
   373  
   374  			// Count & copy path symbols
   375  			var end int
   376  			for end = i + 1; end < len(t.Syms); end++ {
   377  				if c := t.Syms[end].Type; c != 'Z' && c != 'z' {
   378  					break
   379  				}
   380  			}
   381  			obj.Paths = t.Syms[i:end]
   382  			i = end - 1 // loop will i++
   383  
   384  			// Record file names
   385  			depth := 0
   386  			for j := range obj.Paths {
   387  				s := &obj.Paths[j]
   388  				if s.Name == "" {
   389  					depth--
   390  				} else {
   391  					if depth == 0 {
   392  						t.Files[s.Name] = obj
   393  					}
   394  					depth++
   395  				}
   396  			}
   397  
   398  		case 'T', 't', 'L', 'l': // text symbol
   399  			if n := len(t.Funcs); n > 0 {
   400  				t.Funcs[n-1].End = sym.Value
   401  			}
   402  			if sym.Name == "etext" {
   403  				continue
   404  			}
   405  
   406  			// Count parameter and local (auto) syms
   407  			var np, na int
   408  			var end int
   409  		countloop:
   410  			for end = i + 1; end < len(t.Syms); end++ {
   411  				switch t.Syms[end].Type {
   412  				case 'T', 't', 'L', 'l', 'Z', 'z':
   413  					break countloop
   414  				case 'p':
   415  					np++
   416  				case 'a':
   417  					na++
   418  				}
   419  			}
   420  
   421  			// Fill in the function symbol
   422  			n := len(t.Funcs)
   423  			t.Funcs = t.Funcs[0 : n+1]
   424  			fn := &t.Funcs[n]
   425  			sym.Func = fn
   426  			fn.Params = make([]*Sym, 0, np)
   427  			fn.Locals = make([]*Sym, 0, na)
   428  			fn.Sym = sym
   429  			fn.Entry = sym.Value
   430  			fn.Obj = obj
   431  			if t.go12line != nil {
   432  				// All functions share the same line table.
   433  				// It knows how to narrow down to a specific
   434  				// function quickly.
   435  				fn.LineTable = t.go12line
   436  			} else if pcln != nil {
   437  				fn.LineTable = pcln.slice(fn.Entry)
   438  				pcln = fn.LineTable
   439  			}
   440  			for j := i; j < end; j++ {
   441  				s := &t.Syms[j]
   442  				switch s.Type {
   443  				case 'm':
   444  					fn.FrameSize = int(s.Value)
   445  				case 'p':
   446  					n := len(fn.Params)
   447  					fn.Params = fn.Params[0 : n+1]
   448  					fn.Params[n] = s
   449  				case 'a':
   450  					n := len(fn.Locals)
   451  					fn.Locals = fn.Locals[0 : n+1]
   452  					fn.Locals[n] = s
   453  				}
   454  			}
   455  			i = end - 1 // loop will i++
   456  		}
   457  	}
   458  	if obj != nil {
   459  		obj.Funcs = t.Funcs[lastf:]
   460  	}
   461  	return &t, nil
   462  }
   463  
   464  // PCToFunc returns the function containing the program counter pc,
   465  // or nil if there is no such function.
   466  func (t *Table) PCToFunc(pc uint64) *Func {
   467  	funcs := t.Funcs
   468  	for len(funcs) > 0 {
   469  		m := len(funcs) / 2
   470  		fn := &funcs[m]
   471  		switch {
   472  		case pc < fn.Entry:
   473  			funcs = funcs[0:m]
   474  		case fn.Entry <= pc && pc < fn.End:
   475  			return fn
   476  		default:
   477  			funcs = funcs[m+1:]
   478  		}
   479  	}
   480  	return nil
   481  }
   482  
   483  // PCToLine looks up line number information for a program counter.
   484  // If there is no information, it returns fn == nil.
   485  func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) {
   486  	if fn = t.PCToFunc(pc); fn == nil {
   487  		return
   488  	}
   489  	if t.go12line != nil {
   490  		file = t.go12line.go12PCToFile(pc)
   491  		line = t.go12line.go12PCToLine(pc)
   492  	} else {
   493  		file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc))
   494  	}
   495  	return
   496  }
   497  
   498  // LineToPC looks up the first program counter on the given line in
   499  // the named file.  It returns UnknownPathError or UnknownLineError if
   500  // there is an error looking up this line.
   501  func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) {
   502  	obj, ok := t.Files[file]
   503  	if !ok {
   504  		return 0, nil, UnknownFileError(file)
   505  	}
   506  
   507  	if t.go12line != nil {
   508  		pc := t.go12line.go12LineToPC(file, line)
   509  		if pc == 0 {
   510  			return 0, nil, &UnknownLineError{file, line}
   511  		}
   512  		return pc, t.PCToFunc(pc), nil
   513  	}
   514  
   515  	abs, err := obj.alineFromLine(file, line)
   516  	if err != nil {
   517  		return
   518  	}
   519  	for i := range obj.Funcs {
   520  		f := &obj.Funcs[i]
   521  		pc := f.LineTable.LineToPC(abs, f.End)
   522  		if pc != 0 {
   523  			return pc, f, nil
   524  		}
   525  	}
   526  	return 0, nil, &UnknownLineError{file, line}
   527  }
   528  
   529  // LookupSym returns the text, data, or bss symbol with the given name,
   530  // or nil if no such symbol is found.
   531  func (t *Table) LookupSym(name string) *Sym {
   532  	// TODO(austin) Maybe make a map
   533  	for i := range t.Syms {
   534  		s := &t.Syms[i]
   535  		switch s.Type {
   536  		case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
   537  			if s.Name == name {
   538  				return s
   539  			}
   540  		}
   541  	}
   542  	return nil
   543  }
   544  
   545  // LookupFunc returns the text, data, or bss symbol with the given name,
   546  // or nil if no such symbol is found.
   547  func (t *Table) LookupFunc(name string) *Func {
   548  	for i := range t.Funcs {
   549  		f := &t.Funcs[i]
   550  		if f.Sym.Name == name {
   551  			return f
   552  		}
   553  	}
   554  	return nil
   555  }
   556  
   557  // SymByAddr returns the text, data, or bss symbol starting at the given address.
   558  func (t *Table) SymByAddr(addr uint64) *Sym {
   559  	for i := range t.Syms {
   560  		s := &t.Syms[i]
   561  		switch s.Type {
   562  		case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
   563  			if s.Value == addr {
   564  				return s
   565  			}
   566  		}
   567  	}
   568  	return nil
   569  }
   570  
   571  /*
   572   * Object files
   573   */
   574  
   575  // This is legacy code for Go 1.1 and earlier, which used the
   576  // Plan 9 format for pc-line tables. This code was never quite
   577  // correct. It's probably very close, and it's usually correct, but
   578  // we never quite found all the corner cases.
   579  //
   580  // Go 1.2 and later use a simpler format, documented at golang.org/s/go12symtab.
   581  
   582  func (o *Obj) lineFromAline(aline int) (string, int) {
   583  	type stackEnt struct {
   584  		path   string
   585  		start  int
   586  		offset int
   587  		prev   *stackEnt
   588  	}
   589  
   590  	noPath := &stackEnt{"", 0, 0, nil}
   591  	tos := noPath
   592  
   593  pathloop:
   594  	for _, s := range o.Paths {
   595  		val := int(s.Value)
   596  		switch {
   597  		case val > aline:
   598  			break pathloop
   599  
   600  		case val == 1:
   601  			// Start a new stack
   602  			tos = &stackEnt{s.Name, val, 0, noPath}
   603  
   604  		case s.Name == "":
   605  			// Pop
   606  			if tos == noPath {
   607  				return "<malformed symbol table>", 0
   608  			}
   609  			tos.prev.offset += val - tos.start
   610  			tos = tos.prev
   611  
   612  		default:
   613  			// Push
   614  			tos = &stackEnt{s.Name, val, 0, tos}
   615  		}
   616  	}
   617  
   618  	if tos == noPath {
   619  		return "", 0
   620  	}
   621  	return tos.path, aline - tos.start - tos.offset + 1
   622  }
   623  
   624  func (o *Obj) alineFromLine(path string, line int) (int, error) {
   625  	if line < 1 {
   626  		return 0, &UnknownLineError{path, line}
   627  	}
   628  
   629  	for i, s := range o.Paths {
   630  		// Find this path
   631  		if s.Name != path {
   632  			continue
   633  		}
   634  
   635  		// Find this line at this stack level
   636  		depth := 0
   637  		var incstart int
   638  		line += int(s.Value)
   639  	pathloop:
   640  		for _, s := range o.Paths[i:] {
   641  			val := int(s.Value)
   642  			switch {
   643  			case depth == 1 && val >= line:
   644  				return line - 1, nil
   645  
   646  			case s.Name == "":
   647  				depth--
   648  				if depth == 0 {
   649  					break pathloop
   650  				} else if depth == 1 {
   651  					line += val - incstart
   652  				}
   653  
   654  			default:
   655  				if depth == 1 {
   656  					incstart = val
   657  				}
   658  				depth++
   659  			}
   660  		}
   661  		return 0, &UnknownLineError{path, line}
   662  	}
   663  	return 0, UnknownFileError(path)
   664  }
   665  
   666  /*
   667   * Errors
   668   */
   669  
   670  // UnknownFileError represents a failure to find the specific file in
   671  // the symbol table.
   672  type UnknownFileError string
   673  
   674  func (e UnknownFileError) Error() string { return "unknown file: " + string(e) }
   675  
   676  // UnknownLineError represents a failure to map a line to a program
   677  // counter, either because the line is beyond the bounds of the file
   678  // or because there is no code on the given line.
   679  type UnknownLineError struct {
   680  	File string
   681  	Line int
   682  }
   683  
   684  func (e *UnknownLineError) Error() string {
   685  	return "no code at " + e.File + ":" + strconv.Itoa(e.Line)
   686  }
   687  
   688  // DecodingError represents an error during the decoding of
   689  // the symbol table.
   690  type DecodingError struct {
   691  	off int
   692  	msg string
   693  	val interface{}
   694  }
   695  
   696  func (e *DecodingError) Error() string {
   697  	msg := e.msg
   698  	if e.val != nil {
   699  		msg += fmt.Sprintf(" '%v'", e.val)
   700  	}
   701  	msg += fmt.Sprintf(" at byte %#x", e.off)
   702  	return msg
   703  }