cmd/go: force external linking when CGO_LDFLAGS contains static-linking flags

When CGO_LDFLAGS contains flags that trigger static linking (-static,
--static, -Wl,-static, -Wl,--static, -Wl,-Bstatic), the internal
linker cannot resolve libc symbols, producing errors like "relocation
target stderr not defined".

Add checkLinkerFlagsForInternalLink, mirroring the existing
checkCompilerFlagsForInternalLink, to detect these flags in CGO_LDFLAGS
and emit the preferlinkext token to fall back to external linking.
Also check for -static/--static in compiler flags (CGO_CFLAGS etc.),
which some toolchains accept.

Fixes #77768

Change-Id: I5cbbc3cd30880ccf49dd575dd65188d9935d9488
GitHub-Last-Rev: 215efb6360
GitHub-Pull-Request: golang/go#79331
Reviewed-on: https://go-review.googlesource.com/c/go/+/776760
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Auto-Submit: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
Sebastien Tardif 2026-05-11 19:22:09 +00:00 committed by Gopher Robot
parent cd913caa3f
commit 42bdffec2d
3 changed files with 59 additions and 0 deletions

View file

@ -2985,6 +2985,11 @@ func (b *Builder) runCgo(ctx context.Context, a *Action) error {
flagSources := []string{"CGO_CFLAGS", "CGO_CXXFLAGS", "CGO_FFLAGS"}
flagLists := [][]string{cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS}
notCompatibleWithInternalLinking := flagsNotCompatibleWithInternalLinking(flagSources, flagLists)
if !notCompatibleWithInternalLinking {
if err := checkLinkerFlagsForInternalLink("CGO_LDFLAGS", "CGO_LDFLAGS", cgoLDFLAGS); err != nil {
notCompatibleWithInternalLinking = true
}
}
if cfg.BuildMSan {
cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...)

View file

@ -331,10 +331,34 @@ func checkCompilerFlagsForInternalLink(name, source string, list []string) error
}
// Currently the only flag on the allow list that causes problems
// for the linker is "-flto"; check for it manually here.
// Also check for -static/--static, which some toolchains accept
// as a compiler flag.
for _, fl := range list {
if strings.HasPrefix(fl, "-flto") {
return fmt.Errorf("flag %q triggers external linking", fl)
}
if fl == "-static" || fl == "--static" {
return fmt.Errorf("flag %q triggers external linking", fl)
}
}
return nil
}
// checkLinkerFlagsForInternalLink returns an error if 'list'
// contains linker flags that are not compatible with internal linking.
func checkLinkerFlagsForInternalLink(name, source string, list []string) error {
checkOverrides := false
if err := checkFlags(name, source, list, nil, validLinkerFlags, validLinkerFlagsWithNextArg, checkOverrides); err != nil {
return err
}
// Flags that force static linking require the external linker
// to resolve libc symbols. See #77768.
for _, fl := range list {
if fl == "-static" || fl == "--static" ||
fl == "-Wl,-static" || fl == "-Wl,--static" ||
fl == "-Wl,-Bstatic" {
return fmt.Errorf("flag %q triggers external linking", fl)
}
}
return nil
}

View file

@ -91,6 +91,36 @@ go build -x -n -o dummy.exe ./usesInternalCgo
! stderr preferlinkext
env CGO_CFLAGS=
# CGO_LDFLAGS with -static variants are not compatible with internal linking.
env CGO_LDFLAGS=-static
go build -x -n -o dummy.exe ./usesInternalCgo
stderr preferlinkext
env CGO_LDFLAGS=--static
go build -x -n -o dummy.exe ./usesInternalCgo
stderr preferlinkext
env CGO_LDFLAGS=-Wl,-static
go build -x -n -o dummy.exe ./usesInternalCgo
stderr preferlinkext
env CGO_LDFLAGS=-Wl,--static
go build -x -n -o dummy.exe ./usesInternalCgo
stderr preferlinkext
env CGO_LDFLAGS=-Wl,-Bstatic
go build -x -n -o dummy.exe ./usesInternalCgo
stderr preferlinkext
env CGO_LDFLAGS=
# CGO_LDFLAGS=-static-libgcc does NOT trigger external linking.
env CGO_LDFLAGS=-static-libgcc
go build -x -n -o dummy.exe ./usesInternalCgo
! stderr preferlinkext
env CGO_LDFLAGS=
# CGO_CFLAGS=--static triggers external linking (some toolchains accept it).
env CGO_CFLAGS=--static
go build -x -n -o dummy.exe ./usesInternalCgo
stderr preferlinkext
env CGO_CFLAGS=
[short] skip
# In the remaining tests below we do actual builds (without -n) to