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