Summary
Go 1.27 (tip) introduces stricter checklinkname enforcement for assembly symbols via golang/go@aee6009. This causes linker errors when building with CGO_ENABLED=0 on all architectures, because the fakecgo assembly files reference runtime.load_g and runtime.cgocallback, which are now //go:linknamestd (std-only).
Error
# example.com/linkname-repro
link: main: invalid reference to runtime.load_g
After fixing load_g (which can be removed from crosscall2 since cgocallback calls it internally), the next error is:
link: main: invalid reference to runtime.cgocallback
Affected symbols
runtime.load_g — referenced from crosscall2 in asm_{arm64,arm,riscv64,loong64,ppc64le,s390x}.s, and from setg_trampoline in trampolines_{ppc64le,s390x}.s
runtime.cgocallback — referenced from crosscall2 in all asm_*.s files (all 8 architectures)
Both are declared with //go:linknamestd in the Go runtime, meaning only standard library packages can reference them. The runtime/cgo package (which has identical crosscall2 code) works because it is in std.
Minimal reproducer
go.mod:
module example.com/linkname-repro
go 1.24
main.go:
package main
func main() {
crosscall2(0, 0, 0, 0)
}
func crosscall2(fn, a, n, ctxt uintptr)
asm_arm64.s:
#include "textflag.h"
TEXT ·crosscall2(SB), NOSPLIT|NOFRAME, $0
BL runtime·load_g(SB)
BL runtime·cgocallback(SB)
RET
Reproduce:
# Passes on Go 1.26:
docker run --rm --platform linux/arm64 -v "$PWD":/repro -w /repro golang:1.26 go build .
# Fails on Go tip (go1.27-devel):
docker run --rm --platform linux/arm64 -v "$PWD":/repro -w /repro golang:tip go build .
# link: main: invalid reference to runtime.load_g
# Workaround:
docker run --rm --platform linux/arm64 -v "$PWD":/repro -w /repro golang:tip \
go build -ldflags='-checklinkname=0' .
Analysis
The load_g issue can be worked around in the assembly:
- In
crosscall2: remove the load_g call — cgocallback calls it internally
- In
setg_trampoline (ppc64le/s390x only): save the g pointer before calling setg_gcc and assign directly to the g register afterward, instead of round-tripping through TLS via load_g
The cgocallback issue has no assembly-level workaround — it is the core call that crosscall2 must make. Options:
- Request Go upstream change
cgocallback to //go:linkname (open to all) or add an allowlist exception
- Use
-ldflags='-checklinkname=0' as a temporary workaround
- Find an indirect calling mechanism
Cause
golang/go@aee6009 — cmd/link: check linkname access to assembly symbols (cherrymui, May 2026)
Summary
Go 1.27 (tip) introduces stricter
checklinknameenforcement for assembly symbols via golang/go@aee6009. This causes linker errors when building withCGO_ENABLED=0on all architectures, because the fakecgo assembly files referenceruntime.load_gandruntime.cgocallback, which are now//go:linknamestd(std-only).Error
After fixing
load_g(which can be removed fromcrosscall2sincecgocallbackcalls it internally), the next error is:Affected symbols
runtime.load_g— referenced fromcrosscall2inasm_{arm64,arm,riscv64,loong64,ppc64le,s390x}.s, and fromsetg_trampolineintrampolines_{ppc64le,s390x}.sruntime.cgocallback— referenced fromcrosscall2in allasm_*.sfiles (all 8 architectures)Both are declared with
//go:linknamestdin the Go runtime, meaning only standard library packages can reference them. Theruntime/cgopackage (which has identicalcrosscall2code) works because it is in std.Minimal reproducer
go.mod:
main.go:
asm_arm64.s:
Reproduce:
Analysis
The
load_gissue can be worked around in the assembly:crosscall2: remove theload_gcall —cgocallbackcalls it internallysetg_trampoline(ppc64le/s390x only): save the g pointer before callingsetg_gccand assign directly to the g register afterward, instead of round-tripping through TLS viaload_gThe
cgocallbackissue has no assembly-level workaround — it is the core call thatcrosscall2must make. Options:cgocallbackto//go:linkname(open to all) or add an allowlist exception-ldflags='-checklinkname=0'as a temporary workaroundCause
golang/go@aee6009 —
cmd/link: check linkname access to assembly symbols(cherrymui, May 2026)