Relative-To-Executable Path to Ld-Linux Dynamic Linker/Interpreter

patchelf set interpreter to a path relative to executable

You need to specify the path relative to the current working directory (the one you use to execute the program) and not relative to the directory that contains the binary file.

echo 'int main() { return 0; }' > main.c
gcc -Wl,--dynamic-linker=./lib/ld-linux-x86-64.so.2 -o main main.c

mkdir bin
mkdir lib
cp /lib64/ld-linux-x86-64.so.2 lib/
cp main bin/

bin/main # this should run without problems
cd bin; ./main # this throws a "No such file or directory" error

Specifying the dynamic linker / loader to be used when launching an executable on Linux

I'm looking for a way to specify the dynamic loader before launching any executable so when I want to run software which was compiled against uClibc

You should be specifying the correct dynamic loader while building against uClibc, using the linker --dynamic-linker argument. E.g.

gcc -nostdlib -Wl,--dynamic-linker=/lib/ld-uClibc.so.0 \
/lib/uClibc-crt1.o main.o -L/path/to/uClibc -lc

How does the dynamic linker executes /proc/self/exe

As you pointed out, the kernel is not passing the executed binary as a path to the interpreter:

$ /lib64/ld-linux-x86-64.so.2 /proc/self/exe
loader cannot load itself

Although the glibc dynamic linker supports this invocation method (providing the program to run as an argument), it's not what's used during the normal execution of an interpreted ELF binary. In fact, the kernel provides the arguments from the execve unmodified to the dynamic linker.

The dynamic linker doesn't "load" or "execute" the interpreted ELF binary at all. The kernel loads both the interpreter and the interpreted binary into memory and begins execution at the entry point of the interpreter. The entry point of the interpreted binary is passed to the interpreter via the AT_ENTRY field in the auxiliary vector.

The dynamic linker then preforms the necessary runtime linking and jumps to the "real" entry point.

You can observe this all in gdb if you set a break point on _start when executing a normal interpreted ELF executable. With "show args" you'll see the "real" argv without any extra values, and the memory map of the process will already have the interpreted binary loaded (before the interpreter has run a single instruction).

#! scripts work the way you expect (actually manipulating the argv values).

Determine real executable when invoking dynamic linker directly

This is probably too easy and you've already thought about it, but can't you simply read /proc/*/cmdline and find the real executable as an argument to ld? Only if you have detected that /proc/*/exe is a symlink to ld, of course.

Using $ORIGIN to specify the interpreter in ELF binaries isn't working

If I specify an absolute path instead of using $ORIGIN then it seems to work fine.

This is working as intended.

It is the dynamic linker that interprets (expands) $ORIGIN and other special tokens.

The Linux kernel doesn't.

And it is the kernel that reads PT_INTERP segment of the main executable and (if present) loads and invokes the interpreter (the dynamic linker). When you set interpreter to non-existant path (such as $ORIGIN/lib/ld-linux-x86-64.so.2), you get ENOENT from the kernel execve system call.

There is no way to make interpreter itself be anything other than valid path.

Depending on what you are actually trying to achieve, rtldi may be the answer.

Custom ld-linux.so for subprocesses

I think the problem is that the subprocess is reverting to the system /lib64/ld-linux-x86-64.so.2.

That is what one should expect to happen if the execve argument is /path/to/subprocess or subprocess.

If you want the subprocess to use explicit loader invocation /path/to/my/ld-linux-x86-64.so.2 --library-path /path/to/my/libs /path/to/subprocess, then you must arrange for execve arguments to be exactly that.

This is why using patchelf or other solutions from this answer is generally a better approach.

Why the output binary of ld can not be executed?

If you run ldd prog you will see /lib/ld64.so.1 => /lib64/ld-linux-x86-64.so.2. The loader /lib/ld64.so.1 does not exists and you get a No such file or directory error. If you add --dynamic-linker=/lib64/ld-linux-x86-64.so.2 to your linking options the program can be executed. Also see this answer.

Edit:

You can see the arguments used by gcc by executing gcc -v main.c sum.c -o program. I used that output for finding the missing linking arguments. When linking with ld -o prog sum.o main.o -lc --dynamic-linker=/lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/8/crtbeginS.o -lc /usr/lib/gcc/x86_64-linux-gnu/8/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crtn.o there was no segmentation anymore.



Related Topics



Leave a reply



Submit