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