]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: put type descriptors in .go.type section
authorIan Lance Taylor <iant@golang.org>
Mon, 24 Nov 2025 04:47:50 +0000 (20:47 -0800)
committerGopher Robot <gobot@golang.org>
Fri, 23 Jan 2026 06:14:33 +0000 (22:14 -0800)
This change rewrites and simplifies the relro handling.
We eliminate the separate relro SymKind values and the complex
shifting of symbol kinds. Instead, we put the possible relro data
into their own sections, and make those sections relro when appropriate.

We put type descriptors and their associated data into a
new .go.type section. As part of this we change the runtime.etypes
symbol to be the end of the new section, rather than the end of
rodata as it was before.

We put function descriptors into a new .go.func section.

Ordinary rodata relro stays in the .data.rel.ro section.

We stop making the typelink section relro, as it only contains
offsets and never has dynamic relocations.

We drop the typerel:* and go:funcdescrel symbols.

For #76038

Change-Id: I7aab7cfad3f2623ff06c09a70b756fe1e43f4169
Reviewed-on: https://go-review.googlesource.com/c/go/+/723580
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
Auto-Submit: Ian Lance Taylor <iant@golang.org>

src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/macho_test.go
src/cmd/link/internal/ld/symtab.go
src/cmd/link/internal/ld/xcoff.go
src/cmd/link/internal/sym/symkind.go
src/cmd/link/internal/sym/symkind_string.go
src/cmd/link/internal/wasm/asm.go
src/cmd/link/link_test.go

