Static Library

Libraries Vs Object Files

A library is a file containing several object files. Therefore instead of providing several object files at link time a single library files can be provided to the compiler. This makes code more manageable. Apart from this, using libraries holds advantage of faster linking over object files. A library has single symbol table. This means that all the information for all the object files contained in the library is kept in a single place within the library. This reduces the lookup time for a symbol within a library that would otherwise had to be searched within the symbol table of each object file.

Static Library

Almost every popular operating system now-a-days provides the user with the capability to create static libraries. Static libraries are those libraries which are linked with the application during link-time. This means that the name of the library is provided to the compiler at run-time and all the symbols referenced in the program which are in the library are resolved (i.e. searched in the library) during link-time only. If a symbol is not found in the library then a error is generated during link-time. This is in contrast to the dynamic libraries where a symbol is resolved during run-time and any error due unresolved symbols will be detected during run-time only (usually resulting in a core file).

Names of static libraries are suffixed with an extension of ".a".

Creating Static Libraries

Creation of static libraries is a two step process, involving:

1. Archiving different object files into a single archive file

2. Re-indexing the archived file

Archiving object files

Archiving object files simply means collating several object files into one single archived file. The tool for accomplishing this is "ar" (archiver) . Ar simply takes as input the name of the object files to be archived and a name of the archive file which would be created by the ar tool. The extension of archive files is ".a".

Assuming that we have two object files add.o and subtract.o, these can be archived into one file as:

ar c libmath.a add.o subtract.o

The above command tells archiver to create archive file by collating object files (add.o and subtract.o) into archive file (math.a). The "c" option tells archiver to create a new file math.a if it does not exists.

Re-indexing the archived file

After the archive file has been created it needs to be re-indexed. This is required because the object files have symbol table. After the archive file is created the symbol table of all the object files has to be collated into one symbol table. This has two advantages :

1. This speeds up linking time since all the symbol information is located at a single place in archive file.

2. This makes the order of occurrence of symbols in the object files irrelevant.

Indexing the archive file is done in the following way:

ranlib libmath.a

On some of the operating systems archiver performs the indexing process also. So using ranlib utility is not required in that case. However in those operating systems ranlib utility can still be found which does nothing. This is because programmers often create makefiles for automating the process of compiling the program. These makefiles cannot assume that archiver also does the indexing phase too, since the same makefile is supposed to run on different operating systems.

Naming conventions

You can create a static library with any name you like. However, the compiler looks for library in a specific format if you use the -L option. If this option is used then compiler looks for libraries named as libXXX.a or libXXX.so i.e. it assumes that the library has keyword "lib" prefixed. So it is a good idea to name libraries in such way.

Invoking functions present in the library

Functions present in the static libraries are invoked in similar manner to invoking functions present in object files.

Assuming that there are two functions add() and subtract() present in add.o and subtract.o which now have been archived into libmath.o, then the program for using these functions would be as follows.

main.c

#include <stdio.h>


extern add( int a, int b);

extern subtract( int a, int b);


int main()

{

printf(" Addition of 3 and 2 gives %d \n",add(3,2));

printf(" Difference of 3 and 2 gives %d \n",subtract(3,2));

return(0);

}

The "extern" keyword tells the compiler that the functions add and subtract are present on some other module.

Linking libraries

The static libraries are linked to the program by giving their name after the '-l' option. Full path of the libraries or relative path can be provided.

gcc main.c -l/home/guest/mydir/libmath.o -o main

The above command tells the gcc compiler to link the library libmath.o present in directory "/home/guest/mydir/" with the file main.c and create output file main.

Providing path for the Library files

If you have multiple library files and they are present in a single folder then instead of providing full path with every library name, path for searching libraries can be provided using the -L option. However if using using the -L option the compiler assumes that the name of the library file is prefixed with "lib" keyword and ends with .a (for static library) or .so (dynamic library) extension. So while using -L option the name of the libary is specified as math instead of libmath.o in the -l option.

gcc main.c -L/home/guest/mydir -lmath -o main

Linking order of multiple object files

The order in which the name of the library files is given to the compiler is important. When the compiler comes across a library during linking time then it maintains track of the symbols which have been referenced (either in the current library or in the previously encountered libraries) and unresolved symbols (assuming that their definition will be in the following libraries). The compiler does not maintain the information about the symbols which have been defined in the library but not referenced till now. This means that if the symbol is referenced in a later library file then compiler would generate error since it has no track of that symbol's definition. e.g. if liba.a contains function first and libb.a contains function second:

liba.a

void first()

{

/* Do something here */

}

libb.a

void second()

{

/* Do something here */

/* Call first */

            first();

/* Again do something here */

}

then compiling the program as

gcc main.c -lliba.a -llibb.a -o main.o

would give error since when the compiler reached to linking libb.a it has lost track that any function named "first" existed in liba.a. To avoid this the ordering of libraries has to be corrected.

gcc main.c -llibb.a -lliba.a -o main.o

Another interesting situation arises when both libraries call each other's functions i.e.

liba.a

void first()

{

/* Do something here */

/* Call second */

            second();

/* Again something here */

}

libb.a

void second()

{

/* Do something here */

/* Call first */

            first();

/* Again do something here */

}

In this case both of these would generate error:

gcc main.c -lliba.a -llibb.a -o main.o

gcc main.c -llibb.a -lliba.a -o main.o

To avoid this, name of one library has to be repeated

gcc main.c -lliba.a -llibb.a -lliba.a -o main.o

This would definitely slow down the linking phase. Making such libraries with mutual dependencies should be avoided. Care must be taken to put the related functions together in a single library file to avoid such issues.

Selective inclusion of object files

When an object file is linked the entire code of the object file is placed in the executable file. This means greater the number of object files linked with the program bigger the size of executable.

As stated earlier an archive file is a collection of object files. While linking a static library file, all the object files are not linked with the executable file. Only those object files are linked whose symbols have been referenced in the program. Therefore in the above program if only add function had been called then only add.o would have been linked from within libmath.o. But if an object file is linked, the complete code is linked even if only one function is called.

This places a responsibility on the programmer that he should put the related functions in same object files so that the size of the executable file can be kept to minimum.