GCC linking to a shared object's linker name
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:
- symlink
/lib/libsomething.so
pointing to/lib/libsomething.so.1
on machine A; - symlink
/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
andlibsomething.so.2
. Make sureSONAME
is set to unversionedlibsomething.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. Runldconfig -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 runldd
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
orlibsomething.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.
精彩评论