Writing and Using Shared Libraries
A shared library (also known as a shared object, or as a dynamically linked library) is similar to an archive in that it is an amalgam of object files. However, there are many important differences. The most fundamental difference is that when a shared library is linked into a program, the final executable does not actually contain the code that is present in the shared library. Instead, the executable merely contains a reference to the shared library. If several programs on the system are linked against the same shared library, they will all reference the library, but none will actually be included. Thus, the library is "shared" among all the programs that link with it.
A second important difference is that a shared library is not merely a collection of object files, out of which the linker chooses those that are needed to satisfy undefined references. Instead, the object files that compose the shared library are combined into a single object file so that a program that links against a shared library always includes all of the code in the library, rather than just those portions that are needed.
To create a shared library, you must compile the objects that will make up the library using the -fPIC option to the compiler, like this:
% gcc -c -fPIC test1.c
The -fPIC option tells the compiler that you are going to be using test.o as part of a shared object.
Position-Independent Code
PIC stands for position-independent code. The functions in a shared library may be loaded at different addresses in different programs, so the code in the shared object must not depend on the address (or position) at which it is loaded. This consideration has no impact on you, the programmer, except that you must remember to use the -fPIC flag when compiling code that will be used in a shared library.
Then you combine the object files into a shared library, like this:
% gcc -shared -fPIC -o libtest.so test1.o test2.o
The -shared option tells the linker to produce a shared library rather than an ordinary executable. Shared libraries use the extension .so, which stands for shared object. Like static archives, the name always begins with lib to indicate that the file is a library.
Linking with a shared library is just like linking with a static archive. For example, this line will link with libtest.so if it is in the current directory, or one of the standard library search directories on the system:
% gcc -o app app.o -L. -ltest
Suppose that both libtest.a and libtest.so are available. Then the linker must choose one of the libraries and not the other. The linker searches each directory (first those specified with -L options, and then those in the standard directories). When the linker finds a directory that contains either libtest.a or libtest.so, the linker stops search directories. If only one of the two variants is present in the directory, the linker chooses that variant. Otherwise, the linker chooses the shared library version, unless you explicitly instruct it differently. You can use the -static option to demand static archives. For example, this line will use the libtest.a archive, even if the libtest.so shared library is also available:
% gcc -static -o app app.o -L. -ltest
The ldd command displays the shared libraries that are linked into an executable. These libraries need to be available when the executable is run. Note that ldd will list an additional library called ld-linux.so, which is a part of GNU/Linux's dynamic linking mechanism.
Using LD_LIBRARY_PATH
When you link a program with a shared library, the linker does not put the full path to the shared library in the resulting executable. Instead, it places only the name of the shared library. When the program is actually run, the system searches for the shared library and loads it. The system searches only /lib and /usr/lib by default. If a shared library that is linked into your program is installed outside those directories, it will not be found and the system will refuse to run the program.
One solution to this problem is to use the -Wl,-rpath option when linking the program. Suppose that you use this:
% gcc -o app app.o -L. -ltest -Wl,-rpath,/usr/local/lib
Then, when app is run, the system will search /usr/local/lib for any required shared libraries.
Another solution to this problem is to set the LD_LIBRARY_PATH environment variable when running the program. Like the PATH environment variable, LD_LIBRARY_PATH is a colon-separated list of directories. For example, if LD_LIBRARY_PATH is /usr/local/lib:/opt/lib, then /usr/local/lib and /opt/lib will be searched before the standard /lib and /usr/lib directories. You should also note that if you have LD_LIBRARY_PATH, the linker will search the directories given there in addition to the directories given with the -L option when it is building an executable. (You might see a reference to LD_RUN_PATH in some online documentation. Don't believe what you read; this variable does not actually do anything under GNU/Linux.)
Shared libraries are a handy tool for breaking an application into parts. This allows you, for instance, to upgrade parts of your program independently. A shared library can also be used by several executables. This helps keep executable size down, both on disk and in memory.
About This Article
This article is excerpted from Advanced Linux Programming by Mark Mitchell, Jeffrey Oldham, and Alex Samuel of CodeSourcery, LLC (New Riders Publishing , 2001, ISBN 0735710430). Refer to Chapter 2, "Writing Good GNU/Linux Software," for more detailed information on the material covered in this article.