github.com/bgentry/go@v0.0.0-20150121062915-6cf5a733d54d/src/cmd/link/pclntab.go (about)

     1  // Copyright 2014 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  // Generation of runtime function information (pclntab).
     6  
     7  package main
     8  
     9  import (
    10  	"cmd/internal/goobj"
    11  	"encoding/binary"
    12  	"os"
    13  	"sort"
    14  )
    15  
    16  var zerofunc goobj.Func
    17  
    18  // pclntab collects the runtime function data for each function that will
    19  // be listed in the binary and builds a single table describing all functions.
    20  // This table is used at run time for stack traces and to look up PC-specific
    21  // information during garbage collection. The symbol created is named
    22  // "pclntab" for historical reasons; the scope of the table has grown to
    23  // include more than just PC/line number correspondences.
    24  // The table format is documented at http://golang.org/s/go12symtab.
    25  func (p *Prog) pclntab() {
    26  	// Count number of functions going into the binary,
    27  	// so that we can size the initial index correctly.
    28  	nfunc := 0
    29  	for _, sym := range p.SymOrder {
    30  		if sym.Kind != goobj.STEXT {
    31  			continue
    32  		}
    33  		nfunc++
    34  	}
    35  
    36  	// Table header.
    37  	buf := new(SymBuffer)
    38  	buf.Init(p)
    39  	buf.SetSize(8 + p.ptrsize)
    40  	off := 0
    41  	off = buf.Uint32(off, 0xfffffffb)
    42  	off = buf.Uint8(off, 0)
    43  	off = buf.Uint8(off, 0)
    44  	off = buf.Uint8(off, uint8(p.pcquantum))
    45  	off = buf.Uint8(off, uint8(p.ptrsize))
    46  	off = buf.Uint(off, uint64(nfunc), p.ptrsize)
    47  	indexOff := off
    48  	off += (nfunc*2 + 1) * p.ptrsize // function index, to be filled in
    49  	off += 4                         // file table start offset, to be filled in
    50  	buf.SetSize(off)
    51  
    52  	// One-file cache for reading PCData tables from package files.
    53  	// TODO(rsc): Better I/O strategy.
    54  	var (
    55  		file  *os.File
    56  		fname string
    57  	)
    58  
    59  	// Files gives the file numbering for source file names recorded
    60  	// in the binary.
    61  	files := make(map[string]int)
    62  
    63  	// Build the table, build the index, and build the file name numbering.
    64  	// The loop here must visit functions in the same order that they will
    65  	// be stored in the binary, or else binary search over the index will fail.
    66  	// The runtime checks that the index is sorted properly at program start time.
    67  	var lastSym *Sym
    68  	for _, sym := range p.SymOrder {
    69  		if sym.Kind != goobj.STEXT {
    70  			continue
    71  		}
    72  		lastSym = sym
    73  
    74  		// Treat no recorded function information same as all zeros.
    75  		f := sym.Func
    76  		if f == nil {
    77  			f = &zerofunc
    78  		}
    79  
    80  		// Open package file if needed, for reading PC data.
    81  		if fname != sym.Package.File {
    82  			if file != nil {
    83  				file.Close()
    84  			}
    85  			var err error
    86  			file, err = os.Open(sym.Package.File)
    87  			if err != nil {
    88  				p.errorf("%v: %v", sym, err)
    89  				return
    90  			}
    91  			fname = sym.Package.File
    92  		}
    93  
    94  		// off is the offset of the table entry where we're going to write
    95  		// the encoded form of Func.
    96  		// indexOff is the current position in the table index;
    97  		// we add an entry in the index pointing at off.
    98  		off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1)
    99  		indexOff = buf.Addr(indexOff, sym.SymID, 0)
   100  		indexOff = buf.Uint(indexOff, uint64(off), p.ptrsize)
   101  
   102  		// The Func encoding starts with a header giving offsets
   103  		// to data blobs, and then the data blobs themselves.
   104  		// end gives the current write position for the data blobs.
   105  		end := off + p.ptrsize + 3*4 + 5*4 + len(f.PCData)*4 + len(f.FuncData)*p.ptrsize
   106  		if len(f.FuncData) > 0 {
   107  			end += -end & (p.ptrsize - 1)
   108  		}
   109  		buf.SetSize(end)
   110  
   111  		// entry uintptr
   112  		// name int32
   113  		// args int32
   114  		// frame int32
   115  		//
   116  		// The frame recorded in the object file is
   117  		// the frame size used in an assembly listing, which does
   118  		// not include the caller PC on the stack.
   119  		// The frame size we want to list here is the delta from
   120  		// this function's SP to its caller's SP, which does include
   121  		// the caller PC. Add p.ptrsize to f.Frame to adjust.
   122  		// TODO(rsc): Record the same frame size in the object file.
   123  		off = buf.Addr(off, sym.SymID, 0)
   124  		off = buf.Uint32(off, uint32(addString(buf, sym.Name)))
   125  		off = buf.Uint32(off, uint32(f.Args))
   126  		off = buf.Uint32(off, uint32(f.Frame+p.ptrsize))
   127  
   128  		// pcdata
   129  		off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCSP)))
   130  		off = buf.Uint32(off, uint32(addPCFileTable(p, buf, file, f.PCFile, sym, files)))
   131  		off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCLine)))
   132  		off = buf.Uint32(off, uint32(len(f.PCData)))
   133  		off = buf.Uint32(off, uint32(len(f.FuncData)))
   134  		for _, pcdata := range f.PCData {
   135  			off = buf.Uint32(off, uint32(addPCTable(p, buf, file, pcdata)))
   136  		}
   137  
   138  		// funcdata
   139  		if len(f.FuncData) > 0 {
   140  			off += -off & (p.ptrsize - 1) // must be pointer-aligned
   141  			for _, funcdata := range f.FuncData {
   142  				if funcdata.Sym.Name == "" {
   143  					off = buf.Uint(off, uint64(funcdata.Offset), p.ptrsize)
   144  				} else {
   145  					off = buf.Addr(off, funcdata.Sym, funcdata.Offset)
   146  				}
   147  			}
   148  		}
   149  
   150  		if off != end {
   151  			p.errorf("internal error: invalid math in pclntab: off=%#x end=%#x", off, end)
   152  			break
   153  		}
   154  	}
   155  	if file != nil {
   156  		file.Close()
   157  	}
   158  
   159  	// Final entry of index is end PC of last function.
   160  	indexOff = buf.Addr(indexOff, lastSym.SymID, int64(lastSym.Size))
   161  
   162  	// Start file table.
   163  	// Function index is immediately followed by offset to file table.
   164  	off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1)
   165  	buf.Uint32(indexOff, uint32(off))
   166  
   167  	// File table is an array of uint32s.
   168  	// The first entry gives 1+n, the size of the array.
   169  	// The following n entries hold offsets to string data.
   170  	// File number n uses the string pointed at by entry n.
   171  	// File number 0 is invalid.
   172  	buf.SetSize(off + (1+len(files))*4)
   173  	buf.Uint32(off, uint32(1+len(files)))
   174  	var filestr []string
   175  	for file := range files {
   176  		filestr = append(filestr, file)
   177  	}
   178  	sort.Strings(filestr)
   179  	for _, file := range filestr {
   180  		id := files[file]
   181  		buf.Uint32(off+4*id, uint32(addString(buf, file)))
   182  	}
   183  
   184  	pclntab := &Sym{
   185  		Sym: &goobj.Sym{
   186  			SymID: goobj.SymID{Name: "runtime.pclntab"},
   187  			Kind:  goobj.SPCLNTAB,
   188  			Size:  buf.Size(),
   189  			Reloc: buf.Reloc(),
   190  		},
   191  		Bytes: buf.Bytes(),
   192  	}
   193  	p.addSym(pclntab)
   194  }
   195  
   196  // addString appends the string s to the buffer b.
   197  // It returns the offset of the beginning of the string in the buffer.
   198  func addString(b *SymBuffer, s string) int {
   199  	off := b.Size()
   200  	b.SetSize(off + len(s) + 1)
   201  	copy(b.data[off:], s)
   202  	return off
   203  }
   204  
   205  // addPCTable appends the PC-data table stored in the file f at the location loc
   206  // to the symbol buffer b. It returns the offset of the beginning of the table
   207  // in the buffer.
   208  func addPCTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data) int {
   209  	if loc.Size == 0 {
   210  		return 0
   211  	}
   212  	off := b.Size()
   213  	b.SetSize(off + int(loc.Size))
   214  	_, err := f.ReadAt(b.data[off:off+int(loc.Size)], loc.Offset)
   215  	if err != nil {
   216  		p.errorf("%v", err)
   217  	}
   218  	return off
   219  }
   220  
   221  // addPCFileTable is like addPCTable, but it renumbers the file names referred to by the table
   222  // to use the global numbering maintained in the files map. It adds new files to the
   223  // map as necessary.
   224  func addPCFileTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data, sym *Sym, files map[string]int) int {
   225  	if loc.Size == 0 {
   226  		return 0
   227  	}
   228  	off := b.Size()
   229  
   230  	src := make([]byte, loc.Size)
   231  	_, err := f.ReadAt(src, loc.Offset)
   232  	if err != nil {
   233  		p.errorf("%v", err)
   234  		return 0
   235  	}
   236  
   237  	filenum := make([]int, len(sym.Func.File))
   238  	for i, name := range sym.Func.File {
   239  		num := files[name]
   240  		if num == 0 {
   241  			num = len(files) + 1
   242  			files[name] = num
   243  		}
   244  		filenum[i] = num
   245  	}
   246  
   247  	var dst []byte
   248  	newval := int32(-1)
   249  	var it PCIter
   250  	for it.Init(p, src); !it.Done; it.Next() {
   251  		// value delta
   252  		oldval := it.Value
   253  		val := oldval
   254  		if oldval != -1 {
   255  			if oldval < 0 || int(oldval) >= len(filenum) {
   256  				p.errorf("%s: corrupt pc-file table", sym)
   257  				break
   258  			}
   259  			val = int32(filenum[oldval])
   260  		}
   261  		dv := val - newval
   262  		newval = val
   263  		uv := uint32(dv<<1) ^ uint32(dv>>31)
   264  		dst = appendVarint(dst, uv)
   265  
   266  		// pc delta
   267  		dst = appendVarint(dst, it.NextPC-it.PC)
   268  	}
   269  	if it.Corrupt {
   270  		p.errorf("%s: corrupt pc-file table", sym)
   271  	}
   272  
   273  	// terminating value delta
   274  	dst = appendVarint(dst, 0)
   275  
   276  	b.SetSize(off + len(dst))
   277  	copy(b.data[off:], dst)
   278  	return off
   279  }
   280  
   281  // A SymBuffer is a buffer for preparing the data image of a
   282  // linker-generated symbol.
   283  type SymBuffer struct {
   284  	data    []byte
   285  	reloc   []goobj.Reloc
   286  	order   binary.ByteOrder
   287  	ptrsize int
   288  }
   289  
   290  // Init initializes the buffer for writing.
   291  func (b *SymBuffer) Init(p *Prog) {
   292  	b.data = nil
   293  	b.reloc = nil
   294  	b.order = p.byteorder
   295  	b.ptrsize = p.ptrsize
   296  }
   297  
   298  // Bytes returns the buffer data.
   299  func (b *SymBuffer) Bytes() []byte {
   300  	return b.data
   301  }
   302  
   303  // SetSize sets the buffer's data size to n bytes.
   304  func (b *SymBuffer) SetSize(n int) {
   305  	for cap(b.data) < n {
   306  		b.data = append(b.data[:cap(b.data)], 0)
   307  	}
   308  	b.data = b.data[:n]
   309  }
   310  
   311  // Size returns the buffer's data size.
   312  func (b *SymBuffer) Size() int {
   313  	return len(b.data)
   314  }
   315  
   316  // Reloc returns the buffered relocations.
   317  func (b *SymBuffer) Reloc() []goobj.Reloc {
   318  	return b.reloc
   319  }
   320  
   321  // Uint8 sets the uint8 at offset off to v.
   322  // It returns the offset just beyond v.
   323  func (b *SymBuffer) Uint8(off int, v uint8) int {
   324  	b.data[off] = v
   325  	return off + 1
   326  }
   327  
   328  // Uint16 sets the uint16 at offset off to v.
   329  // It returns the offset just beyond v.
   330  func (b *SymBuffer) Uint16(off int, v uint16) int {
   331  	b.order.PutUint16(b.data[off:], v)
   332  	return off + 2
   333  }
   334  
   335  // Uint32 sets the uint32 at offset off to v.
   336  // It returns the offset just beyond v.
   337  func (b *SymBuffer) Uint32(off int, v uint32) int {
   338  	b.order.PutUint32(b.data[off:], v)
   339  	return off + 4
   340  }
   341  
   342  // Uint64 sets the uint64 at offset off to v.
   343  // It returns the offset just beyond v.
   344  func (b *SymBuffer) Uint64(off int, v uint64) int {
   345  	b.order.PutUint64(b.data[off:], v)
   346  	return off + 8
   347  }
   348  
   349  // Uint sets the size-byte unsigned integer at offset off to v.
   350  // It returns the offset just beyond v.
   351  func (b *SymBuffer) Uint(off int, v uint64, size int) int {
   352  	switch size {
   353  	case 1:
   354  		return b.Uint8(off, uint8(v))
   355  	case 2:
   356  		return b.Uint16(off, uint16(v))
   357  	case 4:
   358  		return b.Uint32(off, uint32(v))
   359  	case 8:
   360  		return b.Uint64(off, v)
   361  	}
   362  	panic("invalid use of SymBuffer.SetUint")
   363  }
   364  
   365  // Addr sets the pointer-sized address at offset off to refer
   366  // to symoff bytes past the start of sym. It returns the offset
   367  // just beyond the address.
   368  func (b *SymBuffer) Addr(off int, sym goobj.SymID, symoff int64) int {
   369  	b.reloc = append(b.reloc, goobj.Reloc{
   370  		Offset: off,
   371  		Size:   b.ptrsize,
   372  		Sym:    sym,
   373  		Add:    int(symoff),
   374  		Type:   R_ADDR,
   375  	})
   376  	return off + b.ptrsize
   377  }
   378  
   379  // A PCIter implements iteration over PC-data tables.
   380  //
   381  //	var it PCIter
   382  //	for it.Init(p, data); !it.Done; it.Next() {
   383  //		it.Value holds from it.PC up to (but not including) it.NextPC
   384  //	}
   385  //	if it.Corrupt {
   386  //		data was malformed
   387  //	}
   388  //
   389  type PCIter struct {
   390  	PC        uint32
   391  	NextPC    uint32
   392  	Value     int32
   393  	Done      bool
   394  	Corrupt   bool
   395  	p         []byte
   396  	start     bool
   397  	pcquantum uint32
   398  }
   399  
   400  // Init initializes the iteration.
   401  // On return, if it.Done is true, the iteration is over.
   402  // Otherwise it.Value applies in the pc range [it.PC, it.NextPC).
   403  func (it *PCIter) Init(p *Prog, buf []byte) {
   404  	it.p = buf
   405  	it.PC = 0
   406  	it.NextPC = 0
   407  	it.Value = -1
   408  	it.start = true
   409  	it.pcquantum = uint32(p.pcquantum)
   410  	it.Done = false
   411  	it.Next()
   412  }
   413  
   414  // Next steps forward one entry in the table.
   415  // On return, if it.Done is true, the iteration is over.
   416  // Otherwise it.Value applies in the pc range [it.PC, it.NextPC).
   417  func (it *PCIter) Next() {
   418  	it.PC = it.NextPC
   419  	if it.Done {
   420  		return
   421  	}
   422  	if len(it.p) == 0 {
   423  		it.Done = true
   424  		return
   425  	}
   426  
   427  	// value delta
   428  	uv, p, ok := decodeVarint(it.p)
   429  	if !ok {
   430  		it.Done = true
   431  		it.Corrupt = true
   432  		return
   433  	}
   434  	it.p = p
   435  	if uv == 0 && !it.start {
   436  		it.Done = true
   437  		return
   438  	}
   439  	it.start = false
   440  	sv := int32(uv>>1) ^ int32(uv<<31)>>31
   441  	it.Value += sv
   442  
   443  	// pc delta
   444  	uv, it.p, ok = decodeVarint(it.p)
   445  	if !ok {
   446  		it.Done = true
   447  		it.Corrupt = true
   448  		return
   449  	}
   450  	it.NextPC = it.PC + uv*it.pcquantum
   451  }
   452  
   453  // decodeVarint decodes an unsigned varint from p,
   454  // reporting the value, the remainder of the data, and
   455  // whether the decoding was successful.
   456  func decodeVarint(p []byte) (v uint32, rest []byte, ok bool) {
   457  	for shift := uint(0); ; shift += 7 {
   458  		if len(p) == 0 {
   459  			return
   460  		}
   461  		c := uint32(p[0])
   462  		p = p[1:]
   463  		v |= (c & 0x7F) << shift
   464  		if c&0x80 == 0 {
   465  			break
   466  		}
   467  	}
   468  	return v, p, true
   469  }
   470  
   471  // appendVarint appends an unsigned varint encoding of v to p
   472  // and returns the resulting slice.
   473  func appendVarint(p []byte, v uint32) []byte {
   474  	for ; v >= 0x80; v >>= 7 {
   475  		p = append(p, byte(v)|0x80)
   476  	}
   477  	p = append(p, byte(v))
   478  	return p
   479  }