github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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/objabi"
     9  	"cmd/internal/src"
    10  	"cmd/internal/sys"
    11  	"cmd/link/internal/sym"
    12  	"log"
    13  	"os"
    14  	"path/filepath"
    15  	"strings"
    16  )
    17  
    18  // iteration over encoded pcdata tables.
    19  
    20  func getvarint(pp *[]byte) uint32 {
    21  	v := uint32(0)
    22  	p := *pp
    23  	for shift := 0; ; shift += 7 {
    24  		v |= uint32(p[0]&0x7F) << uint(shift)
    25  		tmp4 := p
    26  		p = p[1:]
    27  		if tmp4[0]&0x80 == 0 {
    28  			break
    29  		}
    30  	}
    31  
    32  	*pp = p
    33  	return v
    34  }
    35  
    36  func pciternext(it *Pciter) {
    37  	it.pc = it.nextpc
    38  	if it.done != 0 {
    39  		return
    40  	}
    41  	if -cap(it.p) >= -cap(it.d.P[len(it.d.P):]) {
    42  		it.done = 1
    43  		return
    44  	}
    45  
    46  	// value delta
    47  	v := getvarint(&it.p)
    48  
    49  	if v == 0 && it.start == 0 {
    50  		it.done = 1
    51  		return
    52  	}
    53  
    54  	it.start = 0
    55  	dv := int32(v>>1) ^ (int32(v<<31) >> 31)
    56  	it.value += dv
    57  
    58  	// pc delta
    59  	v = getvarint(&it.p)
    60  
    61  	it.nextpc = it.pc + v*it.pcscale
    62  }
    63  
    64  func pciterinit(ctxt *Link, it *Pciter, d *sym.Pcdata) {
    65  	it.d = *d
    66  	it.p = it.d.P
    67  	it.pc = 0
    68  	it.nextpc = 0
    69  	it.value = -1
    70  	it.start = 1
    71  	it.done = 0
    72  	it.pcscale = uint32(ctxt.Arch.MinLC)
    73  	pciternext(it)
    74  }
    75  
    76  func addvarint(d *sym.Pcdata, val uint32) {
    77  	n := int32(0)
    78  	for v := val; v >= 0x80; v >>= 7 {
    79  		n++
    80  	}
    81  	n++
    82  
    83  	old := len(d.P)
    84  	for cap(d.P) < len(d.P)+int(n) {
    85  		d.P = append(d.P[:cap(d.P)], 0)
    86  	}
    87  	d.P = d.P[:old+int(n)]
    88  
    89  	p := d.P[old:]
    90  	var v uint32
    91  	for v = val; v >= 0x80; v >>= 7 {
    92  		p[0] = byte(v | 0x80)
    93  		p = p[1:]
    94  	}
    95  	p[0] = byte(v)
    96  }
    97  
    98  func addpctab(ctxt *Link, ftab *sym.Symbol, off int32, d *sym.Pcdata) int32 {
    99  	var start int32
   100  	if len(d.P) > 0 {
   101  		start = int32(len(ftab.P))
   102  		ftab.AddBytes(d.P)
   103  	}
   104  	return int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(start)))
   105  }
   106  
   107  func ftabaddstring(ctxt *Link, ftab *sym.Symbol, s string) int32 {
   108  	n := int32(len(s)) + 1
   109  	start := int32(len(ftab.P))
   110  	ftab.Grow(int64(start) + int64(n) + 1)
   111  	copy(ftab.P[start:], s)
   112  	return start
   113  }
   114  
   115  // numberfile assigns a file number to the file if it hasn't been assigned already.
   116  func numberfile(ctxt *Link, file *sym.Symbol) {
   117  	if file.Type != sym.SFILEPATH {
   118  		ctxt.Filesyms = append(ctxt.Filesyms, file)
   119  		file.Value = int64(len(ctxt.Filesyms))
   120  		file.Type = sym.SFILEPATH
   121  		path := file.Name[len(src.FileSymPrefix):]
   122  		file.Name = expandGoroot(path)
   123  	}
   124  }
   125  
   126  func renumberfiles(ctxt *Link, files []*sym.Symbol, d *sym.Pcdata) {
   127  	// Give files numbers.
   128  	for _, f := range files {
   129  		numberfile(ctxt, f)
   130  	}
   131  
   132  	newval := int32(-1)
   133  	var out sym.Pcdata
   134  	var it Pciter
   135  	for pciterinit(ctxt, &it, d); it.done == 0; pciternext(&it) {
   136  		// value delta
   137  		oldval := it.value
   138  
   139  		var val int32
   140  		if oldval == -1 {
   141  			val = -1
   142  		} else {
   143  			if oldval < 0 || oldval >= int32(len(files)) {
   144  				log.Fatalf("bad pcdata %d", oldval)
   145  			}
   146  			val = int32(files[oldval].Value)
   147  		}
   148  
   149  		dv := val - newval
   150  		newval = val
   151  		v := (uint32(dv) << 1) ^ uint32(dv>>31)
   152  		addvarint(&out, v)
   153  
   154  		// pc delta
   155  		addvarint(&out, (it.nextpc-it.pc)/it.pcscale)
   156  	}
   157  
   158  	// terminating value delta
   159  	addvarint(&out, 0)
   160  
   161  	*d = out
   162  }
   163  
   164  // onlycsymbol reports whether this is a symbol that is referenced by C code.
   165  func onlycsymbol(s *sym.Symbol) bool {
   166  	switch s.Name {
   167  	case "_cgo_topofstack", "_cgo_panic", "crosscall2":
   168  		return true
   169  	}
   170  	if strings.HasPrefix(s.Name, "_cgoexp_") {
   171  		return true
   172  	}
   173  	return false
   174  }
   175  
   176  func emitPcln(ctxt *Link, s *sym.Symbol) bool {
   177  	if s == nil {
   178  		return true
   179  	}
   180  	if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(s) {
   181  		return false
   182  	}
   183  	// We want to generate func table entries only for the "lowest level" symbols,
   184  	// not containers of subsymbols.
   185  	if s.Attr.Container() {
   186  		return true
   187  	}
   188  	return true
   189  }
   190  
   191  // pclntab initializes the pclntab symbol with
   192  // runtime function and file name information.
   193  
   194  var pclntabZpcln sym.FuncInfo
   195  
   196  // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
   197  var pclntabNfunc int32
   198  var pclntabFiletabOffset int32
   199  var pclntabPclntabOffset int32
   200  var pclntabFirstFunc *sym.Symbol
   201  var pclntabLastFunc *sym.Symbol
   202  
   203  func (ctxt *Link) pclntab() {
   204  	funcdataBytes := int64(0)
   205  	ftab := ctxt.Syms.Lookup("runtime.pclntab", 0)
   206  	ftab.Type = sym.SPCLNTAB
   207  	ftab.Attr |= sym.AttrReachable
   208  
   209  	// See golang.org/s/go12symtab for the format. Briefly:
   210  	//	8-byte header
   211  	//	nfunc [thearch.ptrsize bytes]
   212  	//	function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
   213  	//	end PC [thearch.ptrsize bytes]
   214  	//	offset to file table [4 bytes]
   215  	nfunc := int32(0)
   216  
   217  	// Find container symbols and mark them as such.
   218  	for _, s := range ctxt.Textp {
   219  		if s.Outer != nil {
   220  			s.Outer.Attr |= sym.AttrContainer
   221  		}
   222  	}
   223  
   224  	for _, s := range ctxt.Textp {
   225  		if emitPcln(ctxt, s) {
   226  			nfunc++
   227  		}
   228  	}
   229  
   230  	pclntabNfunc = nfunc
   231  	ftab.Grow(8 + int64(ctxt.Arch.PtrSize) + int64(nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4)
   232  	ftab.SetUint32(ctxt.Arch, 0, 0xfffffffb)
   233  	ftab.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC))
   234  	ftab.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize))
   235  	ftab.SetUint(ctxt.Arch, 8, uint64(nfunc))
   236  	pclntabPclntabOffset = int32(8 + ctxt.Arch.PtrSize)
   237  
   238  	funcnameoff := make(map[string]int32)
   239  	nameToOffset := func(name string) int32 {
   240  		nameoff, ok := funcnameoff[name]
   241  		if !ok {
   242  			nameoff = ftabaddstring(ctxt, ftab, name)
   243  			funcnameoff[name] = nameoff
   244  		}
   245  		return nameoff
   246  	}
   247  
   248  	nfunc = 0
   249  	var last *sym.Symbol
   250  	for _, s := range ctxt.Textp {
   251  		last = s
   252  		if !emitPcln(ctxt, s) {
   253  			continue
   254  		}
   255  		pcln := s.FuncInfo
   256  		if pcln == nil {
   257  			pcln = &pclntabZpcln
   258  		}
   259  
   260  		if pclntabFirstFunc == nil {
   261  			pclntabFirstFunc = s
   262  		}
   263  
   264  		if len(pcln.InlTree) > 0 {
   265  			if len(pcln.Pcdata) <= objabi.PCDATA_InlTreeIndex {
   266  				// Create inlining pcdata table.
   267  				pcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1)
   268  				copy(pcdata, pcln.Pcdata)
   269  				pcln.Pcdata = pcdata
   270  			}
   271  
   272  			if len(pcln.Funcdataoff) <= objabi.FUNCDATA_InlTree {
   273  				// Create inline tree funcdata.
   274  				funcdata := make([]*sym.Symbol, objabi.FUNCDATA_InlTree+1)
   275  				funcdataoff := make([]int64, objabi.FUNCDATA_InlTree+1)
   276  				copy(funcdata, pcln.Funcdata)
   277  				copy(funcdataoff, pcln.Funcdataoff)
   278  				pcln.Funcdata = funcdata
   279  				pcln.Funcdataoff = funcdataoff
   280  			}
   281  		}
   282  
   283  		funcstart := int32(len(ftab.P))
   284  		funcstart += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1)
   285  
   286  		ftab.SetAddr(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s)
   287  		ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart))
   288  
   289  		// Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func
   290  		// and package debug/gosym.
   291  
   292  		// fixed size of struct, checked below
   293  		off := funcstart
   294  
   295  		end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(ctxt.Arch.PtrSize)
   296  		if len(pcln.Funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) {
   297  			end += 4
   298  		}
   299  		ftab.Grow(int64(end))
   300  
   301  		// entry uintptr
   302  		off = int32(ftab.SetAddr(ctxt.Arch, int64(off), s))
   303  
   304  		// name int32
   305  		nameoff := nameToOffset(s.Name)
   306  		off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(nameoff)))
   307  
   308  		// args int32
   309  		// TODO: Move into funcinfo.
   310  		args := uint32(0)
   311  		if s.FuncInfo != nil {
   312  			args = uint32(s.FuncInfo.Args)
   313  		}
   314  		off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args))
   315  
   316  		// deferreturn
   317  		deferreturn := uint32(0)
   318  		lastWasmAddr := uint32(0)
   319  		for _, r := range s.R {
   320  			if ctxt.Arch.Family == sys.Wasm && r.Type == objabi.R_ADDR {
   321  				// Wasm does not have a live variable set at the deferreturn
   322  				// call itself. Instead it has one identified by the
   323  				// resumption point immediately preceding the deferreturn.
   324  				// The wasm code has a R_ADDR relocation which is used to
   325  				// set the resumption point to PC_B.
   326  				lastWasmAddr = uint32(r.Add)
   327  			}
   328  			if r.Sym != nil && r.Sym.Name == "runtime.deferreturn" && r.Add == 0 {
   329  				if ctxt.Arch.Family == sys.Wasm {
   330  					deferreturn = lastWasmAddr
   331  				} else {
   332  					// Note: the relocation target is in the call instruction, but
   333  					// is not necessarily the whole instruction (for instance, on
   334  					// x86 the relocation applies to bytes [1:5] of the 5 byte call
   335  					// instruction).
   336  					deferreturn = uint32(r.Off)
   337  				}
   338  				break // only need one
   339  			}
   340  		}
   341  		off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn))
   342  
   343  		if pcln != &pclntabZpcln {
   344  			renumberfiles(ctxt, pcln.File, &pcln.Pcfile)
   345  			if false {
   346  				// Sanity check the new numbering
   347  				var it Pciter
   348  				for pciterinit(ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) {
   349  					if it.value < 1 || it.value > int32(len(ctxt.Filesyms)) {
   350  						Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.value, len(ctxt.Filesyms))
   351  						errorexit()
   352  					}
   353  				}
   354  			}
   355  		}
   356  
   357  		if len(pcln.InlTree) > 0 {
   358  			inlTreeSym := ctxt.Syms.Lookup("inltree."+s.Name, 0)
   359  			inlTreeSym.Type = sym.SRODATA
   360  			inlTreeSym.Attr |= sym.AttrReachable | sym.AttrDuplicateOK
   361  
   362  			for i, call := range pcln.InlTree {
   363  				// Usually, call.File is already numbered since the file
   364  				// shows up in the Pcfile table. However, two inlined calls
   365  				// might overlap exactly so that only the innermost file
   366  				// appears in the Pcfile table. In that case, this assigns
   367  				// the outer file a number.
   368  				numberfile(ctxt, call.File)
   369  				nameoff := nameToOffset(call.Func.Name)
   370  
   371  				inlTreeSym.SetUint16(ctxt.Arch, int64(i*20+0), uint16(call.Parent))
   372  				inlTreeSym.SetUint8(ctxt.Arch, int64(i*20+2), uint8(objabi.GetFuncID(call.Func.Name, call.Func.File)))
   373  				// byte 3 is unused
   374  				inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+4), uint32(call.File.Value))
   375  				inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+8), uint32(call.Line))
   376  				inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+12), uint32(nameoff))
   377  				inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+16), uint32(call.ParentPC))
   378  			}
   379  
   380  			pcln.Funcdata[objabi.FUNCDATA_InlTree] = inlTreeSym
   381  			pcln.Pcdata[objabi.PCDATA_InlTreeIndex] = pcln.Pcinline
   382  		}
   383  
   384  		// pcdata
   385  		off = addpctab(ctxt, ftab, off, &pcln.Pcsp)
   386  
   387  		off = addpctab(ctxt, ftab, off, &pcln.Pcfile)
   388  		off = addpctab(ctxt, ftab, off, &pcln.Pcline)
   389  		off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcln.Pcdata))))
   390  
   391  		// funcID uint8
   392  		var file string
   393  		if s.FuncInfo != nil && len(s.FuncInfo.File) > 0 {
   394  			file = s.FuncInfo.File[0].Name
   395  		}
   396  		funcID := objabi.GetFuncID(s.Name, file)
   397  
   398  		off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
   399  
   400  		// unused
   401  		off += 2
   402  
   403  		// nfuncdata must be the final entry.
   404  		off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(pcln.Funcdata))))
   405  		for i := range pcln.Pcdata {
   406  			off = addpctab(ctxt, ftab, off, &pcln.Pcdata[i])
   407  		}
   408  
   409  		// funcdata, must be pointer-aligned and we're only int32-aligned.
   410  		// Missing funcdata will be 0 (nil pointer).
   411  		if len(pcln.Funcdata) > 0 {
   412  			if off&int32(ctxt.Arch.PtrSize-1) != 0 {
   413  				off += 4
   414  			}
   415  			for i := range pcln.Funcdata {
   416  				if pcln.Funcdata[i] == nil {
   417  					ftab.SetUint(ctxt.Arch, int64(off)+int64(ctxt.Arch.PtrSize)*int64(i), uint64(pcln.Funcdataoff[i]))
   418  				} else {
   419  					// TODO: Dedup.
   420  					funcdataBytes += pcln.Funcdata[i].Size
   421  
   422  					ftab.SetAddrPlus(ctxt.Arch, int64(off)+int64(ctxt.Arch.PtrSize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i])
   423  				}
   424  			}
   425  
   426  			off += int32(len(pcln.Funcdata)) * int32(ctxt.Arch.PtrSize)
   427  		}
   428  
   429  		if off != end {
   430  			Errorf(s, "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), ctxt.Arch.PtrSize)
   431  			errorexit()
   432  		}
   433  
   434  		nfunc++
   435  	}
   436  
   437  	pclntabLastFunc = last
   438  	// Final entry of table is just end pc.
   439  	ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, last.Size)
   440  
   441  	// Start file table.
   442  	start := int32(len(ftab.P))
   443  
   444  	start += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1)
   445  	pclntabFiletabOffset = start
   446  	ftab.SetUint32(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start))
   447  
   448  	ftab.Grow(int64(start) + (int64(len(ctxt.Filesyms))+1)*4)
   449  	ftab.SetUint32(ctxt.Arch, int64(start), uint32(len(ctxt.Filesyms)+1))
   450  	for i := len(ctxt.Filesyms) - 1; i >= 0; i-- {
   451  		s := ctxt.Filesyms[i]
   452  		ftab.SetUint32(ctxt.Arch, int64(start)+s.Value*4, uint32(ftabaddstring(ctxt, ftab, s.Name)))
   453  	}
   454  
   455  	ftab.Size = int64(len(ftab.P))
   456  
   457  	if ctxt.Debugvlog != 0 {
   458  		ctxt.Logf("%5.2f pclntab=%d bytes, funcdata total %d bytes\n", Cputime(), ftab.Size, funcdataBytes)
   459  	}
   460  }
   461  
   462  func gorootFinal() string {
   463  	root := objabi.GOROOT
   464  	if final := os.Getenv("GOROOT_FINAL"); final != "" {
   465  		root = final
   466  	}
   467  	return root
   468  }
   469  
   470  func expandGoroot(s string) string {
   471  	const n = len("$GOROOT")
   472  	if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') {
   473  		return filepath.ToSlash(filepath.Join(gorootFinal(), s[n:]))
   474  	}
   475  	return s
   476  }
   477  
   478  const (
   479  	BUCKETSIZE    = 256 * MINFUNC
   480  	SUBBUCKETS    = 16
   481  	SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
   482  	NOIDX         = 0x7fffffff
   483  )
   484  
   485  // findfunctab generates a lookup table to quickly find the containing
   486  // function for a pc. See src/runtime/symtab.go:findfunc for details.
   487  func (ctxt *Link) findfunctab() {
   488  	t := ctxt.Syms.Lookup("runtime.findfunctab", 0)
   489  	t.Type = sym.SRODATA
   490  	t.Attr |= sym.AttrReachable
   491  	t.Attr |= sym.AttrLocal
   492  
   493  	// find min and max address
   494  	min := ctxt.Textp[0].Value
   495  	lastp := ctxt.Textp[len(ctxt.Textp)-1]
   496  	max := lastp.Value + lastp.Size
   497  
   498  	// for each subbucket, compute the minimum of all symbol indexes
   499  	// that map to that subbucket.
   500  	n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
   501  
   502  	indexes := make([]int32, n)
   503  	for i := int32(0); i < n; i++ {
   504  		indexes[i] = NOIDX
   505  	}
   506  	idx := int32(0)
   507  	for i, s := range ctxt.Textp {
   508  		if !emitPcln(ctxt, s) {
   509  			continue
   510  		}
   511  		p := s.Value
   512  		var e *sym.Symbol
   513  		i++
   514  		if i < len(ctxt.Textp) {
   515  			e = ctxt.Textp[i]
   516  		}
   517  		for !emitPcln(ctxt, e) && i < len(ctxt.Textp) {
   518  			e = ctxt.Textp[i]
   519  			i++
   520  		}
   521  		q := max
   522  		if e != nil {
   523  			q = e.Value
   524  		}
   525  
   526  		//print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
   527  		for ; p < q; p += SUBBUCKETSIZE {
   528  			i = int((p - min) / SUBBUCKETSIZE)
   529  			if indexes[i] > idx {
   530  				indexes[i] = idx
   531  			}
   532  		}
   533  
   534  		i = int((q - 1 - min) / SUBBUCKETSIZE)
   535  		if indexes[i] > idx {
   536  			indexes[i] = idx
   537  		}
   538  		idx++
   539  	}
   540  
   541  	// allocate table
   542  	nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
   543  
   544  	t.Grow(4*int64(nbuckets) + int64(n))
   545  
   546  	// fill in table
   547  	for i := int32(0); i < nbuckets; i++ {
   548  		base := indexes[i*SUBBUCKETS]
   549  		if base == NOIDX {
   550  			Errorf(nil, "hole in findfunctab")
   551  		}
   552  		t.SetUint32(ctxt.Arch, int64(i)*(4+SUBBUCKETS), uint32(base))
   553  		for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
   554  			idx = indexes[i*SUBBUCKETS+j]
   555  			if idx == NOIDX {
   556  				Errorf(nil, "hole in findfunctab")
   557  			}
   558  			if idx-base >= 256 {
   559  				Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
   560  			}
   561  
   562  			t.SetUint8(ctxt.Arch, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
   563  		}
   564  	}
   565  }