@@ -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.
523677func 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
656810func 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}
0 commit comments