rtldi -- indirect runtime loader

John Reiser
BitWagon Software LLC
jreiser BitWagon com
April 25, 2004
revised July 2, 2004

http://www.BitWagon.com/rtldi/rtldi.html
http://www.BitWagon.com/rtldi/rtldi-0.94.tgz (20KB; GPLv2)

rtldi is an ELF program interpreter that enables each main executable program to use its own runtime loader and shared libraries, independent of the default versions installed in /lib, and independent of any other main executable program.  Thus rtldi facilitates the simultaneous interoperation of programs that originally were linked using different generations of glibc6.  In theory glibc6 is backward compatible (a given binary executable should run correctly using any subsequent version of glibc6), but in practice there have been enough bugs and glitches at various times to require workarounds.

Example:  If the PT_INTERP of an executable a.elf is /2.2.4-24/rtldi (instead of /lib/ld-linux.so.2), then the executable runs effectively as if it were invoked via
    /2.2.4-24/ld-linux.so.2  --library-path  /2.2.4-24:$LD_LIBRARY_PATH  /path/of/execve/a.elf  args...
and will use /2.2.4-24/libc.so.6 for its glibc6.  rtldi uses its own path prefix in PT_INTERP as the prefix for ld-linux.so.2, and as the first directory path in the colon-separated list argument following --library-path.  The --library-path argument replaces LD_LIBRARY_PATH for a.elf only, without modifying LD_LIBRARY_PATH for a.elf or for any children that a.elf may invoke.  (This is a feature of ld-linux.so.2.)  Also, the process name remains "a.elf".

New ELF executables can specify the runtime loader by building with   -Wl,--dynamic-linker=/path/to/rtldi.  Old executables that specify /lib/ld-linux.so.2 for their PT_INTERP can be modified by using a binary file editor.  Find the Offset of the INTERP string by using readelf --program-headers a.elf.  The replacement string cannot be longer, and must be terminated by '\0'; thus the limit is 18 bytes.

Strategy for old executables:  Choose a naming convention.  The examples above use the version string of the glibc6 distribution .rpm file.  The final component name of the PT_INTERP does not matter; choosing a one-character name such as ',' [comma: "/path/,"] allows using more characters elsewhere in the path.

Version 0.94 of rtldi sets argv[0] to the complete path that was specified to execve(), unless argv[0] already contains a '/'. ld-linux.so.2 needs this because it does not search $PATH. As a result, rtldi does not work for programs that interpret argv[0] as something else. There is a race in the double interpretation of the path to the executable (once by execve and once by ld-linux.so.2), so don't move, remove or modify the executable while using rtldi.

Version 0.93 of rtldi works better with gdb than previous versions. (gdb) set stop-on-solib-events 1 appears to work, although some versions of gdb give complaints which should be ignored. The next and step commands of gdb-5.3post-0.20021129.18rh do not work; use explicit breakpoints instead. This bug in gdb is fixed in gdb-5.3.90-0.20030710.41rh. Version 0.93 of rtldi manages to preserve a good symlink /proc/pid/exe most of the time.

Note: the full glibc6 environment includes many .so files.  The safest is to include a copy or link to each file in the glibc package that normally lives in /lib.  For example, rpm -ql glibc-2.3.2-27.9.7 shows these files:
tls/libc-2.3.2.so i686/libc-2.3.2.so ld-2.3.2.so
tls/libc.so.6 i686/libc.so.6 ld-linux.so.2
tls/libm-2.3.2.so i686/libm-2.3.2.so libc-2.3.2.so
tls/libm.so.6 i686/libm.so.6 libc.so.6
tls/libpthread-0.34.so i686/libpthread-0.10.so libpthread-0.10.so
tls/libpthread.so.0 i686/libpthread.so.0 libpthread.so.0
tls/librt-2.3.2.so i686/librt-2.3.2.so librt-2.3.2.so
tls/librt.so.1 i686/librt.so.1 librt.so.1
tls/libthread_db-1.0.so
libthread_db-1.0.so
tls/libthread_db.so.1
libthread_db.so.1


libm-2.3.2.so


libm.so.6


libnsl-2.3.2.so


libnsl.so.1


libdl-2.3.2.so


libdl.so.2


libutil-2.3.2.so


libutil.so.1


libresolv-2.3.2.so


libresolv.so.2


libBrokenLocale-2.3.2.so


libBrokenLocale.so.1


libNoVersion-2.3.2.so


libNoVersion.so.1


libSegFault.so


libanl-2.3.2.so


libanl.so.1


libcrypt-2.3.2.so


libcrypt.so.1

plus 23 libnss*.so and 168 /usr/lib/gconv/*.so.  At least all the files in the table above, and all the libnss*.so, should be copied or linked to the path prefix of the PT_INTERP for rtldi.