From 3067a8dc02f62c287a8ccd3fcf16bfdf4f687f5f Mon Sep 17 00:00:00 2001 From: Jeremy Faller Date: Tue, 14 Jul 2020 13:12:58 -0400 Subject: [PATCH] [dev.link] cmd/link: use pclntabState and eliminate globals Non functional change. As runtime.pclntab breaks up, it'll be easier if we can just pass around the pclntab state. Also, eliminate the globals in pclntab. Change-Id: I2a5849e8f5f422a336a881e53a261e3997d11c44 Reviewed-on: https://go-review.googlesource.com/c/go/+/242599 Reviewed-by: Austin Clements Reviewed-by: Cherry Zhang --- src/cmd/link/internal/ld/main.go | 7 +- src/cmd/link/internal/ld/pcln.go | 233 ++++++++++++++++------------- src/cmd/link/internal/ld/symtab.go | 25 ++-- 3 files changed, 146 insertions(+), 119 deletions(-) diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index c2532ac290..3702f28dd8 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -288,13 +288,14 @@ func Main(arch *sys.Arch, theArch Arch) { bench.Start("buildinfo") ctxt.buildinfo() bench.Start("pclntab") - container := ctxt.pclntab() + containers := ctxt.findContainerSyms() + pclnState := ctxt.pclntab(containers) bench.Start("findfunctab") - ctxt.findfunctab(container) + ctxt.findfunctab(pclnState, containers) bench.Start("dwarfGenerateDebugSyms") dwarfGenerateDebugSyms(ctxt) bench.Start("symtab") - symGroupType := ctxt.symtab() + symGroupType := ctxt.symtab(pclnState) bench.Start("dodata") ctxt.dodata(symGroupType) bench.Start("address") diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go index 5ea210c139..3759decbeb 100644 --- a/src/cmd/link/internal/ld/pcln.go +++ b/src/cmd/link/internal/ld/pcln.go @@ -19,28 +19,51 @@ import ( "strings" ) -// pclnState holds state information used during pclntab generation. -// Here 'ldr' is just a pointer to the context's loader, 'container' -// is a bitmap holding whether a given symbol index is an outer or -// container symbol, 'deferReturnSym' is the index for the symbol -// "runtime.deferreturn", 'nameToOffset' is a helper function for -// capturing function names, 'numberedFiles' records the file number -// assigned to a given file symbol, 'filepaths' is a slice of -// expanded paths (indexed by file number). -type pclnState struct { +// oldPclnState holds state information used during pclntab generation. Here +// 'ldr' is just a pointer to the context's loader, 'deferReturnSym' is the +// index for the symbol "runtime.deferreturn", 'nameToOffset' is a helper +// function for capturing function names, 'numberedFiles' records the file +// number assigned to a given file symbol, 'filepaths' is a slice of expanded +// paths (indexed by file number). +// +// NB: This is deprecated, and will be eliminated when pclntab_old is +// eliminated. +type oldPclnState struct { ldr *loader.Loader - container loader.Bitmap deferReturnSym loader.Sym nameToOffset func(name string) int32 numberedFiles map[loader.Sym]int64 filepaths []string } -func makepclnState(ctxt *Link) pclnState { +// pclntab holds the state needed for pclntab generation. +type pclntab struct { + // The first and last functions found. + firstFunc, lastFunc loader.Sym + + // The offset to the filetab. + filetabOffset int32 + + // runtime.pclntab's symbols + carrier loader.Sym + pclntab loader.Sym + pcheader loader.Sym + findfunctab loader.Sym + + // The number of functions + number of TEXT sections - 1. This is such an + // unexpected value because platforms that have more than one TEXT section + // get a dummy function inserted between because the external linker can place + // functions in those areas. We mark those areas as not covered by the Go + // runtime. + // + // On most platforms this is the number of reachable functions. + nfunc int32 +} + +func makeOldPclnState(ctxt *Link) *oldPclnState { ldr := ctxt.loader drs := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal) - return pclnState{ - container: loader.MakeBitmap(ldr.NSym()), + state := &oldPclnState{ ldr: ldr, deferReturnSym: drs, numberedFiles: make(map[loader.Sym]int64), @@ -49,9 +72,42 @@ func makepclnState(ctxt *Link) pclnState { // return a value slot in filepaths. filepaths: []string{""}, } + + return state } -func (state *pclnState) ftabaddstring(ftab *loader.SymbolBuilder, s string) int32 { +// makePclntab makes a pclnState object. +func makePclntab(ctxt *Link, container loader.Bitmap) *pclntab { + ldr := ctxt.loader + + state := &pclntab{} + + // Gather some basic stats and info. + prevSect := ldr.SymSect(ctxt.Textp[0]) + for _, s := range ctxt.Textp { + if !emitPcln(ctxt, s, container) { + continue + } + state.nfunc++ + if state.firstFunc == 0 { + state.firstFunc = s + } + state.lastFunc = s + ss := ldr.SymSect(s) + if ss != prevSect { + // With multiple text sections, the external linker may + // insert functions between the sections, which are not + // known by Go. This leaves holes in the PC range covered + // by the func table. We need to generate an entry to mark + // the hole. + state.nfunc++ + prevSect = ss + } + } + return state +} + +func ftabaddstring(ftab *loader.SymbolBuilder, s string) int32 { start := len(ftab.Data()) ftab.Grow(int64(start + len(s) + 1)) // make room for s plus trailing NUL ftd := ftab.Data() @@ -60,7 +116,7 @@ func (state *pclnState) ftabaddstring(ftab *loader.SymbolBuilder, s string) int3 } // numberfile assigns a file number to the file if it hasn't been assigned already. -func (state *pclnState) numberfile(file loader.Sym) int64 { +func (state *oldPclnState) numberfile(file loader.Sym) int64 { if val, ok := state.numberedFiles[file]; ok { return val } @@ -72,14 +128,14 @@ func (state *pclnState) numberfile(file loader.Sym) int64 { return val } -func (state *pclnState) fileVal(file loader.Sym) int64 { +func (state *oldPclnState) fileVal(file loader.Sym) int64 { if val, ok := state.numberedFiles[file]; ok { return val } panic("should have been numbered first") } -func (state *pclnState) renumberfiles(ctxt *Link, fi loader.FuncInfo, d *sym.Pcdata) { +func (state *oldPclnState) renumberfiles(ctxt *Link, fi loader.FuncInfo, d *sym.Pcdata) { // Give files numbers. nf := fi.NumFile() for i := uint32(0); i < nf; i++ { @@ -146,7 +202,7 @@ func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool { return !container.Has(s) } -func (state *pclnState) computeDeferReturn(target *Target, s loader.Sym) uint32 { +func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint32 { deferreturn := uint32(0) lastWasmAddr := uint32(0) @@ -194,7 +250,7 @@ func (state *pclnState) computeDeferReturn(target *Target, s loader.Sym) uint32 // genInlTreeSym generates the InlTree sym for a function with the // specified FuncInfo. -func (state *pclnState) genInlTreeSym(fi loader.FuncInfo, arch *sys.Arch) loader.Sym { +func (state *oldPclnState) genInlTreeSym(fi loader.FuncInfo, arch *sys.Arch) loader.Sym { ldr := state.ldr its := ldr.CreateExtSym("", 0) inlTreeSym := ldr.MakeSymbolUpdater(its) @@ -229,16 +285,16 @@ func (state *pclnState) genInlTreeSym(fi loader.FuncInfo, arch *sys.Arch) loader // generatePCHeader creates the runtime.pcheader symbol, setting it up as a // generator to fill in its data later. -func generatePCHeader(ctxt *Link, carrier *loader.SymbolBuilder, pclntabSym loader.Sym) { +func (state *pclntab) generatePCHeader(ctxt *Link) { ldr := ctxt.loader writeHeader := func(ctxt *Link, s loader.Sym) { ldr := ctxt.loader header := ctxt.loader.MakeSymbolUpdater(s) // Check symbol order. - diff := ldr.SymValue(pclntabSym) - ldr.SymValue(s) + diff := ldr.SymValue(state.pclntab) - ldr.SymValue(s) if diff <= 0 { - panic(fmt.Sprintf("expected runtime.pcheader(%x) to be placed before runtime.pclntab(%x)", ldr.SymValue(s), ldr.SymValue(pclntabSym))) + panic(fmt.Sprintf("expected runtime.pcheader(%x) to be placed before runtime.pclntab(%x)", ldr.SymValue(s), ldr.SymValue(state.pclntab))) } // Write header. @@ -246,30 +302,21 @@ func generatePCHeader(ctxt *Link, carrier *loader.SymbolBuilder, pclntabSym load header.SetUint32(ctxt.Arch, 0, 0xfffffffa) header.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC)) header.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize)) - off := header.SetUint(ctxt.Arch, 8, uint64(pclntabNfunc)) + off := header.SetUint(ctxt.Arch, 8, uint64(state.nfunc)) header.SetUintptr(ctxt.Arch, off, uintptr(diff)) } size := int64(8 + 2*ctxt.Arch.PtrSize) - s := ctxt.createGeneratorSymbol("runtime.pcheader", 0, sym.SPCLNTAB, size, writeHeader) - ldr.SetAttrReachable(s, true) - ldr.SetCarrierSym(s, carrier.Sym()) + state.pcheader = ctxt.createGeneratorSymbol("runtime.pcheader", 0, sym.SPCLNTAB, size, writeHeader) + ldr.SetAttrReachable(state.pcheader, true) + ldr.SetCarrierSym(state.pcheader, state.carrier) } // pclntab initializes the pclntab symbol with // runtime function and file name information. -// These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab. -var pclntabNfunc int32 -var pclntabFiletabOffset int32 -var pclntabFirstFunc loader.Sym -var pclntabLastFunc loader.Sym - -// pclntab generates the pcln table for the link output. Return value -// is a bitmap indexed by global symbol that marks 'container' text -// symbols, e.g. the set of all symbols X such that Outer(S) = X for -// some other text symbol S. -func (ctxt *Link) pclntab() loader.Bitmap { +// pclntab generates the pcln table for the link output. +func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { // Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the // layout and data has changed since that time. // @@ -291,70 +338,39 @@ func (ctxt *Link) pclntab() loader.Bitmap { // func structures, function names, pcdata tables. // filetable + oldState := makeOldPclnState(ctxt) + state := makePclntab(ctxt, container) + ldr := ctxt.loader - carrier := ldr.CreateSymForUpdate("runtime.pclntab", 0) - carrier.SetType(sym.SPCLNTAB) - carrier.SetReachable(true) + state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0) + ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB) + ldr.SetAttrReachable(state.carrier, true) // runtime.pclntab_old is just a placeholder,and will eventually be deleted. // It contains the pieces of runtime.pclntab that haven't moved to a more // ration form. - pclntabSym := ldr.LookupOrCreateSym("runtime.pclntab_old", 0) - generatePCHeader(ctxt, carrier, pclntabSym) + state.pclntab = ldr.LookupOrCreateSym("runtime.pclntab_old", 0) + state.generatePCHeader(ctxt) funcdataBytes := int64(0) - ldr.SetCarrierSym(pclntabSym, carrier.Sym()) - ftab := ldr.MakeSymbolUpdater(pclntabSym) + ldr.SetCarrierSym(state.pclntab, state.carrier) + ftab := ldr.MakeSymbolUpdater(state.pclntab) ftab.SetType(sym.SPCLNTAB) ftab.SetReachable(true) - state := makepclnState(ctxt) - - // Find container symbols and mark them as such. - for _, s := range ctxt.Textp { - outer := ldr.OuterSym(s) - if outer != 0 { - state.container.Set(outer) - } - } - - // Gather some basic stats and info. - var nfunc int32 - prevSect := ldr.SymSect(ctxt.Textp[0]) - for _, s := range ctxt.Textp { - if !emitPcln(ctxt, s, state.container) { - continue - } - nfunc++ - if pclntabFirstFunc == 0 { - pclntabFirstFunc = s - } - ss := ldr.SymSect(s) - if ss != prevSect { - // With multiple text sections, the external linker may - // insert functions between the sections, which are not - // known by Go. This leaves holes in the PC range covered - // by the func table. We need to generate an entry to mark - // the hole. - nfunc++ - prevSect = ss - } - } - - pclntabNfunc = nfunc - ftab.Grow(int64(nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4) + ftab.Grow(int64(state.nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4) szHint := len(ctxt.Textp) * 2 funcnameoff := make(map[string]int32, szHint) nameToOffset := func(name string) int32 { nameoff, ok := funcnameoff[name] if !ok { - nameoff = state.ftabaddstring(ftab, name) + nameoff = ftabaddstring(ftab, name) funcnameoff[name] = nameoff } return nameoff } - state.nameToOffset = nameToOffset + oldState.nameToOffset = nameToOffset pctaboff := make(map[string]uint32, szHint) writepctab := func(off int32, p []byte) int32 { @@ -392,10 +408,10 @@ func (ctxt *Link) pclntab() loader.Bitmap { funcdata := []loader.Sym{} funcdataoff := []int64{} - nfunc = 0 // repurpose nfunc as a running index + var nfunc int32 prevFunc := ctxt.Textp[0] for _, s := range ctxt.Textp { - if !emitPcln(ctxt, s, state.container) { + if !emitPcln(ctxt, s, container) { continue } @@ -489,20 +505,20 @@ func (ctxt *Link) pclntab() loader.Bitmap { off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args)) // deferreturn - deferreturn := state.computeDeferReturn(&ctxt.Target, s) + deferreturn := oldState.computeDeferReturn(&ctxt.Target, s) off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn)) if fi.Valid() { pcsp = sym.Pcdata{P: fi.Pcsp()} pcfile = sym.Pcdata{P: fi.Pcfile()} pcline = sym.Pcdata{P: fi.Pcline()} - state.renumberfiles(ctxt, fi, &pcfile) + oldState.renumberfiles(ctxt, fi, &pcfile) if false { // Sanity check the new numbering it := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) for it.Init(pcfile.P); !it.Done; it.Next() { - if it.Value < 1 || it.Value > int32(len(state.numberedFiles)) { - ctxt.Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(state.numberedFiles)) + if it.Value < 1 || it.Value > int32(len(oldState.numberedFiles)) { + ctxt.Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(oldState.numberedFiles)) errorexit() } } @@ -510,7 +526,7 @@ func (ctxt *Link) pclntab() loader.Bitmap { } if fi.Valid() && fi.NumInlTree() > 0 { - its := state.genInlTreeSym(fi, ctxt.Arch) + its := oldState.genInlTreeSym(fi, ctxt.Arch) funcdata[objabi.FUNCDATA_InlTree] = its pcdata[objabi.PCDATA_InlTreeIndex] = sym.Pcdata{P: fi.Pcinline()} } @@ -567,36 +583,34 @@ func (ctxt *Link) pclntab() loader.Bitmap { nfunc++ } - last := ctxt.Textp[len(ctxt.Textp)-1] - pclntabLastFunc = last // Final entry of table is just end pc. - setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, ldr.SymSize(last)) + setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), state.lastFunc, ldr.SymSize(state.lastFunc)) // Start file table. dSize := len(ftab.Data()) start := int32(dSize) start += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1) - pclntabFiletabOffset = start + state.filetabOffset = start ftab.SetUint32(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start)) - nf := len(state.numberedFiles) + nf := len(oldState.numberedFiles) ftab.Grow(int64(start) + int64((nf+1)*4)) ftab.SetUint32(ctxt.Arch, int64(start), uint32(nf+1)) for i := nf; i > 0; i-- { - path := state.filepaths[i] + path := oldState.filepaths[i] val := int64(i) - ftab.SetUint32(ctxt.Arch, int64(start)+val*4, uint32(state.ftabaddstring(ftab, path))) + ftab.SetUint32(ctxt.Arch, int64(start)+val*4, uint32(ftabaddstring(ftab, path))) } ftab.SetSize(int64(len(ftab.Data()))) - ctxt.NumFilesyms = len(state.numberedFiles) + ctxt.NumFilesyms = len(oldState.numberedFiles) if ctxt.Debugvlog != 0 { ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size(), funcdataBytes) } - return state.container + return state } func gorootFinal() string { @@ -624,9 +638,7 @@ const ( // findfunctab generates a lookup table to quickly find the containing // function for a pc. See src/runtime/symtab.go:findfunc for details. -// 'container' is a bitmap indexed by global symbol holding whether -// a given text symbols is a container (outer sym). -func (ctxt *Link) findfunctab(container loader.Bitmap) { +func (ctxt *Link) findfunctab(state *pclntab, container loader.Bitmap) { ldr := ctxt.loader // find min and max address @@ -705,7 +717,22 @@ func (ctxt *Link) findfunctab(container loader.Bitmap) { } } - s := ctxt.createGeneratorSymbol("runtime.findfunctab", 0, sym.SRODATA, size, writeFindFuncTab) - ldr.SetAttrReachable(s, true) - ldr.SetAttrLocal(s, true) + state.findfunctab = ctxt.createGeneratorSymbol("runtime.findfunctab", 0, sym.SRODATA, size, writeFindFuncTab) + ldr.SetAttrReachable(state.findfunctab, true) + ldr.SetAttrLocal(state.findfunctab, true) +} + +// findContainerSyms returns a bitmap, indexed by symbol number, where there's +// a 1 for every container symbol. +func (ctxt *Link) findContainerSyms() loader.Bitmap { + ldr := ctxt.loader + container := loader.MakeBitmap(ldr.NSym()) + // Find container symbols and mark them as such. + for _, s := range ctxt.Textp { + outer := ldr.OuterSym(s) + if outer != 0 { + container.Set(outer) + } + } + return container } diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 15fa162c60..bf8ead3d0c 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -401,7 +401,7 @@ func textsectionmap(ctxt *Link) (loader.Sym, uint32) { return t.Sym(), uint32(n) } -func (ctxt *Link) symtab() []sym.SymKind { +func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind { ldr := ctxt.loader if !ctxt.IsAIX() { @@ -608,25 +608,24 @@ func (ctxt *Link) symtab() []sym.SymKind { // This code uses several global variables that are set by pcln.go:pclntab. moduledata := ldr.MakeSymbolUpdater(ctxt.Moduledata) // The pcHeader - moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.pcheader", 0)) + moduledata.AddAddr(ctxt.Arch, pcln.pcheader) // The pclntab slice - pclntab := ldr.Lookup("runtime.pclntab_old", 0) - moduledata.AddAddr(ctxt.Arch, pclntab) - moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pclntab))) - moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pclntab))) + moduledata.AddAddr(ctxt.Arch, pcln.pclntab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab))) // The ftab slice - moduledata.AddAddr(ctxt.Arch, pclntab) - moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1)) - moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1)) + moduledata.AddAddr(ctxt.Arch, pcln.pclntab) + moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1)) + moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1)) // The filetab slice - moduledata.AddAddrPlus(ctxt.Arch, pclntab, int64(pclntabFiletabOffset)) + moduledata.AddAddrPlus(ctxt.Arch, pcln.pclntab, int64(pcln.filetabOffset)) moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1) moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1) // findfunctab - moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.findfunctab", 0)) + moduledata.AddAddr(ctxt.Arch, pcln.findfunctab) // minpc, maxpc - moduledata.AddAddr(ctxt.Arch, pclntabFirstFunc) - moduledata.AddAddrPlus(ctxt.Arch, pclntabLastFunc, ldr.SymSize(pclntabLastFunc)) + moduledata.AddAddr(ctxt.Arch, pcln.firstFunc) + moduledata.AddAddrPlus(ctxt.Arch, pcln.lastFunc, ldr.SymSize(pcln.lastFunc)) // pointers to specific parts of the module moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.text", 0)) moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.etext", 0))