cmd/link: fix AIX builds after recent linker changes

This updates XCOFF-specific code for the recent addition
of funcdata to pclntab.

Because XCOFF puts separate symbols into separate csects,
each with their own alignment, it's important to tell the
external linker the expected alignment of each part of pclntab.
Otherwise the offsets within pclntab may change as the external
linker aligns symbols. This CL sets the correct alignment for
each pclntab child symbol, and sets pclntab's alignment to the
max of that of its children.

Tested on the GCC compile farm.

Fixes #76486

Change-Id: I77d8a90c4b4b79d80ca11ede8d9a2aa9cc89f53f
Reviewed-on: https://go-review.googlesource.com/c/go/+/725603
Auto-Submit: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
Ian Lance Taylor
2025-12-01 16:25:56 -08:00
committed by Gopher Robot
parent f2d96272cb
commit 276cc4d3db
5 changed files with 84 additions and 63 deletions

View File

@@ -55,13 +55,20 @@ type pclntab struct {
// addGeneratedSym adds a generator symbol to pclntab, returning the new Sym.
// It is the caller's responsibility to save the symbol in state.
func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, f generatorFunc) loader.Sym {
func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, align int32, f generatorFunc) loader.Sym {
size = Rnd(size, int64(ctxt.Arch.PtrSize))
state.size += size
s := ctxt.createGeneratorSymbol(name, 0, sym.SPCLNTAB, size, f)
ctxt.loader.SetAttrReachable(s, true)
ctxt.loader.SetCarrierSym(s, state.carrier)
ctxt.loader.SetAttrNotInSymbolTable(s, true)
ldr := ctxt.loader
ldr.SetSymAlign(s, align)
ldr.SetAttrReachable(s, true)
ldr.SetCarrierSym(s, state.carrier)
ldr.SetAttrNotInSymbolTable(s, true)
if align > ldr.SymAlign(state.carrier) {
ldr.SetSymAlign(state.carrier, align)
}
return s
}
@@ -277,7 +284,7 @@ func (state *pclntab) generatePCHeader(ctxt *Link) {
}
}
state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader)
state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, int32(ctxt.Arch.PtrSize), writeHeader)
}
// walkFuncs iterates over the funcs, calling a function for each unique
@@ -326,7 +333,7 @@ func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[lo
size += int64(len(ctxt.loader.SymName(s)) + 1) // NULL terminate
})
state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab)
state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, 1, writeFuncNameTab)
return nameOffsets
}
@@ -442,7 +449,7 @@ func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.Compilat
}
}
}
state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), writeCutab)
state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), 4, writeCutab)
// Write filetab.
writeFiletab := func(ctxt *Link, s loader.Sym) {
@@ -454,7 +461,7 @@ func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.Compilat
}
}
state.nfiles = uint32(len(fileOffsets))
state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, writeFiletab)
state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, 1, writeFiletab)
return cuOffsets
}
@@ -518,7 +525,7 @@ func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) {
}
}
state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, writePctab)
state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, 1, writePctab)
}
// generateFuncdata writes out the funcdata information.
@@ -647,7 +654,7 @@ func (state *pclntab) generateFuncdata(ctxt *Link, funcs []loader.Sym, inlsyms m
}
}
state.funcdata = state.addGeneratedSym(ctxt, "go:func.*", size, writeFuncData)
state.funcdata = state.addGeneratedSym(ctxt, "go:func.*", size, maxAlign, writeFuncData)
// Because the funcdata previously was not in pclntab,
// we need to keep the visible symbol so that tools can find it.
@@ -703,7 +710,7 @@ func (state *pclntab) generateFunctab(ctxt *Link, funcs []loader.Sym, inlSyms ma
writePCToFunc(ctxt, sb, funcs, startLocations)
writeFuncs(ctxt, sb, funcs, inlSyms, startLocations, cuOffsets, nameOffsets)
}
state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, writePcln)
state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, 4, writePcln)
}
// funcData returns the funcdata and offsets for the FuncInfo.
@@ -967,6 +974,10 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
ldr.SetAttrReachable(state.carrier, true)
setCarrierSym(sym.SPCLNTAB, state.carrier)
// Aign pclntab to at least a pointer boundary,
// for pcHeader. This may be raised further by subsymbols.
ldr.SetSymAlign(state.carrier, int32(ctxt.Arch.PtrSize))
state.generatePCHeader(ctxt)
nameOffsets := state.generateFuncnametab(ctxt, funcs)
cuOffsets := state.generateFilenameTabs(ctxt, compUnits, funcs)
@@ -1076,6 +1087,7 @@ func (ctxt *Link) findfunctab(state *pclntab, container loader.Bitmap) {
}
state.findfunctab = ctxt.createGeneratorSymbol("runtime.findfunctab", 0, sym.SPCLNTAB, size, writeFindFuncTab)
ldr.SetSymAlign(state.findfunctab, 4)
ldr.SetAttrReachable(state.findfunctab, true)
ldr.SetAttrLocal(state.findfunctab, true)
}

View File

@@ -672,6 +672,7 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
addRef("runtime.rodata")
addRef("runtime.erodata")
addRef("runtime.epclntab")
addRef("go:func.*")
// As we use relative addressing for text symbols in functab, it is
// important that the offsets we computed stay unchanged by the external
// linker, i.e. all symbols in Textp should not be removed.

