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)