In Go 1.27, the cgo tool gains global GCC-compatible @file response file expansion — all arguments starting with @ are expanded (tokenized by whitespace/quoting rules) before flag parsing begins (via the change to src/cmd/internal/objabi/flag.go in CL 737500).
Rules_go passes ldflags to cgo as -ldflags @, writing the file as a plain space-joined string:
-fsanitize-undefined-strip-path-components=0 -lm -lpthread
Before Go 1.27: cgo handled @ within its own -ldflags flag handler, reading the file content as a single raw string. The ldflags string was passed correctly to the linker.
Go 1.27+: cgo now expands @ globally before flag parsing. The file is GCC-tokenized on whitespace, injecting multiple tokens into cgo's own argument list:
# original args passed by rules_go
cgo -ldflags @/tmp/cgo-ldflags-xxx.txt -- -some-cflag file.go
# after @file expansion in Go 1.27
cgo -ldflags -fsanitize-undefined-strip-path-components=0 -lm -lpthread -- -some-cflag file.go
-ldflags consumes the first token as its value; the remaining tokens (-lm, -lpthread, etc.) are parsed as unknown cgo flags, producing:
flag provided but not defined: -fsanitize-undefined-strip-path-components
The fix: wrap the file content in double quotes so GCC tokenization produces exactly one token — the full quoted string becomes the value of -ldflags:
"-fsanitize-undefined-strip-path-components=0 -lm -lpthread"
In Go 1.27, the cgo tool gains global GCC-compatible @file response file expansion — all arguments starting with @ are expanded (tokenized by whitespace/quoting rules) before flag parsing begins (via the change to src/cmd/internal/objabi/flag.go in CL 737500).
Rules_go passes ldflags to cgo as -ldflags @, writing the file as a plain space-joined string:
Before Go 1.27: cgo handled @ within its own -ldflags flag handler, reading the file content as a single raw string. The ldflags string was passed correctly to the linker.
Go 1.27+: cgo now expands @ globally before flag parsing. The file is GCC-tokenized on whitespace, injecting multiple tokens into cgo's own argument list:
-ldflags consumes the first token as its value; the remaining tokens (-lm, -lpthread, etc.) are parsed as unknown cgo flags, producing:
The fix: wrap the file content in double quotes so GCC tokenization produces exactly one token — the full quoted string becomes the value of -ldflags: