| # Test case to verify that when we have a package that uses CGO in |
| # combination with selected "unusual" flags (involving plugins, LTO) |
| # that we force external linking. See related |
| # issues 58619, 58620, and 58848. |
| |
| [compiler:gccgo] skip # only external linking for gccgo |
| |
| [!cgo] skip 'test verifies behavior that depends on CGO_CFLAGS' |
| [mustlinkext] skip 'test expects internal linking for non-cgo programs' |
| |
| # Here we build three program: one with explicit CGO use, one with no |
| # CGO use, and one that uses a stdlib package ("runtime/cgo") that has |
| # CGO in it. It used to be that only the explicit use of CGO would |
| # trigger external linking, and that the program that only used |
| # "runtime/cgo" would always be handled with internal linking. This caused |
| # issues when users included odd/unusual flags (ex: -fplugin, -flto) |
| # in CGO_CFLAGS, causing the Go linker to have to read and interpret |
| # non-standard host objects. |
| # |
| # As of 1.21 we continue to use internal linking for programs whose |
| # CGO use comes only from stdlib packages in the absence of any flag |
| # funny business, however if the Go command sees flags that may be suspicious, |
| # it signals the Go linker to invoke the external linker. |
| |
| # The next few tests run builds passing "-n" to the Go command, then |
| # checking the output to see if the Go command is trying to pass a |
| # "preferlinkext" token to the linker to request external linking. |
| |
| #----------------------- |
| |
| # Use a fresh GOCACHE for these next steps, so as to have the real |
| # actions for the runtime/cgo package appear in the "-n -x" output. |
| env GOCACHE=$WORK/gocache |
| mkdir $GOCACHE |
| |
| # First build: there is no CGO in use, so no token should be present regardless |
| # of weird CGO flags. |
| go build -x -n -o dummy.exe ./noUseOfCgo |
| ! stderr preferlinkext |
| env CGO_CFLAGS=-flto |
| go build -x -n -o dummy.exe ./noUseOfCgo |
| ! stderr preferlinkext |
| env CGO_CFLAGS= |
| |
| # Second build uses CGO, so we expect to see the token present in the |
| # -n output only when strange flags are used. |
| go build -x -n -o dummy.exe ./usesInternalCgo |
| ! stderr preferlinkext |
| env CGO_CFLAGS=-flto |
| go build -x -n -o dummy.exe ./usesInternalCgo |
| stderr preferlinkext |
| env CGO_CFLAGS=-fplugin |
| go build -x -n -o dummy.exe ./usesInternalCgo |
| stderr preferlinkext |
| env CGO_CFLAGS=-fprofile-instr-generate |
| go build -x -n -o dummy.exe ./usesInternalCgo |
| stderr preferlinkext |
| |
| # The -fdebug-prefix-map=path is permitted for internal linking. |
| env CGO_CFLAGS=-fdebug-prefix-map=/some/sandbox/execroot/workspace=/tmp/new |
| go build -x -n -o dummy.exe ./usesInternalCgo |
| ! stderr preferlinkext |
| env CGO_CFLAGS=-fdebug-prefix-map=/Users/someone/.cache/bazel/_bazel_someone/3fa7e4650c43657ead684537951f49e2/sandbox/linux-sandbox/10/execroot/rules_go_static=. |
| go build -x -n -o dummy.exe ./usesInternalCgo |
| ! stderr preferlinkext |
| # The -ffile-prefix-map=path is permitted for internal linking too. |
| env CGO_CFLAGS=-ffile-prefix-map=/Users/someone/.cache/bazel/_bazel_someone/3fa7e4650c43657ead684537951f49e2/sandbox/linux-sandbox/10/execroot/rules_go_static/bazel-out/aarch64-fastbuild-ST-b33d65c724e6/bin/external/io_bazel_rules_go/stdlib_=. |
| go build -x -n -o dummy.exe ./usesInternalCgo |
| ! stderr preferlinkext |
| # Verifying that -fdebug-prefix-map=path, -ffile-prefix-map, -no-canonical-prefixes |
| # and -fno-canonical-systemd-headers are permitted for internal linking. |
| env CGO_CFLAGS=-fdebug-prefix-map=old=/tmp/new |
| go build -x -n -o dummy.exe ./usesInternalCgo |
| ! stderr preferlinkext |
| env CGO_CFLAGS=-ffile-prefix-map=/Users/someone/_11233/things=new |
| go build -x -n -o dummy.exe ./usesInternalCgo |
| ! stderr preferlinkext |
| env CGO_CFLAGS=-no-canonical-prefixes |
| go build -x -n -o dummy.exe ./usesInternalCgo |
| ! stderr preferlinkext |
| env CGO_CFLAGS=-fno-canonical-system-headers |
| 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 |
| # verify that the Go linker is going the right thing in addition to the |
| # Go command. Here the idea is to pass "-tmpdir" to the linker, then |
| # check after the link is done for the presence of the file |
| # <tmpdir>/go.o, which the Go linker creates prior to kicking off the |
| # external linker. |
| |
| mkdir tmp1 |
| mkdir tmp2 |
| mkdir tmp3 |
| mkdir tmp4 |
| mkdir tmp5 |
| |
| # First build: no external linking expected |
| go build -ldflags=-tmpdir=tmp1 -o $devnull ./noUseOfCgo & |
| |
| # Second build: using only "runtime/cgo", expect internal linking. |
| go build -ldflags=-tmpdir=tmp2 -o $devnull ./usesInternalCgo & |
| |
| # Third build: program uses only "runtime/cgo", so we would normally |
| # expect internal linking, except that cflags contain suspicious entries |
| # (in this case, a flag that does not appear on the allow list). |
| env CGO_CFLAGS=-fmerge-all-constants |
| env CGO_LDFLAGS=-fmerge-all-constants |
| go build -ldflags=-tmpdir=tmp3 -o $devnull ./usesInternalCgo & |
| env CGO_CFLAGS= |
| env CGO_LDFLAGS= |
| |
| # Fourth build: explicit CGO, expect external linking. |
| go build -ldflags=-tmpdir=tmp4 -o $devnull ./usesExplicitCgo & |
| |
| # Fifth build: explicit CGO, but we specifically asked for internal linking |
| # via a flag, so using internal linking it is. |
| [cgolinkext] go list ./usesInternalCgo |
| [!cgolinkext] go build '-ldflags=-tmpdir=tmp5 -linkmode=internal' -o $devnull ./usesInternalCgo & |
| |
| # Sixth build: explicit CGO use in a non-main package. |
| go build -o p.a ./nonMainPackageUsesExplicitCgo & |
| |
| wait |
| |
| # Check first build: no external linking expected |
| ! exists tmp1/go.o |
| |
| # Check second build: using only "runtime/cgo", expect internal linking. |
| [!cgolinkext] ! exists tmp2/go.o |
| [cgolinkext] exists tmp2/go.o |
| |
| # Check third build: has suspicious flag. |
| exists tmp3/go.o |
| |
| # Fourth build: explicit CGO, expect external linking. |
| exists tmp4/go.o |
| |
| # Fifth build: explicit CGO, -linkmode=internal. |
| ! exists tmp5/go.o |
| |
| # Sixth build: make sure that "go tool nm" doesn't get confused |
| # by the presence of the "preferlinkext" sentinel. |
| go tool nm p.a |
| |
| -- go.mod -- |
| |
| module cgo.example |
| |
| go 1.20 |
| |
| -- noUseOfCgo/main.go -- |
| |
| package main |
| |
| func main() { |
| println("clean as a whistle") |
| } |
| |
| -- usesInternalCgo/main.go -- |
| |
| package main |
| |
| import ( |
| "runtime/cgo" |
| ) |
| |
| func main() { |
| q := "hello" |
| h := cgo.NewHandle(q) |
| h.Delete() |
| } |
| |
| -- usesExplicitCgo/main.go -- |
| |
| package main |
| |
| /* |
| int meaningOfLife() { return 42; } |
| */ |
| import "C" |
| |
| func main() { |
| println(C.meaningOfLife()) |
| } |
| |
| -- nonMainPackageUsesExplicitCgo/main.go -- |
| |
| package p |
| |
| /* |
| int meaningOfLife() { return 42; } |
| */ |
| import "C" |
| |
| func PrintIt() { |
| println(C.meaningOfLife()) |
| } |