Pangram verdict · v3.3
We believe that this text is a mix of AI and human-written content.
AI likelihood · overall
AIArticle text · 916 words · 6 segments analyzed
SoLo — a .so loader for static Linux binaries Ship one musl-linked executable. At runtime, load the user's existing glibc-linked GPU driver. No container, no AppImage, and no second libc in the process.
Static binaries are a wonderfully boring way to deploy software on Linux: one file, no dependencies, nothing to break. We build ours with IX, a source-first build system for producing fully static Linux binaries.
The boredom ends the moment the application needs the GPU: Vulkan and OpenGL drivers are supplied by the host as shared objects, usually built against glibc, and a fully static musl binary cannot normally dlopen() them. SoLo crosses that boundary. It provides a dlfcn-style source API backed by its own ELF loader (x86-64 and aarch64) and a glibc ABI bridge implemented on top of musl. The result is still one ordinary static executable, but it can use the graphics driver already installed on the machine. The repository includes an end-to-end Vulkan proof: a fully static executable loads the host's unmodified Vulkan driver, runs a compute shader, and writes the result to a PNG. Tested on AMD radv, radeonsi, Intel, and NVIDIA GPUs under Linux, and on Apple M1 under Asahi Linux. The host keeps the hardware-specific code. You ship everything else.
And not on a demo's word alone: on every commit, CI loads the shared libraries of the 1,000 most-installed Debian packages — over 2,100 host objects — through SoLo, on both x86-64 and aarch64. See it work Grab the prebuilt binary — no clone, no toolchain, any Linux with a Vulkan driver installed (mesa-vulkan-drivers is enough): curl -LO https://github.com/pg83/solo/releases/latest/download/vulkan-x86_64 chmod +x vulkan-x86_64 ./vulkan-x86_64 hello.png vulkan-aarch64 is the same demo for arm64 machines. The command discovers the distro-installed Vulkan ICD in the usual way and produces a 512×512 RGBA image. This is how we build the Shitty release binaries—a blazingly fast terminal emulator, BTW! To force a particular driver: ./vulkan-x86_64 --driver /usr/share/vulkan/icd.d/radeon_icd.x86_64.json radeon.png ./vulkan-x86_64 --driver /usr/share/vulkan/icd.d/lvp_icd.json lavapipe.png ICD manifest names vary slightly between distributions. Passing no --driver lets the embedded Khronos loader perform its normal discovery. You can verify that the executable itself is not dynamically linked: readelf -lW ./vulkan-x86_64 | grep INTERP # no output readelf -dW ./vulkan-x86_64 # "There is no dynamic section" Or build the same demo from source, with Python 3 and a C/C++ compiler in PATH: git clone https://github.com/pg83/solo.git cd solo ./build vulkan ./vulkan hello.png This is not a toy call to vkCreateInstance. The demo: enters the statically linked Khronos Vulkan loader; loads the host's Vulkan ICD and its non-glibc dependencies through SoLo; creates a device, storage buffer, descriptor set, and compute pipeline; dispatches a checked-in SPIR-V shader; maps the result and writes it through statically linked libpng.
The complete example is in bin/vulkan, and the Vulkan program itself is in main.cpp. How it works ┌──────────────────── fully static executable ────────────────────┐ │ │ │ application → embedded Vulkan loader → SoLo dlopen/dlsym │ │ ├─ x86-64 ELF mapper │ │ └─ glibc ABI → musl │ │ │ │ └───────────────────────────────────────────┬─────────────────────┘ │ maps at runtime ▼ system Mesa/Vulkan ICD.so + DSOs elf_loader.cpp maps ELF segments, walks DT_NEEDED, resolves versioned symbols, applies x86-64 relocations, supports ELF TLS and TLSDESC, materializes IFUNCs, applies RELRO, and runs initializers. Dependencies that are themselves ELF DSOs are loaded recursively. glibc is deliberately not loaded. Imports such as malloc@GLIBC_2.2.5 are resolved by glibc_shim.cpp to ABI-correct adapters over the process's existing musl runtime. Unsupported glibc functions have unique generated stubs that fail loudly with the exact symbol and version if they are ever called, instead of silently corrupting the process. Because musl sizes its synchronization objects to the glibc ABI of each architecture, the bridge does not shadow them: a pthread_mutex_t a driver creates is used in place. A lock is therefore one lock for both the loaded DSO and the static executable that may share it, and glibc's static recursive and error-check initializers are adopted on first use. Before loading a DSO from disk, SoLo checks its static provider registry. This lets an application satisfy a dependency—Wayland, for example—with functions already linked into the executable. LD_LIBRARY_PATH and DL_ELF_LIBRARY_PATH are honored for libraries outside the standard system directories. The interesting pieces are small enough to read: lib/dlfcn.cpp — dlopen, dlsym, errors, and static providers lib/elf_loader.cpp — ELF mapping, symbols, relocations, and TLS lib/glibc_shim.cpp — implemented glibc ABI adapters lib/glibc_stubs.cpp — explicit fallbacks for the rest of the ABI Use it as a library The default target builds the standalone archive: ./build The published ./dlfcn symlink points to the resulting libdlfcn.a. Include lib/dlfcn.h, link the archive into a musl-static application, and ordinary dlopen()/dlsym() calls are redirected to SoLo. The source tree is intentionally self-contained and suitable for copying into another static build graph. Reproduce the experiment ./build test # load an Arch glibc DSO closure in the smoke test ./build vulkan_test # build the static demo and verify a native Lavapipe PNG CI performs the native build and test on Alpine/musl with GCC, Fedora with GCC, and Ubuntu with Clang. The Vulkan test installs each distribution's own Lavapipe package; it does not run the driver from an Arch sysroot. Every build input for the standalone Vulkan executable is vendored under bin/vulkan. build.py compiles those sources directly: upstream CMake, Meson, configure, and Make build systems are not invoked.
Vendored versions musl 1.2.5 (0784374d561435f7c787a555aeab8ede699ed298) LLVM runtimes 15.0.7: libc++, libc++abi, libunwind, and compiler-rt builtins (8dfdcc7b7bf66834a761bd8de445840ef68e4d1a) Vulkan Headers 1.4.357 (e3b1eec08173d6b825cd3ac88c885a63b621504a) Vulkan Loader 1.4.357 (5f157b62e333c63260d05d81bf66faa216ab0fb8) zlib 1.3.2 (da607da739fa6047df13e66a2af6b8bec7c2a498) libpng 1.6.50 (2b978915d82377df13fcbb1fb56660195ded868a) License files are retained beside the corresponding sources.