go: cmd/cgo, cmd/link: with zig as CC/CXX, Go linker does not put libc onto the linker line, causing undefined symbol errors
Corresponding Zig issue: https://github.com/ziglang/zig/issues/11398
What version of Go are you using (go version
)?
$ go version go version go1.18.1 linux/amd64
Does this issue reproduce with the latest release?
I only tested with go1.18.1.
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/home/andy/.cache/go-build" GOENV="/home/andy/.config/go/env" GOEXE="" GOEXPERIMENT="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/home/andy/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/home/andy/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/nix/store/qyc0w8vsikzmbdy97gb68l2ri1jzqp9v-go-1.18.1/share/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/nix/store/qyc0w8vsikzmbdy97gb68l2ri1jzqp9v-go-1.18.1/share/go/pkg/tool/linux_amd64" GOVCS="" GOVERSION="go1.18.1" GCCGO="gccgo" GOAMD64="v1" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="/dev/null" GOWORK="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/run/user/1000/go-build3829224601=/tmp/go-build -gno-record-gcc-switches"
What did you do?
go.mod
:
module test
go 1.18
foo.go
:
package test
func foo() {}
foo_test.go
:
package test
import (
"testing"
)
func TestFoo(t *testing.T) {
foo()
}
Then use zig cc as the C toolchain for go test
with race detection:
CGO_ENABLED=1 CC="zig cc" go test -race test
Zig Version 0.10.0-dev.2052+3cfde183f
What did you expect to see?
This should work, as it does without -race
.
What did you see instead?
We get errors from the Go linker:
# test.test
runtime/race(.text): relocation target getuid not defined
runtime/race(.text): relocation target pthread_self not defined
runtime/race(.text): relocation target sleep not defined
runtime/race(.text): relocation target usleep not defined
runtime/race(.text): relocation target abort not defined
runtime/race(.text): relocation target isatty not defined
runtime/race(.text): relocation target pthread_attr_getstack not defined
runtime/race(.text): relocation target sigaction not defined
runtime/race(.text): relocation target getrusage not defined
runtime/race(.text): relocation target syslog not defined
runtime/race(.text): relocation target confstr not defined
runtime/race(.text): relocation target getrlimit not defined
runtime/race(.text): relocation target pipe not defined
runtime/race(.text): relocation target sched_getaffinity not defined
runtime/race(.text): relocation target __sched_cpucount not defined
runtime/race(.text): relocation target pthread_attr_init not defined
runtime/race(.text): relocation target pthread_getattr_np not defined
runtime/race(.text): relocation target pthread_attr_destroy not defined
runtime/race(.text): relocation target exit not defined
runtime/race(.text): relocation target sysconf not defined
runtime/race(.text): relocation target setrlimit not defined
/home/kmicklas/go/pkg/tool/linux_amd64/link: too many errors
FAIL test [build failed]
FAIL
Notes
Related issue: #44695
It appears that cgo has asked the C compiler to compile C source files which contain dependencies on libc symbols, however the Go linker is not actually putting libc onto the linker line, causing these undefined symbol errors.
It does compile and run successfully using -linkmode external
, however, I don’t see why it shouldn’t work with the Go linker as well, given that zig cc
produces standard ELF object files (same as Clang and GCC).
It looks to me like the fix is simple: the Go linker needs to put libc onto the linker line when it is linking objects that contain libc dependencies.
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 3
- Comments: 31 (19 by maintainers)
Commits related to this issue
- [ld.lld] --gc-sections only when output is executable When building object files, `zig cc` will instruct lld to remove unused sections via `--gc-sections`. This is problematic for cgo. Briefly, go bu... — committed to motiejus/zig by motiejus 2 years ago
- [ld.lld] --gc-sections only when output is executable When building object files, `zig cc` will instruct lld to remove unused sections via `--gc-sections`. This is problematic cgo builds that don't e... — committed to motiejus/zig by motiejus 2 years ago
- [ld.lld] --gc-sections only when output is executable When building object files, `zig cc` will instruct lld to remove unused sections via `--gc-sections`. This is problematic cgo builds that don't e... — committed to motiejus/zig by motiejus 2 years ago
- [zig ld] --gc-sections workaround + tests `zig cc` emits `--gc-sections` for the linker, which is incompatbile with what CGo thinks about linking. This commit adds a workaround: it will add `--no-gc... — committed to gmirror/bazel-zig-cc by motiejus 2 years ago
- [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. Minimal example: ... — committed to motiejus/go by motiejus 2 years ago
- [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. Minimal example: ... — committed to motiejus/go by motiejus 2 years ago
- 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. Minimal exampl... — committed to motiejus/go by motiejus 2 years ago
- cmd/cgo: use -Wl,--no-gc-sections if available This change adds `--no-gc-sections` to the external linker if the flag is understood. zig cc passes `--gc-sections` to the underlying linker, which the... — committed to motiejus/go by motiejus 2 years ago
- cmd/cgo: use -Wl,--no-gc-sections if available This change adds `--no-gc-sections` to the external linker if the flag is understood. zig cc passes `--gc-sections` to the underlying linker, which the... — committed to motiejus/go by motiejus 2 years ago
- 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,-... — committed to motiejus/go by motiejus 2 years ago
- 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,-... — committed to motiejus/go by motiejus 2 years ago
- cmd/dist: support spaces and quotes in CC As of 742dcba7bb953a96c9f3fcdeb32b1c03cbbd8d5e `go build` can accept `$CC` with spaces and quotes, which lets us easily use `zig cc` as the C compiler, or ea... — committed to motiejus/go by motiejus 2 years ago
- 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,-... — committed to motiejus/go by motiejus 2 years ago
- 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,-... — committed to motiejus/go by motiejus 2 years ago
- cmd/dist: support spaces and quotes in CC As of CL 334732 `go build` can accept `$CC` with spaces and quotes, which lets us easily use `zig cc` as the C compiler, or easily pass extra compiler parame... — committed to motiejus/go by motiejus 2 years ago
- 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-... — committed to motiejus/go by motiejus 2 years ago
- 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-... — committed to motiejus/go by motiejus 2 years ago
- 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-... — committed to motiejus/go by motiejus 2 years ago
- 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-... — committed to motiejus/go by motiejus 2 years ago
Sure, here it is: https://github.com/ziglang/zig/commit/b3f4e0d091d83874af849f1d1a2328ad7c154d42
I attempted to workaround res_search issue it in bazel-zig-cc (https://git.sr.ht/~motiejus/bazel-zig-cc/commit/7b0de33070bef14265d7ec560fca43f5e132eea4 and https://git.sr.ht/~motiejus/bazel-zig-cc/commit/8d1e1c9fa66712c8f7da3634990ab4ccd2aa47c9), but that doesn’t always work, as you can see.
We agreed with @andrewrk that we will change the offending headers in upsteam zig (add ifdefs on glibc version). To my latest knowledge, @sywhang is working on that.
zig cc
follows the same static/dynamic linking rules as clang and gcc; it shares the exact same command line API. On glibc-based systems, GCC, Clang, andzig cc
all link dynamically against libc.so by default:A typical linker line for any C compiler will look something like this:
In this example,
hello.o
contains libc dependencies, but it is the linker which satisfies them by putting libc on the linker line, as you can see.I’m not sure what you mean when you say the Go linker never links directly against the C library. Indeed,
getuid
and friends come from the libc.so shared C library, as you can see above, but in this issue, the Go linker is omitting libc.so from the list of objects to link, causing the undefined symbol errors.We can simulate this same problem with any C compiler by passing
-nostdlib
, causing the linker driver to omit libc.so from the linker line, as Go is doing:Ultimately, the Go toolchain is compiling C source files into object files which contain libc dependencies. If any C compiler is used to then link these together, everything works fine, because C compilers put libc onto the linker line. However, when the Go linker is used, which is the default, it does not satisfy its own dependencies.
I think one thing that would shed some light on this issue would be seeing, side by side, the Go linker line when
CC=clang
is used vsCC="zig cc"
. I spent a couple hours trying to obtain this information but did not figure out how to do it.