The Holy Grail of Linux Binary Compatibility: musl + dlopen #242
Replies: 4 comments 5 replies
-
|
I use the dlopen/dlsym technique in Linux and loadlibrary/getprocaddress in my language's VM. |
Beta Was this translation helpful? Give feedback.
-
|
Doesn't work for me: Thread 1 "dodge_the_creep" received signal SIGSEGV, Segmentation fault.
0x0000000002b13d16 in foreign_tramp ()
(gdb) bt
#0 0x0000000002b13d16 in foreign_tramp ()
#1 0x00007fffaa55a5a0 in ?? ()
#2 0x00007ffff75e4558 in ?? ()
#3 0x00007fff5ffec436 in ?? ()
#4 0x00007fffaa2ed710 in ?? ()
#5 0x00007fffffffc0d0 in ?? ()
#6 0x00007fff5ffe5ecc in ?? ()
#7 0x00007fffffffc0d0 in ?? ()
#8 0x00007fff5f9cc488 in ?? ()
#9 0x00007fffaa559a40 in ?? ()
#10 0x0000000000000000 in ?? ()
(gdb) |
Beta Was this translation helpful? Give feedback.
-
|
failed on nixos 25.11 |
Beta Was this translation helpful? Give feedback.
-
|
This article was mentioned on hacker news https://news.ycombinator.com/item?id=46762882 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I guess using Go + Godot to build native & installable Android & iOS binaries (without any proprietary SDKs) was too easy. So it's time for a real challenge...
Linux Binary Compatibility
(some background reading: https://jangafx.com/insights/linux-binary-compatibility)
For a while now, it's been very easy to reliably ship command line software & servers for Linux, just run
go buildand out pops a single static binary that will run on any Linux distribution running kernel 3.2 or later (which was released in 2012, so there's plenty of room for backwards compatibility).The problems begin to creep in when you want access to hardware accelerated graphics. All the GPU drivers on Linux require accessing dynamic libraries via the C ABI. These C libraries are built against a particular libc, which is most commonly
glibcbut there are also a selection ofmusl-based distributions. If you compile aglibclibrary or executable, it won't run on amuslsystem and vice-versa. That's a big incompatibility right there!In fact, I've directly experienced this, as I recently replaced the OS on my personal computer with the
musledition of Void Linux. Compiling the Zed editor with musl for example, was quite the challenge. It turns out that buildinggraphics.gdprojects onmuslwas also very broken. Go doesn't properly supportc-sharedorc-archivewhen building againstmusl.That's a problem, firstly because this is my distro now, I need to be able to build graphics.gd projects! Secondly, in theory,
muslhas better support for static linking thanglibc; so if there's any solution to this Linux Binary Compatibility mess, it's probably going to have something to do withmusl.Supporting
muslTo work around these
muslissues with Go, I had to patch the runtime with a build-overlay that applies when building forGOOS=musl. This is a newGOOSthat I've introduced to graphics.gd, specifically to makemuslbuilds possible.Next up, I decided to ditch c-shared builds for
musl, these were only convenient because you could easily plug and play Go into the official Godot binaries. The Godot Foundation doesn't provide official musl builds, so instead, I'm linking the Go code directly with Godotc-archiveto end up with a single binary. Amazing,graphics.gdsupportsmuslnow!There's just one issue, this means whenever somebody wants to release their project for Linux, they would have to create two builds, a Linux
glibcbuild + amuslbuild and somehow communicate to their users, to pick the correct binary. Hell, before I installed Void Linux I didn't even fully comprehend the differences betweenmuslandglibc, this feels like I'm simply contributing to the problem!Single Static Binaries + Graphics
Hold up! Earlier I reported that a key benefit of
muslwas better static library support. There should be a way to build a graphics.gd project into a single static binary. Well, here's the thing. Yes, you can totally do this. Godot includes all of it's dependencies on Linux, everything else is dynamically loaded at runtime, so just add the-staticcommand and...ERROR Dynamic loading not supportedOuch, Godot wants to use
dlopento interface with X11, Wayland, OpenGL, Vulkan etc. As it turns out,muslrefuses to implementdlopenfor static binaries. They don't want anyone to load aglibclibrary frommuslbecause there are fundamental incompatibilities between how they implement TLS (thread-local-storage).Don't worry though! As
dlopenis compiled as a weak symbol, this means, that as long asgraphics.gdimplements it, there's still a chance to get a single static binary that can execute on any Linux system 3.2 onwards.The Holy Grail
There's some precedent for this, there's the detour technique in C which will let you
dlopenSDL and show graphics when running without a standard library. There's also Cosmopolitan's dlopen which uses a similar technique. So the solution here is to extend this formusl.The way this works, is by including (or compiling) a small
Cprogram for the target machine. We load the program and execute into it from the same process. This program brings in the host's dynamic linker so that we can steal the system'sdlopenandlongjmpback intographics.gd. We wrap any dynamically loaded functions with an assembly trampoline that switches to the system's libc TLS for the duration of the call. It all starts looking a lot like cgo.So after much hair pulling and LLM wrangling, it turns out that
musl+dlopenis all we need to produce single static binaries + graphics for Linux. Everyone can now enjoy the Go single-static-binary experience on Linux with full support for hardware accelerated graphics.Try it!
Here's a build of the
graphics.gdDodge The Creeps sample project that should execute (and hopefully render graphics) on any Linux system withgccinstalled (we don't embed the helper binaries yet).https://release.graphics.gd/dodge_the_creeps.static
You can also cross-compile your own project (on any supported platform)
GOOS=musl GOARCH=amd64 gd buildNote you may need to delete your
export_presets.cfgso that the new musl export preset is added to your projectBeta Was this translation helpful? Give feedback.
All reactions