feat: goffi_static build tag for fully static Linux ELFs - #78
Conversation
Exclude all cgo_import_dynamic directives under -tags goffi_static so CGO_ENABLED=0 linux/amd64 and linux/arm64 binaries have no PT_INTERP or DT_NEEDED. LoadLibrary returns ErrStaticBuild. Document linking modes, add ELF CI gates, and ADR-001 for a future userspace ELF loader. Refs: goffi#74, gogpu#474
kolkov
left a comment
There was a problem hiding this comment.
Thanks for this — the goffi_static approach is the right answer to #74. You can't dlopen without ld.so, and a static binary has no ld.so. Returning ErrStaticBuild is honest and clean.
Went through the full diff (80 files). Build tag propagation is thorough — no dynamic symbols leak into the static binary. The check-elf-linking.sh CI gate is exactly what we needed: machine-verified PT_INTERP contract. ADR-001 aligns well with our own elfloader research (ADR-014).
One minor note: errno capture under goffi_static returns 0 silently (ErrnoFnAddr() = 0 skips the assembly call). The code is correct, but a one-line mention in the README's static section would help users understand errno is unavailable in static mode.
Everything else — the stub architecture, ErrStaticBuild error type, crosscall2 abort trampolines, CI job — looks solid. Ready to merge.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…rk main Upstream go-webgpu#78 added its own goffi_static. The fork already has one, and Profile U and goffi_musl are built on it (ffi.Available, internal/static, callback_static.go, ffi.ErrStaticBuild). The two are equivalent for callers: LoadLibrary/GetSymbol return a *LibraryError wrapping ffi.ErrStaticBuild, the ELF has no PT_INTERP or DT_NEEDED, and errno capture is off. So the code keeps the fork's implementation: - conflicting .go/.s files and upstream's build-tag-only edits resolve to the fork's version; - upstream-only static files that would duplicate the fork's definitions are dropped: ffi/dl_static.go, ffi/errors_static.go, ffi/fakecgo_static.go, internal/fakecgo/static.go and asm_static_{amd64,arm64}.s; - upstream's ffi/static_test.go is kept, restricted to linux, where the fork's static mode applies (it is a no-op on Windows and Android). Taken from upstream: scripts/check-elf-linking.sh and the elf-linking CI job (added to ci-success next to platform-matrix), ADR-001, the v0.6.4 CHANGELOG/ROADMAP/README entries, the linking-modes docs (with rows for goffi_musl and the universal build), and the package doc changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Summary
-tags goffi_staticwhich excludes all//go:cgo_import_dynamicdirectives soCGO_ENABLED=0linux/amd64 and linux/arm64 binaries are fully static (noPT_INTERP, noDT_NEEDED).ffi.LoadLibrary/GetSymbolreturnffi.ErrStaticBuildunder that tag; default dynamic FFI path unchanged.scripts/check-elf-linking.sh+ CIelf-linkingjob, anddocs/ADR-001-userspace-elf-loader.mdfor the long-term loader research track.Fixes the consumer-visible half of #74 / gogpu#474. Does not claim
FROM scratch+ hostdlopen(architecturally impossible withoutld.soor a userspace loader).Test plan
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags goffi_static→filereports statically linkedlinux/arm64libdl.so.2/libc.so.6/libpthread.so.0go test -tags goffi_static ./ffi -run StaticBuildelf-linkingjob green on this PR