Skip to content

Commit b437d5b

Browse files
cmd/link: put funcdata symbols in .gopclntab section
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>
1 parent 4bc3410 commit b437d5b

File tree

3 files changed

+165
-42
lines changed

3 files changed

+165
-42
lines changed

src/cmd/link/internal/ld/data.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2128,6 +2128,7 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
21282128
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.filetab", 0), sect)
21292129
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pctab", 0), sect)
21302130
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.functab", 0), sect)
2131+
ldr.SetSymSect(ldr.LookupOrCreateSym("go:func.*", 0), sect)
21312132
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect)
21322133
setCarrierSize(sym.SPCLNTAB, int64(sect.Length))
21332134
if ctxt.HeadType == objabi.Haix {
@@ -3066,6 +3067,7 @@ func (ctxt *Link) address() []*sym.Segment {
30663067
ctxt.defineInternal("runtime.filetab", sym.SRODATA)
30673068
ctxt.defineInternal("runtime.pctab", sym.SRODATA)
30683069
ctxt.defineInternal("runtime.functab", sym.SRODATA)
3070+
ctxt.defineInternal("go:func.*", sym.SRODATA)
30693071
ctxt.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length))
30703072
ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr))
30713073
ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATAEND, int64(noptr.Vaddr+noptr.Length))

src/cmd/link/internal/ld/pcln.go

Lines changed: 161 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ import (
1010
"cmd/internal/sys"
1111
"cmd/link/internal/loader"
1212
"cmd/link/internal/sym"
13+
"cmp"
1314
"fmt"
1415
"internal/abi"
1516
"internal/buildcfg"
1617
"path/filepath"
18+
"slices"
1719
"strings"
1820
)
1921

@@ -36,6 +38,7 @@ type pclntab struct {
3638
cutab loader.Sym
3739
filetab loader.Sym
3840
pctab loader.Sym
41+
funcdata loader.Sym
3942

4043
// The number of functions + number of TEXT sections - 1. This is such an
4144
// 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
183186
// signal to the symtab() phase that it needs to be grouped in with
184187
// other similar symbols (gcdata, etc); the dodata() phase will
185188
// eventually switch the type back to SRODATA.
186-
inlTreeSym.SetType(sym.SGOFUNC)
189+
inlTreeSym.SetType(sym.SPCLNTAB)
187190
ldr.SetAttrReachable(its, true)
188191
ldr.SetSymAlign(its, 4) // it has 32-bit fields
189192
ninl := fi.NumInlTree()
@@ -518,6 +521,157 @@ func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) {
518521
state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, writePctab)
519522
}
520523

