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 <alex.brainman@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
This commit is contained in:
qmuntal
2026-02-10 14:21:15 +01:00
committed by Quim Muntal
parent de5c138eef
commit bbed50aaa3
5 changed files with 33 additions and 26 deletions

View File

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

View File

@@ -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

View File

@@ -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.

View File

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

View File

@@ -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())
}