...
Run Format

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

View as plain text