Linux – overwrite default /lib64/ld-linux-x86-64.so.2 to call executables

linux

I need a newer Glibc than my system provides for a binary I want to run. So I have a directory /my_libs with most lib-files from a recent Ubuntu, including the new Glibc (libc.so.6).

Now I exported LD_LIBRARY_PATH=/my_libs:$LD_LIBRARY_PATH and try to run my executable. That will crash with SEGFAULT. In fact, every binary will crash now with SEGFAULT (even simple things like /bin/ls). So I guess some mixup between different libs from /my_libs and my main system. I traced with LD_DEBUG=libs (or LD_DEBUG=all) and resolved everything except of ld-linux-x86-64.so.2. No matter what I did so far, it always used /lib64/ld-linux-x86-64.so.2 and not /my_libs/ld-linux-x86-64.so.2.

Is there a way to set the path for ld-linux? As I understood, that library, which is also an executable, is always used to run any program, and my environment uses /lib64/ld-linux-x86-64.so.2.

If I directly run /my_libs/ld-linux-x86-64.so.2 /bin/ls, it works.
I can also execute my new binary that way — I just have to be sure that all libs are provided in /my_libs, or that the libs from the system are compatible.

So, what can I do that just calling /bin/ls directly will use /my_libs/ld-linux-x86-64.so.2?

Some related discussion is here.

Note that I don't want to patch /bin/ls or other binaries to make that work.

Best Answer

Tl:dr: If you need to keep using multiple versions of libc, like many of us do, then a key utility to use is PatchElf.

Normally, if all you want is to force your own version of the program interpreter when you build your own program, all you need to do is to compile with the following options:

gcc -Wl,-dynamic-linker,/my/lib/ld-linux.so.2 ...

But this does not work with third-party utilities and with some shared objects which are however executable all by themselves, so you invoke patchelf on a given ELF program as follows:

patchelf --set-interpreter /my/lib/my-ld-linux.so.2 someprogram

The reason why this is not just a matter of editing with an hex editor the old executable and overwriting the old interpreter address with the new one, is that the two need not be the same length; patchelf takes care of enlarging your executable for you.

You can also change the rpath variable, as follows:

patchelf --set-rpath /my_libs:$LD_LIBRARY_PATH someprogram 

I find this much handier than using the usual wrappers around commands with LD_LIBRARY_PATH.

As for execve, it must be just about the most fundamental system call in Unix: execve(filename) executes the given filename. For instance, your shell executes a command by means of a call of the execve family: it first forks, then the child process thus generated execve's the command (ls, cd,,... you name it). Most programs need dynamically linked libraries, for instance for ls:

$ ldd $(which ls)
    linux-vdso.so.1 =>  (0x00007ffe3b0e7000)
    libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f1423dda000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1423a11000)
    libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f14237a0000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f142359c000)
    /lib64/ld-linux-x86-64.so.2 (0x0000563800043000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f142337f000)

When you load ls, it is not the binary normal entry point which is used at first, but ld-linux instead: it takes care of loading all the unresolved, required libraries, and then it transfers control to the real application, ls in this case. It does this by execve'ing the program file.

I am unable to say why exactly your program crashes., but I would try checking that all programs invoked require the same program interpreter; you do this by checking the output of:

    $ readelf -l /bin/ls

    Elf file type is EXEC (Executable file)
    Entry point 0x4049a0
    There are 9 program headers, starting at offset 64

    Program Headers:
      Type           Offset             VirtAddr           PhysAddr
                     FileSiz            MemSiz              Flags  Align
      PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                     0x00000000000001f8 0x00000000000001f8  R E    8
      INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                     0x000000000000001c 0x000000000000001c  R      1
          [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
    ...........................

(the emphasis is mine...).

EDIT: an excellent discussion by one of the authors of the execveat system call can be found here, and the relevant kernel code is here. The code shows that no system variable is read, and the routine load_elf_binary looks at the program interpreter PT_INTERP.

Related Question