View File

@@ -603,14 +603,20 @@ func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) {
outerSymSize["go:string.*"] = size
case sym.SGOFUNC:
if !ctxt.DynlinkingGo() {
outerSymSize["go:func.*"] = size
outerSymSize["go:funcdesc"] = size
}
case sym.SGOFUNCRELRO:
outerSymSize["go:funcrel.*"] = size
outerSymSize["go:funcdescrel"] = size
case sym.SGCBITS:
outerSymSize["runtime.gcbits.*"] = size
case sym.SPCLNTAB:
outerSymSize["runtime.pclntab"] = size
// go:func.* size must be removed from pclntab,
// as it's a real symbol. Same for runtime.findfunctab.
fsize := ldr.SymSize(ldr.Lookup("go:func.*", 0))
fft := ldr.Lookup("runtime.findfunctab", 0)
fsize = Rnd(fsize, int64(symalign(ldr, fft)))
tsize := ldr.SymSize(fft)
outerSymSize["runtime.pclntab"] = size - (fsize + tsize)
}
}

View File

@@ -1960,33 +1960,55 @@ func TestFuncdataPlacement(t *testing.T) {
case xf != nil:
defer xf.Close()
var moddataSym, gofuncSym, pclntabSym, epclntabSym *xcoff.Symbol
for _, sym := range xf.Symbols {
switch sym.Name {
case moddataSymName:
moddataAddr = sym.Value
moddataSym = sym
case gofuncSymName:
gofuncAddr = sym.Value
gofuncSym = sym
case "runtime.pclntab":
pclntabSym = sym
case "runtime.epclntab":
epclntabSym = sym
}
}
for _, sec := range xf.Sections {
if sec.Name == ".go.pclntab" {
data, err := sec.Data()
if err != nil {
t.Fatal(err)
}
pclntab = data
pclntabAddr = sec.VirtualAddress
pclntabEnd = sec.VirtualAddress + sec.Size
}
if moddataAddr >= sec.VirtualAddress && moddataAddr < sec.VirtualAddress+sec.Size {
data, err := sec.Data()
if err != nil {
t.Fatal(err)
}
moddataBytes = data[moddataAddr-sec.VirtualAddress:]
}
if moddataSym == nil {
t.Fatalf("could not find symbol %s", moddataSymName)
}
if gofuncSym == nil {
t.Fatalf("could not find symbol %s", gofuncSymName)
}
if pclntabSym == nil {
t.Fatal("could not find symbol runtime.pclntab")
}
if epclntabSym == nil {
t.Fatal("could not find symbol runtime.epclntab")
}
sec := xf.Sections[moddataSym.SectionNumber-1]
data, err := sec.Data()
if err != nil {
t.Fatal(err)
}
moddataBytes = data[moddataSym.Value:]
moddataAddr = uint64(sec.VirtualAddress + moddataSym.Value)
sec = xf.Sections[gofuncSym.SectionNumber-1]
gofuncAddr = uint64(sec.VirtualAddress + gofuncSym.Value)
if pclntabSym.SectionNumber != epclntabSym.SectionNumber {
t.Fatalf("runtime.pclntab section %d != runtime.epclntab section %d", pclntabSym.SectionNumber, epclntabSym.SectionNumber)
}
sec = xf.Sections[pclntabSym.SectionNumber-1]
data, err = sec.Data()
if err != nil {
t.Fatal(err)
}
pclntab = data[pclntabSym.Value:epclntabSym.Value]
pclntabAddr = uint64(sec.VirtualAddress + pclntabSym.Value)
pclntabEnd = uint64(sec.VirtualAddress + epclntabSym.Value)
default:
panic("can't happen")
@@ -2183,31 +2205,16 @@ func TestModuledataPlacement(t *testing.T) {
}
}
case pf != nil:
defer pf.Close()
// On Windows all the Go specific sections seem to
// get stuffed into a few Windows sections,
// so there is nothing to test here.
case xf != nil:
defer xf.Close()
for _, sym := range xf.Symbols {
if sym.Name == moddataSymName {
if sym.SectionNumber == 0 {
t.Errorf("moduledata not in a section")
} else {
sec := xf.Sections[sym.SectionNumber-1]
if sec.Name != ".go.module" {
t.Errorf("moduledata in section %s, not .go.module", sec.Name)
}
if sym.Value != sec.VirtualAddress {
t.Errorf("moduledata address %#x != section start address %#x", sym.Value, sec.VirtualAddress)
}
}
break
}
case pf != nil, xf != nil:
if pf != nil {
defer pf.Close()
}
if xf != nil {
defer xf.Close()
}
// On Windows and AIX all the Go specific sections
// get stuffed into a few sections,
// so there is nothing to test here.
}
}

View File

@@ -118,11 +118,6 @@ func testGoExec(t *testing.T, iscgo, isexternallinker bool) {
"runtime.noptrdata": "D",
}
if runtime.GOOS == "aix" && iscgo {
// pclntab is moved to .data section on AIX.
runtimeSyms["runtime.epclntab"] = "D"
}
out, err = testenv.Command(t, testenv.Executable(t), exe).CombinedOutput()
if err != nil {
t.Fatalf("go tool nm: %v\n%s", err, string(out))