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