11

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.

1

1 Answer 1

12
+50

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.

8
  • I don't have write access to my binaries (such as /bin/ls) and I don't think it is a good idea to patch the system binaries to make this work. Isn't it possible to just overwrite the requested program interpreter by some env var? Where is the related code in the Linux kernel where it reads out the requested program interpreter and runs it?
    – Albert
    Dec 28, 2016 at 13:50
  • 1
    @Albert You are not supposed to overwrite the system binaries. You should copy to one of your directories those you need and modify those in situ, i.e. the copy in your possession. Dec 28, 2016 at 14:16
  • But I want to do that for many system binaries. I don't want to copy them all over somewhere that I can patch them. Can I not just run them inplace? Esp, my main questions: Isn't it possible to just overwrite the requested program interpreter by some env var? Where is the related code in the Linux kernel where it reads out the requested program interpreter and runs it?
    – Albert
    Dec 28, 2016 at 14:45
  • @Albert No, you cannot use a system variable for programs and some libraries in binary form, because they contain the INTERP information above, which is impervious to system variables. If you want to try a hack (I never tried it myself). since the program interpreter (=runtime linker) ld-linux-x86-64.so.2 is a symbolic link to the specific version of the linker which is now current, you can alter it to point to your own program iinterpreter. YMMV. As for the kernel code, it is here, lxr.free-electrons.com/source/fs/binfmt_elf.c?v=3.18 Dec 28, 2016 at 15:35
  • Ah, thanks for the link. I guess the relevant function is load_elf_binary, and then the check for p_type == PT_INTERP? Yes, from the code I see, it will just use whatever is written in the file and not look at any env var. You should point that out in your answer. For me, that means that my solution is really to always use the ld-linux loader explicitly (no patching).
    – Albert
    Dec 28, 2016 at 16:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .