Skip to content

Commit 55004bc

Browse files
committed
cmd/cgo: use -Wl,--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 Fixes #52690
1 parent 5f2fdbe commit 55004bc

File tree

2 files changed

+49
-18
lines changed

2 files changed

+49
-18
lines changed

src/cmd/go/internal/work/exec.go

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2342,7 +2342,7 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s
23422342
// TODO(golang.org/issue/36072): cgo also generates files with #line
23432343
// directives pointing to the source directory. It should not generate those
23442344
// when -trimpath is enabled.
2345-
if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
2345+
if b.gccSupportsCompilerFlag(compiler, "-fdebug-prefix-map=a=b") {
23462346
if cfg.BuildTrimpath || p.Goroot {
23472347
// Keep in sync with Action.trimpath.
23482348
// The trimmed paths are a little different, but we need to trim in the
@@ -2520,19 +2520,26 @@ func (b *Builder) compilerCmd(compiler []string, incdir, workdir string) []strin
25202520
}
25212521

25222522
// disable ASCII art in clang errors, if possible
2523-
if b.gccSupportsFlag(compiler, "-fno-caret-diagnostics") {
2523+
if b.gccSupportsCompilerFlag(compiler, "-fno-caret-diagnostics") {
25242524
a = append(a, "-fno-caret-diagnostics")
25252525
}
25262526
// clang is too smart about command-line arguments
2527-
if b.gccSupportsFlag(compiler, "-Qunused-arguments") {
2527+
if b.gccSupportsCompilerFlag(compiler, "-Qunused-arguments") {
25282528
a = append(a, "-Qunused-arguments")
25292529
}
25302530

2531+
// zig cc passes --gc-sections to the underlying linker, which then causes
2532+
// undefined symbol errors when compiling with cgo but without C code.
2533+
// https://github.com/golang/go/issues/52690
2534+
if b.gccSupportsLinkerFlag(compiler, "-Wl,--no-gc-sections") {
2535+
a = append(a, "-Wl,--no-gc-sections")
2536+
}
2537+
25312538
// disable word wrapping in error messages
25322539
a = append(a, "-fmessage-length=0")
25332540

25342541
// Tell gcc not to include the work directory in object files.
2535-
if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
2542+
if b.gccSupportsCompilerFlag(compiler, "-fdebug-prefix-map=a=b") {
25362543
if workdir == "" {
25372544
workdir = b.WorkDir
25382545
}
@@ -2542,7 +2549,7 @@ func (b *Builder) compilerCmd(compiler []string, incdir, workdir string) []strin
25422549

25432550
// Tell gcc not to include flags in object files, which defeats the
25442551
// point of -fdebug-prefix-map above.
2545-
if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") {
2552+
if b.gccSupportsCompilerFlag(compiler, "-gno-record-gcc-switches") {
25462553
a = append(a, "-gno-record-gcc-switches")
25472554
}
25482555

@@ -2561,17 +2568,36 @@ func (b *Builder) compilerCmd(compiler []string, incdir, workdir string) []strin
25612568
// -no-pie must be passed when doing a partial link with -Wl,-r.
25622569
// But -no-pie is not supported by all compilers, and clang spells it -nopie.
25632570
func (b *Builder) gccNoPie(linker []string) string {
2564-
if b.gccSupportsFlag(linker, "-no-pie") {
2571+
if b.gccSupportsCompilerFlag(linker, "-no-pie") {
25652572
return "-no-pie"
25662573
}
2567-
if b.gccSupportsFlag(linker, "-nopie") {
2574+
if b.gccSupportsCompilerFlag(linker, "-nopie") {
25682575
return "-nopie"
25692576
}
25702577
return ""
25712578
}
25722579

2573-
// gccSupportsFlag checks to see if the compiler supports a flag.
2574-
func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool {
2580+
// gccSupportsCompilerFlag checks to see if the compiler supports a flag.
2581+
// Does not apply to linker settings (-Wl,...)
2582+
func (b *Builder) gccSupportsCompilerFlag(compiler []string, flag string) bool {
2583+
if strings.HasPrefix(flag, "-Wl,") {
2584+
base.Fatalf("use gccSupportsLinkerFlag for %q", flag)
2585+
}
2586+
return b.gccSupportsFlag(compiler, false, flag)
2587+
}
2588+
2589+
// gccSupportsLinkerFlag checks to see if compiler's linker supports a flag.
2590+
// Flags need to be prefixed with `-Wl,<...>`.
2591+
func (b *Builder) gccSupportsLinkerFlag(compiler []string, flag string) bool {
2592+
if !strings.HasPrefix(flag, "-Wl,") {
2593+
base.Fatalf("use gccSupportsCompilerFlag for %q", flag)
2594+
}
2595+
return b.gccSupportsFlag(compiler, true, flag)
2596+
}
2597+
2598+
// gccSupportsFlag should not be used; use gccSupportsCompilerFlag or
2599+
// gccSupportsLinkerFlag
2600+
func (b *Builder) gccSupportsFlag(compiler []string, link bool, flag string) bool {
25752601
key := [2]string{compiler[0], flag}
25762602

25772603
b.exec.Lock()
@@ -2600,7 +2626,12 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool {
26002626
// version of GCC, so some systems have frozen on it.
26012627
// Now we pass an empty file on stdin, which should work at least for
26022628
// GCC and clang.
2603-
cmdArgs := str.StringList(compiler, flag, "-c", "-x", "c", "-", "-o", tmp)
2629+
cmdArgs := str.StringList(compiler, flag)
2630+
if !link {
2631+
cmdArgs = append(cmdArgs, "-c")
2632+
}
2633+
cmdArgs = append(cmdArgs, "-x", "c", "-", "-o", tmp)
2634+
26042635
if cfg.BuildN || cfg.BuildX {
26052636
b.Showcmd(b.WorkDir, "%s || true", joinUnambiguously(cmdArgs))
26062637
if cfg.BuildN {
@@ -2792,7 +2823,7 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
27922823
}
27932824

27942825
if cfg.BuildToolchainName == "gccgo" {
2795-
if b.gccSupportsFlag([]string{BuildToolchain.compiler()}, "-fsplit-stack") {
2826+
if b.gccSupportsCompilerFlag([]string{BuildToolchain.compiler()}, "-fsplit-stack") {
27962827
cgoCFLAGS = append(cgoCFLAGS, "-fsplit-stack")
27972828
}
27982829
cgoflags = append(cgoflags, "-gccgo")

src/cmd/go/internal/work/gccgo.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg,
8080

8181
args := str.StringList(tools.compiler(), "-c", gcargs, "-o", ofile, forcedGccgoflags)
8282
if importcfg != nil {
83-
if b.gccSupportsFlag(args[:1], "-fgo-importcfg=/dev/null") {
83+
if b.gccSupportsCompilerFlag(args[:1], "-fgo-importcfg=/dev/null") {
8484
if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
8585
return "", nil, err
8686
}
@@ -93,14 +93,14 @@ func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg,
9393
args = append(args, "-I", root)
9494
}
9595
}
96-
if embedcfg != nil && b.gccSupportsFlag(args[:1], "-fgo-embedcfg=/dev/null") {
96+
if embedcfg != nil && b.gccSupportsCompilerFlag(args[:1], "-fgo-embedcfg=/dev/null") {
9797
if err := b.writeFile(objdir+"embedcfg", embedcfg); err != nil {
9898
return "", nil, err
9999
}
100100
args = append(args, "-fgo-embedcfg="+objdir+"embedcfg")
101101
}
102102

103-
if b.gccSupportsFlag(args[:1], "-ffile-prefix-map=a=b") {
103+
if b.gccSupportsCompilerFlag(args[:1], "-ffile-prefix-map=a=b") {
104104
if cfg.BuildTrimpath {
105105
args = append(args, "-ffile-prefix-map="+base.Cwd()+"=.")
106106
args = append(args, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
@@ -567,17 +567,17 @@ func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error
567567
defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
568568
}
569569
compiler := envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
570-
if b.gccSupportsFlag(compiler, "-fsplit-stack") {
570+
if b.gccSupportsCompilerFlag(compiler, "-fsplit-stack") {
571571
defs = append(defs, "-fsplit-stack")
572572
}
573573
defs = tools.maybePIC(defs)
574-
if b.gccSupportsFlag(compiler, "-ffile-prefix-map=a=b") {
574+
if b.gccSupportsCompilerFlag(compiler, "-ffile-prefix-map=a=b") {
575575
defs = append(defs, "-ffile-prefix-map="+base.Cwd()+"=.")
576576
defs = append(defs, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
577-
} else if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
577+
} else if b.gccSupportsCompilerFlag(compiler, "-fdebug-prefix-map=a=b") {
578578
defs = append(defs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
579579
}
580-
if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") {
580+
if b.gccSupportsCompilerFlag(compiler, "-gno-record-gcc-switches") {
581581
defs = append(defs, "-gno-record-gcc-switches")
582582
}
583583
return b.run(a, p.Dir, p.ImportPath, nil, compiler, "-Wall", "-g",

0 commit comments

Comments
 (0)