HN Debrief

Solo – a .so loader for static Linux binaries

  • Infrastructure
  • Open Source
  • Programming
  • Developer Tools
  • Graphics

Solo is a loader for static Linux binaries that tries to bridge a specific gap. A fully static binary, especially one built with musl, normally cannot just dlopen host-provided graphics drivers and other shared objects that were built against glibc. Solo embeds its own ELF loader plus a glibc ABI bridge so the binary stays self-contained while still loading those system libraries. The pitch is portability across glibc distros, Alpine, and eventually Android bionic, without forcing the developer to build separately for every distro or freeze everything to an ancient glibc sysroot.

If you ship Linux binaries outside distro repos, this is a reminder that portability is still not a solved platform feature. Treat Solo as an interesting niche tactic for musl or self-contained apps, not a general replacement for building against an older glibc, shipping multiple targets, or using containers/AppImage where those fit.

Discussion mood

Mixed but engaged. People respected the technical ambition and agreed the Linux binary portability problem is real, but the dominant mood was wary because Solo appears to rely on brittle glibc and ELF behavior, with likely forward-compatibility and security hazards.

Key insights

  1. 01

    Old glibc builds poison your toolchain

    Supporting old enterprise Linux is not just a matter of linking against an older glibc. In practice you often have to build inside that old distro, which drags your whole sysroot backward with old OpenSSL, libcurl, libpcap, GCC behavior, and older optimization support. That turns compatibility into an ongoing build and dependency management tax, not a linker flag.

    If you still support older RHEL-family systems, budget for a dedicated compatibility build pipeline or separate release targets. Do not assume “build against old glibc” is cheap once modern dependencies and plugins enter the picture.

      Attribution:
    • danudey #1
    • egorfine #1
    • coredog64 #1
  2. 02

    The loader and libc are one blob

    What breaks portability is not only symbol versioning. glibc’s loader, libdl, thread-local storage setup, startup path, and other runtime details are deeply intertwined, and much of that contract is not a clean external standard. That is why swapping libc implementations or loading glibc-built shared objects from a foreign runtime becomes a reverse-engineering exercise rather than routine ABI work.

    When evaluating Linux binary portability, treat ld.so behavior and runtime initialization as part of the platform contract. Compatibility plans that only count exported libc symbols will miss the hard part.

      Attribution:
    • okanat #1 #2
    • adev_ #1
  3. 03

    Custom ELF loading raises security stakes

    Writing your own loader is not just another packaging trick. ELF startup involves security-sensitive details like auxiliary vector handling, privilege mode behavior, relocation rules, and other conventions that are easy to get subtly wrong. Even if Solo’s tests are broad, correctness here needs the kind of scrutiny people usually reserve for loaders, kernels, and language runtimes.

    If you consider embedding a loader in production software, give it a security review budget and fuzzing plan from day one. Treat it like attack-surface code, not release engineering glue.

      Attribution:
    • rfgplk #1
    • pg83 #1
  4. 04

    GPU drivers are more than shared objects

    The graphics problem is not simply “need libGL.so.” Modern Linux graphics pushes a lot of driver logic into user space, including vendor-specific shader compilers and Mesa components that track kernel and hardware details closely. That is why the dream of a pure freestanding static binary falls apart once you want hardware acceleration instead of a software-rendered framebuffer.

    If your product needs accelerated graphics, plan around the host graphics stack or ship a large chunk of your own. Do not assume a syscall-only or fully freestanding strategy will carry you all the way to Vulkan or OpenGL portability.

      Attribution:
    • matheusmoreira #1
    • pjmlp #1
    • pg83 #1
  5. 05

    Zig-style glibc targeting solves less than it seems

    Pinning a minimum glibc version with tools like Zig is useful, but it only addresses one slice of the problem. It does not help with musl and bionic targets, and it gets messy once newer libstdc++ or other dependencies quietly raise your runtime floor. That is why some people saw Solo as solving a different class of pain than AppImage-style advice does.

    Use minimum-glibc targeting for mainstream glibc desktop support, but verify the full dependency graph, not just libc. If you need Alpine, Android, or plugin-host portability, expect extra machinery beyond that trick.

      Attribution:
    • emidoots #1
    • pg83 #1 #2

