diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 444d2934338aac476c3c8b85a4a83967cf1d5085..1c63c6ebc7b182fdd11b453993f12986b6bf278c 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -539,7 +539,36 @@ func setup() { // mustLinkExternal is a copy of internal/platform.MustLinkExternal, // duplicated here to avoid version skew in the MustLinkExternal function // during bootstrapping. -func mustLinkExternal(goos, goarch string) bool { +func mustLinkExternal(goos, goarch string, cgoEnabled bool) bool { + if cgoEnabled { + switch goarch { + case "loong64", + "mips", "mipsle", "mips64", "mips64le", + "riscv64": + // Internally linking cgo is incomplete on some architectures. + // https://golang.org/issue/14449 + return true + case "arm64": + if goos == "windows" { + // windows/arm64 internal linking is not implemented. + return true + } + case "ppc64": + // Big Endian PPC64 cgo internal linking is not implemented for aix or linux. + return true + } + + switch goos { + case "android": + return true + case "dragonfly": + // It seems that on Dragonfly thread local storage is + // set up by the dynamic linker, so internal cgo linking + // doesn't work. Test case is "go test runtime/cgo". + return true + } + } + switch goos { case "android": if goarch != "arm64" { @@ -1283,7 +1312,7 @@ func timelog(op, name string) { // to switch between the host and target configurations when cross-compiling. func toolenv() []string { var env []string - if !mustLinkExternal(goos, goarch) { + if !mustLinkExternal(goos, goarch, false) { // Unless the platform requires external linking, // we disable cgo to get static binaries for cmd/go and cmd/pprof, // so that they work on systems without the same dynamic libraries diff --git a/src/cmd/dist/build_test.go b/src/cmd/dist/build_test.go index a97c4cbc324bebd19078db15ecb583eece1d47bc..158ac2678d404122ab54f746239c7b7af219fbe8 100644 --- a/src/cmd/dist/build_test.go +++ b/src/cmd/dist/build_test.go @@ -14,10 +14,12 @@ import ( func TestMustLinkExternal(t *testing.T) { for _, goos := range okgoos { for _, goarch := range okgoarch { - got := mustLinkExternal(goos, goarch) - want := platform.MustLinkExternal(goos, goarch) - if got != want { - t.Errorf("mustLinkExternal(%q, %q) = %v; want %v", goos, goarch, got, want) + for _, cgoEnabled := range []bool{true, false} { + got := mustLinkExternal(goos, goarch, cgoEnabled) + want := platform.MustLinkExternal(goos, goarch, cgoEnabled) + if got != want { + t.Errorf("mustLinkExternal(%q, %q, %v) = %v; want %v", goos, goarch, cgoEnabled, got, want) + } } } } diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go index 3f59b6d41fb741a57029209dc4c37cfe51bacffa..51d1760d9ce2fe457f8354284dfe08358d4f7780 100644 --- a/src/cmd/go/internal/work/gc.go +++ b/src/cmd/go/internal/work/gc.go @@ -643,7 +643,7 @@ func (gcToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) // linker's build id, which will cause our build id to not // match the next time the tool is built. // Rely on the external build id instead. - if !platform.MustLinkExternal(cfg.Goos, cfg.Goarch) { + if !platform.MustLinkExternal(cfg.Goos, cfg.Goarch, false) { ldflags = append(ldflags, "-X=cmd/internal/objabi.buildID="+root.buildID) } } diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go index f2dfa1c1cd986ee28178f3cafe3ec57d2696512c..7cce28dac5205911df458e27f1897271f027befc 100644 --- a/src/cmd/link/internal/ld/config.go +++ b/src/cmd/link/internal/ld/config.go @@ -5,7 +5,6 @@ package ld import ( - "cmd/internal/sys" "fmt" "internal/buildcfg" "internal/platform" @@ -125,7 +124,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) { }() } - if platform.MustLinkExternal(buildcfg.GOOS, buildcfg.GOARCH) { + if platform.MustLinkExternal(buildcfg.GOOS, buildcfg.GOARCH, false) { return true, fmt.Sprintf("%s/%s requires external linking", buildcfg.GOOS, buildcfg.GOARCH) } @@ -137,25 +136,9 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) { return true, "asan" } - // Internally linking cgo is incomplete on some architectures. - // https://golang.org/issue/14449 - if iscgo && ctxt.Arch.InFamily(sys.Loong64, sys.MIPS64, sys.MIPS, sys.RISCV64) { + if iscgo && platform.MustLinkExternal(buildcfg.GOOS, buildcfg.GOARCH, true) { return true, buildcfg.GOARCH + " does not support internal cgo" } - if iscgo && (buildcfg.GOOS == "android" || buildcfg.GOOS == "dragonfly") { - // It seems that on Dragonfly thread local storage is - // set up by the dynamic linker, so internal cgo linking - // doesn't work. Test case is "go test runtime/cgo". - return true, buildcfg.GOOS + " does not support internal cgo" - } - if iscgo && buildcfg.GOOS == "windows" && buildcfg.GOARCH == "arm64" { - // windows/arm64 internal linking is not implemented. - return true, buildcfg.GOOS + "/" + buildcfg.GOARCH + " does not support internal cgo" - } - if iscgo && ctxt.Arch == sys.ArchPPC64 { - // Big Endian PPC64 cgo internal linking is not implemented for aix or linux. - return true, buildcfg.GOOS + " does not support internal cgo" - } // Some build modes require work the internal linker cannot do (yet). switch ctxt.BuildMode { @@ -164,12 +147,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) { case BuildModeCShared: return true, "buildmode=c-shared" case BuildModePIE: - switch buildcfg.GOOS + "/" + buildcfg.GOARCH { - case "android/arm64": - case "linux/amd64", "linux/arm64", "linux/ppc64le": - case "windows/386", "windows/amd64", "windows/arm", "windows/arm64": - case "darwin/amd64", "darwin/arm64": - default: + if !platform.InternalLinkPIESupported(buildcfg.GOOS, buildcfg.GOARCH) { // Internal linking does not support TLS_IE. return true, "buildmode=pie" } diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go index 6fac85a01d8140c2c50441e1129bdecc831b48ca..ee3ea9d175b4ff4d27988fc780b1755104a925d8 100644 --- a/src/cmd/link/internal/ld/dwarf_test.go +++ b/src/cmd/link/internal/ld/dwarf_test.go @@ -274,7 +274,7 @@ func TestSizes(t *testing.T) { } // External linking may bring in C symbols with unknown size. Skip. - testenv.MustInternalLink(t) + testenv.MustInternalLink(t, false) t.Parallel() @@ -882,7 +882,7 @@ func TestAbstractOriginSanityIssue26237(t *testing.T) { func TestRuntimeTypeAttrInternal(t *testing.T) { testenv.MustHaveGoBuild(t) - testenv.MustInternalLink(t) + testenv.MustInternalLink(t, false) if runtime.GOOS == "plan9" { t.Skip("skipping on plan9; no DWARF symbol table in executables") @@ -1165,7 +1165,7 @@ func main() { // TODO: maybe there is some way to tell the external linker not to put // those symbols in the executable's symbol table? Prefix the symbol name // with "." or "L" to pretend it is a label? - if !testenv.CanInternalLink() { + if !testenv.CanInternalLink(false) { return } diff --git a/src/cmd/link/internal/ld/ld_test.go b/src/cmd/link/internal/ld/ld_test.go index 314dab7d7daeff1d95a35bacb9be7485a490bc83..22bc11eff3d41da03e59bbf497cc580239351112 100644 --- a/src/cmd/link/internal/ld/ld_test.go +++ b/src/cmd/link/internal/ld/ld_test.go @@ -21,7 +21,7 @@ func TestUndefinedRelocErrors(t *testing.T) { // When external linking, symbols may be defined externally, so we allow // undefined symbols and let external linker resolve. Skip the test. - testenv.MustInternalLink(t) + testenv.MustInternalLink(t, false) t.Parallel() diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go index b4ef9ada17c6826bba48df8d9406175f94dba2d2..121ef958532fbcd44c621e7fbb724e7cb3762cba 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go @@ -176,19 +176,7 @@ main.x: relocation target main.zero not defined func TestIssue33979(t *testing.T) { testenv.MustHaveGoBuild(t) testenv.MustHaveCGO(t) - testenv.MustInternalLink(t) - - // Skip test on platforms that do not support cgo internal linking. - switch runtime.GOARCH { - case "loong64": - t.Skipf("Skipping on %s/%s", runtime.GOOS, runtime.GOARCH) - case "mips", "mipsle", "mips64", "mips64le": - t.Skipf("Skipping on %s/%s", runtime.GOOS, runtime.GOARCH) - } - if runtime.GOOS == "aix" || - runtime.GOOS == "windows" && runtime.GOARCH == "arm64" { - t.Skipf("Skipping on %s/%s", runtime.GOOS, runtime.GOARCH) - } + testenv.MustInternalLink(t, true) t.Parallel() @@ -751,8 +739,8 @@ func TestTrampolineCgo(t *testing.T) { // Test internal linking mode. - if runtime.GOARCH == "ppc64" || (runtime.GOARCH == "arm64" && runtime.GOOS == "windows") || !testenv.CanInternalLink() { - return // internal linking cgo is not supported + if !testenv.CanInternalLink(true) { + continue } cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src) out, err = cmd.CombinedOutput() diff --git a/src/cmd/nm/nm_cgo_test.go b/src/cmd/nm/nm_cgo_test.go index 210577e6f77697887355625808f0d2122f252ab6..face58c311f7b50a937c7a2d49a959d8acff5e78 100644 --- a/src/cmd/nm/nm_cgo_test.go +++ b/src/cmd/nm/nm_cgo_test.go @@ -2,56 +2,25 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build cgo - package main import ( - "runtime" + "internal/testenv" "testing" ) -func canInternalLink() bool { - switch runtime.GOOS { - case "aix": - return false - case "dragonfly": - return false - case "freebsd": - switch runtime.GOARCH { - case "arm64", "riscv64": - return false - } - case "linux": - switch runtime.GOARCH { - case "arm64", "loong64", "mips64", "mips64le", "mips", "mipsle", "ppc64", "ppc64le", "riscv64": - return false - } - case "openbsd": - switch runtime.GOARCH { - case "arm64", "mips64": - return false - } - case "windows": - switch runtime.GOARCH { - case "arm64": - return false - } - } - return true -} - func TestInternalLinkerCgoExec(t *testing.T) { - if !canInternalLink() { - t.Skip("skipping; internal linking is not supported") - } + testenv.MustHaveCGO(t) + testenv.MustInternalLink(t, true) testGoExec(t, true, false) } func TestExternalLinkerCgoExec(t *testing.T) { + testenv.MustHaveCGO(t) testGoExec(t, true, true) } func TestCgoLib(t *testing.T) { + testenv.MustHaveCGO(t) testGoLib(t, true) } diff --git a/src/internal/platform/supported.go b/src/internal/platform/supported.go index 71bf1c54771a3677738420f529dbfa056c63737b..8bf68a6d5844f860f3089fb39d9fa25507a1e7bd 100644 --- a/src/internal/platform/supported.go +++ b/src/internal/platform/supported.go @@ -71,8 +71,39 @@ func FuzzInstrumented(goos, goarch string) bool { } } -// MustLinkExternal reports whether goos/goarch requires external linking. -func MustLinkExternal(goos, goarch string) bool { +// MustLinkExternal reports whether goos/goarch requires external linking +// with or without cgo dependencies. +func MustLinkExternal(goos, goarch string, withCgo bool) bool { + if withCgo { + switch goarch { + case "loong64", + "mips", "mipsle", "mips64", "mips64le", + "riscv64": + // Internally linking cgo is incomplete on some architectures. + // https://go.dev/issue/14449 + return true + case "arm64": + if goos == "windows" { + // windows/arm64 internal linking is not implemented. + return true + } + case "ppc64": + // Big Endian PPC64 cgo internal linking is not implemented for aix or linux. + // https://go.dev/issue/8912 + return true + } + + switch goos { + case "android": + return true + case "dragonfly": + // It seems that on Dragonfly thread local storage is + // set up by the dynamic linker, so internal cgo linking + // doesn't work. Test case is "go test runtime/cgo". + return true + } + } + switch goos { case "android": if goarch != "arm64" { @@ -178,10 +209,10 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { func InternalLinkPIESupported(goos, goarch string) bool { switch goos + "/" + goarch { - case "darwin/amd64", "darwin/arm64", + case "android/arm64", + "darwin/amd64", "darwin/arm64", "linux/amd64", "linux/arm64", "linux/ppc64le", - "android/arm64", - "windows-amd64", "windows-386", "windows-arm": + "windows/386", "windows/amd64", "windows/arm", "windows/arm64": return true } return false diff --git a/src/internal/testenv/testenv.go b/src/internal/testenv/testenv.go index 65a82fd5f767d05901083e0528e2c0743692a9c2..816a1a100f5540e8222838a0a6539e1db8ad5d00 100644 --- a/src/internal/testenv/testenv.go +++ b/src/internal/testenv/testenv.go @@ -288,15 +288,18 @@ func MustHaveCGO(t testing.TB) { // CanInternalLink reports whether the current system can link programs with // internal linking. -func CanInternalLink() bool { - return !platform.MustLinkExternal(runtime.GOOS, runtime.GOARCH) +func CanInternalLink(withCgo bool) bool { + return !platform.MustLinkExternal(runtime.GOOS, runtime.GOARCH, withCgo) } // MustInternalLink checks that the current system can link programs with internal // linking. // If not, MustInternalLink calls t.Skip with an explanation. -func MustInternalLink(t testing.TB) { - if !CanInternalLink() { +func MustInternalLink(t testing.TB, withCgo bool) { + if !CanInternalLink(withCgo) { + if withCgo && CanInternalLink(false) { + t.Skipf("skipping test: internal linking on %s/%s is not supported with cgo", runtime.GOOS, runtime.GOARCH) + } t.Skipf("skipping test: internal linking on %s/%s is not supported", runtime.GOOS, runtime.GOARCH) } } diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go index 9f9cb598d8a92fc2bf0b2713390e0a8eeba14ee8..67cd446f420e8ae1dad83ca2f19aea03f8d5d113 100644 --- a/src/os/exec/exec_test.go +++ b/src/os/exec/exec_test.go @@ -694,7 +694,7 @@ func TestExtraFiles(t *testing.T) { // This test runs with cgo disabled. External linking needs cgo, so // it doesn't work if external linking is required. - testenv.MustInternalLink(t) + testenv.MustInternalLink(t, false) if runtime.GOOS == "windows" { t.Skipf("skipping test on %q", runtime.GOOS) diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index a2f09265993cb14967f1b5d5d791d410bbbdc54f..3a64c30e2b4e8b3c770e1036102c4d3d07c5b838 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -184,7 +184,7 @@ func TestCrashHandler(t *testing.T) { func testDeadlock(t *testing.T, name string) { // External linking brings in cgo, causing deadlock detection not working. - testenv.MustInternalLink(t) + testenv.MustInternalLink(t, false) output := runTestProg(t, "testprog", name) want := "fatal error: all goroutines are asleep - deadlock!\n" @@ -211,7 +211,7 @@ func TestLockedDeadlock2(t *testing.T) { func TestGoexitDeadlock(t *testing.T) { // External linking brings in cgo, causing deadlock detection not working. - testenv.MustInternalLink(t) + testenv.MustInternalLink(t, false) output := runTestProg(t, "testprog", "GoexitDeadlock") want := "no goroutines (main called runtime.Goexit) - deadlock!" @@ -311,7 +311,7 @@ panic: third panic func TestGoexitCrash(t *testing.T) { // External linking brings in cgo, causing deadlock detection not working. - testenv.MustInternalLink(t) + testenv.MustInternalLink(t, false) output := runTestProg(t, "testprog", "GoexitExit") want := "no goroutines (main called runtime.Goexit) - deadlock!" @@ -372,7 +372,7 @@ func TestBreakpoint(t *testing.T) { func TestGoexitInPanic(t *testing.T) { // External linking brings in cgo, causing deadlock detection not working. - testenv.MustInternalLink(t) + testenv.MustInternalLink(t, false) // see issue 8774: this code used to trigger an infinite recursion output := runTestProg(t, "testprog", "GoexitInPanic") @@ -439,7 +439,7 @@ func TestPanicAfterGoexit(t *testing.T) { func TestRecoveredPanicAfterGoexit(t *testing.T) { // External linking brings in cgo, causing deadlock detection not working. - testenv.MustInternalLink(t) + testenv.MustInternalLink(t, false) output := runTestProg(t, "testprog", "RecoveredPanicAfterGoexit") want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" @@ -450,7 +450,7 @@ func TestRecoveredPanicAfterGoexit(t *testing.T) { func TestRecoverBeforePanicAfterGoexit(t *testing.T) { // External linking brings in cgo, causing deadlock detection not working. - testenv.MustInternalLink(t) + testenv.MustInternalLink(t, false) t.Parallel() output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit") @@ -462,7 +462,7 @@ func TestRecoverBeforePanicAfterGoexit(t *testing.T) { func TestRecoverBeforePanicAfterGoexit2(t *testing.T) { // External linking brings in cgo, causing deadlock detection not working. - testenv.MustInternalLink(t) + testenv.MustInternalLink(t, false) t.Parallel() output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit2") diff --git a/src/runtime/time_test.go b/src/runtime/time_test.go index afd9af2af4796537a63f10d8f3e66d023d9e26af..f08682055b3d26b41792d03025a6b0a591564cc5 100644 --- a/src/runtime/time_test.go +++ b/src/runtime/time_test.go @@ -22,7 +22,7 @@ func TestFakeTime(t *testing.T) { // Faketime is advanced in checkdead. External linking brings in cgo, // causing checkdead not working. - testenv.MustInternalLink(t) + testenv.MustInternalLink(t, false) t.Parallel()