From 20bc1949722c1fd66de5d568f1422845332d18f3 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 11 Feb 2026 21:23:49 -0800 Subject: [PATCH] cmd/link: handle runtime.type based on size, not GOOS When handling type descriptors, we add some space at the start to ensure that offset 0 does not refer to a valid type descriptor. AIX has an initial runtime.types symbol with a non-zero size, so we used that instead of adding some space. In some cases Darwin also has a runtime.types symbol with a non-zero size. Before CL 727021, this was always fine, because the 8 byte size of runtime.types was swamped by the 32-byte alignment of type descriptors. That didn't work for AIX with the external linker, because on AIX the external linker lays out each symbol separately. Darwin doesn't have that problem, so the layout of the internal linker was preserved. However, CL 727021 changed the alignment of type descriptors to 8. That means that on Darwin the 8 byte size of runtime.types was no longer hidden by the alignment. In effect we were skipping twice: once for runtime.types, and then again explicitly. This only failed when runtime.types has a non-zero size, which is only in a few specific cases. This CL cleans this up by not skipping explicitly in any case where runtime.types has a non-zero size. That handles both AIX and Darwin consistently. To make this clearer, I changed the skip from a single byte to the size of a pointer in all cases. I considered always giving runtime.types a non-zero size, but that is a bigger change, and potentially confusing since there really isn't any data associated with runtime.types. The cases where we must give it a non-zero size are special, and I think it's simpler to keep it that way. For #6853 For #36313 Fixes #77569 Change-Id: I22ebbd0194527ecca96d48849aa00a4fc899e55c Reviewed-on: https://go-review.googlesource.com/c/go/+/744820 Reviewed-by: Keith Randall Reviewed-by: Michael Pratt LUCI-TryBot-Result: Go LUCI Reviewed-by: Keith Randall Auto-Submit: Ian Lance Taylor --- src/cmd/link/internal/ld/data.go | 29 ++++++++++++++++------------- src/runtime/type.go | 15 ++++++++------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index de9e9ea59e..43c3cd38db 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1498,7 +1498,7 @@ func fixZeroSizedSymbols(ctxt *Link) { defineRuntimeTypes := func() { types := ldr.CreateSymForUpdate("runtime.types", 0) types.SetType(sym.STYPE) - types.SetSize(8) + types.SetSize(int64(ctxt.Arch.PtrSize)) types.SetAlign(int32(ctxt.Arch.PtrSize)) ldr.SetAttrSpecial(types.Sym(), false) } @@ -2206,20 +2206,20 @@ func (state *dodataState) allocateDataSections(ctxt *Link) { 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 + if symn == sym.STYPE { + // Increment state.datsize so that no type // reference uses a zero offset. // This is unlikely but possible in small // programs with no other read-only data. // - // Don't skip forward on AIX because the external - // linker, when used, will align symbols itself. - // The external linker won't know about this skip, - // and will mess up the constant offsets we need - // within the type section. On AIX we just live - // with the possibility of a broken program - // if there is no other read-only data. - state.datsize++ + // But don't skip ahead if there is a runtime.types + // symbol with non-zero size. That can be created + // in fixZeroSizedSymbols. In that case the + // runtime.types symbol itself serves as the skip. + typesSym := ldr.Lookup("runtime.types", 0) + if typesSym == 0 || ldr.SymSize(typesSym) == 0 { + state.datsize += int64(ctxt.Arch.PtrSize) + } } // Align to first symbol. @@ -2479,10 +2479,13 @@ func (state *dodataState) dodataSect(ctxt *Link, symn sym.SymKind, syms []loader }) // Find the end of the typelink descriptors. - // The size starts at 1 to match the increment in + // The size starts at PtrSize to match the value in // createRelroSect in allocateDataSections. + // Note that we skip runtime.types in the loop, + // so we don't need to worry about that case; + // there will be an increment either way. // TODO: This wastes some space. - typeLinkSize := int64(1) + typeLinkSize := int64(ctxt.Arch.PtrSize) for i := range sl { si := sl[i].sym if si == head || si == typeStar { diff --git a/src/runtime/type.go b/src/runtime/type.go index 1abd271c3f..58349e921a 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -524,14 +524,15 @@ func moduleTypelinks(md *moduledata) []*_type { td := md.types - // We have to increment by 1 to match the increment done in - // cmd/link/internal/data.go createRelroSect in allocateDataSections. + // We have to increment by the pointer size to match the + // increment in cmd/link/internal/data.go createRelroSect + // in allocateDataSections. // - // We don't do that increment on AIX, but on AIX we need to adjust - // for the fact that the runtime.types symbol has a size of 8, - // and the type descriptors will follow that. This increment, - // followed by the forced alignment to 8, will do that. - td++ + // The linker doesn't do that increment when runtime.types + // has a non-zero size, but in that case the runtime.types + // symbol itself pushes the other symbols forward. + // So either way this increment is correct. + td += goarch.PtrSize etypedesc := md.types + md.typedesclen for td < etypedesc { -- 2.52.0