index 5b6dabb62b58cb6dff26d158ad7b7c5d9198aa22..baf7dafce572f86caecfcc1305768d8807cbbd8e 100644 (file)
@@ -1536,7 +1536,7 @@ func fixZeroSizedSymbols(ctxt *Link) {
        ldr.SetAttrSpecial(types.Sym(), false)
 
        etypes := ldr.CreateSymForUpdate("runtime.etypes", 0)
-       etypes.SetType(sym.SFUNCTAB)
+       etypes.SetType(sym.STYPE)
        ldr.SetAttrSpecial(etypes.Sym(), false)
 
        if ctxt.HeadType == objabi.Haix {
@@ -1558,67 +1558,40 @@ func (state *dodataState) makeRelroForSharedLib(target *Link) {
 
        // "read only" data with relocations needs to go in its own section
        // when building a shared library. We do this by boosting objects of
-       // type SXXX with relocations to type SXXXRELRO.
+       // type SRODATA with relocations to type SRODATARELRO.
        ldr := target.loader
-       for _, symnro := range sym.ReadOnly {
-               symnrelro := sym.RelROMap[symnro]
-
-               ro := []loader.Sym{}
-               relro := state.data[symnrelro]
-
-               for _, s := range state.data[symnro] {
-                       relocs := ldr.Relocs(s)
-                       isRelro := relocs.Count() > 0
-                       switch state.symType(s) {
-                       case sym.STYPE, sym.STYPERELRO, sym.SGOFUNCRELRO:
-                               // Symbols are not sorted yet, so it is possible
-                               // that an Outer symbol has been changed to a
-                               // relro Type before it reaches here.
-                               isRelro = true
-                       case sym.SFUNCTAB:
-                               if ldr.SymName(s) == "runtime.etypes" {
-                                       // runtime.etypes must be at the end of
-                                       // the relro data.
-                                       isRelro = true
-                               }
-                       case sym.SGOFUNC, sym.SPCLNTAB:
-                               // The only SGOFUNC symbols that contain relocations are .stkobj,
-                               // and their relocations are of type objabi.R_ADDROFF,
-                               // which always get resolved during linking.
-                               isRelro = false
-                       }
-                       if isRelro {
-                               if symnrelro == sym.Sxxx {
-                                       state.ctxt.Errorf(s, "cannot contain relocations (type %v)", symnro)
-                               }
-                               state.setSymType(s, symnrelro)
-                               if outer := ldr.OuterSym(s); outer != 0 {
-                                       state.setSymType(outer, symnrelro)
-                               }
-                               relro = append(relro, s)
-                       } else {
-                               ro = append(ro, s)
+       ro := []loader.Sym{}
+       relro := state.data[sym.SRODATARELRO]
+       for _, s := range state.data[sym.SRODATA] {
+               relocs := ldr.Relocs(s)
+               if relocs.Count() == 0 {
+                       ro = append(ro, s)
+               } else {
+                       state.setSymType(s, sym.SRODATARELRO)
+                       if outer := ldr.OuterSym(s); outer != 0 {
+                               state.setSymType(outer, sym.SRODATARELRO)
                        }
+                       relro = append(relro, s)
                }
+       }
 
-               // Check that we haven't made two symbols with the same .Outer into
-               // different types (because references two symbols with non-nil Outer
-               // become references to the outer symbol + offset it's vital that the
-               // symbol and the outer end up in the same section).
-               for _, s := range relro {
-                       if outer := ldr.OuterSym(s); outer != 0 {
-                               st := state.symType(s)
-                               ost := state.symType(outer)
-                               if st != ost {
-                                       state.ctxt.Errorf(s, "inconsistent types for symbol and its Outer %s (%v != %v)",
-                                               ldr.SymName(outer), st, ost)
-                               }
+       // Check that we haven't made two symbols with the same .Outer into
+       // different types (because references two symbols with non-nil Outer
+       // become references to the outer symbol + offset it's vital that the
+       // symbol and the outer end up in the same section).
+       for _, s := range relro {
+               if outer := ldr.OuterSym(s); outer != 0 {
+                       st := state.symType(s)
+                       ost := state.symType(outer)
+                       if st != ost {
+                               state.ctxt.Errorf(s, "inconsistent types for symbol and its Outer %s (%v != %v)",
+                                       ldr.SymName(outer), st, ost)
                        }
                }
-
-               state.data[symnro] = ro
-               state.data[symnrelro] = relro
        }
+
+       state.data[sym.SRODATA] = ro
+       state.data[sym.SRODATARELRO] = relro
 }
 
 // dodataState holds bits of state information needed by dodata() and the
@@ -2119,10 +2092,6 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
        sect = state.allocateNamedDataSection(segro, ".rodata", sym.ReadOnly, 04)
        ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.rodata", 0), sect)
        ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.erodata", 0), sect)
-       if !ctxt.UseRelro() {
-               ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect)
-               ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect)
-       }
        for _, symn := range sym.ReadOnly {
                symnStartValue := state.datsize
                if len(state.data[symn]) != 0 {
@@ -2155,32 +2124,35 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
                xcoffUpdateOuterSize(ctxt, int64(sect.Length), sym.SPCLNTAB)
        }
 
+       /* typelink */
+       sect = state.allocateNamedDataSection(segro, ".typelink", []sym.SymKind{sym.STYPELINK}, 04)
+
+       typelink := ldr.CreateSymForUpdate("runtime.typelink", 0)
+       ldr.SetSymSect(typelink.Sym(), sect)
+       typelink.SetType(sym.SRODATA)
+       state.datsize += typelink.Size()
+       state.checkdatsize(sym.STYPELINK)
+       sect.Length = uint64(state.datsize) - sect.Vaddr
+
        /* read-only ELF, Mach-O sections */
        state.allocateSingleSymSections(segro, sym.SELFROSECT, sym.SRODATA, 04)
 
-       // There is some data that are conceptually read-only but are written to by
-       // relocations. On GNU systems, we can arrange for the dynamic linker to
+       // Read-only data that may require dynamic relocations at run time.
+       //
+       // On GNU systems, we can arrange for the dynamic linker to
        // mprotect sections after relocations are applied by giving them write
-       // permissions in the object file and calling them ".data.rel.ro.FOO". We
-       // divide the .rodata section between actual .rodata and .data.rel.ro.rodata,
-       // but for the other sections that this applies to, we just write a read-only
-       // .FOO section or a read-write .data.rel.ro.FOO section depending on the
-       // situation.
-       // TODO(mwhudson): It would make sense to do this more widely, but it makes
-       // the system linker segfault on darwin.
-       const relroPerm = 06
-       const fallbackPerm = 04
-       relroSecPerm := fallbackPerm
+       // permissions in the object file and calling them ".data.rel.ro.FOO".
+
+       relroPerm := 04
        genrelrosecname := func(suffix string) string {
                if suffix == "" {
                        return ".rodata"
                }
                return suffix
        }
-       seg := segro
+       segRelro := segro
 
        if ctxt.UseRelro() {
-               segrelro := &Segrelrodata
                if ctxt.LinkMode == LinkExternal && !ctxt.IsAIX() && !ctxt.IsDarwin() {
                        // Using a separate segment with an external
                        // linker results in some programs moving
@@ -2188,84 +2160,79 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
                        // corrupts the moduledata. So we use the
                        // rodata segment and let the external linker
                        // sort out a rel.ro segment.
-                       segrelro = segro
                } else {
+                       segRelro = &Segrelrodata
                        // Reset datsize for new segment.
                        state.datsize = 0
                }
 
+               relroPerm = 06
+
                if !ctxt.IsDarwin() { // We don't need the special names on darwin.
                        genrelrosecname = func(suffix string) string {
                                return ".data.rel.ro" + suffix
                        }
                }
+       }
 
-               relroReadOnly := []sym.SymKind{}
-               for _, symnro := range sym.ReadOnly {
-                       symn := sym.RelROMap[symnro]
-                       relroReadOnly = append(relroReadOnly, symn)
+       // checkOuter is a sanity check that for all the symbols of some kind,
+       // which are in a given section, any carrier symbol is also in
+       // that section.
+       checkOuter := func(sect *sym.Section, symn sym.SymKind) {
+               for _, s := range state.data[symn] {
+                       outer := ldr.OuterSym(s)
+                       if s != 0 && ldr.SymSect(outer) != nil && ldr.SymSect(outer) != sect {
+                               ctxt.Errorf(s, "s.Outer (%s) in different section from s, %s != %s", ldr.SymName(outer), ldr.SymSect(outer).Name, sect.Name)
+                       }
                }
-               seg = segrelro
-               relroSecPerm = relroPerm
-
-               /* data only written by relocations */
-               sect = state.allocateNamedDataSection(segrelro, genrelrosecname(""), relroReadOnly, relroSecPerm)
+       }
 
-               ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect)
-               ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect)
+       // createRelroSect will create a section that wil be a relro
+       // section if this link is using relro.
+       createRelroSect := func(name string, symn sym.SymKind) *sym.Section {
+               sect := state.allocateNamedDataSection(segRelro, genrelrosecname(name), []sym.SymKind{symn}, relroPerm)
 
-               for i, symnro := range sym.ReadOnly {
-                       if i == 0 && symnro == sym.STYPE && ctxt.HeadType != objabi.Haix {
-                               // Skip forward so that no type
-                               // reference uses a zero offset.
-                               // This is unlikely but possible in small
-                               // programs with no other read-only data.
-                               state.datsize++
-                       }
+               if symn == sym.STYPE && ctxt.HeadType != objabi.Haix {
+                       // Skip forward so that no type
+                       // reference uses a zero offset.
+                       // This is unlikely but possible in small
+                       // programs with no other read-only data.
+                       state.datsize++
+               }
 
-                       symn := sym.RelROMap[symnro]
-                       if symn == sym.Sxxx {
-                               continue
-                       }
-                       symnStartValue := state.datsize
-                       if len(state.data[symn]) != 0 {
-                               symnStartValue = aligndatsize(state, symnStartValue, state.data[symn][0])
-                       }
+               // Align to first symbol.
+               symnStartValue := state.datsize
+               if len(state.data[symn]) > 0 {
+                       symnStartValue = aligndatsize(state, state.datsize, state.data[symn][0])
+               }
 
-                       for _, s := range state.data[symn] {
-                               outer := ldr.OuterSym(s)
-                               if s != 0 && ldr.SymSect(outer) != nil && ldr.SymSect(outer) != sect {
-                                       ctxt.Errorf(s, "s.Outer (%s) in different section from s, %s != %s", ldr.SymName(outer), ldr.SymSect(outer).Name, sect.Name)
-                               }
-                       }
-                       state.assignToSection(sect, symn, sym.SRODATA)
-                       setCarrierSize(symn, state.datsize-symnStartValue)
-                       if ctxt.HeadType == objabi.Haix {
-                               // Read-only symbols might be wrapped inside their outer
-                               // symbol.
-                               // XCOFF symbol table needs to know the size of
-                               // these outer symbols.
-                               xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn)
-                       }
+               checkOuter(sect, symn)
+               state.assignToSection(sect, symn, sym.SRODATA)
+               setCarrierSize(symn, state.datsize-symnStartValue)
+               if ctxt.HeadType == objabi.Haix {
+                       // XCOFF symbol table needs to know the size
+                       // of outer symbols.
+                       xcoffUpdateOuterSize(ctxt, state.datsize-symnStartValue, symn)
                }
                sect.Length = uint64(state.datsize) - sect.Vaddr
+               return sect
+       }
 
-               state.allocateSingleSymSections(segrelro, sym.SELFRELROSECT, sym.SRODATA, relroSecPerm)
-               state.allocateSingleSymSections(segrelro, sym.SMACHORELROSECT, sym.SRODATA, relroSecPerm)
+       if len(state.data[sym.SRODATARELRO]) > 0 {
+               createRelroSect("", sym.SRODATARELRO)
        }
 
-       /* typelink */
-       sect = state.allocateNamedDataSection(seg, genrelrosecname(".typelink"), []sym.SymKind{sym.STYPELINK}, relroSecPerm)
+       sect = createRelroSect(".go.type", sym.STYPE)
+       ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect)
+       ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect)
 
-       typelink := ldr.CreateSymForUpdate("runtime.typelink", 0)
-       ldr.SetSymSect(typelink.Sym(), sect)
-       typelink.SetType(sym.SRODATA)
-       state.datsize += typelink.Size()
-       state.checkdatsize(sym.STYPELINK)
-       sect.Length = uint64(state.datsize) - sect.Vaddr
+       sect = createRelroSect(".go.func", sym.SGOFUNC)
+
+       state.allocateSingleSymSections(segRelro, sym.SELFRELROSECT, sym.SRODATA, relroPerm)
+       state.allocateSingleSymSections(segRelro, sym.SMACHORELROSECT, sym.SRODATA, relroPerm)
 
        /* itablink */
-       sect = state.allocateNamedDataSection(seg, genrelrosecname(".itablink"), []sym.SymKind{sym.SITABLINK}, relroSecPerm)
+       sect = state.allocateNamedDataSection(segRelro, genrelrosecname(".itablink"), []sym.SymKind{sym.SITABLINK}, relroPerm)
 
        itablink := ldr.CreateSymForUpdate("runtime.itablink", 0)
        ldr.SetSymSect(itablink.Sym(), sect)
index 905de67f6016ed13f672aeb6982b02b01dce423a..71261fdcb53df8cc7d79adc1be4e65d327a19ed9 100644 (file)
@@ -37,14 +37,14 @@ func TestMachoSectionsReadOnly(t *testing.T) {
                        args:             []string{"-ldflags", "-linkmode=internal"},
                        prog:             prog,
                        mustInternalLink: true,
-                       wantSecsRO:       []string{"__got", "__rodata", "__itablink", "__typelink"},
+                       wantSecsRO:       []string{"__got", "__rodata", "__itablink"},
                },
                {
                        name:        "linkmode-external",
                        args:        []string{"-ldflags", "-linkmode=external"},
                        prog:        prog,
                        mustHaveCGO: true,
-                       wantSecsRO:  []string{"__got", "__rodata", "__itablink", "__typelink"},
+                       wantSecsRO:  []string{"__got", "__rodata", "__itablink"},
                },
                {
                        name:             "cgo-linkmode-internal",
@@ -52,14 +52,14 @@ func TestMachoSectionsReadOnly(t *testing.T) {
                        prog:             progC,
                        mustHaveCGO:      true,
                        mustInternalLink: true,
-                       wantSecsRO:       []string{"__got", "__rodata", "__itablink", "__typelink"},
+                       wantSecsRO:       []string{"__got", "__rodata", "__itablink"},
                },
                {
                        name:        "cgo-linkmode-external",
                        args:        []string{"-ldflags", "-linkmode=external"},
                        prog:        progC,
                        mustHaveCGO: true,
-                       wantSecsRO:  []string{"__got", "__rodata", "__itablink", "__typelink"},
+                       wantSecsRO:  []string{"__got", "__rodata", "__itablink"},
                },
        }
 
index 67b1f11c60bdd860bee95f742a788d3e8a4c4cec..d2bd5d660c0d391d1c98d484d3ed5e97966ebbff 100644 (file)
@@ -459,30 +459,14 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
        ctxt.xdefine("runtime.egcbss", sym.SRODATA, 0)
 
        // pseudo-symbols to mark locations of type, string, and go string data.
-       var symtype, symtyperel loader.Sym
+       var symtype loader.Sym
        if !ctxt.DynlinkingGo() {
-               if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) {
-                       s = ldr.CreateSymForUpdate("type:*", 0)
-                       s.SetType(sym.STYPE)
-                       s.SetSize(0)
-                       s.SetAlign(int32(ctxt.Arch.PtrSize))
-                       symtype = s.Sym()
-
-                       s = ldr.CreateSymForUpdate("typerel.*", 0)
-                       s.SetType(sym.STYPERELRO)
-                       s.SetSize(0)
-                       s.SetAlign(int32(ctxt.Arch.PtrSize))
-                       symtyperel = s.Sym()
-               } else {
-                       s = ldr.CreateSymForUpdate("type:*", 0)
-                       s.SetType(sym.STYPE)
-                       s.SetSize(0)
-                       s.SetAlign(int32(ctxt.Arch.PtrSize))
-                       symtype = s.Sym()
-                       symtyperel = s.Sym()
-               }
+               s = ldr.CreateSymForUpdate("type:*", 0)
+               s.SetType(sym.STYPE)
+               s.SetSize(0)
+               s.SetAlign(int32(ctxt.Arch.PtrSize))
+               symtype = s.Sym()
                setCarrierSym(sym.STYPE, symtype)
-               setCarrierSym(sym.STYPERELRO, symtyperel)
        }
 
        groupSym := func(name string, t sym.SymKind) loader.Sym {
@@ -500,11 +484,6 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
                symgcbits   = groupSym("runtime.gcbits.*", sym.SGCBITS)
        )
 
-       symgofuncrel := symgofunc
-       if ctxt.UseRelro() {
-               symgofuncrel = groupSym("go:funcdescrel", sym.SGOFUNCRELRO)
-       }
-
        // assign specific types so that they sort together.
        // within a type they sort by size, so the .* symbols
        // just defined above will be first.
@@ -537,31 +516,17 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
                case strings.HasSuffix(name, "·f"):
                        if !ctxt.DynlinkingGo() {
                                ldr.SetAttrNotInSymbolTable(s, true)
-                       }
-                       if ctxt.UseRelro() {
-                               symGroupType[s] = sym.SGOFUNCRELRO
-                               if !ctxt.DynlinkingGo() {
-                                       ldr.SetCarrierSym(s, symgofuncrel)
-                               }
-                       } else {
-                               symGroupType[s] = sym.SGOFUNC
                                ldr.SetCarrierSym(s, symgofunc)
                        }
+                       symGroupType[s] = sym.SGOFUNC
 
                case strings.HasPrefix(name, "type:"):
                        if !ctxt.DynlinkingGo() {
                                ldr.SetAttrNotInSymbolTable(s, true)
                        }
-                       if ctxt.UseRelro() {
-                               symGroupType[s] = sym.STYPERELRO
-                               if symtyperel != 0 {
-                                       ldr.SetCarrierSym(s, symtyperel)
-                               }
-                       } else {
-                               symGroupType[s] = sym.STYPE
-                               if symtyperel != 0 {
-                                       ldr.SetCarrierSym(s, symtype)
-                               }
+                       symGroupType[s] = sym.STYPE
+                       if symtype != 0 {
+                               ldr.SetCarrierSym(s, symtype)
                        }
                }
        }
index 5f01eb2507dd5e9db15d79d84fa0c3007f2250bb..2bb5026879873304aa3548643602674530a8ce44 100644 (file)
@@ -583,17 +583,12 @@ func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) {
        switch stype {
        default:
                Errorf("unknown XCOFF outer symbol for type %s", stype.String())
-       case sym.SRODATA, sym.SRODATARELRO, sym.SFUNCTAB, sym.SSTRING:
+       case sym.SRODATA, sym.SFUNCTAB, sym.SSTRING:
                // Nothing to do
-       case sym.STYPERELRO:
+       case sym.STYPE:
                if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) {
-                       // runtime.types size must be removed, as it's a real symbol.
-                       tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0))
-                       outerSymSize["typerel.*"] = size - tsize
                        return
                }
