From: Than McIntosh Date: Tue, 7 Apr 2020 21:08:00 +0000 (-0400) Subject: [dev.link] cmd/link: convert pcln linker phase to use loader APIs X-Git-Tag: go1.15beta1~556^2^2~17 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=db48c404cf8dc955a07d3a110b8e7099055075b3;p=gostls13.git [dev.link] cmd/link: convert pcln linker phase to use loader APIs Rework the linker's pcln phase to work with the new loader. As part of this set of changes the handling of "go.file..." symbols has been revised somewhat -- previously they were treated as always live in the loader, and now we no longer do this. The original plan had been to have the new implementation generate nameless "inltree" symbols, however the plan now is to keep them named for now and convert them to nameless in a subsequent patch. Change-Id: If71c93ff1f146dbb63b6ee2546308acdc94b643c Reviewed-on: https://go-review.googlesource.com/c/go/+/227759 Run-TryBot: Than McIntosh TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang Reviewed-by: Jeremy Faller --- diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index e560fea2f1..ebabc20378 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -2808,6 +2808,10 @@ func (ctxt *Link) loadlibfull() { // Set special global symbols. ctxt.setArchSyms(AfterLoadlibFull) + + // Convert special symbols created by pcln. + pclntabFirstFunc = ctxt.loader.Syms[pclntabFirstFunc2] + pclntabLastFunc = ctxt.loader.Syms[pclntabLastFunc2] } func (ctxt *Link) dumpsyms() { diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index a2ea34ea22..8695a3a904 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -73,7 +73,7 @@ type Link struct { Shlibs []Shlib Textp []*sym.Symbol Textp2 []loader.Sym - Filesyms []*sym.Symbol + NumFilesyms int Moduledata *sym.Symbol Moduledata2 loader.Sym diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index 0f36cd0cbe..4429b74d1e 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -890,7 +890,7 @@ func machosymtab(ctxt *Link) { // symbols like crosscall2 are in pclntab and end up // pointing at the host binary, breaking unwinding. // See Issue #18190. - cexport := !isGoSymbol && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(s)) + cexport := !isGoSymbol && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(s.Name)) if cexport || export || isGoSymbol { symstr.AddUint8('_') } diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 1985f41ff1..48f89c96d4 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -294,10 +294,10 @@ func Main(arch *sys.Arch, theArch Arch) { ctxt.typelink() bench.Start("buildinfo") ctxt.buildinfo() - bench.Start("loadlibfull") - ctxt.loadlibfull() // XXX do it here for now bench.Start("pclntab") ctxt.pclntab() + bench.Start("loadlibfull") + ctxt.loadlibfull() // XXX do it here for now bench.Start("findfunctab") ctxt.findfunctab() bench.Start("symtab") diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go index 3e8135c959..d6bba56eee 100644 --- a/src/cmd/link/internal/ld/pcln.go +++ b/src/cmd/link/internal/ld/pcln.go @@ -9,6 +9,7 @@ import ( "cmd/internal/objabi" "cmd/internal/src" "cmd/internal/sys" + "cmd/link/internal/loader" "cmd/link/internal/sym" "encoding/binary" "fmt" @@ -18,28 +19,71 @@ import ( "strings" ) -func ftabaddstring(ftab *sym.Symbol, s string) int32 { - start := len(ftab.P) +// 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 { + 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 { + ldr := ctxt.loader + drs := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal) + return pclnState{ + container: loader.MakeBitmap(ldr.NSym()), + ldr: ldr, + deferReturnSym: drs, + numberedFiles: make(map[loader.Sym]int64), + // NB: initial entry in filepaths below is to reserve the zero value, + // so that when we do a map lookup in numberedFiles fails, it will not + // return a value slot in filepaths. + filepaths: []string{""}, + } +} + +func (state *pclnState) 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 - copy(ftab.P[start:], s) + ftd := ftab.Data() + copy(ftd[start:], s) return int32(start) } // numberfile assigns a file number to the file if it hasn't been assigned already. -func numberfile(ctxt *Link, file *sym.Symbol) { - if file.Type != sym.SFILEPATH { - ctxt.Filesyms = append(ctxt.Filesyms, file) - file.Value = int64(len(ctxt.Filesyms)) - file.Type = sym.SFILEPATH - path := file.Name[len(src.FileSymPrefix):] - file.Name = expandGoroot(path) +func (state *pclnState) numberfile(file loader.Sym) int64 { + if val, ok := state.numberedFiles[file]; ok { + return val } + sn := state.ldr.SymName(file) + path := sn[len(src.FileSymPrefix):] + val := int64(len(state.filepaths)) + state.numberedFiles[file] = val + state.filepaths = append(state.filepaths, expandGoroot(path)) + return val } -func renumberfiles(ctxt *Link, files []*sym.Symbol, d *sym.Pcdata) { +func (state *pclnState) 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) { // Give files numbers. - for _, f := range files { - numberfile(ctxt, f) + nf := fi.NumFile() + for i := uint32(0); i < nf; i++ { + state.numberfile(fi.File(int(i))) } buf := make([]byte, binary.MaxVarintLen32) @@ -54,10 +98,10 @@ func renumberfiles(ctxt *Link, files []*sym.Symbol, d *sym.Pcdata) { if oldval == -1 { val = -1 } else { - if oldval < 0 || oldval >= int32(len(files)) { + if oldval < 0 || oldval >= int32(nf) { log.Fatalf("bad pcdata %d", oldval) } - val = int32(files[oldval].Value) + val = int32(state.fileVal(fi.File(int(oldval)))) } dv := val - newval @@ -80,23 +124,33 @@ func renumberfiles(ctxt *Link, files []*sym.Symbol, d *sym.Pcdata) { *d = out } -// onlycsymbol reports whether this is a symbol that is referenced by C code. -func onlycsymbol(s *sym.Symbol) bool { - switch s.Name { +// onlycsymbol looks at a symbol's name to report whether this is a +// symbol that is referenced by C code +func onlycsymbol(sname string) bool { + switch sname { case "_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2": return true } - if strings.HasPrefix(s.Name, "_cgoexp_") { + if strings.HasPrefix(sname, "_cgoexp_") { return true } return false } +func (state *pclnState) emitPcln(ctxt *Link, s loader.Sym) bool { + if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(state.ldr.SymName(s)) { + return false + } + // We want to generate func table entries only for the "lowest + // level" symbols, not containers of subsymbols. + return !state.container.Has(s) +} + func emitPcln(ctxt *Link, s *sym.Symbol) bool { if s == nil { return true } - if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(s) { + if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(s.Name) { return false } // We want to generate func table entries only for the "lowest level" symbols, @@ -104,23 +158,108 @@ func emitPcln(ctxt *Link, s *sym.Symbol) bool { return !s.Attr.Container() } +func (state *pclnState) computeDeferReturn(target *Target, s loader.Sym) uint32 { + deferreturn := uint32(0) + lastWasmAddr := uint32(0) + + relocs := state.ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At2(ri) + if target.IsWasm() && r.Type() == objabi.R_ADDR { + // Wasm does not have a live variable set at the deferreturn + // call itself. Instead it has one identified by the + // resumption point immediately preceding the deferreturn. + // The wasm code has a R_ADDR relocation which is used to + // set the resumption point to PC_B. + lastWasmAddr = uint32(r.Add()) + } + if r.Type().IsDirectCall() && r.Sym() == state.deferReturnSym { + if target.IsWasm() { + deferreturn = lastWasmAddr - 1 + } else { + // Note: the relocation target is in the call instruction, but + // is not necessarily the whole instruction (for instance, on + // x86 the relocation applies to bytes [1:5] of the 5 byte call + // instruction). + deferreturn = uint32(r.Off()) + switch target.Arch.Family { + case sys.AMD64, sys.I386: + deferreturn-- + case sys.PPC64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64: + // no change + case sys.RISCV64: + // TODO(jsing): The JALR instruction is marked with + // R_CALLRISCV, whereas the actual reloc is currently + // one instruction earlier starting with the AUIPC. + deferreturn -= 4 + case sys.S390X: + deferreturn -= 2 + default: + panic(fmt.Sprint("Unhandled architecture:", target.Arch.Family)) + } + } + break // only need one + } + } + return deferreturn +} + +// genInlTreeSym generates the InlTree sym for the a given function symbol +// with name 'sn'. +func (state *pclnState) genInlTreeSym(sn string, fi loader.FuncInfo, arch *sys.Arch) loader.Sym { + itsName := "inltree." + sn + ldr := state.ldr + if ldr.Lookup(itsName, 0) != 0 { + panic("should not exist yet") + } + its := ldr.LookupOrCreateSym(itsName, 0) + inlTreeSym := ldr.MakeSymbolUpdater(its) + inlTreeSym.SetType(sym.SRODATA) + ldr.SetAttrReachable(its, true) + ninl := fi.NumInlTree() + for i := 0; i < int(ninl); i++ { + call := fi.InlTree(i) + // Usually, call.File is already numbered since the file + // shows up in the Pcfile table. However, two inlined calls + // might overlap exactly so that only the innermost file + // appears in the Pcfile table. In that case, this assigns + // the outer file a number. + val := state.numberfile(call.File) + fn := ldr.SymName(call.Func) + nameoff := state.nameToOffset(fn) + + inlTreeSym.SetUint16(arch, int64(i*20+0), uint16(call.Parent)) + inlTreeSym.SetUint8(arch, int64(i*20+2), uint8(objabi.GetFuncID(fn, ""))) + // byte 3 is unused + inlTreeSym.SetUint32(arch, int64(i*20+4), uint32(val)) + inlTreeSym.SetUint32(arch, int64(i*20+8), uint32(call.Line)) + inlTreeSym.SetUint32(arch, int64(i*20+12), uint32(nameoff)) + inlTreeSym.SetUint32(arch, int64(i*20+16), uint32(call.ParentPC)) + } + return its +} + // pclntab initializes the pclntab symbol with // runtime function and file name information. -var pclntabZpcln sym.FuncInfo - // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab. var pclntabNfunc int32 var pclntabFiletabOffset int32 var pclntabPclntabOffset int32 var pclntabFirstFunc *sym.Symbol var pclntabLastFunc *sym.Symbol +var pclntabFirstFunc2 loader.Sym +var pclntabLastFunc2 loader.Sym func (ctxt *Link) pclntab() { funcdataBytes := int64(0) - ftab := ctxt.Syms.Lookup("runtime.pclntab", 0) - ftab.Type = sym.SPCLNTAB - ftab.Attr |= sym.AttrReachable + ldr := ctxt.loader + ftabsym := ldr.LookupOrCreateSym("runtime.pclntab", 0) + ftab := ldr.MakeSymbolUpdater(ftabsym) + ftab.SetType(sym.SPCLNTAB) + ldr.SetAttrReachable(ftabsym, true) + + state := makepclnState(ctxt) // See golang.org/s/go12symtab for the format. Briefly: // 8-byte header @@ -130,30 +269,33 @@ func (ctxt *Link) pclntab() { // offset to file table [4 bytes] // Find container symbols and mark them as such. - for _, s := range ctxt.Textp { - if s.Outer != nil { - s.Outer.Attr |= sym.AttrContainer + for _, s := range ctxt.Textp2 { + outer := ldr.OuterSym(s) + if outer != 0 { + state.container.Set(outer) } } // Gather some basic stats and info. var nfunc int32 - prevSect := ctxt.Textp[0].Sect - for _, s := range ctxt.Textp { - if !emitPcln(ctxt, s) { + prevSect := ldr.SymSect(ctxt.Textp2[0]) + for _, s := range ctxt.Textp2 { + if !state.emitPcln(ctxt, s) { continue } nfunc++ - if pclntabFirstFunc == nil { - pclntabFirstFunc = s + if pclntabFirstFunc2 == 0 { + pclntabFirstFunc2 = s } - if s.Sect != 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. + 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 = s.Sect + prevSect = ss } } @@ -165,22 +307,24 @@ func (ctxt *Link) pclntab() { ftab.SetUint(ctxt.Arch, 8, uint64(nfunc)) pclntabPclntabOffset = int32(8 + ctxt.Arch.PtrSize) - funcnameoff := make(map[string]int32) + szHint := len(ctxt.Textp2) * 2 + funcnameoff := make(map[string]int32, szHint) nameToOffset := func(name string) int32 { nameoff, ok := funcnameoff[name] if !ok { - nameoff = ftabaddstring(ftab, name) + nameoff = state.ftabaddstring(ftab, name) funcnameoff[name] = nameoff } return nameoff } + state.nameToOffset = nameToOffset - pctaboff := make(map[string]uint32) + pctaboff := make(map[string]uint32, szHint) writepctab := func(off int32, p []byte) int32 { start, ok := pctaboff[string(p)] if !ok { if len(p) > 0 { - start = uint32(len(ftab.P)) + start = uint32(len(ftab.Data())) ftab.AddBytes(p) } pctaboff[string(p)] = start @@ -189,52 +333,82 @@ func (ctxt *Link) pclntab() { return newoff } + pcsp := sym.Pcdata{} + pcfile := sym.Pcdata{} + pcline := sym.Pcdata{} + pcdata := []sym.Pcdata{} + funcdata := []loader.Sym{} + funcdataoff := []int64{} + nfunc = 0 // repurpose nfunc as a running index - prevFunc := ctxt.Textp[0] - for _, s := range ctxt.Textp { - if !emitPcln(ctxt, s) { + prevFunc := ctxt.Textp2[0] + for _, s := range ctxt.Textp2 { + if !state.emitPcln(ctxt, s) { continue } - if s.Sect != prevFunc.Sect { - // With multiple text sections, there may be a hole here in the address - // space (see the comment above). We use an invalid funcoff value to - // mark the hole. - // See also runtime/symtab.go:findfunc - ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFunc.Size) + thisSect := ldr.SymSect(s) + prevSect := ldr.SymSect(prevFunc) + if thisSect != prevSect { + // With multiple text sections, there may be a hole here + // in the address space (see the comment above). We use an + // invalid funcoff value to mark the hole. See also + // runtime/symtab.go:findfunc + prevFuncSize := int64(ldr.SymSize(prevFunc)) + ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFuncSize) ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), ^uint64(0)) nfunc++ } prevFunc = s - pcln := s.FuncInfo - if pcln == nil { - pcln = &pclntabZpcln + pcsp.P = pcsp.P[:0] + pcline.P = pcline.P[:0] + pcfile.P = pcfile.P[:0] + pcdata = pcdata[:0] + funcdataoff = funcdataoff[:0] + funcdata = funcdata[:0] + fi := ldr.FuncInfo(s) + if fi.Valid() { + fi.Preload() + npc := fi.NumPcdata() + for i := uint32(0); i < npc; i++ { + pcdata = append(pcdata, sym.Pcdata{P: fi.Pcdata(int(i))}) + } + nfd := fi.NumFuncdataoff() + for i := uint32(0); i < nfd; i++ { + funcdataoff = append(funcdataoff, fi.Funcdataoff(int(i))) + } + funcdata = fi.Funcdata(s, funcdata) } - if len(pcln.InlTree) > 0 { - if len(pcln.Pcdata) <= objabi.PCDATA_InlTreeIndex { + if fi.Valid() && fi.NumInlTree() > 0 { + + if len(pcdata) <= objabi.PCDATA_InlTreeIndex { // Create inlining pcdata table. - pcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1) - copy(pcdata, pcln.Pcdata) - pcln.Pcdata = pcdata + newpcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1) + copy(newpcdata, pcdata) + pcdata = newpcdata } - if len(pcln.Funcdataoff) <= objabi.FUNCDATA_InlTree { + if len(funcdataoff) <= objabi.FUNCDATA_InlTree { // Create inline tree funcdata. - funcdata := make([]*sym.Symbol, objabi.FUNCDATA_InlTree+1) - funcdataoff := make([]int64, objabi.FUNCDATA_InlTree+1) - copy(funcdata, pcln.Funcdata) - copy(funcdataoff, pcln.Funcdataoff) - pcln.Funcdata = funcdata - pcln.Funcdataoff = funcdataoff + newfuncdata := make([]loader.Sym, objabi.FUNCDATA_InlTree+1) + newfuncdataoff := make([]int64, objabi.FUNCDATA_InlTree+1) + copy(newfuncdata, funcdata) + copy(newfuncdataoff, funcdataoff) + funcdata = newfuncdata + funcdataoff = newfuncdataoff } } - funcstart := int32(len(ftab.P)) - funcstart += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1) // align to ptrsize + dSize := len(ftab.Data()) + funcstart := int32(dSize) + funcstart += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1) // align to ptrsize - ftab.SetAddr(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s) + // NB: for the static binary internal-link case, we could just + // emit the symbol value instead of creating a relocation here + // (might speed things up for that case). + ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s, 0) ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart)) // Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func @@ -243,122 +417,68 @@ func (ctxt *Link) pclntab() { // fixed size of struct, checked below off := funcstart - end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(ctxt.Arch.PtrSize) - if len(pcln.Funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) { + end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcdata))*4 + int32(len(funcdata))*int32(ctxt.Arch.PtrSize) + if len(funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) { end += 4 } ftab.Grow(int64(end)) // entry uintptr - off = int32(ftab.SetAddr(ctxt.Arch, int64(off), s)) + off = int32(ftab.SetAddrPlus(ctxt.Arch, int64(off), s, 0)) // name int32 - nameoff := nameToOffset(s.Name) + sn := ldr.SymName(s) + nameoff := nameToOffset(sn) off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(nameoff))) // args int32 // TODO: Move into funcinfo. args := uint32(0) - if s.FuncInfo != nil { - args = uint32(s.FuncInfo.Args) + if fi.Valid() { + args = uint32(fi.Args()) } off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args)) // deferreturn - deferreturn := uint32(0) - lastWasmAddr := uint32(0) - for _, r := range s.R { - if ctxt.Arch.Family == sys.Wasm && r.Type == objabi.R_ADDR { - // Wasm does not have a live variable set at the deferreturn - // call itself. Instead it has one identified by the - // resumption point immediately preceding the deferreturn. - // The wasm code has a R_ADDR relocation which is used to - // set the resumption point to PC_B. - lastWasmAddr = uint32(r.Add) - } - if r.Type.IsDirectCall() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" { - if ctxt.Arch.Family == sys.Wasm { - deferreturn = lastWasmAddr - 1 - } else { - // Note: the relocation target is in the call instruction, but - // is not necessarily the whole instruction (for instance, on - // x86 the relocation applies to bytes [1:5] of the 5 byte call - // instruction). - deferreturn = uint32(r.Off) - switch ctxt.Arch.Family { - case sys.AMD64, sys.I386: - deferreturn-- - case sys.PPC64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64: - // no change - case sys.RISCV64: - // TODO(jsing): The JALR instruction is marked with - // R_CALLRISCV, whereas the actual reloc is currently - // one instruction earlier starting with the AUIPC. - deferreturn -= 4 - case sys.S390X: - deferreturn -= 2 - default: - panic(fmt.Sprint("Unhandled architecture:", ctxt.Arch.Family)) - } - } - break // only need one - } - } + deferreturn := state.computeDeferReturn(&ctxt.Target, s) off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn)) - if pcln != &pclntabZpcln { - renumberfiles(ctxt, pcln.File, &pcln.Pcfile) + 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) if false { // Sanity check the new numbering it := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) - for it.Init(pcln.Pcfile.P); !it.Done; it.Next() { - if it.Value < 1 || it.Value > int32(len(ctxt.Filesyms)) { - Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(ctxt.Filesyms)) + 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)) errorexit() } } } } - if len(pcln.InlTree) > 0 { - inlTreeSym := ctxt.Syms.Lookup("inltree."+s.Name, 0) - inlTreeSym.Type = sym.SRODATA - inlTreeSym.Attr |= sym.AttrReachable | sym.AttrDuplicateOK - - for i, call := range pcln.InlTree { - // Usually, call.File is already numbered since the file - // shows up in the Pcfile table. However, two inlined calls - // might overlap exactly so that only the innermost file - // appears in the Pcfile table. In that case, this assigns - // the outer file a number. - numberfile(ctxt, call.File) - nameoff := nameToOffset(call.Func) - - inlTreeSym.SetUint16(ctxt.Arch, int64(i*20+0), uint16(call.Parent)) - inlTreeSym.SetUint8(ctxt.Arch, int64(i*20+2), uint8(objabi.GetFuncID(call.Func, ""))) - // byte 3 is unused - inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+4), uint32(call.File.Value)) - inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+8), uint32(call.Line)) - inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+12), uint32(nameoff)) - inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+16), uint32(call.ParentPC)) - } - - pcln.Funcdata[objabi.FUNCDATA_InlTree] = inlTreeSym - pcln.Pcdata[objabi.PCDATA_InlTreeIndex] = pcln.Pcinline + if fi.Valid() && fi.NumInlTree() > 0 { + its := state.genInlTreeSym(sn, fi, ctxt.Arch) + funcdata[objabi.FUNCDATA_InlTree] = its + pcdata[objabi.PCDATA_InlTreeIndex] = sym.Pcdata{P: fi.Pcinline()} } // pcdata - off = writepctab(off, pcln.Pcsp.P) - off = writepctab(off, pcln.Pcfile.P) - off = writepctab(off, pcln.Pcline.P) - off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcln.Pcdata)))) + off = writepctab(off, pcsp.P) + off = writepctab(off, pcfile.P) + off = writepctab(off, pcline.P) + off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcdata)))) // funcID uint8 var file string - if s.FuncInfo != nil && len(s.FuncInfo.File) > 0 { - file = s.FuncInfo.File[0].Name + if fi.Valid() && fi.NumFile() > 0 { + filesymname := ldr.SymName(fi.File(0)) + file = filesymname[len(src.FileSymPrefix):] } - funcID := objabi.GetFuncID(s.Name, file) + funcID := objabi.GetFuncID(sn, file) off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID))) @@ -366,61 +486,65 @@ func (ctxt *Link) pclntab() { off += 2 // nfuncdata must be the final entry. - off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(pcln.Funcdata)))) - for i := range pcln.Pcdata { - off = writepctab(off, pcln.Pcdata[i].P) + off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata)))) + for i := range pcdata { + off = writepctab(off, pcdata[i].P) } // funcdata, must be pointer-aligned and we're only int32-aligned. // Missing funcdata will be 0 (nil pointer). - if len(pcln.Funcdata) > 0 { + if len(funcdata) > 0 { if off&int32(ctxt.Arch.PtrSize-1) != 0 { off += 4 } - for i := range pcln.Funcdata { + for i := range funcdata { dataoff := int64(off) + int64(ctxt.Arch.PtrSize)*int64(i) - if pcln.Funcdata[i] == nil { - ftab.SetUint(ctxt.Arch, dataoff, uint64(pcln.Funcdataoff[i])) + if funcdata[i] == 0 { + ftab.SetUint(ctxt.Arch, dataoff, uint64(funcdataoff[i])) continue } // TODO: Dedup. - funcdataBytes += pcln.Funcdata[i].Size - ftab.SetAddrPlus(ctxt.Arch, dataoff, pcln.Funcdata[i], pcln.Funcdataoff[i]) + funcdataBytes += int64(len(ldr.Data(funcdata[i]))) + ftab.SetAddrPlus(ctxt.Arch, dataoff, funcdata[i], funcdataoff[i]) } - off += int32(len(pcln.Funcdata)) * int32(ctxt.Arch.PtrSize) + off += int32(len(funcdata)) * int32(ctxt.Arch.PtrSize) } if off != end { - 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) + ctxt.Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcdata), len(funcdata), ctxt.Arch.PtrSize) errorexit() } nfunc++ } - last := ctxt.Textp[len(ctxt.Textp)-1] - pclntabLastFunc = last + last := ctxt.Textp2[len(ctxt.Textp2)-1] + pclntabLastFunc2 = last // Final entry of table is just end pc. - ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, last.Size) + ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, ldr.SymSize(last)) // Start file table. - start := int32(len(ftab.P)) - - start += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1) + dSize := len(ftab.Data()) + start := int32(dSize) + start += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1) pclntabFiletabOffset = start ftab.SetUint32(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start)) - ftab.Grow(int64(start) + (int64(len(ctxt.Filesyms))+1)*4) - ftab.SetUint32(ctxt.Arch, int64(start), uint32(len(ctxt.Filesyms)+1)) - for i := len(ctxt.Filesyms) - 1; i >= 0; i-- { - s := ctxt.Filesyms[i] - ftab.SetUint32(ctxt.Arch, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name))) + nf := len(state.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] + val := int64(i) + ftab.SetUint32(ctxt.Arch, int64(start)+val*4, uint32(state.ftabaddstring(ftab, path))) } - ftab.Size = int64(len(ftab.P)) + ftab.SetSize(int64(len(ftab.Data()))) + + ctxt.NumFilesyms = len(state.numberedFiles) if ctxt.Debugvlog != 0 { - ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size, funcdataBytes) + ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size(), funcdataBytes) } } diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 97cbb5616e..290bf5edaa 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -554,8 +554,8 @@ func (ctxt *Link) symtab() { moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1)) // The filetab slice moduledata.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup("runtime.pclntab", 0), int64(pclntabFiletabOffset)) - moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Filesyms))+1) - moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Filesyms))+1) + moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1) + moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1) // findfunctab moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.findfunctab", 0)) // minpc, maxpc diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 390eb1af24..6e814c1e64 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -1861,7 +1861,7 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) { toConvert := make([]Sym, 0, len(l.payloads)) for _, i := range l.extReader.syms { sname := l.RawSymName(i) - if !l.attrReachable.Has(i) && !strings.HasPrefix(sname, "gofile..") { // XXX file symbols are used but not marked + if !l.attrReachable.Has(i) { continue } pp := l.getPayload(i)