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