-               fallthrough
-       case sym.STYPE:
                if !ctxt.DynlinkingGo() {
                        // runtime.types size must be removed, as it's a real symbol.
                        tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0))
@@ -605,8 +600,6 @@ func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) {
                if !ctxt.DynlinkingGo() {
                        outerSymSize["go:funcdesc"] = size
                }
-       case sym.SGOFUNCRELRO:
-               outerSymSize["go:funcdescrel"] = size
        case sym.SGCBITS:
                outerSymSize["runtime.gcbits.*"] = size
        case sym.SPCLNTAB:
index 746a12efd6252cd804d6a75659b9c97de6810fd8..5ee4bc74c2e21afeca42c19efc55da11f9681811 100644 (file)
@@ -52,11 +52,9 @@ const (
        SELFRXSECT     // Executable PLT; PPC64 .glink.
        SMACHOPLT      // Mach-O PLT.
 
-       // Read-only, non-executable, segment.
-       STYPE            // Type descriptors.
+       // Read-only, non-executable, unrelocated segment.
        SSTRING          // Used only for XCOFF runtime.rodata symbol?
        SGOSTRING        // Go string constants.
-       SGOFUNC          // Function descriptors and funcdata symbols.
        SGCBITS          // GC bit masks and programs.
        SRODATA          // General read-only data.
        SRODATAFIPSSTART // Start of FIPS read-only data.
@@ -65,32 +63,26 @@ const (
        SRODATAEND       // End of read-only data.
        SFUNCTAB         // Appears to be unused, except for runtime.etypes.
        SPCLNTAB         // Pclntab data.
+       STYPELINK        // Type links.
        SELFROSECT       // ELF read-only data: relocs, dynamic linking info.
 
        // Read-only, non-executable, dynamically relocatable segment.
        //
-       // Types STYPE-SFUNCTAB above are written to the .rodata section by default.
-       // When linking a shared object, some conceptually "read only" types need to
-       // be written to by relocations and putting them in a section called
-       // ".rodata" interacts poorly with the system linkers. The GNU linkers
-       // support this situation by arranging for sections of the name
-       // ".data.rel.ro.XXX" to be mprotected read only by the dynamic linker after
-       // relocations have applied, so when the Go linker is creating a shared
-       // object it checks all objects of the above types and bumps any object that
-       // has a relocation to it to the corresponding type below, which are then
-       // written to sections with appropriate magic names.
-       STYPERELRO
-       SSTRINGRELRO
-       SGOSTRINGRELRO
-       SGOFUNCRELRO
-       SGCBITSRELRO
+       // This segment holds read-only data that contains pointers to
+       // other parts of the program. When generating a position
+       // independent executable or a shared library, these sections
+       // are "relro", meaning that they start as writable, and are
+       // changed to be read-only after dynamic relocations are applied.
+       //
+       // When no dynamic relocations are required, as when generating
+       // an executable that is not position independent, this is just
+       // part of the normal read-only segment.
        SRODATARELRO
-       SFUNCTABRELRO
-
+       STYPE
+       SGOFUNC
        SELFRELROSECT   // ELF-specific read-only relocatable: PLT, etc.
        SMACHORELROSECT // Mach-O specific read-only relocatable.
 
-       STYPELINK // Type links.
        SITABLINK // Itab links.
 
        // Allocated writable segment.
@@ -185,14 +177,11 @@ var AbiSymKindToSymKind = [...]SymKind{
        objabi.SSEHUNWINDINFO:          SSEHUNWINDINFO,
 }
 
-// ReadOnly are the symbol kinds that form read-only sections. In some
-// cases, if they will require relocations, they are transformed into
-// rel-ro sections using relROMap.
+// ReadOnly are the symbol kinds that form read-only sections
+// that never require runtime relocations.
 var ReadOnly = []SymKind{
-       STYPE,
        SSTRING,
        SGOSTRING,
-       SGOFUNC,
        SGCBITS,
        SRODATA,
        SRODATAFIPSSTART,
@@ -202,18 +191,6 @@ var ReadOnly = []SymKind{
        SFUNCTAB,
 }
 
-// RelROMap describes the transformation of read-only symbols to rel-ro
-// symbols.
-var RelROMap = map[SymKind]SymKind{
-       STYPE:     STYPERELRO,
-       SSTRING:   SSTRINGRELRO,
-       SGOSTRING: SGOSTRINGRELRO,
-       SGOFUNC:   SGOFUNCRELRO,
-       SGCBITS:   SGCBITSRELRO,
-       SRODATA:   SRODATARELRO,
-       SFUNCTAB:  SFUNCTABRELRO,
-}
-
 // IsText returns true if t is a text type.
 func (t SymKind) IsText() bool {
        return STEXT <= t && t <= STEXTEND
index 80da278b3323a7df7003f4984f005f5ce7f434f5..d2e43f4e7202a3009f1372ec0ebac4b55e6be80a 100644 (file)
@@ -16,84 +16,78 @@ func _() {
        _ = x[STEXTEND-5]
        _ = x[SELFRXSECT-6]
        _ = x[SMACHOPLT-7]
-       _ = x[STYPE-8]
-       _ = x[SSTRING-9]
-       _ = x[SGOSTRING-10]
-       _ = x[SGOFUNC-11]
-       _ = x[SGCBITS-12]
-       _ = x[SRODATA-13]
-       _ = x[SRODATAFIPSSTART-14]
-       _ = x[SRODATAFIPS-15]
-       _ = x[SRODATAFIPSEND-16]
-       _ = x[SRODATAEND-17]
-       _ = x[SFUNCTAB-18]
-       _ = x[SPCLNTAB-19]
-       _ = x[SELFROSECT-20]
-       _ = x[STYPERELRO-21]
-       _ = x[SSTRINGRELRO-22]
-       _ = x[SGOSTRINGRELRO-23]
-       _ = x[SGOFUNCRELRO-24]
-       _ = x[SGCBITSRELRO-25]
-       _ = x[SRODATARELRO-26]
-       _ = x[SFUNCTABRELRO-27]
-       _ = x[SELFRELROSECT-28]
-       _ = x[SMACHORELROSECT-29]
-       _ = x[STYPELINK-30]
-       _ = x[SITABLINK-31]
-       _ = x[SFirstWritable-32]
-       _ = x[SBUILDINFO-33]
-       _ = x[SFIPSINFO-34]
-       _ = x[SELFSECT-35]
-       _ = x[SMACHO-36]
-       _ = x[SWINDOWS-37]
-       _ = x[SMODULEDATA-38]
-       _ = x[SELFGOT-39]
-       _ = x[SMACHOGOT-40]
-       _ = x[SNOPTRDATA-41]
-       _ = x[SNOPTRDATAFIPSSTART-42]
-       _ = x[SNOPTRDATAFIPS-43]
-       _ = x[SNOPTRDATAFIPSEND-44]
-       _ = x[SNOPTRDATAEND-45]
-       _ = x[SINITARR-46]
-       _ = x[SDATA-47]
-       _ = x[SDATAFIPSSTART-48]
-       _ = x[SDATAFIPS-49]
-       _ = x[SDATAFIPSEND-50]
-       _ = x[SDATAEND-51]
-       _ = x[SXCOFFTOC-52]
-       _ = x[SBSS-53]
-       _ = x[SNOPTRBSS-54]
-       _ = x[SLIBFUZZER_8BIT_COUNTER-55]
-       _ = x[SCOVERAGE_COUNTER-56]
-       _ = x[SCOVERAGE_AUXVAR-57]
-       _ = x[STLSBSS-58]
-       _ = x[SFirstUnallocated-59]
-       _ = x[SXREF-60]
-       _ = x[SMACHOSYMSTR-61]
-       _ = x[SMACHOSYMTAB-62]
-       _ = x[SMACHOINDIRECTPLT-63]
-       _ = x[SMACHOINDIRECTGOT-64]
-       _ = x[SDYNIMPORT-65]
-       _ = x[SHOSTOBJ-66]
-       _ = x[SUNDEFEXT-67]
-       _ = x[SDWARFSECT-68]
-       _ = x[SDWARFCUINFO-69]
-       _ = x[SDWARFCONST-70]
-       _ = x[SDWARFFCN-71]
-       _ = x[SDWARFABSFCN-72]
-       _ = x[SDWARFTYPE-73]
-       _ = x[SDWARFVAR-74]
-       _ = x[SDWARFRANGE-75]
-       _ = x[SDWARFLOC-76]
-       _ = x[SDWARFLINES-77]
-       _ = x[SDWARFADDR-78]
-       _ = x[SSEHUNWINDINFO-79]
-       _ = x[SSEHSECT-80]
+       _ = x[SSTRING-8]
+       _ = x[SGOSTRING-9]
+       _ = x[SGCBITS-10]
+       _ = x[SRODATA-11]
+       _ = x[SRODATAFIPSSTART-12]
+       _ = x[SRODATAFIPS-13]
+       _ = x[SRODATAFIPSEND-14]
+       _ = x[SRODATAEND-15]
+       _ = x[SFUNCTAB-16]
+       _ = x[SPCLNTAB-17]
+       _ = x[STYPELINK-18]
+       _ = x[SELFROSECT-19]
+       _ = x[SRODATARELRO-20]
+       _ = x[STYPE-21]
+       _ = x[SGOFUNC-22]
+       _ = x[SELFRELROSECT-23]
+       _ = x[SMACHORELROSECT-24]
+       _ = x[SITABLINK-25]
+       _ = x[SFirstWritable-26]
+       _ = x[SBUILDINFO-27]
+       _ = x[SFIPSINFO-28]
+       _ = x[SELFSECT-29]
+       _ = x[SMACHO-30]
+       _ = x[SWINDOWS-31]
+       _ = x[SMODULEDATA-32]
+       _ = x[SELFGOT-33]
+       _ = x[SMACHOGOT-34]
+       _ = x[SNOPTRDATA-35]
+       _ = x[SNOPTRDATAFIPSSTART-36]
+       _ = x[SNOPTRDATAFIPS-37]
+       _ = x[SNOPTRDATAFIPSEND-38]
+       _ = x[SNOPTRDATAEND-39]
+       _ = x[SINITARR-40]
+       _ = x[SDATA-41]
+       _ = x[SDATAFIPSSTART-42]
+       _ = x[SDATAFIPS-43]
+       _ = x[SDATAFIPSEND-44]
+       _ = x[SDATAEND-45]
+       _ = x[SXCOFFTOC-46]
+       _ = x[SBSS-47]
+       _ = x[SNOPTRBSS-48]
+       _ = x[SLIBFUZZER_8BIT_COUNTER-49]
+       _ = x[SCOVERAGE_COUNTER-50]
+       _ = x[SCOVERAGE_AUXVAR-51]
+       _ = x[STLSBSS-52]
+       _ = x[SFirstUnallocated-53]
+       _ = x[SXREF-54]
+       _ = x[SMACHOSYMSTR-55]
+       _ = x[SMACHOSYMTAB-56]
+       _ = x[SMACHOINDIRECTPLT-57]
+       _ = x[SMACHOINDIRECTGOT-58]
+       _ = x[SDYNIMPORT-59]
+       _ = x[SHOSTOBJ-60]
+       _ = x[SUNDEFEXT-61]
+       _ = x[SDWARFSECT-62]
+       _ = x[SDWARFCUINFO-63]
+       _ = x[SDWARFCONST-64]
+       _ = x[SDWARFFCN-65]
+       _ = x[SDWARFABSFCN-66]
+       _ = x[SDWARFTYPE-67]
+       _ = x[SDWARFVAR-68]
+       _ = x[SDWARFRANGE-69]
+       _ = x[SDWARFLOC-70]
+       _ = x[SDWARFLINES-71]
+       _ = x[SDWARFADDR-72]
+       _ = x[SSEHUNWINDINFO-73]
+       _ = x[SSEHSECT-74]
 }
 
-const _SymKind_name = "SxxxSTEXTSTEXTFIPSSTARTSTEXTFIPSSTEXTFIPSENDSTEXTENDSELFRXSECTSMACHOPLTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASRODATAFIPSSTARTSRODATAFIPSSRODATAFIPSENDSRODATAENDSFUNCTABSPCLNTABSELFROSECTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSELFRELROSECTSMACHORELROSECTSTYPELINKSITABLINKSFirstWritableSBUILDINFOSFIPSINFOSELFSECTSMACHOSWINDOWSSMODULEDATASELFGOTSMACHOGOTSNOPTRDATASNOPTRDATAFIPSSTARTSNOPTRDATAFIPSSNOPTRDATAFIPSENDSNOPTRDATAENDSINITARRSDATASDATAFIPSSTARTSDATAFIPSSDATAFIPSENDSDATAENDSXCOFFTOCSBSSSNOPTRBSSSLIBFUZZER_8BIT_COUNTERSCOVERAGE_COUNTERSCOVERAGE_AUXVARSTLSBSSSFirstUnallocatedSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSDYNIMPORTSHOSTOBJSUNDEFEXTSDWARFSECTSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINESSDWARFADDRSSEHUNWINDINFOSSEHSECT"
+const _SymKind_name = "SxxxSTEXTSTEXTFIPSSTARTSTEXTFIPSSTEXTFIPSENDSTEXTENDSELFRXSECTSMACHOPLTSSTRINGSGOSTRINGSGCBITSSRODATASRODATAFIPSSTARTSRODATAFIPSSRODATAFIPSENDSRODATAENDSFUNCTABSPCLNTABSTYPELINKSELFROSECTSRODATARELROSTYPESGOFUNCSELFRELROSECTSMACHORELROSECTSITABLINKSFirstWritableSBUILDINFOSFIPSINFOSELFSECTSMACHOSWINDOWSSMODULEDATASELFGOTSMACHOGOTSNOPTRDATASNOPTRDATAFIPSSTARTSNOPTRDATAFIPSSNOPTRDATAFIPSENDSNOPTRDATAENDSINITARRSDATASDATAFIPSSTARTSDATAFIPSSDATAFIPSENDSDATAENDSXCOFFTOCSBSSSNOPTRBSSSLIBFUZZER_8BIT_COUNTERSCOVERAGE_COUNTERSCOVERAGE_AUXVARSTLSBSSSFirstUnallocatedSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSDYNIMPORTSHOSTOBJSUNDEFEXTSDWARFSECTSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINESSDWARFADDRSSEHUNWINDINFOSSEHSECT"
 
-var _SymKind_index = [...]uint16{0, 4, 9, 23, 32, 44, 52, 62, 71, 76, 83, 92, 99, 106, 113, 129, 140, 154, 164, 172, 180, 190, 200, 212, 226, 238, 250, 262, 275, 288, 303, 312, 321, 335, 345, 354, 362, 368, 376, 387, 394, 403, 413, 432, 446, 463, 476, 484, 489, 503, 512, 524, 532, 541, 545, 554, 577, 594, 610, 617, 634, 639, 651, 663, 680, 697, 707, 715, 724, 734, 746, 757, 766, 778, 788, 797, 808, 817, 828, 838, 852, 860}
+var _SymKind_index = [...]uint16{0, 4, 9, 23, 32, 44, 52, 62, 71, 78, 87, 94, 101, 117, 128, 142, 152, 160, 168, 177, 187, 199, 204, 211, 224, 239, 248, 262, 272, 281, 289, 295, 303, 314, 321, 330, 340, 359, 373, 390, 403, 411, 416, 430, 439, 451, 459, 468, 472, 481, 504, 521, 537, 544, 561, 566, 578, 590, 607, 624, 634, 642, 651, 661, 673, 684, 693, 705, 715, 724, 735, 744, 755, 765, 779, 787}
 
 func (i SymKind) String() string {
        if i >= SymKind(len(_SymKind_index)-1) {
index 947f2da2b5d323df8d3a0375a740735b0dbfdef7..5c25cc603b6beb99e32240c81cf59cf861b095c3 100644 (file)
@@ -127,6 +127,8 @@ func asmb(ctxt *ld.Link, ldr *loader.Loader) {
                ldr.SymSect(ldr.Lookup("runtime.rodata", 0)),
                ldr.SymSect(ldr.Lookup("runtime.typelink", 0)),
                ldr.SymSect(ldr.Lookup("runtime.itablink", 0)),
+               ldr.SymSect(ldr.Lookup("runtime.types", 0)),
+               ldr.SymSect(ldr.Lookup("go:funcdesc", 0)),
                ldr.SymSect(ldr.Lookup("runtime.firstmoduledata", 0)),
                ldr.SymSect(ldr.Lookup("runtime.pclntab", 0)),
                ldr.SymSect(ldr.Lookup("runtime.noptrdata", 0)),
index 036eda13bc88073f505e541c44106d905b6f7e35..541733b8e615f0d7a9abb33e7d6cb626c10f4433 100644 (file)
@@ -2221,3 +2221,229 @@ func TestModuledataPlacement(t *testing.T) {
                // so there is nothing to test here.
        }
 }
+
+const typeSrc = `
+package main
+
+import (
+       "fmt"
+       "unsafe"
+)
+
+type MyInt int
+
+var vals = []any{
+       0,
+       0.1,
+       "",
+       MyInt(0),
+       struct{ f int }{0},
+       func() {},
+}
+
+var global int
+
+func main() {
+       fmt.Printf("global %#x\n", &global)
+       for _, v := range vals {
+               // Unsafe assumption: the first word of a value
+               // of type any is the type descriptor.
+               td := *(*uintptr)(unsafe.Pointer(&v))
+               fmt.Printf("%#x\n", td)
+       }
+}
+`
+
+// Test that type data is stored in the types section.
+func TestTypePlacement(t *testing.T) {
+       testenv.MustHaveGoRun(t)
+       t.Parallel()
+
+       tmpdir := t.TempDir()
+       src := filepath.Join(tmpdir, "x.go")
+       if err := os.WriteFile(src, []byte(typeSrc), 0o444); err != nil {
+               t.Fatal(err)
+       }
+
+       exe := filepath.Join(tmpdir, "x.exe")
+       cmd := goCmd(t, "build", "-o", exe, src)
+       if out, err := cmd.CombinedOutput(); err != nil {
+               t.Fatalf("build failed; %v, output:\n%s", err, out)
+       }
+
+       cmd = testenv.Command(t, exe)
+       var stdout, stderr strings.Builder
+       cmd.Stdout = &stdout
+       cmd.Stderr = &stderr
+       if err := cmd.Run(); err != nil {
+               t.Fatalf("running test program failed: %v, stdout:\n%s\nstderr:\n%s", err, &stdout, &stderr)
+       }
+       stderrString := stderr.String()
+       if stderrString != "" {
+               t.Fatalf("running test program printed to stderr:\n%s", stderrString)
+       }
+
+       t.Logf("\n%s", &stdout)
+
+       var globalExeAddr uint64
+       var addrs []uint64
+       globalNext := false
+       for s := range strings.FieldsSeq(stdout.String()) {
+               if globalNext {
+                       v, err := strconv.ParseUint(s, 0, 64)
+                       if err != nil {
+                               t.Errorf("failed to parse test program output %s: %v", s, err)
+                       }
+                       globalExeAddr = v
+                       globalNext = false
+               } else if s == "global" {
+                       globalNext = true
+               } else {
+                       addr, err := strconv.ParseUint(s, 0, 64)
+                       if err != nil {
+                               t.Errorf("failed to parse test program output %q: %v", s, err)
+                       }
+                       addrs = append(addrs, addr)
+               }
+       }
+
+       ef, _ := elf.Open(exe)
+       mf, _ := macho.Open(exe)
+       pf, _ := pe.Open(exe)
+       xf, _ := xcoff.Open(exe)
+       // TODO: plan9
+       if ef == nil && mf == nil && pf == nil && xf == nil {
+               t.Skip("unrecognized executable file format")
+       }
+
+       const globalName = "main.global"
+       var typeStart, typeEnd uint64
+       var globalObjAddr uint64
+       switch {
+       case ef != nil:
+               defer ef.Close()
+
+               for _, sec := range ef.Sections {
+                       if sec.Name == ".go.type" {
+                               typeStart = sec.Addr
+                               typeEnd = sec.Addr + sec.Size
+                               break
+                       }
+               }
+
+               syms, err := ef.Symbols()
+               if err != nil {
+                       t.Fatal(err)
+               }
+
+               if typeStart == 0 && typeEnd == 0 {
+                       // We can fail to find the section for PIE.
+                       // Fall back to symbols.
+                       for _, sym := range syms {
+                               switch sym.Name {
+                               case "runtime.types":
+                                       typeStart = sym.Value
+                               case "runtime.etypes":
+                                       typeEnd = sym.Value
+                               }
+                       }
+               }
+
+               for _, sym := range syms {
+                       if sym.Name == globalName {
+                               globalObjAddr = sym.Value
+                               break
+                       }
+               }
+
+       case mf != nil:
+               defer mf.Close()
+
+               for _, sec := range mf.Sections {
+                       if sec.Name == "__go_type" {
+                               typeStart = sec.Addr
+                               typeEnd = sec.Addr + sec.Size
+                               break
+                       }
+               }
+
+               for _, sym := range mf.Symtab.Syms {
+                       if sym.Name == globalName {
+                               globalObjAddr = sym.Value
+                               break
+                       }
+               }
+
+       case pf != nil:
+               defer pf.Close()
+
+               var imageBase uint64
+               switch ohdr := pf.OptionalHeader.(type) {
+               case *pe.OptionalHeader32:
+                       imageBase = uint64(ohdr.ImageBase)
+               case *pe.OptionalHeader64:
+                       imageBase = ohdr.ImageBase
+               }
+
+               var typeSym, eTypeSym *pe.Symbol
+               for _, sym := range pf.Symbols {
+                       switch sym.Name {
+                       case "runtime.types":
+                               typeSym = sym
+                       case "runtime.etypes":
+                               eTypeSym = sym
+                       case globalName:
+                               globalSec := pf.Sections[sym.SectionNumber-1]
+                               globalObjAddr = imageBase + uint64(globalSec.VirtualAddress+sym.Value)
+                       }
+               }
+
+               if typeSym == nil {
+                       t.Fatal("could not find symbol runtime.types")
+               }
+               if eTypeSym == nil {
+                       t.Fatal("could not find symbol runtime.etypes")
+               }
+               if typeSym.SectionNumber != eTypeSym.SectionNumber {
+                       t.Fatalf("runtime.types section %d != runtime.etypes section %d", typeSym.SectionNumber, eTypeSym.SectionNumber)
+               }
+
+               sec := pf.Sections[typeSym.SectionNumber-1]
+
+               typeStart = imageBase + uint64(sec.VirtualAddress+typeSym.Value)
+               typeEnd = imageBase + uint64(sec.VirtualAddress+eTypeSym.Value)
+
+       case xf != nil:
+               defer xf.Close()
+
+               for _, sec := range xf.Sections {
+                       if sec.Name == ".go.type" {
+                               typeStart = sec.VirtualAddress
+                               typeEnd = sec.VirtualAddress + sec.Size
+                               break
+                       }
+               }
+
+               for _, sym := range xf.Symbols {
+                       if sym.Name == globalName {
+                               globalObjAddr = sym.Value
+                               break
+                       }
+               }
+       }
+
+       if typeStart == 0 || typeEnd == 0 {
+               t.Fatalf("failed to find type descriptor addresses; found %#x to %#x", typeStart, typeEnd)
+       }
+       t.Logf("type start: %#x type end: %#x", typeStart, typeEnd)
+
+       offset := globalExeAddr - globalObjAddr
+       t.Logf("execution offset: %#x", offset)
+
+       for _, addr := range addrs {
+               addr -= offset
+               if addr < typeStart || addr >= typeEnd {
+                       t.Errorf("type descriptor address %#x out of range: not between %#x and %#x", addr, typeStart, typeEnd)
+               }
+       }
+}