Suppose I have:
/usr/lib/libsomething.so.1
on machine A;/usr/lib/libsomething.so.2
on machine B.Both machines have /usr/lib/libsomething.so
symlinking to their respective libs.
If I link using gcc
with -lsomething
(or even /usr/lib/libsomething.so
) it will follow the symlink, and ldd
on machine A produces something like:
libsomething.so.1 => /usr/lib/libsomething.so.1
This means it won't be able to find the library on machine B.
Now I know these are major version number changes and I know they may not be compatible, but I'm willing to take that risk. What I'd like to tell the linker is to look for libsomething.so
, and don't follow the symlink so ldd
will show
libsomething.so => /usr/lib/libsomething.so.1
on A but
libsomething.so => /usr/lib/libsomething.so.2
on B. And then the loader will follow the symlink to whatever version is there.
Also, I don't want delayed loading with dlopen or anything. I want it to link to the shared object at compile time.
Is this even possible?
Making executable which uses any available version of shared library is, of course, possible.
The problem was that you linked your executable to version-specific soname (libsomething.so.1
and libsomething.so.2
). You should have done it with unversioned soname libsomething.so
instead.
In order to achieve this, on the build machine you should compile and install library with soname (ELF SONAME
) equal to libsomething.so
(without version) so that linker can choose this soname while executable is built.
According to the Shared Libraries HOWTO, you can pass required unversioned soname while building the library:
gcc -shared -Wl,-soname,libsomething.so -o libsomething.so.X objectsomething.o
Then, as soon as you install the library and run ldconfig
, you have:
/lib/libsomething.so
pointing to /lib/libsomething.so.1
on machine A;/lib/libsomething.so
pointing to /lib/libsomething.so.2
on machine B.The loader (run ldd
) will choose unversioned symlinks regardless where it points to:
libsomething.so => /lib/libsomething.so (0xNNNNNNNN)
on machine A;libsomething.so => /lib/libsomething.so (0xNNNNNNNN)
on machine B.Linux dynamic loader (ld.so
) resolves libraries based on their soname value written in the executable (ELF NEEDED
). The value is copied from library file (ELF SONAME
) while building the executable. As long as there is a symlink on the target system matching the soname recorded in the executable, the library pointed by this symlink will be loaded.
Let's run through your setup and show commands to verifing assumptions.
I used Fedora 18 X86_64
for the test and adjusted output to i686
for clarity.
Compile both libsomething.so.1
and libsomething.so.2
. Make sure SONAME
is set to unversioned libsomething.so
:
readelf -a libsomething.so.1 | grep SONAME
0xNNNNNNNN (SONAME) Library soname: [libsomething.so]
readelf -a libsomething.so.2 | grep SONAME
0xNNNNNNNN (SONAME) Library soname: [libsomething.so]
Install the libraries into their respective machines under /lib/
directory. Run ldconfig -v
on both machines and verify the output.
ldconfig -v 2>&1 | grep something
libsomething.so -> libsomething.so.1 (changed)
ldconfig -v 2>&1 | grep something
libsomething.so -> libsomething.so.2 (changed)
Compile executable and make sure that it refers to the same soname without version in NEEDED
.
readelf -a executable | grep NEEDED
0xNNNNNNNN (NEEDED) Shared library: [libsomething.so]
You executable depends on unversioned libsomething.so
now. Copy executable to both machines and run ldd
against both copies.
ldd executable
libsomething.so => /lib/libsomething.so (0xNNNNNNNN)
The last output is the same on both machines as the executable was built with soname without version. This makes loader take unversioned symlinks on targets machines. And depending on the machine, the symlink can point to different implementation of the library libsomething.so.1
or libsomething.so.2
.
This means it won't be able to find the library on machine B.
And it's not supposed to anyway.
By the very definition of soversions, libsomething.so.2
denotes that the API/ABI is incompatible to libsomething.so.1
. Therefore, just adding libsomething.so
in the program's table of libraries to be loaded would be factually wrong. The libsomething.so
symlink merely serves as a hint to ld as to which soversion to pick by default.
Of whatever file ld actually ended up opening, it will take the DTNAME/SONAME field to encode in the program. If you don't want that, don't equip libsomething with a soname. But it can easily become pain... starting with running into unavailable symbols when trying to run the program.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With