5
votes

I apologize if my question is not precise because I don't have a lot of Linux related experience. I'm currently building a Linux from scratch (mostly following the guide at linuxfromscratch.org version 7.3). I ran into the following problem: when I build an executable it gets a hardcoded path to something called ELF interpreter.

readelf -l program

shows something like

[Requesting program interpreter: /lib/ld-linux.so.2]

I traced this library ld-linux-so.2 to be part of glibc. I am not very happy with this behaviour because it makes the binary very unportable - if I change the location of /lib/ld-linux.so.2 the executable no longer works and the only "fix" I found is to use the patchelf utility from NixOS to change the hardcoded path to another hardcoded path. For this reason I would like to link against a static version of the ld library but such is not produced. And so this is my question, could you please explain how could I build glibc so that it will produce a static version of ld-linux.so.2 which I could later link to my executables. I don't fully understand what this ld library does, but I assume this is the part that loads other dynamic libraries (or at least glibc.so). I would like to link my executables dynamically, but I would like the dynamic linker itself to be statically built into them, so they would not depend on hardcoded paths. Or alternatively I would like to be able to set the path to the interpreter with environment variable similar to LD_LIBRARY_PATH, maybe LD_INTERPRETER_PATH. The goal is to be able to produce portable binaries, that would run on any platform with the same ABI no matter what the directory structure is.

Some background that may be relevant: I'm using Slackware 14 x86 to build i686 compiler toolchain, so overall it is all x86 host and target. I am using glibc 2.17 and gcc 4.7.x.

2
I believe it is a bad idea to change the ELF program interpreter (unless you are a Linux & binutils guru); it is built with Glibc. You could try something else (e.g. MUSL-Libc...). Why do you want to change it? To what dynamic loader?? If you don't want to depend on its location and existence (which is a bad idea), give up dynamic linking and have only statically linked programs. And the dynamic linker /lib/ld-linux.so.2 is statically built (but still a shared library, not using any external library except the kernel provided VDSO).Basile Starynkevitch
Dynamic linking is meant to be dynamic, not to depend on static location. This [hardwired] approach is completely wrong. I want to fix it in my system. And the solution is easy I'm just not familiar enough with all the aspects to fix it myself.bobef
Then, patch the kernel to fit your needs. Then you'll have to patch the toolchain (compiler & linker) to obey them. Indeed, all this is free software and you can improve it.Basile Starynkevitch
Sure, that's the next thing to do, but I was hoping Linux was more evolved after so many decades and that's why I'm asking. I didn't expect to face such [can't find the right word] obstacles.bobef
Stack Overflow is not the best place to discuss Linux kernel design, or linker features. Use more specialized forums for that.Basile Starynkevitch

2 Answers

11
votes

I would like to be able to set the path to the interpreter with environment variable similar to LD_LIBRARY_PATH, maybe LD_INTERPRETER_PATH.

This is simply not possible. Read carefully (and several times) the execve(2), elf(5) & ld.so(8) man pages and the Linux ABI & ELF specifications. And also the kernel code doing execve.

The ELF interpreter is responsible for dynamic linking. It has to be a file (technically a statically linked ELF shared library) at some fixed location in the file hierarchy (often /lib/ld.so.2 or /lib/ld-linux.so.2 or /lib64/ld-linux-x86-64.so.2)

The old a.out format from the 1990s had a builtin dynamic linker, partly implemented in old Linux 1.x kernel. It was much less flexible, and much less powerful.

The kernel enables, by such (in principle) arbitrary dynamic linker path, to have various dynamic linkers. But most systems have only one. This is a good way to parameterize the dynamic linker. If you want to try another one, install it in the file system and generate ELF executables mentioning that path.

With great pain and effort, you might make your own ld.so-like dynamic linker implementing your LD_INTERPRETER_PATH wish, but that linker still has to be an ELF shared library sitting at some fixed location in the file tree.

If you want a system not needing any files (at some predefined, and wired locations, like /lib/ld.so, /dev/null, /sbin/init ...), you'll need to build all its executable binaries statically. You may want (but current Linux distributions usually don't do that) to have a few statically linked executables (like /sbin/init, /bin/sash...) that will enable you to repair a system broken to the point of not having any dynamic linker.

BTW, the /sbin/init -or /bin/sh - path is wired inside the kernel itself. You may pass some argument to the kernel at boot load time -e.g. with GRUB- to overwrite the default. So even the kernel wants some files to be here!

As I commented, you might look into MUSL-Libc for an alternative Libc implementation (providing its own dynamic linker). Read also about VDSO and ASLR and initrd.

In practice, accept the fact that modern Linuxes and Unixes are expecting some non-empty file system ... Notice that dynamic linking and shared libraries are a huge progress (it was much more painful in the 1990s Linux kernels and distributions).

Alternatively, define your own binary format, then make a kernel module or a binfmt_misc entry to handle it.

BTW, most (or all) of Linux is free software, so you can improve it (but this will take months -or many years- of work to you). Please share your improvements by publishing them.

Read also Drepper's Hwo to Write Shared Libraries paper; and this question.

1
votes

I ran into the same issue. In my case I want to bundle my application with a different GLIBC than comes system installed. Since ld-linux.so must match the GLIBC version I can't simply deploy my application with the according GLIBC. The problem is that I can't run my application on older installations that don't have the required GLIBC version.

The path to the loader interpreter can be modified with --dynamic-linker=/path/to/interp. However, this needs to be set at compile time and therefore would require my application to be installed in that location (or at least I would need to deploy the ld-linux.so that goes with my GLIBC in that location which goes against a simple xcopy deployment.

So what's needed is an $ORIGIN option equivalent to what the -rpath option can handle. That would allow for a fully dynamic deployment.

Given the lack of a dynamic interpreter path (at runtime) leaves two options:

a) Use patchelf to modify the path before the executable gets launched. b) Invoke the ld-linux.so directly with the executable as an argument.

Both options are not as 'integrated' as a compiled $ORIGIN path in the executable itself.