cmd/link: put type descriptors in .go.type section

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>
This commit is contained in:
Ian Lance Taylor 2025-11-23 20:47:50 -08:00 committed by Gopher Robot
parent 6edb9f9c51
commit 13096a62e6
8 changed files with 457 additions and 333 deletions

View 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)
}
}
// 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 {
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 {
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.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)
}
}
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,107 +2124,8 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
xcoffUpdateOuterSize(ctxt, int64(sect.Length), sym.SPCLNTAB)
}
/* 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
// 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
genrelrosecname := func(suffix string) string {
if suffix == "" {
return ".rodata"
}
return suffix
}
seg := 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
// their data sections unexpectedly, which
// corrupts the moduledata. So we use the
// rodata segment and let the external linker
// sort out a rel.ro segment.
segrelro = segro
} else {
// Reset datsize for new segment.
state.datsize = 0
}
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)
}
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)
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++
}
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])
}
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)
}
}
sect.Length = uint64(state.datsize) - sect.Vaddr
state.allocateSingleSymSections(segrelro, sym.SELFRELROSECT, sym.SRODATA, relroSecPerm)
state.allocateSingleSymSections(segrelro, sym.SMACHORELROSECT, sym.SRODATA, relroSecPerm)
}
/* typelink */
sect = state.allocateNamedDataSection(seg, genrelrosecname(".typelink"), []sym.SymKind{sym.STYPELINK}, relroSecPerm)
sect = state.allocateNamedDataSection(segro, ".typelink", []sym.SymKind{sym.STYPELINK}, 04)
typelink := ldr.CreateSymForUpdate("runtime.typelink", 0)
ldr.SetSymSect(typelink.Sym(), sect)
@ -2264,8 +2134,105 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
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)
// 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".
relroPerm := 04
genrelrosecname := func(suffix string) string {
if suffix == "" {
return ".rodata"
}
return suffix
}
segRelro := segro
if ctxt.UseRelro() {
if ctxt.LinkMode == LinkExternal && !ctxt.IsAIX() && !ctxt.IsDarwin() {
// Using a separate segment with an external
// linker results in some programs moving
// their data sections unexpectedly, which
// corrupts the moduledata. So we use the
// rodata segment and let the external linker
// sort out a rel.ro segment.
} 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
}
}
}
// 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)
}
}
}
// 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)
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++
}
// Align to first symbol.
symnStartValue := state.datsize
if len(state.data[symn]) > 0 {
symnStartValue = aligndatsize(state, state.datsize, state.data[symn][0])
}
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
}
if len(state.data[sym.SRODATARELRO]) > 0 {
createRelroSect("", sym.SRODATARELRO)
}
sect = createRelroSect(".go.type", sym.STYPE)
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.types", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.etypes", 0), sect)
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)

View 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"},
},
}

View 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)
}
}
}

View 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:

View 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

View 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) {

View 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)),

View 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)
}
}
}