| author | |
| committer | |
| log | b3f4e0d091d83874af849f1d1a2328ad7c154d42 |
| tree | 1d9d749d6b5024c87b5dcd37a1b08dedb98c0064 |
| parent | cf85462a78aa767ce7cfd1ebdb06d7405e1b0e5e |
Given `main.go`:
package main
import _ "os/user"
func main() {}
Compiling it to linux/arm64:
$ CGO_CFLAGS='-O0' GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC="zig cc -target aarch64-linux-gnu.2.28" go build main.go
Results in this error:
runtime/cgo(.text): unknown symbol memset in callarm64
runtime/cgo(.text): unknown symbol memset in callarm64
runtime/cgo(.text): relocation target memset not defined
In the midst of intermediate compilations files we can see this commmand:
ld.lld -o _cgo_.o <...> /tmp/go-build206961058/b043/_x009.o <...> ~/.cache/zig/.../libcompiler_rt.a <...> ~/.cache/.../libc.so.6
`_x009.o` needs memset:
$ readelf -Ws ./b043/_x009.o | grep memset
22: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND memset
Both `libcompiler_rt.a` and `libc.so.6` provide it:
$ readelf -Ws ~/.cache/zig/.../libcompiler_rt.a | grep memset
870: 0000000000000000 318 FUNC WEAK DEFAULT 519 memset
$ readelf -Ws ~/.cache/zig/.../libc.so.6 | grep -w memset
476: 000000000001d34c 0 FUNC GLOBAL DEFAULT 7 memset@@GLIBC_2.2.5
Since `libcompiler_rt.a` comes before libc in the linker line, the
resulting `_cgo_.o` still links to a weak, unversioned memset:
$ readelf -Ws ./b043/_cgo_.o | grep -w memset
40: 000000000022c07c 160 FUNC WEAK DEFAULT 14 memset
719: 000000000022c07c 160 FUNC WEAK DEFAULT 14 memset
Since the final linking step is done by Golang's linker, it does not
know of `libcompiler_rt.a`, and fails to link with the error message
above. However, Go linker does recognize memset from glibc. If we
specify an `-lc` equivalent before the `libcompiler_rt.a`, it will link
to memset from libc:
$ readelf -Wa ./b043/_x009.o |grep memset
14: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memset@GLIBC_2.17 (2)
157: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memset@GLIBC_2.17
... and then `main.go` will compile+link successfully.
Why doesn't Go linker take memset from glibc? An educated guess: Go
determines whether to link with glibc from what the program asks (I
presume `.dynsym`). Since `memset` is no longer attributed to glibc, Go
skips linking to glibc altogether.
Bonus question: curious why `-O0` is necessary? Because when
optimizations are enabled (the default), the C compiler replaces
`memset` function call with plain `stp` instructions (on aarch64).1 files changed, 7 insertions(+), 5 deletions(-)
src/link/Elf.zig+7-5| ... | @@ -1753,11 +1753,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v | ... | @@ -1753,11 +1753,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v |
| 1753 | try argv.append(ssp.full_object_path); | 1753 | try argv.append(ssp.full_object_path); |
| 1754 | } | 1754 | } |
| 1755 | 1755 | ||
| 1756 | // compiler-rt | ||
| 1757 | if (compiler_rt_path) |p| { | ||
| 1758 | try argv.append(p); | ||
| 1759 | } | ||
| 1760 | |||
| 1761 | // Shared libraries. | 1756 | // Shared libraries. |
| 1762 | if (is_exe_or_dyn_lib) { | 1757 | if (is_exe_or_dyn_lib) { |
| 1763 | const system_libs = self.base.options.system_libs.keys(); | 1758 | const system_libs = self.base.options.system_libs.keys(); |
| ... | @@ -1836,6 +1831,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v | ... | @@ -1836,6 +1831,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v |
| 1836 | } | 1831 | } |
| 1837 | } | 1832 | } |
| 1838 | 1833 | ||
| 1834 | // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs | ||
| 1835 | // to be after the shared libraries, so they are picked up from the shared | ||
| 1836 | // libraries, not libcompiler_rt. | ||
| 1837 | if (compiler_rt_path) |p| { | ||
| 1838 | try argv.append(p); | ||
| 1839 | } | ||
| 1840 | |||
| 1839 | // crt postlude | 1841 | // crt postlude |
| 1840 | if (csu.crtend) |v| try argv.append(v); | 1842 | if (csu.crtend) |v| try argv.append(v); |
| 1841 | if (csu.crtn) |v| try argv.append(v); | 1843 | if (csu.crtn) |v| try argv.append(v); |