github.com/sean-/go@v0.0.0-20151219100004-97f854cd7bb6/src/cmd/link/internal/ld/pcln.go (about)

     1  // Copyright 2013 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 ld
     6  
     7  import (
     8  	"cmd/internal/obj"
     9  	"fmt"
    10  	"log"
    11  )
    12  
    13  // funcpctab writes to dst a pc-value table mapping the code in func to the values
    14  // returned by valfunc parameterized by arg. The invocation of valfunc to update the
    15  // current value is, for each p,
    16  //
    17  //	val = valfunc(func, val, p, 0, arg);
    18  //	record val as value at p->pc;
    19  //	val = valfunc(func, val, p, 1, arg);
    20  //
    21  // where func is the function, val is the current value, p is the instruction being
    22  // considered, and arg can be used to further parameterize valfunc.
    23  
    24  // pctofileline computes either the file number (arg == 0)
    25  // or the line number (arg == 1) to use at p.
    26  // Because p->lineno applies to p, phase == 0 (before p)
    27  // takes care of the update.
    28  
    29  // pctospadj computes the sp adjustment in effect.
    30  // It is oldval plus any adjustment made by p itself.
    31  // The adjustment by p takes effect only after p, so we
    32  // apply the change during phase == 1.
    33  
    34  // pctopcdata computes the pcdata value in effect at p.
    35  // A PCDATA instruction sets the value in effect at future
    36  // non-PCDATA instructions.
    37  // Since PCDATA instructions have no width in the final code,
    38  // it does not matter which phase we use for the update.
    39  
    40  // iteration over encoded pcdata tables.
    41  
    42  func getvarint(pp *[]byte) uint32 {
    43  	v := uint32(0)
    44  	p := *pp
    45  	for shift := 0; ; shift += 7 {
    46  		v |= uint32(p[0]&0x7F) << uint(shift)
    47  		tmp4 := p
    48  		p = p[1:]
    49  		if tmp4[0]&0x80 == 0 {
    50  			break
    51  		}
    52  	}
    53  
    54  	*pp = p
    55  	return v
    56  }
    57  
    58  func pciternext(it *Pciter) {
    59  	it.pc = it.nextpc
    60  	if it.done != 0 {
    61  		return
    62  	}
    63  	if -cap(it.p) >= -cap(it.d.P[len(it.d.P):]) {
    64  		it.done = 1
    65  		return
    66  	}
    67  
    68  	// value delta
    69  	v := getvarint(&it.p)
    70  
    71  	if v == 0 && it.start == 0 {
    72  		it.done = 1
    73  		return
    74  	}
    75  
    76  	it.start = 0
    77  	dv := int32(v>>1) ^ (int32(v<<31) >> 31)
    78  	it.value += dv
    79  
    80  	// pc delta
    81  	v = getvarint(&it.p)
    82  
    83  	it.nextpc = it.pc + v*it.pcscale
    84  }
    85  
    86  func pciterinit(ctxt *Link, it *Pciter, d *Pcdata) {
    87  	it.d = *d
    88  	it.p = it.d.P
    89  	it.pc = 0
    90  	it.nextpc = 0
    91  	it.value = -1
    92  	it.start = 1
    93  	it.done = 0
    94  	it.pcscale = uint32(ctxt.Arch.Minlc)
    95  	pciternext(it)
    96  }
    97  
    98  // Copyright 2013 The Go Authors.  All rights reserved.
    99  // Use of this source code is governed by a BSD-style
   100  // license that can be found in the LICENSE file.
   101  
   102  func addvarint(d *Pcdata, val uint32) {
   103  	n := int32(0)
   104  	for v := val; v >= 0x80; v >>= 7 {
   105  		n++
   106  	}
   107  	n++
   108  
   109  	old := len(d.P)
   110  	for cap(d.P) < len(d.P)+int(n) {
   111  		d.P = append(d.P[:cap(d.P)], 0)
   112  	}
   113  	d.P = d.P[:old+int(n)]
   114  
   115  	p := d.P[old:]
   116  	var v uint32
   117  	for v = val; v >= 0x80; v >>= 7 {
   118  		p[0] = byte(v | 0x80)
   119  		p = p[1:]
   120  	}
   121  	p[0] = byte(v)
   122  }
   123  
   124  func addpctab(ftab *LSym, off int32, d *Pcdata) int32 {
   125  	var start int32
   126  	if len(d.P) > 0 {
   127  		start = int32(len(ftab.P))
   128  		Symgrow(Ctxt, ftab, int64(start)+int64(len(d.P)))
   129  		copy(ftab.P[start:], d.P)
   130  	}
   131  	return int32(setuint32(Ctxt, ftab, int64(off), uint32(start)))
   132  }
   133  
   134  func ftabaddstring(ftab *LSym, s string) int32 {
   135  	n := int32(len(s)) + 1
   136  	start := int32(len(ftab.P))
   137  	Symgrow(Ctxt, ftab, int64(start)+int64(n)+1)
   138  	copy(ftab.P[start:], s)
   139  	return start
   140  }
   141  
   142  func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) {
   143  	var f *LSym
   144  
   145  	// Give files numbers.
   146  	for i := 0; i < len(files); i++ {
   147  		f = files[i]
   148  		if f.Type != obj.SFILEPATH {
   149  			ctxt.Nhistfile++
   150  			f.Value = int64(ctxt.Nhistfile)
   151  			f.Type = obj.SFILEPATH
   152  			f.Next = ctxt.Filesyms
   153  			ctxt.Filesyms = f
   154  		}
   155  	}
   156  
   157  	newval := int32(-1)
   158  	var out Pcdata
   159  
   160  	var dv int32
   161  	var it Pciter
   162  	var oldval int32
   163  	var v uint32
   164  	var val int32
   165  	for pciterinit(ctxt, &it, d); it.done == 0; pciternext(&it) {
   166  		// value delta
   167  		oldval = it.value
   168  
   169  		if oldval == -1 {
   170  			val = -1
   171  		} else {
   172  			if oldval < 0 || oldval >= int32(len(files)) {
   173  				log.Fatalf("bad pcdata %d", oldval)
   174  			}
   175  			val = int32(files[oldval].Value)
   176  		}
   177  
   178  		dv = val - newval
   179  		newval = val
   180  		v = (uint32(dv) << 1) ^ uint32(int32(dv>>31))
   181  		addvarint(&out, v)
   182  
   183  		// pc delta
   184  		addvarint(&out, (it.nextpc-it.pc)/it.pcscale)
   185  	}
   186  
   187  	// terminating value delta
   188  	addvarint(&out, 0)
   189  
   190  	*d = out
   191  }
   192  
   193  func container(s *LSym) int {
   194  	// We want to generate func table entries only for the "lowest level" symbols,
   195  	// not containers of subsymbols.
   196  	if s != nil && s.Type&obj.SCONTAINER != 0 {
   197  		return 1
   198  	}
   199  	return 0
   200  }
   201  
   202  // pclntab initializes the pclntab symbol with
   203  // runtime function and file name information.
   204  
   205  var pclntab_zpcln Pcln
   206  
   207  // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
   208  var pclntabNfunc int32
   209  var pclntabFiletabOffset int32
   210  var pclntabPclntabOffset int32
   211  var pclntabFirstFunc *LSym
   212  var pclntabLastFunc *LSym
   213  
   214  func pclntab() {
   215  	funcdata_bytes := int64(0)
   216  	ftab := Linklookup(Ctxt, "runtime.pclntab", 0)
   217  	ftab.Type = obj.SPCLNTAB
   218  	ftab.Reachable = true
   219  
   220  	// See golang.org/s/go12symtab for the format. Briefly:
   221  	//	8-byte header
   222  	//	nfunc [thearch.ptrsize bytes]
   223  	//	function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
   224  	//	end PC [thearch.ptrsize bytes]
   225  	//	offset to file table [4 bytes]
   226  	nfunc := int32(0)
   227  
   228  	// Find container symbols, mark them with SCONTAINER
   229  	for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
   230  		if Ctxt.Cursym.Outer != nil {
   231  			Ctxt.Cursym.Outer.Type |= obj.SCONTAINER
   232  		}
   233  	}
   234  
   235  	for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
   236  		if container(Ctxt.Cursym) == 0 {
   237  			nfunc++
   238  		}
   239  	}
   240  
   241  	pclntabNfunc = nfunc
   242  	Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4)
   243  	setuint32(Ctxt, ftab, 0, 0xfffffffb)
   244  	setuint8(Ctxt, ftab, 6, uint8(Thearch.Minlc))
   245  	setuint8(Ctxt, ftab, 7, uint8(Thearch.Ptrsize))
   246  	setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(Thearch.Ptrsize))
   247  	pclntabPclntabOffset = int32(8 + Thearch.Ptrsize)
   248  
   249  	nfunc = 0
   250  	var last *LSym
   251  	var end int32
   252  	var funcstart int32
   253  	var i int32
   254  	var it Pciter
   255  	var off int32
   256  	var pcln *Pcln
   257  	for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
   258  		last = Ctxt.Cursym
   259  		if container(Ctxt.Cursym) != 0 {
   260  			continue
   261  		}
   262  		pcln = Ctxt.Cursym.Pcln
   263  		if pcln == nil {
   264  			pcln = &pclntab_zpcln
   265  		}
   266  
   267  		if pclntabFirstFunc == nil {
   268  			pclntabFirstFunc = Ctxt.Cursym
   269  		}
   270  
   271  		funcstart = int32(len(ftab.P))
   272  		funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1)
   273  
   274  		setaddr(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), Ctxt.Cursym)
   275  		setuintxx(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint64(funcstart), int64(Thearch.Ptrsize))
   276  
   277  		// fixed size of struct, checked below
   278  		off = funcstart
   279  
   280  		end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(pcln.Npcdata)*4 + int32(pcln.Nfuncdata)*int32(Thearch.Ptrsize)
   281  		if pcln.Nfuncdata > 0 && (end&int32(Thearch.Ptrsize-1) != 0) {
   282  			end += 4
   283  		}
   284  		Symgrow(Ctxt, ftab, int64(end))
   285  
   286  		// entry uintptr
   287  		off = int32(setaddr(Ctxt, ftab, int64(off), Ctxt.Cursym))
   288  
   289  		// name int32
   290  		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(ftabaddstring(ftab, Ctxt.Cursym.Name))))
   291  
   292  		// args int32
   293  		// TODO: Move into funcinfo.
   294  		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args)))
   295  
   296  		// frame int32
   297  		// This has been removed (it was never set quite correctly anyway).
   298  		// Nothing should use it.
   299  		// Leave an obviously incorrect value.
   300  		// TODO: Remove entirely.
   301  		off = int32(setuint32(Ctxt, ftab, int64(off), 0x1234567))
   302  
   303  		if pcln != &pclntab_zpcln {
   304  			renumberfiles(Ctxt, pcln.File, &pcln.Pcfile)
   305  			if false {
   306  				// Sanity check the new numbering
   307  				for pciterinit(Ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) {
   308  					if it.value < 1 || it.value > Ctxt.Nhistfile {
   309  						Diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, Ctxt.Nhistfile)
   310  						errorexit()
   311  					}
   312  				}
   313  			}
   314  		}
   315  
   316  		// pcdata
   317  		off = addpctab(ftab, off, &pcln.Pcsp)
   318  
   319  		off = addpctab(ftab, off, &pcln.Pcfile)
   320  		off = addpctab(ftab, off, &pcln.Pcline)
   321  		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Npcdata)))
   322  		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Nfuncdata)))
   323  		for i = 0; i < int32(pcln.Npcdata); i++ {
   324  			off = addpctab(ftab, off, &pcln.Pcdata[i])
   325  		}
   326  
   327  		// funcdata, must be pointer-aligned and we're only int32-aligned.
   328  		// Missing funcdata will be 0 (nil pointer).
   329  		if pcln.Nfuncdata > 0 {
   330  			if off&int32(Thearch.Ptrsize-1) != 0 {
   331  				off += 4
   332  			}
   333  			for i = 0; i < int32(pcln.Nfuncdata); i++ {
   334  				if pcln.Funcdata[i] == nil {
   335  					setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize))
   336  				} else {
   337  					// TODO: Dedup.
   338  					funcdata_bytes += pcln.Funcdata[i].Size
   339  
   340  					setaddrplus(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i])
   341  				}
   342  			}
   343  
   344  			off += int32(pcln.Nfuncdata) * int32(Thearch.Ptrsize)
   345  		}
   346  
   347  		if off != end {
   348  			Diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, pcln.Npcdata, pcln.Nfuncdata, Thearch.Ptrsize)
   349  			errorexit()
   350  		}
   351  
   352  		nfunc++
   353  	}
   354  
   355  	pclntabLastFunc = last
   356  	// Final entry of table is just end pc.
   357  	setaddrplus(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), last, last.Size)
   358  
   359  	// Start file table.
   360  	start := int32(len(ftab.P))
   361  
   362  	start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1)
   363  	pclntabFiletabOffset = start
   364  	setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start))
   365  
   366  	Symgrow(Ctxt, ftab, int64(start)+(int64(Ctxt.Nhistfile)+1)*4)
   367  	setuint32(Ctxt, ftab, int64(start), uint32(Ctxt.Nhistfile))
   368  	for s := Ctxt.Filesyms; s != nil; s = s.Next {
   369  		setuint32(Ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name)))
   370  	}
   371  
   372  	ftab.Size = int64(len(ftab.P))
   373  
   374  	if Debug['v'] != 0 {
   375  		fmt.Fprintf(&Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes))
   376  	}
   377  }
   378  
   379  const (
   380  	BUCKETSIZE    = 256 * MINFUNC
   381  	SUBBUCKETS    = 16
   382  	SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
   383  	NOIDX         = 0x7fffffff
   384  )
   385  
   386  // findfunctab generates a lookup table to quickly find the containing
   387  // function for a pc.  See src/runtime/symtab.go:findfunc for details.
   388  func findfunctab() {
   389  	t := Linklookup(Ctxt, "runtime.findfunctab", 0)
   390  	t.Type = obj.SRODATA
   391  	t.Reachable = true
   392  	t.Local = true
   393  
   394  	// find min and max address
   395  	min := Ctxt.Textp.Value
   396  
   397  	max := int64(0)
   398  	for s := Ctxt.Textp; s != nil; s = s.Next {
   399  		max = s.Value + s.Size
   400  	}
   401  
   402  	// for each subbucket, compute the minimum of all symbol indexes
   403  	// that map to that subbucket.
   404  	n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
   405  
   406  	indexes := make([]int32, n)
   407  	for i := int32(0); i < n; i++ {
   408  		indexes[i] = NOIDX
   409  	}
   410  	idx := int32(0)
   411  	var e *LSym
   412  	var i int32
   413  	var p int64
   414  	var q int64
   415  	for s := Ctxt.Textp; s != nil; s = s.Next {
   416  		if container(s) != 0 {
   417  			continue
   418  		}
   419  		p = s.Value
   420  		e = s.Next
   421  		for container(e) != 0 {
   422  			e = e.Next
   423  		}
   424  		if e != nil {
   425  			q = e.Value
   426  		} else {
   427  			q = max
   428  		}
   429  
   430  		//print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
   431  		for ; p < q; p += SUBBUCKETSIZE {
   432  			i = int32((p - min) / SUBBUCKETSIZE)
   433  			if indexes[i] > idx {
   434  				indexes[i] = idx
   435  			}
   436  		}
   437  
   438  		i = int32((q - 1 - min) / SUBBUCKETSIZE)
   439  		if indexes[i] > idx {
   440  			indexes[i] = idx
   441  		}
   442  		idx++
   443  	}
   444  
   445  	// allocate table
   446  	nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
   447  
   448  	Symgrow(Ctxt, t, 4*int64(nbuckets)+int64(n))
   449  
   450  	// fill in table
   451  	var base int32
   452  	var j int32
   453  	for i := int32(0); i < nbuckets; i++ {
   454  		base = indexes[i*SUBBUCKETS]
   455  		if base == NOIDX {
   456  			Diag("hole in findfunctab")
   457  		}
   458  		setuint32(Ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base))
   459  		for j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
   460  			idx = indexes[i*SUBBUCKETS+j]
   461  			if idx == NOIDX {
   462  				Diag("hole in findfunctab")
   463  			}
   464  			if idx-base >= 256 {
   465  				Diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
   466  			}
   467  
   468  			setuint8(Ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
   469  		}
   470  	}
   471  }