SoLo lets fully static Linux binaries load host GPU drivers

SoLo lets fully static Linux binaries load host GPU drivers

A GitHub project called SoLo (pg83/solo) tackles a specific gap in fully static Linux binaries: they cannot normally load GPU drivers, because Vulkan and OpenGL drivers ship as shared objects built against glibc, and a static musl binary cannot dlopen() them. SoLo closes that gap with its own dlfcn-style API, backed by a custom ELF loader for x86-64 and aarch64 and a glibc ABI bridge implemented on top of musl. The result stays one ordinary static executable, but that executable can now call the graphics driver already installed on the host machine.

The repository ships an end-to-end proof: a fully static executable loads the host's unmodified Vulkan driver, runs a compute shader, and writes the result to a PNG. The README states this has been tested on AMD radv, AMD radeonsi, Intel, and NVIDIA GPUs under Linux, and on Apple M1 under Asahi Linux. A prebuilt binary is available with no clone and no toolchain required: on any Linux with a Vulkan driver installed, it downloads and runs directly, producing a 512x512 RGBA image; an aarch64 build covers arm64 machines. The same demo can be built from source with a Python 3 and C/C++ toolchain. The project also ships a standalone dlfcn archive that other musl-static applications can link against to redirect their own dlopen()/dlsym() calls through SoLo.

On the engineering side, the ELF loader 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 load recursively. glibc itself is never loaded. Instead, a shim resolves imports like malloc@GLIBC_2.2.5 to ABI-correct adapters over the process's existing musl runtime, and any glibc function SoLo does not support gets a generated stub that fails loudly, naming the exact missing symbol and version, instead of silently corrupting the process. Because musl sizes its synchronization objects to match each architecture's glibc ABI, a pthread_mutex_t created by a loaded driver is used in place rather than shadowed, so the static executable and the DSO it loaded share one lock, not two. The README also describes support for C++ exceptions crossing the static/glibc boundary in both directions, all four TLS models without wrappers or patching, ld.so's actual binding semantics (global-scope interposition, RTLD_DEEPBIND, DT_SYMBOLIC, symbol versioning, lazy PLT binding, GNU and SysV hash lookups), cross-world backtraces and introspection through dladdr, dl_iterate_phdr and the link_map facade, and legacy glibc internals such as getcontext/makecontext/swapcontext, pre-2.34 pthread ABIs, GNU obstacks, and the fortified _chk family.

Every commit runs a conformance battery compiled against real glibc headers, and CI loads the shared libraries of the 1,000 most-installed Debian packages, over 2,100 host objects in total, through SoLo on both x86-64 and aarch64. Native build and test also run on Alpine/musl with GCC, Fedora with GCC, and Ubuntu with Clang, with the Vulkan test installing each distribution's own Lavapipe package rather than borrowing one from another distro's sysroot.

The README places SoLo against several existing approaches. gcompat, a distribution-level glibc shim, re-executes a program through musl's own dynamic linker with a preloaded compatibility library, but according to the README does not give a fully static musl process a dynamic loader at all; SoLo instead embeds both the ELF loader and the ABI bridge directly in the executable and can leave individual unsupported functions behind fail-loud stubs rather than blocking an entire DSO. Detour bootstraps the system's ld-linux and runs multiple C runtimes side by side; SoLo maps the required DSOs itself and translates their glibc imports onto musl so a second libc and its TLS state never enter the process. Cosmopolitan Libc's cosmo_dlopen() and graphics.gd's musl+dlopen experiment follow the same split-runtime scheme as Detour, which the README says leaves two independent TLS worlds, every boundary crossing needing a trampoline, and callbacks implemented in musl that cannot safely be passed to glibc code. ClickHouse's experimental userspace dynamic loader maps ELF objects itself but currently stops short of loading glibc, with a proposed path to libraries such as CUDA that the README also calls Detour-like. Against Flatpak, AppImage, and containers, the README argues that hiding a small Linux distribution inside or around a program is not portability, comparing it to moving house to fix a missing power adapter.