524+
// generateFuncdata writes out the funcdata information.
525+
func (state *pclntab) generateFuncdata(ctxt *Link, funcs []loader.Sym, inlsyms map[loader.Sym]loader.Sym) {
526+
ldr := ctxt.loader
527+
528+
// Walk the functions and collect the funcdata.
529+
seen := make(map[loader.Sym]struct{}, len(funcs))
530+
fdSyms := make([]loader.Sym, 0, len(funcs))
531+
fd := []loader.Sym{}
532+
for _, s := range funcs {
533+
fi := ldr.FuncInfo(s)
534+
if !fi.Valid() {
535+
continue
536+
}
537+
fi.Preload()
538+
fd := funcData(ldr, s, fi, inlsyms[s], fd)
539+
for j, fdSym := range fd {
540+
if ignoreFuncData(ldr, s, j, fdSym) {
541+
continue
542+
}
543+
544+
if _, ok := seen[fdSym]; !ok {
545+
fdSyms = append(fdSyms, fdSym)
546+
seen[fdSym] = struct{}{}
547+
}
548+
}
549+
}
550+
seen = nil
551+
552+
// Sort the funcdata in reverse order by alignment
553+
// to minimize alignment gaps. Use a stable sort
554+
// for reproducible results.
555+
var maxAlign int32
556+
slices.SortStableFunc(fdSyms, func(a, b loader.Sym) int {
557+
aAlign := symalign(ldr, a)
558+
bAlign := symalign(ldr, b)
559+
560+
// Remember maximum alignment.
561+
maxAlign = max(maxAlign, aAlign, bAlign)
562+
563+
// Negate to sort by decreasing alignment.
564+
return -cmp.Compare(aAlign, bAlign)
565+
})
566+
567+
// We will output the symbols in the order of fdSyms.
568+
// Set the value of each symbol to its offset in the funcdata.
569+
// This way when writeFuncs writes out the funcdata offset,
570+
// it can simply write out the symbol value.
571+
572+
// Accumulated size of funcdata info.
573+
size := int64(0)
574+
575+
for _, fdSym := range fdSyms {
576+
datSize := ldr.SymSize(fdSym)
577+
if datSize == 0 {
578+
ctxt.Errorf(fdSym, "zero size funcdata")
579+
continue
580+
}
581+
582+
size = Rnd(size, int64(symalign(ldr, fdSym)))
583+
ldr.SetSymValue(fdSym, size)
584+
size += datSize
585+
586+
// We do not put the funcdata symbols in the symbol table.
587+
ldr.SetAttrNotInSymbolTable(fdSym, true)
588+
589+
// Mark the symbol as special so that it does not get
590+
// adjusted by the section offset.
591+
ldr.SetAttrSpecial(fdSym, true)
592+
}
593+
594+
// Funcdata symbols are permitted to have R_ADDROFF relocations,
595+
// which the linker can fully resolve.
596+
resolveRelocs := func(ldr *loader.Loader, fdSym loader.Sym, data []byte) {
597+
relocs := ldr.Relocs(fdSym)
598+
for i := 0; i < relocs.Count(); i++ {
599+
r := relocs.At(i)
600+
if r.Type() != objabi.R_ADDROFF {
601+
ctxt.Errorf(fdSym, "unsupported reloc %d (%s) for funcdata symbol", r.Type(), sym.RelocName(ctxt.Target.Arch, r.Type()))
602+
return
603+
}
604+
if r.Siz() != 4 {
605+
ctxt.Errorf(fdSym, "unsupported ADDROFF reloc size %d for funcdata symbol", r.Siz())
606+
return
607+
}
608+
rs := r.Sym()
609+
if r.Weak() && !ldr.AttrReachable(rs) {
610+
return
611+
}
612+
sect := ldr.SymSect(rs)
613+
if sect == nil {
614+
ctxt.Errorf(fdSym, "missing section for relocation target %s for funcdata symbol", ldr.SymName(rs))
615+
}
616+
o := ldr.SymValue(rs)
617+
if sect.Name != ".text" {
618+
o -= int64(sect.Vaddr)
619+
} else {
620+
// With multiple .text sections the offset
621+
// is from the start of the first one.
622+
o -= int64(Segtext.Sections[0].Vaddr)
623+
if ctxt.Target.IsWasm() {
624+
if o&(1<<16-1) != 0 {
625+
ctxt.Errorf(fdSym, "textoff relocation does not target function entry for funcdata symbol: %s %#x", ldr.SymName(rs), o)
626+
}
627+
o >>= 16
628+
}
629+
}
630+
o += r.Add()
631+
if o != int64(int32(o)) && o != int64(uint32(o)) {
632+
ctxt.Errorf(fdSym, "ADDROFF relocation out of range for funcdata symbol: %#x", o)
633+
}
634+
ctxt.Target.Arch.ByteOrder.PutUint32(data[r.Off():], uint32(o))
635+
}
636+
}
637+
638+
writeFuncData := func(ctxt *Link, s loader.Sym) {
639+
ldr := ctxt.loader
640+
sb := ldr.MakeSymbolUpdater(s)
641+
for _, fdSym := range fdSyms {
642+
off := ldr.SymValue(fdSym)
643+
fdSymData := ldr.Data(fdSym)
644+
sb.SetBytesAt(off, fdSymData)
645+
// Resolve any R_ADDROFF relocations.
646+
resolveRelocs(ldr, fdSym, sb.Data()[off:off+int64(len(fdSymData))])
647+
}
648+
}
649+
650+
state.funcdata = state.addGeneratedSym(ctxt, "go:func.*", size, writeFuncData)
651+
652+
// Because the funcdata previously was not in pclntab,
653+
// we need to keep the visible symbol so that tools can find it.
654+
ldr.SetAttrNotInSymbolTable(state.funcdata, false)
655+
}
656+
657+
// ignoreFuncData reports whether we should ignore a funcdata symbol.
658+
//
659+
// cmd/internal/obj optimistically populates ArgsPointerMaps and
660+
// ArgInfo for assembly functions, hoping that the compiler will
661+
// emit appropriate symbols from their Go stub declarations. If
662+
// it didn't though, just ignore it.
663+
//
664+
// TODO(cherryyz): Fix arg map generation (see discussion on CL 523335).
665+
func ignoreFuncData(ldr *loader.Loader, s loader.Sym, j int, fdSym loader.Sym) bool {
666+
if fdSym == 0 {
667+
return true
668+
}
669+
if (j == abi.FUNCDATA_ArgsPointerMaps || j == abi.FUNCDATA_ArgInfo) && ldr.IsFromAssembly(s) && ldr.Data(fdSym) == nil {
670+
return true
671+
}
672+
return false
673+
}
674+
521675
// numPCData returns the number of PCData syms for the FuncInfo.
522676
// NB: Preload must be called on valid FuncInfos before calling this function.
523677
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
656810
func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) {
657811
ldr := ctxt.loader
658812
deferReturnSym := ldr.Lookup("runtime.deferreturn", abiInternalVer)
659-
gofunc := ldr.Lookup("go:func.*", 0)
660-
gofuncBase := ldr.SymValue(gofunc)
661813
textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0))
662814
funcdata := []loader.Sym{}
663815
var pcsp, pcfile, pcline, pcinline loader.Sym
@@ -755,25 +907,12 @@ func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSym
755907
dataoff := off + int64(4*j)
756908
fdsym := funcdata[j]
757909

