Next: , Previous: Linking libraries, Up: Using libtool


3.3 Linking executables

If you choose at this point to install the library (put it in a permanent location) before linking executables against it, then you don't need to use libtool to do the linking. Simply use the appropriate -L and -l flags to specify the library's location.

Some system linkers insist on encoding the full directory name of each shared library in the resulting executable. Libtool has to work around this misfeature by special magic to ensure that only permanent directory names are put into installed executables.

The importance of this bug must not be overlooked: it won't cause programs to crash in obvious ways. It creates a security hole, and possibly even worse, if you are modifying the library source code after you have installed the package, you will change the behaviour of the installed programs!

So, if you want to link programs against the library before you install it, you must use libtool to do the linking.

Here's the old way of linking against an uninstalled library:

     burger$ gcc -g -O -o hell.old main.o libhello.a -lm
     burger$

Libtool's way is almost the same1 (see Link mode):

     a23$ libtool --mode=link gcc -g -O -o hell main.o libhello.la
     gcc -g -O -o hell main.o  ./.libs/libhello.a -lm
     a23$

That looks too simple to be true. All libtool did was transform libhello.la to ./.libs/libhello.a, but remember that ‘a23’ has no shared libraries. Notice that Libtool also remembered that libhello.la depends on -lm, so even though we didn't specify -lm on the libtool command line2 Libtool has added it to the gcc link line for us.

On ‘burger’ Libtool links against the uninstalled shared library:

     burger$ libtool --mode=link gcc -g -O -o hell main.o libhello.la
     gcc -g -O -o .libs/hell main.o -L./.libs -R/usr/local/lib -lhello -lm
     creating hell
     burger$

Now assume libhello.la had already been installed, and you want to link a new program with it. You could figure out where it lives by yourself, then run:

     burger$ gcc -g -O -o test test.o -L/usr/local/lib -lhello -lm

However, unless /usr/local/lib is in the standard library search path, you won't be able to run test. However, if you use libtool to link the already-installed libtool library, it will do The Right Thing (TM) for you:

     burger$ libtool --mode=link gcc -g -O -o test test.o \
                     /usr/local/lib/libhello.la
     gcc -g -O -o .libs/test test.o -Wl,--rpath \
             -Wl,/usr/local/lib /usr/local/lib/libhello.a -lm
     creating test
     burger$

Note that libtool added the necessary run-time path flag, as well as -lm, the library libhello.la depended upon. Nice, huh?

Notice that the executable, hell, was actually created in the .libs subdirectory. Then, a wrapper script (or, on certain platforms, a wrapper executable see Wrapper executables) was created in the current directory.

Since libtool created a wrapper script, you should use libtool to install it and debug it too. However, since the program does not depend on any uninstalled libtool library, it is probably usable even without the wrapper script.

On NetBSD 1.2, libtool encodes the installation directory of libhello, by using the ‘-R/usr/local/lib’ compiler flag. Then, the wrapper script guarantees that the executable finds the correct shared library (the one in ./.libs) until it is properly installed.

Let's compare the two different programs:

     burger$ time ./hell.old
     Welcome to GNU Hell!
     ** This is not GNU Hello.  There is no built-in mail reader. **
             0.21 real         0.02 user         0.08 sys
     burger$ time ./hell
     Welcome to GNU Hell!
     ** This is not GNU Hello.  There is no built-in mail reader. **
             0.63 real         0.09 user         0.59 sys
     burger$

The wrapper script takes significantly longer to execute, but at least the results are correct, even though the shared library hasn't been installed yet.

So, what about all the space savings that shared libraries are supposed to yield?

     burger$ ls -l hell.old libhello.a
     -rwxr-xr-x  1 gord  gord  15481 Nov 14 12:11 hell.old
     -rw-r--r--  1 gord  gord   4274 Nov 13 18:02 libhello.a
     burger$ ls -l .libs/hell .libs/libhello.*
     -rwxr-xr-x  1 gord  gord  11647 Nov 14 12:10 .libs/hell
     -rw-r--r--  1 gord  gord   4274 Nov 13 18:44 .libs/libhello.a
     -rwxr-xr-x  1 gord  gord  12205 Nov 13 18:44 .libs/libhello.so.0.0
     burger$

Well, that sucks. Maybe I should just scrap this project and take up basket weaving.

Actually, it just proves an important point: shared libraries incur overhead because of their (relative) complexity. In this situation, the price of being dynamic is eight kilobytes, and the payoff is about four kilobytes. So, having a shared libhello won't be an advantage until we link it against at least a few more programs.


Footnotes

[1] However, you should avoid using -L or -l flags to link against an uninstalled libtool library. Just specify the relative path to the .la file, such as ../intl/libintl.la. This is a design decision to eliminate any ambiguity when linking against uninstalled shared libraries.

[2] And why should we? main.o doesn't directly depend on -lm after all.