]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: put funcdata symbols in .gopclntab section
authorIan Lance Taylor <iant@golang.org>
Tue, 11 Nov 2025 00:08:48 +0000 (16:08 -0800)
committerIan Lance Taylor <iant@golang.org>
Thu, 27 Nov 2025 04:04:57 +0000 (20:04 -0800)
There is a test for this in CL 721460 later in this series.

For #76038

Change-Id: Icd7a52cbabde5162139dbc4b2c61306c0c748545
Reviewed-on: https://go-review.googlesource.com/c/go/+/719440
Reviewed-by: David Chase <drchase@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/pcln.go
src/cmd/link/internal/ld/symtab.go

index e7e202fc1f638ef7fb748d16c62848cd960e40b0..f25b6c11da9ae71ecdefc260921a1933a5f08fee 100644 (file)
@@ -2128,6 +2128,7 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
        ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.filetab", 0), sect)
        ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pctab", 0), sect)
        ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.functab", 0), sect)
+       ldr.SetSymSect(ldr.LookupOrCreateSym("go:func.*", 0), sect)
        ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect)
        setCarrierSize(sym.SPCLNTAB, int64(sect.Length))
        if ctxt.HeadType == objabi.Haix {
@@ -3066,6 +3067,7 @@ func (ctxt *Link) address() []*sym.Segment {
        ctxt.defineInternal("runtime.filetab", sym.SRODATA)
        ctxt.defineInternal("runtime.pctab", sym.SRODATA)
        ctxt.defineInternal("runtime.functab", sym.SRODATA)
+       ctxt.defineInternal("go:func.*", sym.SRODATA)
        ctxt.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length))
        ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr))
        ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATAEND, int64(noptr.Vaddr+noptr.Length))
index 68af94a405a942ae33d36cda51833c787d3cb034..eef8e8108781c54fab879f919c0460b06be5cc0b 100644 (file)
@@ -10,10 +10,12 @@ import (
        "cmd/internal/sys"
        "cmd/link/internal/loader"
        "cmd/link/internal/sym"
+       "cmp"
        "fmt"
        "internal/abi"
        "internal/buildcfg"
        "path/filepath"
+       "slices"
        "strings"
 )
 
@@ -36,6 +38,7 @@ type pclntab struct {
        cutab       loader.Sym
        filetab     loader.Sym
        pctab       loader.Sym
+       funcdata    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
@@ -183,7 +186,7 @@ func genInlTreeSym(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, arch
        // signal to the symtab() phase that it needs to be grouped in with
        // other similar symbols (gcdata, etc); the dodata() phase will
        // eventually switch the type back to SRODATA.
-       inlTreeSym.SetType(sym.SGOFUNC)
+       inlTreeSym.SetType(sym.SPCLNTAB)
        ldr.SetAttrReachable(its, true)
        ldr.SetSymAlign(its, 4) // it has 32-bit fields
        ninl := fi.NumInlTree()
@@ -518,6 +521,157 @@ func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) {
        state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, writePctab)
 }
 
