Linking: Why does linker not honour symlink to library?

638 Views Asked by At

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?

2

There are 2 best solutions below

0
On BEST ANSWER

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.

0
On

If the linker put libz.so.1.2.11 in your executable, then it would break when you updated the library. Instead it puts the major version known to ldconfig in the executable, which is in turn a link to the current installed version.

See ldconfig http://man7.org/linux/man-pages/man8/ldconfig.8.html