a block of code, loaded on request, providing data types and procedures
Basically, a library is a set of functions and/or structures to be used by applications. For example, if a programmer wishes to use more complex mathematical functions in C like sin he would have to use the library libm where this and other functions are defined. To do so, he can link the library in the compilation stage, using the -l option with the name of the library (without the lib prefix and the extension: in this case, -lm).
A shared library opposes to a static one in the sense that the library is only loaded at runtime and not in the compilation stage. So, while in static libraries the program carries the library code, when linked to a shared one the library code is not present and is only loaded into memory when needed.
One of the named advantages of shared libraries, and also another characteristic that differs them from static ones, is the fact that the system has, at most, one copy of the library's code per library. When loading, the system first checks if there is already a copy of the library on memory. The code is only copied if there is none, while as in static the code is included in every compiled program to which the library is linked.
So, if the program doesn't carry the library's code, when is it loaded? There are two alternatives:
automatically at the start of the process and, thus, only unloaded at the end: the library must be linked at compilation stage;
by request of the program itself (using, for example, dlopen()): the code can be loaded and unloaded in the middle of the process or not even loaded at all; furthermore, the library is not linked at compilation stage;
A small example at the end of this section will illustrate these concepts.
But why use a shared library instead of a static one? And why use demand loading instead of automatic loading or the other way around? Shared libraries bring a number of advantages...
libraries are only loaded at run time (thus, the possibility to change the library without having to recompile the end application);
lighter applications and saving of space (since the application doesn't carry the library's code);
In particular, demand loading of a shared library can be very useful
as stated before it is the application itself which requests the loading: the code can be loaded and unloaded at any given stage or not loaded at all; this is particularly useful when the program may use resources from several libraries but not necessarily at that moment, thus, avoiding that dozens of libraries that are not being used would be wasting memory;
since the library is not linked at compilation stage the application doesn't have to know it beforehand: for example, all the libraries can be put in a certain directory where the application will fetch them; this enables that resources can be added or changed without touching the application itself;
But they have a cost. Also, some problems may arise and should be taken in account.
First, errors only show at runtime. This makes the debugging more difficult but also imposes some care on moving libraries and changing them;
Creating a shared library is more complex and requires a few number of flags and details. However, this can be easily overcome (see Libtool);
The dynamic loading obliges the code to be Platform Independent Code(PIC). This makes the library heavier so, although applications are lighter, space is only actually saved if there is more than one application linked or there is demand loading;
The resources that demand loading enables (see above) also bring dangerous situations:
What happens if the program doesn't find the library expected? The error can occur at anytime of the process life and not at the beginning as in automatic loading;
How does the program know which interface the library provides? If it is defined beforehand, how to ally versioning and heterogeneity of libraries with this prior knowledge without having to touch the application?
Developing a shared library, as is, is generally more difficult than developing a static one. The complexity increases in a great amount when trying to make it platform independent and easily built (a good user interface). There are, however, some very powerful utilities that not only simplify this procedure but are also greatly recommended since they produce a standard, compliant and robust final product. Their use is quite straightforward and apply to static ones as well.
These two utilities combined are especially adequate for projects intended to be easily developed, used and installed in different platforms, helping to provide:
platform independence and/or ability to check for the features needed by the package;
a custom and straightforward user interface when installing the package;
an easy maintenance of Makefile (thus, helping compilation)
To configure the software package, a configure script is generated from a file called configure.in created by the developer. This file should contain invocations to Autoconf macros that test the system features needed by the package. Autoscan can be used to help write this file.
Automake assumes that Autoconf is being used and is used to produce Makefile.in files from Makefile.am files created by the developer. When the configure script is run these files will originate Makefile's. These Makefile.am files consist mainly of a series of make macro definitions.
The final user will only have to type
$ ./configure $ make $ make install |
Autoconf and Automake manuals in the GNU documentation provide a very good explanation on how to use these tools ( see Links and further information)
Libtool is a tool for developing libraries that provides the complete functionality of a host type in a generic interface but hiding the confusing and complex details. It can be used together with Autoconf and Automake.
Some points about libtool:
Libtool is based on the paradigm that " libraries are programs with multiple entry points, and more formally defined interfaces."; in fact, the rules to create a library look much like a compilation of a program;
By default, libtool tries to produce both a static and shared library; if the system doesn't permit shared libraries, it produces a static version;
When creating the library, wrappers are used (for extra meta-data, since the meta-data stored varies with the platforms) as well as temporary directories for storing the libraries.
Libtool provides several facilities:
Creating object files (including PIC)
Linking libraries
Linking executables
Debugging executables
Installing libraries
Installing executables
In A small example, it's illustrated the use of libtool to create a library.
GNU documentation has also a very good, step by step, manual of libtool (see Links and further information)
To illustrate, here is the development of a very small library, libgarfield.so, from the files eating.c and sleeping.c with the header file garfield.h. An executable called garfield is created by linking main.c to libgarfield.la (which is a wrapper to libgarfield.so).
Autoconf and automake are not used here, only libtool. It is presented the steps for the most common actions; see the libtool manual for the explanation of these steps and other possible actions that can be performed (see Links and further information).
Creating object files. Note that besides the normal object files it is also created .lo files which are PIC object files needed when creating the shared library.
$ gcc -g -O -c main.c $ libtool gcc -g -O -c eating.c mkdir .libs gcc -g -O -c eating.c -fPIC -DPIC -o .libs/eating.lo 5 gcc -g -O -c eating.c -o eating.o >/dev/null 2>&1 mv -f .libs/eating.lo eating.lo $ libtool gcc -g -O -c sleeping.c rm -f .libs/sleeping.lo gcc -g -O -c sleeping.c -fPIC -DPIC -o .libs/sleeping.lo 10 gcc -g -O -c sleeping.c -o sleeping.o >/dev/null 2>&1 mv -f .libs/sleeping.lo sleeping.lo |
Linking libraries. The libraries are put in the .libs/ directory and libgarfield.la is created in place which, as was said, is a wrapper for the shared library.
$ libtool gcc -g -O -o libgarfield.la eating.lo sleeping.lo \ -rpath /usr/local/lib rm -fr .libs/libgarfield.la .libs/libgarfield.* .libs/libgarfield.* gcc -shared eating.lo sleeping.lo -lc -Wl,-soname -Wl,libgarfield.so.0 -o .libs/libgarfield.so.0.0.0 5 (cd .libs && rm -f libgarfield.so.0 && ln -s libgarfield.so.0.0.0 libgarfield.so.0) (cd .libs && rm -f libgarfield.so && ln -s libgarfield.so.0.0.0 libgarfield.so) ar cru .libs/libgarfield.a eating.o sleeping.o ranlib .libs/libgarfield.a creating libgarfield.la 10 (cd .libs && rm -f libgarfield.la && ln -s ../libgarfield.la libgarfield.la) |
Linking the executable. The executable is put inside .libs/ and a wrapper script is created in place.
$ libtool gcc -g -O -o garfield main.o libgarfield.la gcc -g -O -o .libs/garfield main.o .libs/libgarfield.so -Wl,--rpath -Wl,/usr/local/lib creating garfield |
Installing libraries. libgarfield.la is also installed with the static and shared libraries. All the necessary operations are usually done (like, for example, calling ldconfig) but running libtool -n --finish libdir will give further hints on what to do (in this example, the installation command already gives this information).
$ libtool install -c libgarfield.la /usr/local/lib/libgarfield.la install -c .libs/libgarfield.so.0.0.0 /usr/local/lib/libgarfield.so.0.0.0 (cd /usr/local/lib && rm -f libgarfield.so.0 && ln -s libgarfield.so.0.0.0 libgarfield.so.0) (cd /usr/local/lib && rm -f libgarfield.so && ln -s libgarfield.so.0.0.0 libgarfield.so) 5 install -c .libs/libgarfield.lai /usr/local/lib/libgarfield.la install -c .libs/libgarfield.a /usr/local/lib/libgarfield.a ranlib /usr/local/lib/libgarfield.a chmod 644 /usr/local/lib/libgarfield.a PATH="$PATH:/sbin" ldconfig -n /usr/local/lib 10 ---------------------------------------------------------------------- Libraries have been installed in: /usr/local/lib If you ever happen to want to link against installed libraries 15 in a given directory, LIBDIR, you must either use libtool, and specify the full pathname of the library, or use `-LLIBDIR' flag during linking and do at least one of the following: - add LIBDIR to the `LD_LIBRARY_PATH' environment variable during execution 20 - add LIBDIR to the `LD_RUN_PATH' environment variable during linking - use the `-Wl,--rpath -Wl,LIBDIR' linker flag - have your system administrator add LIBDIR to `/etc/ld.so.conf' 25 See any operating system documentation about shared libraries for more information, such as the ld(1) and ld.so(8) manual pages. ---------------------------------------------------------------------- |
Other actions like debugging are also easily done and take advantage of the wrapper scripts.
Autoconf and it's manual can be found at http://www.gnu.org/software/autoconf/autoconf.html.
Automake and it's manual can be found at http://www.gnu.org/software/automake/automake.html.
Libtool's homepage is located at http://www.gnu.org/software/libtool/libtool.html.
The man pages of dlopen , ld, ldconfig also provide useful information.
General description and implementation information on shared libraries can be found in Building and Using Static and Shared "C" Libraries and Shared Libraries (Section 4).
In the GLib Reference Manual, there is a section describing the gmodule library, Dynamic Loading of Modules.