For want of a relative path
Distributing dynamically-linked ELF executables on Linux can be arduous. Some downstream effects of this include:
- Distributing Linux applications in source form.
 - Distributing Linux applications as Docker containers.
 - Relying on OS package managers to provide pre-built packages.
 - The popularity of Go, with its statically-linked ELF executables.
 
At first, the problem doesn't look arduous: an ELF executable can contain an rpath or runpath attribute telling the dynamic linker where to find its shared object dependencies, and if that attribute starts with the magic placeholder $ORIGIN/, then the dynamic linker will look in the directory containing the executable (or a directory nearby) for its shared object dependencies. For example, if my_executable depended upon libz.so.1, and my_executable had an rpath or runpath of $ORIGIN/libs, then the executable and the library could be distributed using the following directory structure:
my_executable
libs/
  libz.so.1
This is great, but it has one limitation: an ELF executable also contains an attribute telling the kernel where to find the dynamic linker, and that attribute has to be an absolute path (or a path relative to the current working directory); it cannot be a path relative to the executable. On contemporary x86-64 systems, that absolute path tends to be /lib64/ld-linux-x86-64.so.2. This forces ELF executables to use whatever the system provides at /lib64/ld-linux-x86-64.so.2, which is typically version N of glibc's dynamic linker, for some N. In turn, this forces the ELF executable to use version N of the rest of glibc (libc.so.6, libm.so.6, libpthread.so.0, etc).
Continuing the example, it is likely that my_executable and libz.so.1 were built against some version M of glibc. If M ≤ N, then everything will work fine, but problems often crop up when M > N. One commonly touted solution is to set up a build environment with a very old version M of glibc, build my_executable and libz.so.1 in that environment, and then distribute them and hope for M ≤ N.
The polyfill-glibc project presents another possible solution: build my_executable and libz.so.1 against whatever version of glibc is convenient, and then run polyfill-glibc --target-glibc=N my_executable libz.so.1 to make them compatible with version N of glibc.
Sometimes we don't want either of these solutions, and what we want is to distribute the required version of glibc along with the executable, as in:
my_executable
libs/
  ld-linux-x86-64.so.2
  libc.so.6
  libz.so.1
We can get close to this by adding a launcher script:
launch_my_executable
my_executable
libs/
  ld-linux-x86-64.so.2
  libc.so.6
  libz.so.1
Where launch_my_executable is something like:
#!/usr/bin/env bash
ORIGIN="$(dirname "$(readlink -f "$0")")"
exec "$ORIGIN/libs/ld-linux-x86-64.so.2" --library-path "$ORIGIN/libs:$LD_LIBRARY_PATH" "$ORIGIN/my_executable" "$@"
This will work most of the time, though comes with caveats:
- Execing the dynamic linker is a slightly obscure feature, which can increase the chance of hitting bugs (e.g. BZ#16381, BZ#24900).
 - If 
my_executabletries toopen("/proc/self/exe")orreadlink("/proc/self/exe")or similar, it'll getld-linux-x86-64.so.2rather than itself. - Having a launcher script is aesthetically displeasing.
 
As an alternative without these caveats, there's an experimental tool in the polyfill-glibc repository called set_relative_interp. For our running example, the tool would be invoked as:
$ set_relative_interp my_executable libs/ld-linux-x86-64.so.2
After running the tool as above, my_executable will use $ORIGIN/libs/ld-linux-x86-64.so.2 as its dynamic linker.