go: cmd/link: missing CGO symbols in `.dynsym` on Go >= 1.21 on arm64
What version of Go are you using (go version
)?
$ go version go version go1.21.1 linux/arm64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env GO111MODULE='' GOARCH='arm64' GOBIN='' GOCACHE='/home/mahe.linux/.cache/go-build' GOENV='/home/mahe.linux/.config/go/env' GOEXE='' GOEXPERIMENT='' GOFLAGS='' GOHOSTARCH='arm64' GOHOSTOS='linux' GOINSECURE='' GOMODCACHE='/home/mahe.linux/go/pkg/mod' GONOPROXY='' GONOSUMDB='' GOOS='linux' GOPATH='/home/mahe.linux/go' GOPRIVATE='' GOPROXY='https://proxy.golang.org,direct' GOROOT='/usr/local/go' GOSUMDB='sum.golang.org' GOTMPDIR='' GOTOOLCHAIN='auto' GOTOOLDIR='/usr/local/go/pkg/tool/linux_arm64' GOVCS='' GOVERSION='go1.21.1' GCCGO='gccgo' AR='ar' CC='gcc' CXX='g++' CGO_ENABLED='1' GOMOD='/dev/null' GOWORK='' CGO_CFLAGS='-O2 -g' CGO_CPPFLAGS='' CGO_CXXFLAGS='-O2 -g' CGO_FFLAGS='-O2 -g' CGO_LDFLAGS='-O2 -g' PKG_CONFIG='pkg-config' GOGCCFLAGS='-fPIC -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build1359563043=/tmp/go-build -gno-record-gcc-switches'
What did you do?
So the issue is a difference of behavior from Go 1.20 to Go 1.21 on arm64 (and not on amd64).
Let’s make a minimal reproducing setup:
-
Put that in a file, for example main.go and
go build main
.//go:build linux // +build linux package main /* int symbol_test(void) { return 0; } */ import "C" func main() { C.symbol_test() }
-
Use a binary to read the symbols, for example
readelf -s main | grep symbol_test
, you can useless
as well to see:- With Go
1.20.8
(and before), there is asymbol_test
in.dynsym
andsymbol_test
in.symtab
. - With Go
1.21.0
and1.21.1
, there is only asymbol_test
in.symtab
, it was removed from.dynsym
.
- With Go
So I cloned Go and did a git bissect to see which commit was the culprit and found that it was 1f29f39795e736238200840c368c4e0c6edbfbae -> https://go-review.googlesource.com/c/go/+/414654. Unfortunately, I lack context and understanding of the Go codebase to understand why this changed the behavior like that on arm64 specifically and if this is unintended.
For context, I was using uprobes on a Go binary, and for a reason that is not entirely clear for now, the lib I was using to load the probe is only reading .dynsym
.
What did you expect to see?
I expected this behavior not to change, and especially not to change only on arm64 while amd64 is still behaving the same as in the past.
What did you see instead?
I saw that the symbol was missing in .dynsym
using Go >= 1.21.
About this issue
- Original URL
- State: closed
- Created 10 months ago
- Comments: 16 (11 by maintainers)
Commits related to this issue
- pkg/sensors: uprobe test fix using dyn export Go 1.21 broke dynamic symbol linking export, see more - https://github.com/golang/go/issues/62520 - https://github.com/golang/go/issues/62520#issuecommen... — committed to cilium/tetragon by mtardy 9 months ago
- pkg/sensors: uprobe test fix using dyn export Go 1.21 broke dynamic symbol linking export, see more - https://github.com/golang/go/issues/62520 - https://github.com/golang/go/issues/62520#issuecommen... — committed to cilium/tetragon by mtardy 9 months ago
This seems to be true. On my Linux/arm64 machine:
$ go env
gcc version 13.1.0 (Ubuntu 13.1.0-8ubuntu1~22.04)
On my Linux/amd64 machine:
$go env
gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)
I don’t know if this is caused by the difference in GCC version, but this difference does exist.
Thanks for the report. Previously we dynamically export ~all symbols, which is too many and unnecessary for many of them. With that CL, we only export Go symbols that are dynamically exported to C. I think we probably should also export C symbols (that are not marked as static or hidden visibility) per C semantics. @ianlancetaylor what do you think?