TL;DR: The executable needs to ship with all dynamically linked libraries and the necessary ELF-interpreter (ld-linux.so). The magic happens in a tiny, statically linked bootstrap program which exec’s the ELF-interpreter as userspace exec for a correct /proc/self/exe and with library path pointing to the bundled shared libraries.
I figure this could be very useful for Crystal as it would allow us to ship portable builds of the interpreter. It needs to be dynamically linked for full support of loading libraries in the interpreter runtime (cf. Shipping the interpreter).
What’s the advantages of packaging the entire dynamic linking runtime environment compared to directly distributing static-compiled programs?
I think it is a project somewhat similar to conda, but I would prefer conda because it offers more packages, is more stable and mature (as Rust, Ruby, Clang, etc. have already done), provides more comprehensive platform support, and has lighter startup tools (such as micromamba, pixi, etc.).
One major reason is that some libraries are incredibly difficult to link statically. Glibc is an example for that. For that reason, statically linked programs usually use musl libc. That’s not generally bad, but there are differences between these libc implementations and some use cases may need glibc specifically.
The Crystal interpreter has a specific use case for libdl: it needs to load dynamic libraries at runtime for handling lib bindings in interpreted code. libdl is simply not available as a static library. So it’s impossible to statically link a fully functional interpreter.
See Shipping the interpreter for details.