Source file src/debug/gosym/symtab.go

Documentation: debug/gosym

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

View as plain text