From: Motiejus Jakštys Date: Wed, 25 May 2022 02:55:34 +0000 (+0000) Subject: cmd/cgo: use --no-gc-sections if available X-Git-Tag: go1.19beta1~117 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=16e7613b2e1861b25c6549051337d1d8a850cf63;p=gostls13.git cmd/cgo: use --no-gc-sections if available zig cc passes `--gc-sections` to the underlying linker, which then causes undefined symbol errors when compiling with cgo but without C code. Add `-Wl,--no-gc-sections` to make it work with zig cc. Minimal example: **main.go** package main import _ "runtime/cgo" func main() {} Run (works after the patch, doesn't work before): CC="zig cc" go build main.go Among the existing code, `src/runtime/testdata/testprognet` fails to build: src/runtime/testdata/testprognet$ CC="zig cc" go build . net(.text): relocation target __errno_location not defined net(.text): relocation target getaddrinfo not defined net(.text): relocation target freeaddrinfo not defined net(.text): relocation target gai_strerror not defined runtime/cgo(.text): relocation target stderr not defined runtime/cgo(.text): relocation target fwrite not defined runtime/cgo(.text): relocation target vfprintf not defined runtime/cgo(.text): relocation target fputc not defined runtime/cgo(.text): relocation target abort not defined runtime/cgo(.text): relocation target pthread_create not defined runtime/cgo(.text): relocation target nanosleep not defined runtime/cgo(.text): relocation target pthread_detach not defined runtime/cgo(.text): relocation target stderr not defined runtime/cgo(.text): relocation target strerror not defined runtime/cgo(.text): relocation target fprintf not defined runtime/cgo(.text): relocation target abort not defined runtime/cgo(.text): relocation target pthread_mutex_lock not defined runtime/cgo(.text): relocation target pthread_cond_wait not defined runtime/cgo(.text): relocation target pthread_mutex_unlock not defined runtime/cgo(.text): relocation target pthread_cond_broadcast not defined runtime/cgo(.text): relocation target malloc not defined With the patch both examples build as expected. @ianlancetaylor suggested: > It would be fine with me if somebody wants to send a cgo patch that passes -Wl,--no-gc-sections, with a fallback if that option is not supported. ... and this is what we are doing. Tested with zig 0.10.0-dev.2252+a4369918b This is a continuation of CL 405414: the original one broke AIX and iOS builds. To fix that, added `unknown option` to the list of strings under lookup. Fixes #52690 Change-Id: Id6743e1e759a02627b0fc6d2ac89bb69b706d04c GitHub-Last-Rev: 86f227a14e9f326f1b461b641e4865bc4dc70780 GitHub-Pull-Request: golang/go#53028 Reviewed-on: https://go-review.googlesource.com/c/go/+/407814 Reviewed-by: Cherry Mui Auto-Submit: Ian Lance Taylor Reviewed-by: Bryan Mills Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor --- diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index 15b9e1ef45..2becc6d946 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -2528,6 +2528,13 @@ func (b *Builder) compilerCmd(compiler []string, incdir, workdir string) []strin a = append(a, "-Qunused-arguments") } + // zig cc passes --gc-sections to the underlying linker, which then causes + // undefined symbol errors when compiling with cgo but without C code. + // https://github.com/golang/go/issues/52690 + if b.gccSupportsFlag(compiler, "-Wl,--no-gc-sections") { + a = append(a, "-Wl,--no-gc-sections") + } + // disable word wrapping in error messages a = append(a, "-fmessage-length=0") @@ -2584,7 +2591,12 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool { } tmp := os.DevNull - if runtime.GOOS == "windows" { + + // On the iOS builder the command + // $CC -Wl,--no-gc-sections -x c - -o /dev/null < /dev/null + // is failing with: + // Unable to remove existing file: Invalid argument + if runtime.GOOS == "windows" || runtime.GOOS == "ios" { f, err := os.CreateTemp(b.WorkDir, "") if err != nil { return false @@ -2594,13 +2606,21 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool { defer os.Remove(tmp) } - // We used to write an empty C file, but that gets complicated with - // go build -n. We tried using a file that does not exist, but that - // fails on systems with GCC version 4.2.1; that is the last GPLv2 - // version of GCC, so some systems have frozen on it. - // Now we pass an empty file on stdin, which should work at least for - // GCC and clang. - cmdArgs := str.StringList(compiler, flag, "-c", "-x", "c", "-", "-o", tmp) + // We used to write an empty C file, but that gets complicated with go + // build -n. We tried using a file that does not exist, but that fails on + // systems with GCC version 4.2.1; that is the last GPLv2 version of GCC, + // so some systems have frozen on it. Now we pass an empty file on stdin, + // which should work at least for GCC and clang. + // + // If the argument is "-Wl,", then it's testing the linker. In that case, + // skip "-c". If it's not "-Wl,", then we are testing the compiler and + // can emit the linking step with "-c". + cmdArgs := str.StringList(compiler, flag) + if !strings.HasPrefix(flag, "-Wl,") /* linker flag */ { + cmdArgs = append(cmdArgs, "-c") + } + cmdArgs = append(cmdArgs, "-x", "c", "-", "-o", tmp) + if cfg.BuildN || cfg.BuildX { b.Showcmd(b.WorkDir, "%s || true", joinUnambiguously(cmdArgs)) if cfg.BuildN { @@ -2613,12 +2633,16 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool { out, _ := cmd.CombinedOutput() // GCC says "unrecognized command line option". // clang says "unknown argument". + // tcc says "unsupported" + // AIX says "not recognized" // Older versions of GCC say "unrecognised debug output level". // For -fsplit-stack GCC says "'-fsplit-stack' is not supported". supported := !bytes.Contains(out, []byte("unrecognized")) && !bytes.Contains(out, []byte("unknown")) && !bytes.Contains(out, []byte("unrecognised")) && - !bytes.Contains(out, []byte("is not supported")) + !bytes.Contains(out, []byte("is not supported")) && + !bytes.Contains(out, []byte("not recognized")) && + !bytes.Contains(out, []byte("unsupported")) b.flagCache[key] = supported return supported }