rpath things
interesting load library path things
Saw this post: https://stoppels.ch/2022/08/04/stop-searching-for-shared-libraries.html
You should read it. I am just repeating a lot here.
My code for all of this is at: https://github.com/tonetheman/rpath_linking
Basically there is a thing called RPATH that at least in linux gives you some interesting powers.
You can end up hard coding the library paths into your final binary! And it can be good really.
Normally when you use shared libraries when you run the load time linking has to know where the shared libraries are located on disk.
If you do not you get the ever present
error while loading shared libraries: XXXXX: cannot open shared object file: No such file or directory
Normally I would fix this by looking at the main file using ldd
to figure out what it was dynamically linking.
It never hit me that you can change the names inside of the binary. Not sure why... it seems obvious now.
the first way
In the directory way1
There is a single shared object libf that has a single function that is shared. There is a test file that I compile to two different binaries: junk and junkr.
I compile libf like this in the way1 directory
libf.so.4.2.1 : justf.c
# tell compiler to make an so and put the so name into the binary
gcc -o libf.so.4.2.1 -shared justf.c -Wl,-soname,libf.so.1
# create some other links
ln -s libf.so.4.2.1 libf.so
ln -s libf.so.4.2.1 libf.so.1
The only oddness is setting the soname in the shared object. Which is used when you link against it.
So when I run this command to make the binary
junk : testf.c
gcc -o junk testf.c -lf -L.
You will get a binary that during the build is looking for libf.so.1 and it is using the current directory tacked onto the search path.
But the binary junk
will still fail when you try to run it! Because in order to run junk
needs a shared object file named libf.so.1 and you can verify that with this command
agcc@DESKTOP-REHHCAH:~/rpath/way1$ ldd ./junk
linux-vdso.so.1 (0x00007ffeb81f1000)
libf.so.1 => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdac7db0000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdac7fb4000)
So in this case you need to give linux a way to find that shared object.
LD_LIBRARY_PATH=. && ./junk
Then just to make sure I am back to my base state I reset LD_LIBRARY_PATH again explicitly.
export LD_LIBRARY_PATH=
If you look at the next binary we see a different story
Now we are going to build the same binary with a little more information.
junkr : testf.c
gcc -o junkr testf.c -lf -L. -Wl,-rpath,${PWD}
This time we are going to include rpath and hard code it to the current directory. When you do that you can run run ./junkr
directly and you do not need to set the LD_LIBRARY_PATH at all!
Lets look at ldd and see what it shows
agcc@DESKTOP-REHHCAH:~/rpath/way1$ ldd junkr
linux-vdso.so.1 (0x00007fffd09eb000)
libf.so.1 => /home/agcc/rpath/way1/libf.so.1 (0x00007f5c5129c000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5c5109f000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5c512a8000)
This time libf is now hardcoded all the way to my directory. So linux knows exactly what to do here to find the shared object files.
hardcoding ... is this ok?
I think it can be but you have to understand what you have done. If these binaries are destined to live in a certain spot for their entire life then I think a hardcode is great.
This also means that you could move the final binary (junkr in this case) anywhere on the file system as long as the hardcode is still available at an operating system level.
another subtle point or it was for me
The name that ldd is using is the SAME soname we gave it when we made the shared object.
gcc -o libf.so.4.2.1 -shared justf.c -Wl,-soname,libf.so.1
So in this case the soname is libf.so.1 and that is what shows in the ldd lines for the linked binary junk and junkr.
Just to drive this point home I will change the makefile and then run clean and try it again
libf.so.4.2.1 : justf.c
# tell compiler to make an so and put the so name into the binary
gcc -o libf.so.4.2.1 -shared justf.c -Wl,-soname,punkmonkey
# create some other links
ln -s libf.so.4.2.1 libf.so
ln -s libf.so.4.2.1 libf.so.1
Notice the changed line our soname is now punkmonkey
I did NOT change the compiles for the two binaries but when we look at ldd it shows the point (hopefully).
agcc@DESKTOP-REHHCAH:~/rpath/way1$ ldd junk
linux-vdso.so.1 (0x00007fff525d6000)
punkmonkey => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1bd6011000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1bd6215000)
agcc@DESKTOP-REHHCAH:~/rpath/way1$ ldd junkr
linux-vdso.so.1 (0x00007ffd00da8000)
punkmonkey => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fad4f1a5000)
/lib64/ld-linux-x86-64.so.2 (0x00007fad4f3a9000)
In both cases the command now shows that the binaries are looking for something called punkmonkey. That name comes from the soname that is hard coded at build/link time for that shared object.
the 2nd way
Looking in directory way2 it all comes to a final end with the hardcoding.
libf.so.4.2.1 : justf.c
# tell compiler to make an so and put the so name into the binary
# this time include the full path so when the binary link happens
# it should get the full path hard coded into the junk binary also
gcc -o libf.so.4.2.1 -shared justf.c -Wl,-soname,${PWD}/libf.so
# create some other links
ln -s libf.so.4.2.1 libf.so
ln -s libf.so.4.2.1 libf.so.1
The link step now hard codes the full path to libf.so in the soname.
And when you look at the result binaries junk and junkr with ldd you see this
agcc@DESKTOP-REHHCAH:~/rpath/way2$ ldd junk
linux-vdso.so.1 (0x00007ffed15bb000)
/home/agcc/rpath/way2/libf.so (0x00007f0eb3fde000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0eb3de1000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0eb3fea000)
agcc@DESKTOP-REHHCAH:~/rpath/way2$ ldd junkr
linux-vdso.so.1 (0x00007ffd120bc000)
/home/agcc/rpath/way2/libf.so (0x00007f57bf89f000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f57bf6a2000)
/lib64/ld-linux-x86-64.so.2 (0x00007f57bf8ab000)
You can both of them directly without needing to change LD_LIBARY_PATH both will execute as they sit on the disk now. And it is because we set the soname in the shared object to be a full path. Or really to a link but it is the full path. So both will run!
Interesting and cool technique.
Other places I read about some of this https://en.wikipedia.org/wiki/Rpath#:~:text=In%20computing%2C%20rpath%20designates%20the,(or%20another%20shared%20library).