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
Maximum Number of Threads Allowed to Run
Linux Shared Library Depends on Symbols in Another Shared Library Opened by Dlopen with Rtld_Local
Succinct Way to Print All Lines Up Until The Last Line That Matches a Given Pattern
Having an Issue Passing Variables to Subshell
How to Change Some "Special" Gui Colors in Eclipse
How to Get Jenkins Working with Binaries from a Subfolder of The Root User
How to Use Multiple Threads for Zlib Compression (Same Input Source)
Cannot Connect to The Docker Daemon at Unix:///Var/Run/Docker.Sock
Kill Background Process on Sigint
Testing - Intentionally Corrupt a .Z File Using 'Dd'
Understanding Linux Display Variable
Splitting a File Using Awk on MAC Os X
Splitting a Large Directory into Smaller Ones in Linux
Linux Sysfs Gpio: What Re-Arms Interrupt
Parallel Processes: Appending Outputs to an Array in a Bash Script