+// generateFuncdata writes out the funcdata information.
+func (state *pclntab) generateFuncdata(ctxt *Link, funcs []loader.Sym, inlsyms map[loader.Sym]loader.Sym) {
+       ldr := ctxt.loader
+
+       // Walk the functions and collect the funcdata.
+       seen := make(map[loader.Sym]struct{}, len(funcs))
+       fdSyms := make([]loader.Sym, 0, len(funcs))
+       fd := []loader.Sym{}
+       for _, s := range funcs {
+               fi := ldr.FuncInfo(s)
+               if !fi.Valid() {
+                       continue
+               }
+               fi.Preload()
+               fd := funcData(ldr, s, fi, inlsyms[s], fd)
+               for j, fdSym := range fd {
+                       if ignoreFuncData(ldr, s, j, fdSym) {
+                               continue
+                       }
+
+                       if _, ok := seen[fdSym]; !ok {
+                               fdSyms = append(fdSyms, fdSym)
+                               seen[fdSym] = struct{}{}
+                       }
+               }
+       }
+       seen = nil
+
+       // Sort the funcdata in reverse order by alignment
+       // to minimize alignment gaps. Use a stable sort
+       // for reproducible results.
+       var maxAlign int32
+       slices.SortStableFunc(fdSyms, func(a, b loader.Sym) int {
+               aAlign := symalign(ldr, a)
+               bAlign := symalign(ldr, b)
+
+               // Remember maximum alignment.
+               maxAlign = max(maxAlign, aAlign, bAlign)
+
+               // Negate to sort by decreasing alignment.
+               return -cmp.Compare(aAlign, bAlign)
+       })
+
+       // We will output the symbols in the order of fdSyms.
+       // Set the value of each symbol to its offset in the funcdata.
+       // This way when writeFuncs writes out the funcdata offset,
+       // it can simply write out the symbol value.
+
+       // Accumulated size of funcdata info.
+       size := int64(0)
+
+       for _, fdSym := range fdSyms {
+               datSize := ldr.SymSize(fdSym)
+               if datSize == 0 {
+                       ctxt.Errorf(fdSym, "zero size funcdata")
+                       continue
+               }
+
+               size = Rnd(size, int64(symalign(ldr, fdSym)))
+               ldr.SetSymValue(fdSym, size)
+               size += datSize
+
+               // We do not put the funcdata symbols in the symbol table.
+               ldr.SetAttrNotInSymbolTable(fdSym, true)
+
+               // Mark the symbol as special so that it does not get
+               // adjusted by the section offset.
+               ldr.SetAttrSpecial(fdSym, true)
+       }
+
+       // Funcdata symbols are permitted to have R_ADDROFF relocations,
+       // which the linker can fully resolve.
+       resolveRelocs := func(ldr *loader.Loader, fdSym loader.Sym, data []byte) {
+               relocs := ldr.Relocs(fdSym)
+               for i := 0; i < relocs.Count(); i++ {
+                       r := relocs.At(i)
+                       if r.Type() != objabi.R_ADDROFF {
+                               ctxt.Errorf(fdSym, "unsupported reloc %d (%s) for funcdata symbol", r.Type(), sym.RelocName(ctxt.Target.Arch, r.Type()))
+                               return
+                       }
+                       if r.Siz() != 4 {
+                               ctxt.Errorf(fdSym, "unsupported ADDROFF reloc size %d for funcdata symbol", r.Siz())
+                               return
+                       }
+                       rs := r.Sym()
+                       if r.Weak() && !ldr.AttrReachable(rs) {
+                               return
+                       }
+                       sect := ldr.SymSect(rs)
+                       if sect == nil {
+                               ctxt.Errorf(fdSym, "missing section for relocation target %s for funcdata symbol", ldr.SymName(rs))
+                       }
+                       o := ldr.SymValue(rs)
+                       if sect.Name != ".text" {
+                               o -= int64(sect.Vaddr)
+                       } else {
+                               // With multiple .text sections the offset
+                               // is from the start of the first one.
+                               o -= int64(Segtext.Sections[0].Vaddr)
+                               if ctxt.Target.IsWasm() {
+                                       if o&(1<<16-1) != 0 {
+                                               ctxt.Errorf(fdSym, "textoff relocation does not target function entry for funcdata symbol: %s %#x", ldr.SymName(rs), o)
+                                       }
+                                       o >>= 16
+                               }
+                       }
+                       o += r.Add()
+                       if o != int64(int32(o)) && o != int64(uint32(o)) {
+                               ctxt.Errorf(fdSym, "ADDROFF relocation out of range for funcdata symbol: %#x", o)
+                       }
+                       ctxt.Target.Arch.ByteOrder.PutUint32(data[r.Off():], uint32(o))
+               }
+       }
+
+       writeFuncData := func(ctxt *Link, s loader.Sym) {
+               ldr := ctxt.loader
+               sb := ldr.MakeSymbolUpdater(s)
+               for _, fdSym := range fdSyms {
+                       off := ldr.SymValue(fdSym)
+                       fdSymData := ldr.Data(fdSym)
+                       sb.SetBytesAt(off, fdSymData)
+                       // Resolve any R_ADDROFF relocations.
+                       resolveRelocs(ldr, fdSym, sb.Data()[off:off+int64(len(fdSymData))])
+               }
+       }
+
+       state.funcdata = state.addGeneratedSym(ctxt, "go:func.*", size, writeFuncData)
+
+       // Because the funcdata previously was not in pclntab,
+       // we need to keep the visible symbol so that tools can find it.
+       ldr.SetAttrNotInSymbolTable(state.funcdata, false)
+}
+
+// ignoreFuncData reports whether we should ignore a funcdata symbol.
+//
+// cmd/internal/obj optimistically populates ArgsPointerMaps and
+// ArgInfo for assembly functions, hoping that the compiler will
+// emit appropriate symbols from their Go stub declarations. If
+// it didn't though, just ignore it.
+//
+// TODO(cherryyz): Fix arg map generation (see discussion on CL 523335).
+func ignoreFuncData(ldr *loader.Loader, s loader.Sym, j int, fdSym loader.Sym) bool {
+       if fdSym == 0 {
+               return true
+       }
+       if (j == abi.FUNCDATA_ArgsPointerMaps || j == abi.FUNCDATA_ArgInfo) && ldr.IsFromAssembly(s) && ldr.Data(fdSym) == nil {
+               return true
+       }
+       return false
+}
+
 // numPCData returns the number of PCData syms for the FuncInfo.
 // NB: Preload must be called on valid FuncInfos before calling this function.
 func numPCData(ldr *loader.Loader, s loader.Sym, fi loader.FuncInfo) uint32 {
@@ -656,8 +810,6 @@ func writePCToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, sta
 func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) {
        ldr := ctxt.loader
        deferReturnSym := ldr.Lookup("runtime.deferreturn", abiInternalVer)
-       gofunc := ldr.Lookup("go:func.*", 0)
-       gofuncBase := ldr.SymValue(gofunc)
        textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0))
        funcdata := []loader.Sym{}
        var pcsp, pcfile, pcline, pcinline loader.Sym