The project vendors specific pinned versions of its dependencies: musl 1.2.5, LLVM runtimes 15.0.7 (libc++, libc++abi, libunwind, and compiler-rt builtins), Vulkan Headers 1.4.357, Vulkan Loader 1.4.357, zlib 1.3.2, and libpng 1.6.50, each identified by commit hash in the README, with license files retained beside the corresponding sources.

Key facts

  • SoLo is a custom ELF loader (x86-64 and aarch64) plus a glibc ABI bridge over musl, giving a fully static musl-linked Linux binary a dlfcn-style API to load host glibc-linked shared objects, GPU drivers included.
  • The included demo runs a fully static Vulkan executable that executes a compute shader and writes a PNG, tested on AMD radv, AMD radeonsi, Intel, and NVIDIA GPUs under Linux, and on Apple M1 under Asahi Linux.
  • 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.
  • The bridge covers cross-boundary C++ exceptions, all four TLS models, ld.so's actual binding semantics, and cross-world backtraces and introspection, not just a thin dlsym shim.
  • The README contrasts SoLo with gcompat, Detour, Cosmopolitan Libc's cosmo_dlopen(), ClickHouse's experimental loader, and graphics.gd's musl+dlopen experiment, arguing each of those keeps a second libc or TLS runtime alive while SoLo does not.

Why it matters

Static binaries are an easy way to ship Linux software: one file, no dependency chasing, nothing to break on the target machine. That model has always broken down at the GPU, because Vulkan and OpenGL drivers are shipped by the host as glibc-linked shared objects that a static musl binary cannot normally open. SoLo removes that specific blocker without giving up the static-binary model: the shipped artifact is still one ordinary executable, and it borrows only the one piece that genuinely belongs to the host, its hardware driver, instead of bundling a second Linux distribution the way Flatpak, AppImage, or a container would.

Who it affects

The people who benefit are developers building fully static Linux software that still needs graphics or GPU compute, the kind of project the SoLo authors describe building themselves with their IX static-build system, including a terminal emulator called Shitty that ships its release binaries this way. More broadly, it targets anyone who currently reaches for a container, AppImage, or a distribution-bundling scheme purely to get one GPU-facing dependency working.

How to use it

A prebuilt demo binary can be fetched directly from the GitHub releases page and run on any Linux machine with a Vulkan driver installed (mesa-vulkan-drivers is enough); separate builds exist for x86-64 and aarch64, and a --driver flag can force a specific ICD manifest instead of relying on automatic discovery. Building from source needs only Python 3 and a C/C++ compiler. Applications that want SoLo's loader directly can link the project's standalone dlfcn archive and include its header, after which ordinary dlopen()/dlsym() calls are redirected through SoLo; LD_LIBRARY_PATH and a SoLo-specific DL_ELF_LIBRARY_PATH are both honored for libraries outside standard system directories. The captured text does not state a license for the project.

How solid is it

The claims are backed by a substantial automated test surface rather than a single demo. Every commit runs a conformance battery against real glibc headers and 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. Native builds and tests also run on Alpine/musl with GCC, Fedora with GCC, and Ubuntu with Clang, each installing its own distribution's Lavapipe package for the Vulkan test rather than reusing one from a different sysroot. The Vulkan demo itself is reported tested across AMD radv, AMD radeonsi, Intel, and NVIDIA GPUs, and on Apple M1 under Asahi Linux.

Risks and caveats

The captured project text cuts off mid-sentence right where it appears to start listing limitations, so whatever follows that point is not known and is not covered here. What is confirmed from the text: SoLo supports Linux only, on x86-64 and aarch64. No performance numbers or overhead measurements are given anywhere in the text for the glibc ABI bridge compared to native dynamic linking, so the practical cost of the approach is unstated. The text also does not name an individual author or maintainer, and states no license for the project.

“Flatpak, AppImage, and containers solve the problem by hiding a small Linux distribution inside or around your program. This works in roughly the same way that moving house solves a missing power adapter.”

— the SoLo README