github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/bootstrap/internal/ld/pcln.go (about)

     1  // Do not edit. Bootstrap copy of /Users/rsc/g/go/src/cmd/internal/ld/pcln.go
     2  
     3  // Copyright 2013 The Go Authors.  All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  package ld
     8  
     9  import (
    10  	"rsc.io/tmp/bootstrap/internal/obj"
    11  	"fmt"
    12  	"log"
    13  )
    14  
    15  // funcpctab writes to dst a pc-value table mapping the code in func to the values
    16  // returned by valfunc parameterized by arg. The invocation of valfunc to update the
    17  // current value is, for each p,
    18  //
    19  //	val = valfunc(func, val, p, 0, arg);
    20  //	record val as value at p->pc;
    21  //	val = valfunc(func, val, p, 1, arg);
    22  //
    23  // where func is the function, val is the current value, p is the instruction being
    24  // considered, and arg can be used to further parameterize valfunc.
    25  
    26  // pctofileline computes either the file number (arg == 0)
    27  // or the line number (arg == 1) to use at p.
    28  // Because p->lineno applies to p, phase == 0 (before p)
    29  // takes care of the update.
    30  
    31  // pctospadj computes the sp adjustment in effect.
    32  // It is oldval plus any adjustment made by p itself.
    33  // The adjustment by p takes effect only after p, so we
    34  // apply the change during phase == 1.
    35  
    36  // pctopcdata computes the pcdata value in effect at p.
    37  // A PCDATA instruction sets the value in effect at future
    38  // non-PCDATA instructions.
    39  // Since PCDATA instructions have no width in the final code,
    40  // it does not matter which phase we use for the update.
    41  
    42  // iteration over encoded pcdata tables.
    43  
    44  func getvarint(pp *[]byte) uint32 {
    45  	v := uint32(0)
    46  	p := *pp
    47  	for shift := 0; ; shift += 7 {
    48  		v |= uint32(p[0]&0x7F) << uint(shift)
    49  		tmp4 := p
    50  		p = p[1:]
    51  		if tmp4[0]&0x80 == 0 {
    52  			break
    53  		}
    54  	}
    55  
    56  	*pp = p
    57  	return v
    58  }
    59  
    60  func pciternext(it *Pciter) {
    61  	it.pc = it.nextpc
    62  	if it.done != 0 {
    63  		return
    64  	}
    65  	if -cap(it.p) >= -cap(it.d.P[len(it.d.P):]) {
    66  		it.done = 1
    67  		return
    68  	}
    69  
    70  	// value delta
    71  	v := getvarint(&it.p)
    72  
    73  	if v == 0 && it.start == 0 {
    74  		it.done = 1
    75  		return
    76  	}
    77  
    78  	it.start = 0
    79  	dv := int32(v>>1) ^ (int32(v<<31) >> 31)
    80  	it.value += dv
    81  
    82  	// pc delta
    83  	v = getvarint(&it.p)
    84  
    85  	it.nextpc = it.pc + v*it.pcscale
    86  }
    87  
    88  func pciterinit(ctxt *Link, it *Pciter, d *Pcdata) {
    89  	it.d = *d
    90  	it.p = it.d.P
    91  	it.pc = 0
    92  	it.nextpc = 0
    93  	it.value = -1
    94  	it.start = 1
    95  	it.done = 0
    96  	it.pcscale = uint32(ctxt.Arch.Minlc)
    97  	pciternext(it)
    98  }
    99  
   100  // Copyright 2013 The Go Authors.  All rights reserved.
   101  // Use of this source code is governed by a BSD-style
   102  // license that can be found in the LICENSE file.
   103  
   104  func addvarint(d *Pcdata, val uint32) {
   105  	n := int32(0)
   106  	for v := val; v >= 0x80; v >>= 7 {
   107  		n++
   108  	}
   109  	n++
   110  
   111  	old := len(d.P)
   112  	for cap(d.P) < len(d.P)+int(n) {
   113  		d.P = append(d.P[:cap(d.P)], 0)
   114  	}
   115  	d.P = d.P[:old+int(n)]
   116  
   117  	p := d.P[old:]
   118  	var v uint32
   119  	for v = val; v >= 0x80; v >>= 7 {
   120  		p[0] = byte(v | 0x80)
   121  		p = p[1:]
   122  	}
   123  	p[0] = byte(v)
   124  }
   125  
   126  func addpctab(ftab *LSym, off int32, d *Pcdata) int32 {
   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.Sub != nil {
   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  	for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
   229  		if container(Ctxt.Cursym) == 0 {
   230  			nfunc++
   231  		}
   232  	}
   233  
   234  	pclntabNfunc = nfunc
   235  	Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4)
   236  	setuint32(Ctxt, ftab, 0, 0xfffffffb)
   237  	setuint8(Ctxt, ftab, 6, uint8(Thearch.Minlc))
   238  	setuint8(Ctxt, ftab, 7, uint8(Thearch.Ptrsize))
   239  	setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(Thearch.Ptrsize))
   240  	pclntabPclntabOffset = int32(8 + Thearch.Ptrsize)
   241  
   242  	nfunc = 0
   243  	var last *LSym
   244  	var end int32
   245  	var funcstart int32
   246  	var i int32
   247  	var it Pciter
   248  	var off int32
   249  	var pcln *Pcln
   250  	for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
   251  		last = Ctxt.Cursym
   252  		if container(Ctxt.Cursym) != 0 {
   253  			continue
   254  		}
   255  		pcln = Ctxt.Cursym.Pcln
   256  		if pcln == nil {
   257  			pcln = &pclntab_zpcln
   258  		}
   259  
   260  		if pclntabFirstFunc == nil {
   261  			pclntabFirstFunc = Ctxt.Cursym
   262  		}
   263  
   264  		funcstart = int32(len(ftab.P))
   265  		funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1)
   266  
   267  		setaddr(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), Ctxt.Cursym)
   268  		setuintxx(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint64(funcstart), int64(Thearch.Ptrsize))
   269  
   270  		// fixed size of struct, checked below
   271  		off = funcstart
   272  
   273  		end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(pcln.Npcdata)*4 + int32(pcln.Nfuncdata)*int32(Thearch.Ptrsize)
   274  		if pcln.Nfuncdata > 0 && (end&int32(Thearch.Ptrsize-1) != 0) {
   275  			end += 4
   276  		}
   277  		Symgrow(Ctxt, ftab, int64(end))
   278  
   279  		// entry uintptr
   280  		off = int32(setaddr(Ctxt, ftab, int64(off), Ctxt.Cursym))
   281  
   282  		// name int32
   283  		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(ftabaddstring(ftab, Ctxt.Cursym.Name))))
   284  
   285  		// args int32
   286  		// TODO: Move into funcinfo.
   287  		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.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 != &pclntab_zpcln {
   297  			renumberfiles(Ctxt, pcln.File, &pcln.Pcfile)
   298  			if false {
   299  				// Sanity check the new numbering
   300  				for pciterinit(Ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) {
   301  					if it.value < 1 || it.value > Ctxt.Nhistfile {
   302  						Diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, Ctxt.Nhistfile)
   303  						errorexit()
   304  					}
   305  				}
   306  			}
   307  		}
   308  
   309  		// pcdata
   310  		off = addpctab(ftab, off, &pcln.Pcsp)
   311  
   312  		off = addpctab(ftab, off, &pcln.Pcfile)
   313  		off = addpctab(ftab, off, &pcln.Pcline)
   314  		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Npcdata)))
   315  		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Nfuncdata)))
   316  		for i = 0; i < int32(pcln.Npcdata); i++ {
   317  			off = addpctab(ftab, off, &pcln.Pcdata[i])
   318  		}
   319  
   320  		// funcdata, must be pointer-aligned and we're only int32-aligned.
   321  		// Missing funcdata will be 0 (nil pointer).
   322  		if pcln.Nfuncdata > 0 {
   323  			if off&int32(Thearch.Ptrsize-1) != 0 {
   324  				off += 4
   325  			}
   326  			for i = 0; i < int32(pcln.Nfuncdata); i++ {
   327  				if pcln.Funcdata[i] == nil {
   328  					setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize))
   329  				} else {
   330  					// TODO: Dedup.
   331  					funcdata_bytes += pcln.Funcdata[i].Size
   332  
   333  					setaddrplus(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i])
   334  				}
   335  			}
   336  
   337  			off += int32(pcln.Nfuncdata) * int32(Thearch.Ptrsize)
   338  		}
   339  
   340  		if off != end {
   341  			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)
   342  			errorexit()
   343  		}
   344  
   345  		nfunc++
   346  	}
   347  
   348  	pclntabLastFunc = last
   349  	// Final entry of table is just end pc.
   350  	setaddrplus(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), last, last.Size)
   351  
   352  	// Start file table.
   353  	start := int32(len(ftab.P))
   354  
   355  	start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1)
   356  	pclntabFiletabOffset = start
   357  	setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start))
   358  
   359  	Symgrow(Ctxt, ftab, int64(start)+(int64(Ctxt.Nhistfile)+1)*4)
   360  	setuint32(Ctxt, ftab, int64(start), uint32(Ctxt.Nhistfile))
   361  	for s := Ctxt.Filesyms; s != nil; s = s.Next {
   362  		setuint32(Ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name)))
   363  	}
   364  
   365  	ftab.Size = int64(len(ftab.P))
   366  
   367  	if Debug['v'] != 0 {
   368  		fmt.Fprintf(&Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes))
   369  	}
   370  }
   371  
   372  const (
   373  	BUCKETSIZE    = 256 * MINFUNC
   374  	SUBBUCKETS    = 16
   375  	SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
   376  	NOIDX         = 0x7fffffff
   377  )
   378  
   379  // findfunctab generates a lookup table to quickly find the containing
   380  // function for a pc.  See src/runtime/symtab.go:findfunc for details.
   381  func findfunctab() {
   382  	t := Linklookup(Ctxt, "runtime.findfunctab", 0)
   383  	t.Type = obj.SRODATA
   384  	t.Reachable = true
   385  	t.Local = true
   386  
   387  	// find min and max address
   388  	min := Ctxt.Textp.Value
   389  
   390  	max := int64(0)
   391  	for s := Ctxt.Textp; s != nil; s = s.Next {
   392  		max = s.Value + s.Size
   393  	}
   394  
   395  	// for each subbucket, compute the minimum of all symbol indexes
   396  	// that map to that subbucket.
   397  	n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
   398  
   399  	indexes := make([]int32, n)
   400  	for i := int32(0); i < n; i++ {
   401  		indexes[i] = NOIDX
   402  	}
   403  	idx := int32(0)
   404  	var e *LSym
   405  	var i int32
   406  	var p int64
   407  	var q int64
   408  	for s := Ctxt.Textp; s != nil; s = s.Next {
   409  		if container(s) != 0 {
   410  			continue
   411  		}
   412  		p = s.Value
   413  		e = s.Next
   414  		for container(e) != 0 {
   415  			e = e.Next
   416  		}
   417  		if e != nil {
   418  			q = e.Value
   419  		} else {
   420  			q = max
   421  		}
   422  
   423  		//print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
   424  		for ; p < q; p += SUBBUCKETSIZE {
   425  			i = int32((p - min) / SUBBUCKETSIZE)
   426  			if indexes[i] > idx {
   427  				indexes[i] = idx
   428  			}
   429  		}
   430  
   431  		i = int32((q - 1 - min) / SUBBUCKETSIZE)
   432  		if indexes[i] > idx {
   433  			indexes[i] = idx
   434  		}
   435  		idx++
   436  	}
   437  
   438  	// allocate table
   439  	nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
   440  
   441  	Symgrow(Ctxt, t, 4*int64(nbuckets)+int64(n))
   442  
   443  	// fill in table
   444  	var base int32
   445  	var j int32
   446  	for i := int32(0); i < nbuckets; i++ {
   447  		base = indexes[i*SUBBUCKETS]
   448  		if base == NOIDX {
   449  			Diag("hole in findfunctab")
   450  		}
   451  		setuint32(Ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base))
   452  		for j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
   453  			idx = indexes[i*SUBBUCKETS+j]
   454  			if idx == NOIDX {
   455  				Diag("hole in findfunctab")
   456  			}
   457  			if idx-base >= 256 {
   458  				Diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
   459  			}
   460  
   461  			setuint8(Ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
   462  		}
   463  	}
   464  }