758-
// cmd/internal/obj optimistically populates ArgsPointerMaps and
759-
// ArgInfo for assembly functions, hoping that the compiler will
760-
// emit appropriate symbols from their Go stub declarations. If
761-
// it didn't though, just ignore it.
762-
//
763-
// TODO(cherryyz): Fix arg map generation (see discussion on CL 523335).
764-
if fdsym != 0 && (j == abi.FUNCDATA_ArgsPointerMaps || j == abi.FUNCDATA_ArgInfo) && ldr.IsFromAssembly(s) && ldr.Data(fdsym) == nil {
765-
fdsym = 0
766-
}
767-
768-
if fdsym == 0 {
910+
if ignoreFuncData(ldr, s, j, fdsym) {
769911
sb.SetUint32(ctxt.Arch, dataoff, ^uint32(0)) // ^0 is a sentinel for "no value"
770912
continue
771913
}
772914

773-
if outer := ldr.OuterSym(fdsym); outer != gofunc {
774-
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)))
775-
}
776-
sb.SetUint32(ctxt.Arch, dataoff, uint32(ldr.SymValue(fdsym)-gofuncBase))
915+
sb.SetUint32(ctxt.Arch, dataoff, uint32(ldr.SymValue(fdsym)))
777916
}
778917
}
779918
}
@@ -816,6 +955,9 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
816955
// function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
817956
// end PC [thearch.ptrsize bytes]
818957
// func structures, pcdata offsets, func data.
958+
//
959+
// runtime.funcdata
960+
// []byte of deduplicated funcdata
819961

820962
state, compUnits, funcs := makePclntab(ctxt, container)
821963

@@ -831,6 +973,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
831973
state.generatePctab(ctxt, funcs)
832974
inlSyms := makeInlSyms(ctxt, funcs, nameOffsets)
833975
state.generateFunctab(ctxt, funcs, inlSyms, cuOffsets, nameOffsets)
976+
state.generateFuncdata(ctxt, funcs, inlSyms)
834977

835978
return state
836979
}

src/cmd/link/internal/ld/symtab.go

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -496,13 +496,13 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
496496
}
497497
var (
498498
symgostring = groupSym("go:string.*", sym.SGOSTRING)
499-
symgofunc = groupSym("go:func.*", sym.SGOFUNC)
499+
symgofunc = groupSym("go:funcdesc", sym.SGOFUNC)
500500
symgcbits = groupSym("runtime.gcbits.*", sym.SGCBITS)
501501
)
502502

503503
symgofuncrel := symgofunc
504504
if ctxt.UseRelro() {
505-
symgofuncrel = groupSym("go:funcrel.*", sym.SGOFUNCRELRO)
505+
symgofuncrel = groupSym("go:funcdescrel", sym.SGOFUNCRELRO)
506506
}
507507

508508
// assign specific types so that they sort together.
@@ -548,28 +548,6 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
548548
ldr.SetCarrierSym(s, symgofunc)
549549
}
550550

551-
case strings.HasPrefix(name, "gcargs."),
552-
strings.HasPrefix(name, "gclocals."),
553-
strings.HasPrefix(name, "gclocals·"),
554-
ldr.SymType(s) == sym.SGOFUNC && s != symgofunc, // inltree, see pcln.go
555-
strings.HasSuffix(name, ".opendefer"),
556-
strings.HasSuffix(name, ".arginfo0"),
557-
strings.HasSuffix(name, ".arginfo1"),
558-
strings.HasSuffix(name, ".argliveinfo"),
559-
strings.HasSuffix(name, ".wrapinfo"),
560-
strings.HasSuffix(name, ".args_stackmap"),
561-
strings.HasSuffix(name, ".stkobj"):
562-
ldr.SetAttrNotInSymbolTable(s, true)
563-
symGroupType[s] = sym.SGOFUNC
564-
ldr.SetCarrierSym(s, symgofunc)
565-
if ctxt.Debugvlog != 0 {
566-
align := ldr.SymAlign(s)
567-
liveness += (ldr.SymSize(s) + int64(align) - 1) &^ (int64(align) - 1)
568-
}
569-
570-
// Note: Check for "type:" prefix after checking for .arginfo1 suffix.
571-
// That way symbols like "type:.eq.[2]interface {}.arginfo1" that belong
572-
// in go:func.* end up there.
573551
case strings.HasPrefix(name, "type:"):
574552
if !ctxt.DynlinkingGo() {
575553
ldr.SetAttrNotInSymbolTable(s, true)

0 commit comments

Comments
 (0)