linuxcompilationldshared-objectslibz

Linking: Why does linker not honour symlink to library?


I have the following C program:

#include <stdio.h>
#include <zlib.h>

int main()
{
    z_stream strm;
    int integer = 0;
    scanf("heloworld %d", &integer);
    printf("ok\n");

    if (integer == 10)
    {
        strm.zalloc = Z_NULL;
        strm.zfree = Z_NULL;
        strm.opaque = Z_NULL;
        deflateInit(&strm, 0);
    }

    return 0;
}

This is a basic helloworld program which uses zlib.

If I search for the libz library, I can find it under /usr/lib/x86_64-linux-gnu/libz.so:

$ ls -lah libz.so
lrwxrwxrwx 1 root root 40 May 20 14:55 libz.so -> /usr/lib/x86_64-linux-gnu/libz.so.1.2.11

and it is pointing to the real version of libz rather than the soname.

I compile it with the following command and check the dependencies:

$ gcc a.c -lz
$ ldd a.out
linux-vdso.so.1 (0x00007ffec44b6000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f6674055000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6673e63000)
/lib64/ld-linux-x86-64.so.2 (0x00007f667408c000)

How is it pointing to libz.so.1 instead of libz.so.1.2.11 (realname) since the symlink of libz.so.1 is pointing there? I am assuming that the linker is using the symlink however this is not the case.

Further to this, if I perform the following command:

$ objdump -p libz.so.1.2.11 | grep SONAME
SONAME               libz.so.1 

My question is, is it using the symlink name or the SONAME from the file the symlink provides?


Solution

  • I have discovered that under /lib/x86_64-linux-gnu there are two symlinks for libz.so which are:

    libz.so
    libz.so.1
    

    When I compile, the linker uses the libz.so as a symlink to point to another .so file.

    The .so file which libz.so is pointing to contains an SONAME entry which can be viewed as so:

    $ objdump -p ./libz.so | grep SONAME
    SONAME               libz.so.1
    

    Further I can see that the symlink points to libz.so.1.2.11

    $ ls -alh libz.so
    lrwxrwxrwx 1 root root 36 May 21 00:51 libz.so -> /lib/x86_64-linux-gnu/libz.so.1.2.11
    

    If I edit the .dynstr section of that library like so:

    $ sudo objcopy --dump-section .dynstr=/tmp/dyn.dump ./libz.so.1.2.11
    # Find the bytes for libz.so.1 and change them to libz.so.2
    $ hexedit /tmp/dyn.dump
    $ sudo objcopy --update-section .dynstr=/tmp/dyn.dump ./libz.so.1.2.11
    

    and then recompile my binary I notice:

    $ ldd a.out
    linux-vdso.so.1 (0x00007ffe54bbe000)
    libz.so.2 => not found
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f58bb5c7000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f58bb7f0000)
    

    Once I create a symlink of libz.so.2 under /lib/x86_64-linux-gnu/libz.so.2 to point to libz.so.1.2.11 I get the following output:

    $ ldd a.out
    linux-vdso.so.1 (0x00007ffe54bbe000)
    libz.so.2 => /lib/x86_64-linux-gnu/libz.so.2 (0x00007f58bb7b9000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f58bb5c7000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f58bb7f0000)
    

    Therefore to answer my question it is not the symlink which adds an entry in the runtime dependencies of the executable but the SONAME entry in the shared object.