Building static binaries with Go on Linux
eli.thegreenplace.net
eli.thegreenplace.net
This is explained in https://www.arp242.net/static-go.html
Performance so far has been better than the modernc transpile and it’s probably sufficient for a lot of use cases.
Faster was a by product. Maintainability was the goal.
API coverage, ergonomics, extensibility all rank higher in my book than performance.
An example I'd like to cite is sqlite-vec. Alex was able to build a Cgo-free version of it, on his own, which works fine with my bindings. This would be much harder to do with modernc.
https://github.com/asg017/sqlite-vec-go-bindings
I'm also adding support for building off the bedrock branch (begin concurrent, wal2). You just build the branch with wasi-sdk, then embed the resulting blob.
I can wrap my head around the small amount of wrapping the go-sqlite3 WASM library does. If I had to I can maintain that should the maintainer lose interest. I can’t say the same for the modernc transpile. You can also apply the WASM trick to other libraries with much less effort.
And as noted, it seems to be performing better. As the wasm runtime improves it should pull further ahead.
(Needless to say, we have better load testing tooling now)
I forget the details, but something about the libc allocator used by SQLite-with-Zig-libc being ... not good.
I haven’t tried this in about a year, so maybe the tooling doesn’t have these issues now.
I have been building & using a statically compiled tailscale (with CGO) for a while but didn't notice any performance hits. Script: https://github.com/Azathothas/Toolpacks/blob/main/.github/sc...
$ go build -ldflags '-linkmode external -s -w -extldflags "--static-pie"' -buildmode=pie -tags 'osusergo,netgo,static_build' main.go
For anyone curious to delve into what is this and why: https://www.leviathansecurity.com/blog/aslr-protection-for-s...Go always did ~libc on Windows (and Solaris) and still does.
Go still does raw syscalls on Linux, as that's a stable ABI.
On any other no-UNIX derived OS, it is the C compiler standard library, covering only the ISO C specifies.
All the remaining OS services are exposed by other libraries, which in Windows case, the bare minimum is user, kernel and gdi dlls for the Win32 personality.
Systems like IBM i, IBM z, ClearPath MCP, .... also have similar set of libraries, as do non-POSIX RTOS for embedded.
The ability to build a single static binary that works on Linux, Mac and Windows using Go would be life changing for the internal tools I develop at work.
Just curious, life changing in what way? Obviously, 1 is better than 3, but I'm wondering if there is some other interesting reason.
You're welcome to ignore it of course, it's just unofficial and a large pain.
How do you mean? Like, is it possible to run such binaries on M1? If so I'd really like to know how
Alternatively, Linux :).
Once upon at time, static linking was the only thing OSes were capable of, all of them moved away from that, and there is no turning back outside embedded bare metal deployments, just become some people are obsessed with static linking.
Though doesn’t convert the WASM to C, it runs the WASM in Wazero instead.
You're suggesting compiling Go to Wasm (presumably using the wasip1 target?), then converting that to C using wabt, then using Cosmopolitan to create an APE… is that it?
Well, that's not going to work.
First of all, Go's wasip1 target doesn't even support cgo, so if you want SQLite, you're dead right there.
Then, even if you used say TinyGo (which might support cgo, not sure), WASI just isn't a great target to compile SQLite into. WASI is a pretty limited syscall layer. You'd end up with no file locking, no shared memory. Also no threads.
Then, on top of that, you'd layer Cosmopolitan issues. Having written a portable SQLite VFS from scratch, I am not impressed with how they just paper over file locking semantics incompatibilities between OSes, and then confidently ship a forking webserver with SQLite bundled in. It takes a certain temerity, and not running many SQLite torture tests.
Wasm as an intermediate target is great for (single threaded) CPU stuff. WASI is great if you can fit it, but otherwise, it's not, not really.