...

Source file src/pkg/cmd/link/internal/ld/pcln.go

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

View as plain text