...

Source file src/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	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