Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 449
Bill Prin Avatar asked Jan 13 '11 21:01

Bill Prin


2 Answers

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 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.

like image 51
3 revs Avatar answered Sep 27 '22 22:09

3 revs


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.

like image 45
user562374 Avatar answered Sep 27 '22 21:09

user562374