From bbed50aaa3033c9be6a5268268a7faf226be9de0 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 10 Feb 2026 14:21:15 +0100 Subject: [PATCH] cmd/link: sort .pdata by function start address Loosely based on CL 678795. The PE/COFF spec requires RUNTIME_FUNCTION entries in the .pdata section to be ordered by their function start address. Previously the linker emitted them in symbol order. An unsorted table triggers the MSVC linker error: fatal error LNK1223: invalid or corrupt file: file contains invalid .pdata contributions Fixes #65116. Change-Id: I589cb4e6787a9edb34400b56e60fe23065b59162 Reviewed-on: https://go-review.googlesource.com/c/go/+/743820 Reviewed-by: Alex Brainman LUCI-TryBot-Result: Go LUCI Reviewed-by: Cherry Mui Reviewed-by: Michael Pratt --- .../test/seh_internal_windows_test.go | 16 --------- src/cmd/cgo/internal/test/seh_windows_test.go | 2 +- src/cmd/link/internal/ld/data.go | 5 +++ src/cmd/link/internal/ld/pe.go | 1 - src/cmd/link/internal/ld/seh.go | 35 ++++++++++++++----- 5 files changed, 33 insertions(+), 26 deletions(-) delete mode 100644 src/cmd/cgo/internal/test/seh_internal_windows_test.go diff --git a/src/cmd/cgo/internal/test/seh_internal_windows_test.go b/src/cmd/cgo/internal/test/seh_internal_windows_test.go deleted file mode 100644 index 708ffdc6f6..0000000000 --- a/src/cmd/cgo/internal/test/seh_internal_windows_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2024 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build cgo && windows && internal - -package cgotest - -import ( - "internal/testenv" - "testing" -) - -func TestCallbackCallersSEH(t *testing.T) { - testenv.SkipFlaky(t, 65116) -} diff --git a/src/cmd/cgo/internal/test/seh_windows_test.go b/src/cmd/cgo/internal/test/seh_windows_test.go index 4a8d5bbd4d..7bbed5b04e 100644 --- a/src/cmd/cgo/internal/test/seh_windows_test.go +++ b/src/cmd/cgo/internal/test/seh_windows_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build cgo && windows && !internal +//go:build cgo && windows package cgotest diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 43c3cd38db..89df83cfeb 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -2777,6 +2777,11 @@ func (ctxt *Link) textaddress() { ldr.SetSymValue(etext, int64(va)) ldr.SetSymValue(text, int64(Segtext.Sections[0].Vaddr)) } + if ctxt.IsWindows() { + // .pdata entries should be sorted by address, so process them now + // that we have final addresses for the text symbols. + collectSEH(ctxt) + } } // assigns address for a text symbol, returns (possibly new) section, its number, and the address. diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go index b49da42c4c..8184a32f83 100644 --- a/src/cmd/link/internal/ld/pe.go +++ b/src/cmd/link/internal/ld/pe.go @@ -1635,7 +1635,6 @@ func addPEBaseReloc(ctxt *Link) { func (ctxt *Link) dope() { initdynimport(ctxt) initdynexport(ctxt) - writeSEH(ctxt) } func setpersrc(ctxt *Link, syms []loader.Sym) { diff --git a/src/cmd/link/internal/ld/seh.go b/src/cmd/link/internal/ld/seh.go index 9f0f747c04..2bee6d4cf7 100644 --- a/src/cmd/link/internal/ld/seh.go +++ b/src/cmd/link/internal/ld/seh.go @@ -8,6 +8,8 @@ import ( "cmd/internal/sys" "cmd/link/internal/loader" "cmd/link/internal/sym" + "cmp" + "slices" ) var sehp struct { @@ -15,14 +17,16 @@ var sehp struct { xdata []sym.LoaderSym } -func writeSEH(ctxt *Link) { +// collectSEH collects the SEH unwind information for all functions and organizes +// it into .pdata and .xdata sections. +func collectSEH(ctxt *Link) { switch ctxt.Arch.Family { case sys.AMD64: - writeSEHAMD64(ctxt) + collectSEHAMD64(ctxt) } } -func writeSEHAMD64(ctxt *Link) { +func collectSEHAMD64(ctxt *Link) { ldr := ctxt.loader mkSecSym := func(name string, kind sym.SymKind) *loader.SymbolBuilder { s := ldr.CreateSymForUpdate(name, 0) @@ -39,6 +43,11 @@ func writeSEHAMD64(ctxt *Link) { // an RVA, so it is possible, and binary-size wise, // to deduplicate .xdata entries. uwcache := make(map[string]int64) // aux symbol name --> .xdata offset + type pdataEntry struct { + start sym.LoaderSym + xdataOff int64 + } + var entries []pdataEntry for _, s := range ctxt.Textp { if fi := ldr.FuncInfo(s); !fi.Valid() { continue @@ -66,12 +75,22 @@ func writeSEHAMD64(ctxt *Link) { } } + entries = append(entries, pdataEntry{start: s, xdataOff: off}) + } + slices.SortFunc(entries, func(a, b pdataEntry) int { + return cmp.Compare(ldr.SymAddr(a.start), ldr.SymAddr(b.start)) + }) + for _, ent := range entries { // Reference: // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-runtime_function - pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, s, 0) - pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, s, ldr.SymSize(s)) - pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, xdata.Sym(), off) + pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, ent.start, 0) // function start address + pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, ent.start, ldr.SymSize(ent.start)) // function end address + pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, xdata.Sym(), ent.xdataOff) // xdata symbol offset + } + if pdata.Size() > 0 { + sehp.pdata = append(sehp.pdata, pdata.Sym()) + } + if xdata.Size() > 0 { + sehp.xdata = append(sehp.xdata, xdata.Sym()) } - sehp.pdata = append(sehp.pdata, pdata.Sym()) - sehp.xdata = append(sehp.xdata, xdata.Sym()) }