How to Change 'Rpath' in an Already Compiled Binary

Can I change 'rpath' in an already compiled binary?

There is a tool called chrpath which can do this - it's probably available in your distribution's packages.

How do I set DT_RPATH or DT_RUNPATH?

When you are compiling a program, you create object files and then link them together. You may use GNU ld(1) to link them, there are also other linkers, LLVM linker. A linker combines object files into executable. GNU ld(1) is part of binutils with documentation available here.

When you execute an already compiled ready to use executable then the dynamic linker ld.so(8) finds the libraries on the system that the executable depends on, loads them and executes the executable. ld.so(8) is a shared library usually distributed as part of C standard library, usually on linux that's glibc, but there are also other, like musl.

I think it's confusing that both these programs are named "linker". One is like "compiling linker" and the other is "executable linker".

How do I set DT_RPATH or DT_RUNPATH?

Edit the elf file to include specific section.

When creating the elf file with GNU ld, nowadays you set RUNPATH section with -rpath=something. You can set RPATH section with --disable-new-dtags -rpath=something. RPATH is deprecated, so normally -rpath sets RUNPATH. https://www.javaer101.com/en/article/1127068.html *This does not check out on my system and I have to compile with gcc -Wl,--enable-new-dtags -Wl,-rpath=/tmp to set RUNPATH...

You can also set sections in any ELF file after compilation. See Can I change 'rpath' in an already compiled binary?

whether they are the same as -rpath and -rpath-link?

From ld documentation:

The difference between -rpath and -rpath-link is that directories specified by -rpath options are included in the executable and used at runtime, whereas the -rpath-link option is only effective at link time.

And the documentation also explains how -rpath-link works. It's to specify directories for searching dependent shared libraries.

Finally there is an LD_RUN_PATH environment variable

When compiling an executable GNU ld(1) also searches libraries in directories specified in this variable.

How to set RPATH and RUNPATH with GCC/LD?

Is there a way to force the old behavior?

Yes. You can use this option -Wl,--disable-new-dtags to tell the new linker to use the old behavior, i.e. RPATH.

Is it possible to tell the old version of linker to produce the new output (i.e. RUNPATH instead of RPATH)?

Yes. Use -Wl,--enable-new-dtags to tell the old linker to use the new behavior, i.e. RUNPATH.

I verified the executable with readelf and these two options seem to control what will be written in the ELF Dynamic section. I think the problem was caused by a change in the defaults for the new version, although, interestingly, the manual page for ld would suggest that it should still be the same:

--enable-new-dtags

--disable-new-dtags

This linker can create the new dynamic tags in ELF. But the older ELF systems may not understand them. If you specify
--enable-new-dtags, the new dynamic tags will be created as needed and
older dynamic tags will be omitted. If you specify --disable-new-dtags, no new dynamic tags will be created. By default, the new dynamic tags are not created. Note that those options are
only available for ELF systems.

How can I set an executable's rpath and check its value after building it?

If I didnt miss something here, you are not linking any libs in your build command.

Lets say you want to link libusb.so shared library, which is located in libusb sub-folder of your current folder where is main.cpp.
I will not take any details here, about soname, linkname of lib etc, just to make clear about rpath.

rpath will provide runtime linker path to library, not for linktime, cause even shared library need to be present(accessible) in compile/link time. So, to provide your application loader with possibility to look for needed library in start time, relatively to your app folder, there is $ORIGIN variable, you can see it with readelf but only if you link some library with $ORIGIN in rpath.
Here is example based on your question:

g++ main.cpp -o main -L./libusb -Wl,-rpath,'$ORIGIN/libusb' -lusb 

As you see, you need to provide -L directory for compile/link time search, and rpath for runtime linker. Now you will be able to examin all needed libs for your app using readelf and location for search.

Can you relink/modify relative shared library look up paths?

Is there a way I can poke the files Ai.so and tell them "Hey that other library whose symbols you need is actually over here now"?

You can use patchelf to do that, but you shouldn't.

Since you control how A*.so is built, you should set their RPATH so that it works "out of the box". Adding -rpath=/path/to/BLibraries to A*.so link command is probably all that's needed.

Is there a programmatic way to inspect the current rpath on Linux?

#include <stdio.h>
#include <elf.h>
#include <link.h>

int main()
{
const ElfW(Dyn) *dyn = _DYNAMIC;
const ElfW(Dyn) *rpath = NULL;
const char *strtab = NULL;
for (; dyn->d_tag != DT_NULL; ++dyn) {
if (dyn->d_tag == DT_RPATH) {
rpath = dyn;
} else if (dyn->d_tag == DT_STRTAB) {
strtab = (const char *)dyn->d_un.d_val;
}
}

if (strtab != NULL && rpath != NULL) {
printf("RPATH: %s\n", strtab + rpath->d_un.d_val);
}
return 0;
}

How to set the path that a .so library will search for other .so libraries?

The bottom line is that your final executable must know where your library resides. You can accomplish that 2 ways (1) exporting the LD_LIBRARY_PATH that includes the path to your library, or (2) using rpath so your executable knows where to find your library. Exporting the LD_LIBRARY_PATH generally looks something like this:

LD_LIBRARY_PATH=/path/to/your/lib:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH

I prefer to use rpath. To use rpath compile your library as normal (example below for my extended test function library libetf.so)

gcc -fPIC -Wall -W -Werror -Wno-unused -c -o lib_etf.o lib_etf.c
gcc -o libetf.so.1.0 lib_etf.o -shared -Wl,-soname,libetf.so.1

Then to compile an executable making use of this library, you compile to object, then link the object with rpath given as a linker option. You would provide a path for both libA.so and libB.so in your case. Building my testso executable:

gcc -O2 -Wall -W -Wno-unused -c -o testetf.o testetf.c
gcc -o testso testetf.o -L/home/david/dev/src-c/lib/etf -Wl,-rpath=/home/david/dev/src-c/lib/etf -letf

Use ldd to confirm that the executable has correctly located your library:

$ ldd testso
linux-vdso.so.1 (0x00007fffd79fe000)
libetf.so.1 => /home/david/dev/src-c/lib/etf/libetf.so.1 (0x00007f4d1ef23000)
libc.so.6 => /lib64/libc.so.6 (0x00007f4d1eb75000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4d1f126000)

Note: libetf.so.1 points to /home/david/dev/src-c/lib/etf/libetf.so.1.

-RPATH preference not working when executable run with SETUID set

Just in case any soul runs into this issue, here is what I did to resolve this issue.

Simply load the dylibs programmatically by dlopen APIs, by hardcoding the paths.



Related Topics



Leave a reply



Submit