@@ -755,25 +907,12 @@ func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSym
                        dataoff := off + int64(4*j)
                        fdsym := funcdata[j]
 
-                       // cmd/internal/obj optimistically populates ArgsPointerMaps and
-                       // ArgInfo for assembly functions, hoping that the compiler will
-                       // emit appropriate symbols from their Go stub declarations. If
-                       // it didn't though, just ignore it.
-                       //
-                       // TODO(cherryyz): Fix arg map generation (see discussion on CL 523335).
-                       if fdsym != 0 && (j == abi.FUNCDATA_ArgsPointerMaps || j == abi.FUNCDATA_ArgInfo) && ldr.IsFromAssembly(s) && ldr.Data(fdsym) == nil {
-                               fdsym = 0
-                       }
-
-                       if fdsym == 0 {
+                       if ignoreFuncData(ldr, s, j, fdsym) {
                                sb.SetUint32(ctxt.Arch, dataoff, ^uint32(0)) // ^0 is a sentinel for "no value"
                                continue
                        }
 
-                       if outer := ldr.OuterSym(fdsym); outer != gofunc {
-                               panic(fmt.Sprintf("bad carrier sym for symbol %s (funcdata %s#%d), want go:func.* got %s", ldr.SymName(fdsym), ldr.SymName(s), j, ldr.SymName(outer)))
-                       }
-                       sb.SetUint32(ctxt.Arch, dataoff, uint32(ldr.SymValue(fdsym)-gofuncBase))
+                       sb.SetUint32(ctxt.Arch, dataoff, uint32(ldr.SymValue(fdsym)))
                }
        }
 }
@@ -816,6 +955,9 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
        //        function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
        //        end PC [thearch.ptrsize bytes]
        //        func structures, pcdata offsets, func data.
+       //
+       //      runtime.funcdata
+       //        []byte of deduplicated funcdata
 
        state, compUnits, funcs := makePclntab(ctxt, container)
 
@@ -831,6 +973,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
        state.generatePctab(ctxt, funcs)
        inlSyms := makeInlSyms(ctxt, funcs, nameOffsets)
        state.generateFunctab(ctxt, funcs, inlSyms, cuOffsets, nameOffsets)
+       state.generateFuncdata(ctxt, funcs, inlSyms)
 
        return state
 }
index eb2a302c05e4b294fe0ea04c9059b1de835f0ff6..f9bc7007eda0bbb730423bb6a0ce9e1dd70ef366 100644 (file)
@@ -496,13 +496,13 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
        }
        var (
                symgostring = groupSym("go:string.*", sym.SGOSTRING)
-               symgofunc   = groupSym("go:func.*", sym.SGOFUNC)
+               symgofunc   = groupSym("go:funcdesc", sym.SGOFUNC)
                symgcbits   = groupSym("runtime.gcbits.*", sym.SGCBITS)
        )
 
        symgofuncrel := symgofunc
        if ctxt.UseRelro() {
-               symgofuncrel = groupSym("go:funcrel.*", sym.SGOFUNCRELRO)
+               symgofuncrel = groupSym("go:funcdescrel", sym.SGOFUNCRELRO)
        }
 
        // assign specific types so that they sort together.
@@ -548,28 +548,6 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
                                ldr.SetCarrierSym(s, symgofunc)
                        }
 
-               case strings.HasPrefix(name, "gcargs."),
-                       strings.HasPrefix(name, "gclocals."),
-                       strings.HasPrefix(name, "gclocals·"),
-                       ldr.SymType(s) == sym.SGOFUNC && s != symgofunc, // inltree, see pcln.go
-                       strings.HasSuffix(name, ".opendefer"),
-                       strings.HasSuffix(name, ".arginfo0"),
-                       strings.HasSuffix(name, ".arginfo1"),
-                       strings.HasSuffix(name, ".argliveinfo"),
-                       strings.HasSuffix(name, ".wrapinfo"),
-                       strings.HasSuffix(name, ".args_stackmap"),
-                       strings.HasSuffix(name, ".stkobj"):
-                       ldr.SetAttrNotInSymbolTable(s, true)
-                       symGroupType[s] = sym.SGOFUNC
-                       ldr.SetCarrierSym(s, symgofunc)
-                       if ctxt.Debugvlog != 0 {
-                               align := ldr.SymAlign(s)
-                               liveness += (ldr.SymSize(s) + int64(align) - 1) &^ (int64(align) - 1)
-                       }
-
-               // Note: Check for "type:" prefix after checking for .arginfo1 suffix.
-               // That way symbols like "type:.eq.[2]interface {}.arginfo1" that belong
-               // in go:func.* end up there.
                case strings.HasPrefix(name, "type:"):
                        if !ctxt.DynlinkingGo() {
                                ldr.SetAttrNotInSymbolTable(s, true)