Against the grain

  1. 01

    Old enough glibc is still the sane default

    Much of the portability pain here comes from building on too-new systems and then blaming glibc. For ordinary C and C-like applications, glibc’s forward compatibility works well if you build against an older baseline and avoid undocumented behavior. From that view, Solo adds a lot of complexity to dodge a problem that already has a boring solution.

    Before adopting a custom loader, try a compatibility build rooted in an older glibc and audit your use of nonportable features. For many server and desktop apps, that will get you most of the way with far less risk.

      Attribution:
    • account42 #1 #2
    • badsectoracula #1
  2. 02

    Bring your own Mesa instead

    For graphics-heavy software, loading the host’s libGL stack may be the wrong boundary entirely. A commenter argued that modern drivers already keep the kernel side relatively stable and move the complexity into user-space Mesa, so a static application could simply ship its own graphics stack rather than depend on whatever host libGL happens to expose. That makes Solo look less like a necessity and more like one design choice among several ugly ones.

    If graphics compatibility is your blocker, compare Solo against bundling Mesa or ANGLE before committing. The larger artifact may be worth it if it gives you a runtime you actually control.

      Attribution:
    • colinsane #1
  3. 03

    This is a niche problem outside distro packaging

    Linux distributions were built to compile and package software as a coordinated set, not as arbitrary binaries fetched from the web. From that angle, Solo is solving the pain of independent binary distribution and cross-libc deployment, which is real but far from the mainstream Linux case. Most users on major distros can get by with per-distro builds, custom repos, or existing packaging channels.

    Match your distribution strategy to your audience. If your users already live inside a few known distros, simpler packaging may beat heroic portability hacks.

      Attribution:
    • jeroenhd #1
    • skydhash #1

In plain english

ABI
Application Binary Interface, the low-level calling convention and data representation rules used when compiled code interacts at runtime.
ANGLE
Almost Native Graphics Layer Engine, a graphics compatibility layer that often translates OpenGL ES calls to other backends like Vulkan.
AppImage
A packaging format for distributing Linux desktop applications as self-contained executable files.
auxiliary vector
A small set of key-value data the Linux kernel passes to a new process at startup, used by loaders and runtimes.
Bionic
Android’s C library and runtime, distinct from glibc and musl.
dlopen
A runtime function that loads a shared library into a running process.
ELF
Executable and Linkable Format, the standard binary file format used for executables and shared libraries on many Unix-like systems including Linux.
ELF loader
The part of the system or runtime that maps an ELF binary or shared library into memory and starts it correctly.
glibc
The GNU C Library, the standard C library used by most Linux distributions.
ld.so
The dynamic linker and loader on Linux that loads shared libraries and resolves symbols before a program runs.
libdl
The Linux library interface for dynamic loading functions such as dlopen and dlsym.
libGL.so
The shared library interface commonly used to provide OpenGL functionality on Linux.
libstdc++
The GNU implementation of the C++ standard library.
Mesa
The main open source user-space graphics stack for Linux, including OpenGL and parts of Vulkan support.
musl
A lightweight standard C library often used in small, static, or security-focused Linux systems.
sysroot
A directory tree used by a compiler and linker as the target system’s root, containing headers and libraries for that target environment.
thread-local storage
A runtime mechanism that gives each thread its own separate instance of a variable.

Reference links

Project and related implementations

  • Solo GitHub repository
    The submitted project implementing a custom ELF loader and glibc ABI bridge for static Linux binaries.
  • crossldso
    A prior-art project mentioned as having similar goals around cross-loading shared objects.
  • lone
    A freestanding Lisp project used as an example of writing applications directly against Linux system calls.
  • liblinux
    A Rust crate for direct Linux system call use, cited as an alternative path away from libc.

Compatibility and packaging references

  • glibc_version_header
    A helper project suggested for targeting older glibc versions without building on an old OS.
  • AppImage best practices
    Referenced as standard guidance for building Linux binaries against an older glibc baseline.
  • AnyLinux AppImages FAQ
    Linked in discussion of AppImage-style distribution on older enterprise systems where container assumptions fail.
  • Mach engine
    An example of using Zig’s glibc version targeting to build portable Linux GUI apps.

Loader and libc internals

  • musl design concepts
    Cited to explain why glibc loader behavior and libc internals are hard to replicate cleanly.
  • glibc libc-start source
    Used to show glibc startup code calling into loader internals, illustrating tight coupling.
  • glibc compatibility break discussion
    Referenced as a recent narrow case where glibc compatibility changed, despite the general backward-compatibility story.
  • cosmo_dlopen
    Mentioned as an alternative approach for manually mapping a preferred loader and exposing dlopen-like behavior.

Graphics and dynamic linking background

Windows runtime comparisons