Emit GNU symbol versioning in the synthetic vDSO
Problem
Static Go aarch64 binaries (GOOS=linux GOARCH=arm64 CGO_ENABLED=0) SIGSEGV under elfuse during runtime startup, before the first syscall: a data abort at FAR=0x2, PC in runtime.vdsoFindVersion.
The Go runtime parses the kernel vDSO (vdsoInitFromSysinfoEhdr) and treats any image with a hash table, symtab and strtab as valid, then calls vdsoFindVersion guarded only by that flag. vdsoFindVersion dereferences info.verdef unconditionally. The synthetic vDSO advertised DT_HASH/DT_SYMTAB/DT_STRTAB but no DT_VERDEF, so verdef was NULL and Go read vd_flags at struct offset 2 -> address 0x2. Real Linux vDSOs always ship .gnu.version_d (LINUX_2.6.39); glibc and musl tolerate the omission, which is why only Go was affected.
Fix
Emit .gnu.version (DT_VERSYM) and .gnu.version_d (DT_VERDEF): a base version node plus LINUX_2.6.39, with each exported symbol bound to it. The page layout shifts to make room (dynsym and hash move; versym and verdef are inserted before the dynamic table). This matches the real arm64 vDSO and keeps the vDSO fast path live for glibc/musl, which look up the same version.
Test
tests/test-go.sh (wired into make check) builds a tiny linux/arm64 Go binary and runs it under elfuse, asserting it reaches main and that time.Now() (the resolved vDSO clock_gettime) returns a sane value. It skips (exit 77) when the Go toolchain is unavailable, mirroring the other optional checks.
Verification
- Before: the Go binary exits 139 (SIGSEGV,
FAR=0x2). After: it prints and exits 0;make test-gopasses, and skips cleanly with nogoonPATH. - Ran
tests/driver.shagainst a prebuilt guest-binary corpus before and after the change: identical pass/fail set, no regression. (The local toolchain could not cross-build the in-tree guest tests, so the fullmake checkplustest-gowill exercise on the CI Apple Silicon runner.) clang-format-22,cppcheck(.ci/check-cppcheck.sh),shellcheck,gofmt: clean.
Closes #49
Summary by cubic
Adds GNU symbol versioning to the synthetic vDSO to fix startup SIGSEGVs in static linux/arm64 Go binaries. The vDSO now matches Linux (with LINUX_2.6.39) and keeps the glibc/musl fast path.
- Bug Fixes
- Emit
.gnu.version(DT_VERSYM) and.gnu.version_d(DT_VERDEF) with a base node andLINUX_2.6.39; bind all exported symbols; addDT_VERDEFNUM. - Adjust vDSO layout and section counts to include the new tables; behavior matches the real arm64 vDSO for glibc/musl.
- Add regression test
tests/test-go.shwired tomake check(targettest-go): builds a tiny static linux/arm64 Go binary, runs it under elfuse, and skips when the Go toolchain is absent.
- Emit