github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/cmd/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  	start := int32(len(ftab.P))
   126  	Symgrow(Ctxt, ftab, int64(start)+int64(len(d.P)))
   127  	copy(ftab.P[start:], d.P)
   128  
   129  	return int32(setuint32(Ctxt, ftab, int64(off), uint32(start)))
   130  }
   131  
   132  func ftabaddstring(ftab *LSym, s string) int32 {
   133  	n := int32(len(s)) + 1
   134  	start := int32(len(ftab.P))
   135  	Symgrow(Ctxt, ftab, int64(start)+int64(n)+1)
   136  	copy(ftab.P[start:], s)
   137  	return start
   138  }
   139  
   140  func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) {
   141  	var f *LSym
   142  
   143  	// Give files numbers.
   144  	for i := 0; i < len(files); i++ {
   145  		f = files[i]
   146  		if f.Type != SFILEPATH {
   147  			ctxt.Nhistfile++
   148  			f.Value = int64(ctxt.Nhistfile)
   149  			f.Type = SFILEPATH
   150  			f.Next = ctxt.Filesyms
   151  			ctxt.Filesyms = f
   152  		}
   153  	}
   154  
   155  	newval := int32(-1)
   156  	var out Pcdata
   157  
   158  	var dv int32
   159  	var it Pciter
   160  	var oldval int32
   161  	var v uint32
   162  	var val int32
   163  	for pciterinit(ctxt, &it, d); it.done == 0; pciternext(&it) {
   164  		// value delta
   165  		oldval = it.value
   166  
   167  		if oldval == -1 {
   168  			val = -1
   169  		} else {
   170  			if oldval < 0 || oldval >= int32(len(files)) {
   171  				log.Fatalf("bad pcdata %d", oldval)
   172  			}
   173  			val = int32(files[oldval].Value)
   174  		}
   175  
   176  		dv = val - newval
   177  		newval = val
   178  		v = (uint32(dv) << 1) ^ uint32(int32(dv>>31))
   179  		addvarint(&out, v)
   180  
   181  		// pc delta
   182  		addvarint(&out, (it.nextpc-it.pc)/it.pcscale)
   183  	}
   184  
   185  	// terminating value delta
   186  	addvarint(&out, 0)
   187  
   188  	*d = out
   189  }
   190  
   191  func container(s *LSym) int {
   192  	// We want to generate func table entries only for the "lowest level" symbols,
   193  	// not containers of subsymbols.
   194  	if s != nil && s.Sub != nil {
   195  		return 1
   196  	}
   197  	return 0
   198  }
   199  
   200  // pclntab initializes the pclntab symbol with
   201  // runtime function and file name information.
   202  
   203  var pclntab_zpcln Pcln
   204  
   205  // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
   206  var pclntabNfunc int32
   207  var pclntabFiletabOffset int32
   208  var pclntabPclntabOffset int32
   209  var pclntabFirstFunc *LSym
   210  var pclntabLastFunc *LSym
   211  
   212  func pclntab() {
   213  	funcdata_bytes := int64(0)
   214  	ftab := Linklookup(Ctxt, "runtime.pclntab", 0)
   215  	ftab.Type = SPCLNTAB
   216  	ftab.Reachable = true
   217  
   218  	// See golang.org/s/go12symtab for the format. Briefly:
   219  	//	8-byte header
   220  	//	nfunc [thearch.ptrsize bytes]
   221  	//	function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
   222  	//	end PC [thearch.ptrsize bytes]
   223  	//	offset to file table [4 bytes]
   224  	nfunc := int32(0)
   225  
   226  	for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
   227  		if container(Ctxt.Cursym) == 0 {
   228  			nfunc++
   229  		}
   230  	}
   231  
   232  	pclntabNfunc = nfunc
   233  	Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4)
   234  	setuint32(Ctxt, ftab, 0, 0xfffffffb)
   235  	setuint8(Ctxt, ftab, 6, uint8(Thearch.Minlc))
   236  	setuint8(Ctxt, ftab, 7, uint8(Thearch.Ptrsize))
   237  	setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(Thearch.Ptrsize))
   238  	pclntabPclntabOffset = int32(8 + Thearch.Ptrsize)
   239  
   240  	nfunc = 0
   241  	var last *LSym
   242  	var end int32
   243  	var funcstart int32
   244  	var i int32
   245  	var it Pciter
   246  	var off int32
   247  	var pcln *Pcln
   248  	for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
   249  		last = Ctxt.Cursym
   250  		if container(Ctxt.Cursym) != 0 {
   251  			continue
   252  		}
   253  		pcln = Ctxt.Cursym.Pcln
   254  		if pcln == nil {
   255  			pcln = &pclntab_zpcln
   256  		}
   257  
   258  		if pclntabFirstFunc == nil {
   259  			pclntabFirstFunc = Ctxt.Cursym
   260  		}
   261  
   262  		funcstart = int32(len(ftab.P))
   263  		funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1)
   264  
   265  		setaddr(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), Ctxt.Cursym)
   266  		setuintxx(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint64(funcstart), int64(Thearch.Ptrsize))
   267  
   268  		// fixed size of struct, checked below
   269  		off = funcstart
   270  
   271  		end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(pcln.Npcdata)*4 + int32(pcln.Nfuncdata)*int32(Thearch.Ptrsize)
   272  		if pcln.Nfuncdata > 0 && (end&int32(Thearch.Ptrsize-1) != 0) {
   273  			end += 4
   274  		}
   275  		Symgrow(Ctxt, ftab, int64(end))
   276  
   277  		// entry uintptr
   278  		off = int32(setaddr(Ctxt, ftab, int64(off), Ctxt.Cursym))
   279  
   280  		// name int32
   281  		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(ftabaddstring(ftab, Ctxt.Cursym.Name))))
   282  
   283  		// args int32
   284  		// TODO: Move into funcinfo.
   285  		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args)))
   286  
   287  		// frame int32
   288  		// This has been removed (it was never set quite correctly anyway).
   289  		// Nothing should use it.
   290  		// Leave an obviously incorrect value.
   291  		// TODO: Remove entirely.
   292  		off = int32(setuint32(Ctxt, ftab, int64(off), 0x1234567))
   293  
   294  		if pcln != &pclntab_zpcln {
   295  			renumberfiles(Ctxt, pcln.File, &pcln.Pcfile)
   296  			if false {
   297  				// Sanity check the new numbering
   298  				for pciterinit(Ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) {
   299  					if it.value < 1 || it.value > Ctxt.Nhistfile {
   300  						Diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, Ctxt.Nhistfile)
   301  						Errorexit()
   302  					}
   303  				}
   304  			}
   305  		}
   306  
   307  		// pcdata
   308  		off = addpctab(ftab, off, &pcln.Pcsp)
   309  
   310  		off = addpctab(ftab, off, &pcln.Pcfile)
   311  		off = addpctab(ftab, off, &pcln.Pcline)
   312  		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Npcdata)))
   313  		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Nfuncdata)))
   314  		for i = 0; i < int32(pcln.Npcdata); i++ {
   315  			off = addpctab(ftab, off, &pcln.Pcdata[i])
   316  		}
   317  
   318  		// funcdata, must be pointer-aligned and we're only int32-aligned.
   319  		// Missing funcdata will be 0 (nil pointer).
   320  		if pcln.Nfuncdata > 0 {
   321  			if off&int32(Thearch.Ptrsize-1) != 0 {
   322  				off += 4
   323  			}
   324  			for i = 0; i < int32(pcln.Nfuncdata); i++ {
   325  				if pcln.Funcdata[i] == nil {
   326  					setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize))
   327  				} else {
   328  					// TODO: Dedup.
   329  					funcdata_bytes += pcln.Funcdata[i].Size
   330  
   331  					setaddrplus(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i])
   332  				}
   333  			}
   334  
   335  			off += int32(pcln.Nfuncdata) * int32(Thearch.Ptrsize)
   336  		}
   337  
   338  		if off != end {
   339  			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)
   340  			Errorexit()
   341  		}
   342  
   343  		nfunc++
   344  	}
   345  
   346  	pclntabLastFunc = last
   347  	// Final entry of table is just end pc.
   348  	setaddrplus(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), last, last.Size)
   349  
   350  	// Start file table.
   351  	start := int32(len(ftab.P))
   352  
   353  	start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1)
   354  	pclntabFiletabOffset = start
   355  	setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start))
   356  
   357  	Symgrow(Ctxt, ftab, int64(start)+(int64(Ctxt.Nhistfile)+1)*4)
   358  	setuint32(Ctxt, ftab, int64(start), uint32(Ctxt.Nhistfile))
   359  	for s := Ctxt.Filesyms; s != nil; s = s.Next {
   360  		setuint32(Ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name)))
   361  	}
   362  
   363  	ftab.Size = int64(len(ftab.P))
   364  
   365  	if Debug['v'] != 0 {
   366  		fmt.Fprintf(&Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes))
   367  	}
   368  }
   369  
   370  const (
   371  	BUCKETSIZE    = 256 * MINFUNC
   372  	SUBBUCKETS    = 16
   373  	SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
   374  	NOIDX         = 0x7fffffff
   375  )
   376  
   377  // findfunctab generates a lookup table to quickly find the containing
   378  // function for a pc.  See src/runtime/symtab.go:findfunc for details.
   379  func findfunctab() {
   380  	t := Linklookup(Ctxt, "runtime.findfunctab", 0)
   381  	t.Type = SRODATA
   382  	t.Reachable = true
   383  	t.Local = true
   384  
   385  	// find min and max address
   386  	min := Ctxt.Textp.Value
   387  
   388  	max := int64(0)
   389  	for s := Ctxt.Textp; s != nil; s = s.Next {
   390  		max = s.Value + s.Size
   391  	}
   392  
   393  	// for each subbucket, compute the minimum of all symbol indexes
   394  	// that map to that subbucket.
   395  	n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
   396  
   397  	indexes := make([]int32, n)
   398  	for i := int32(0); i < n; i++ {
   399  		indexes[i] = NOIDX
   400  	}
   401  	idx := int32(0)
   402  	var e *LSym
   403  	var i int32
   404  	var p int64
   405  	var q int64
   406  	for s := Ctxt.Textp; s != nil; s = s.Next {
   407  		if container(s) != 0 {
   408  			continue
   409  		}
   410  		p = s.Value
   411  		e = s.Next
   412  		for container(e) != 0 {
   413  			e = e.Next
   414  		}
   415  		if e != nil {
   416  			q = e.Value
   417  		} else {
   418  			q = max
   419  		}
   420  
   421  		//print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
   422  		for ; p < q; p += SUBBUCKETSIZE {
   423  			i = int32((p - min) / SUBBUCKETSIZE)
   424  			if indexes[i] > idx {
   425  				indexes[i] = idx
   426  			}
   427  		}
   428  
   429  		i = int32((q - 1 - min) / SUBBUCKETSIZE)
   430  		if indexes[i] > idx {
   431  			indexes[i] = idx
   432  		}
   433  		idx++
   434  	}
   435  
   436  	// allocate table
   437  	nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
   438  
   439  	Symgrow(Ctxt, t, 4*int64(nbuckets)+int64(n))
   440  
   441  	// fill in table
   442  	var base int32
   443  	var j int32
   444  	for i := int32(0); i < nbuckets; i++ {
   445  		base = indexes[i*SUBBUCKETS]
   446  		if base == NOIDX {
   447  			Diag("hole in findfunctab")
   448  		}
   449  		setuint32(Ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base))
   450  		for j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
   451  			idx = indexes[i*SUBBUCKETS+j]
   452  			if idx == NOIDX {
   453  				Diag("hole in findfunctab")
   454  			}
   455  			if idx-base >= 256 {
   456  				Diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
   457  			}
   458  
   459  			setuint8(Ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
   460  		}
   461  	}
   462  }