Silmor . de
Site Links:
Impressum / Publisher

Distributing all shared libraries

...or how to make your program really big

Needed: GCC and a real operating system (read: Linux or another Unix-like system).

Often as a programmer you have the problem that you cannot predict the environment your users have installed on their computers. This article tries to solve some of them.

For many programs you see solutions like "this program runs with version X.Y of operating system Z flavour wibblywoggly". While this makes it very easy for the programmer to support the system, it is very annoying to the users, since they often have to upgrade/downgrade their system and are often confronted with conflicting requirements for different programs.

There are two other solutions to this: link everything statically and distribute all shared libraries (a'ka DLL's) with your program. If you are able to link statically (ie. you have static versions of all used libraries), then it is the easiest solution - just add "-static" to your linker command. This has also the nice feature of creating a single self-contained executable.

Why you might not want to link statically

Static linking has several caveats:

Dynamic linking plus wrapping solves all these problems, but needs to be done carefully.

Wrapping binaries

Unix like systems know the variable LD_LIBRARY_PATH, which tells the dynamic loader to look in certain directories first before loading libraries from the system directories. The solution is very easy in concept: wrap your binary in a script that sets this variable and then calls the application. The script should look like this:

#!/bin/sh
MYDIR=$(dirname $0)
LD_LIBRARY_PATH=$MYDIR/lib:$LD_LIBARY_PATH
export LD_LIBARY_PATH
exec $MYDIR/realapp "$@"

The first line (#!/bin/sh) tells the operating system with which shell to execute this script when it is called directly (/bin/sh should always be a korn shell derivative, or bash on most Linux distributions). The second line determines in what directory the script was installed. The third and fourth line prepend the subdir lib of the scripts directory to LD_LIBRARY_PATH and export it so that programs started from the shell actually see it. The last line replaces the shell process with the actual application (realapp in this case) and passes on all shell arguments (you could also use $*, but that would behave unexpectedly when one of the arguments contains spaces).

Chosing Libraries

When you do ldd realapp in your development directory you should get something like this for a Qt application:

        libQtGui.so.4 => /usr/local/Trolltech/Qt-4.0.1/lib/libQtGui.so.4 (0x0000002a9566e000)
        libQtNetwork.so.4 => /usr/local/Trolltech/Qt-4.0.1/lib/libQtNetwork.so.4 (0x0000002a95ce3000)
        libQtCore.so.4 => /usr/local/Trolltech/Qt-4.0.1/lib/libQtCore.so.4 (0x0000002a95e33000)
        libpng12.so.0 => /usr/lib/libpng12.so.0 (0x0000002a96889000)
        libSM.so.6 => /usr/X11R6/lib/libSM.so.6 (0x0000002a969b1000)
        libICE.so.6 => /usr/X11R6/lib/libICE.so.6 (0x0000002a96abb000)
        libXi.so.6 => /usr/X11R6/lib/libXi.so.6 (0x0000002a96bd4000)
        libXrender.so.1 => /usr/lib/libXrender.so.1 (0x0000002a96cdd000)
        libXrandr.so.2 => /usr/X11R6/lib/libXrandr.so.2 (0x0000002a96de6000)
        libfreetype.so.6 => /usr/lib/libfreetype.so.6 (0x0000002a96ee9000)
        libfontconfig.so.1 => /usr/lib/libfontconfig.so.1 (0x0000002a97078000)
        libXext.so.6 => /usr/X11R6/lib/libXext.so.6 (0x0000002a971b7000)
        libX11.so.6 => /usr/X11R6/lib/libX11.so.6 (0x0000002a972c8000)
        libz.so.1 => /usr/lib/libz.so.1 (0x0000002a978c3000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x0000002a979d8000)
        libdl.so.2 => /lib/libdl.so.2 (0x0000002a97aed000)
        libstdc++.so.5 => /usr/lib/libstdc++.so.5 (0x0000002a97bf0000)
        libm.so.6 => /lib/libm.so.6 (0x0000002a97dce000)
        libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x0000002a97f53000)
        libc.so.6 => /lib/libc.so.6 (0x0000002a98060000)
        libexpat.so.1 => /usr/lib/libexpat.so.1 (0x0000002a9829e000)
        /lib64/ld-linux-x86-64.so.2 (0x0000002a95556000)

(see here on how to find this on Windows or MacOS)

The library name on the left is the library the executable tries to find when starting, the path name on the right is the file that actually gets linked, the hex number in parenteses is the memory location it gets linked to.

All of the libraries which you copy into your own lib subdirectory will be taken from there, the others will be taken from. With Qt applications built using qmake you will discover that this is not entirely true: Qt is still taken from the system directory (usually /usr/local/Trolltech/Qt-4.0.?). This happens because the linker line generated by qmake includes the Qt directory in the executable. You can fix this by adding the line QMAKE_RPATH= to your .pro file - this line disables adding the path to the binary.

to copy or not to copy

The first thing to copy is of course Qt - either because you want to ship with a specific version or because you are not sure that your users have it. But there is another good reason: the ABI (application binary interface - the way method names and objects are layed out in the application) differes from GCC 2.95 to 2.96 (a RedHat specific version that is not recommended to be used) to 3.0 to 3.1 to 3.2/3.3 to 3.4/4.0. This is due to the fact that GCC developers are trying to follow the ABI standard for C++ and each release fixes a few points on the path to that goal.

So the first batch of files are the C++ libraries, in the example above that would be:

The remainder depends on how much compatibility you expect from your users:

The last library /lib64/ld-linux-x86-64.so.2 is the AMD64 version of the ELF binary loader (it is called ld-linux.so.2 on 32bit/x86 systems), you can depend on it being there (otherwise the system is so crippled that your application would not run anyway) and you actually cannot exchange it since it is the only library that is linked with its full path name.

The last thing that you cannot influence is the kernel your users run, but that should also not concern you too much, since libc does a good job of hiding the kernel behind its interface. Anyway if you've reached that level of paranoia you should deliver a fully pre-installed system.

Your choice to copy things into your distribution depends on several factors:


Webmaster: webmaster AT silmor DOT de