Static and dynamic libraries in C

A brief intro into its characteristics, main differences and usage.

Alina de los Santos
7 min readJan 17, 2022

--

According to the merriam-webster dictionary a library is “a place in which literary, musical, artistic, or reference materials (such as books, manuscripts, recordings, or films) are kept for use but not for sale”. From a practical perspective libraries allow you to optimize your time and resources since you do not need to acquire tons of books and dictionaries whenever you want to retrieve some piece of information for a particular purpose. It suffices to go up to your local library in order to obtain the knowledge you are pining for.

Likewise, in C libraries can hold all sorts of variables and functions that you can use within your programs without the need to define them every time you use them. This will also optimize your resources since having functions that can be reused results in clean and portable code any mother would be proud of. Libraries can be standard, which are included by default in your operating system and can always be referenced such as <stdio.h>, or can be created with your own functions.

For instance, say you’ve written a large program which uses several functions contained within their own files. At compilation, in order to make use of these functions you would need to keep all these files in the same directory your source code is located or to keep track of the location of each file. However, this will result in a fairly long compilation command as well as compilation time. The larger your program the less practical this becomes. This is where libraries come in handy.

A C library is a file containing object code files, those ending with the .o extension, which work as a single unit during the linking stage of compilation. These object code files might include variables, functions and so on and must be defined in your header file so that your program can use them. This decreases compilation time since you do not need to convert your files to object code every time you compile your program. Additionally, you do not need to keep track of multiple files containing your functions but only the file containing your library.

C libraries are divided into static and dynamic libraries. Static libraries are obtained by compiling object files (those ending in .o) after indexing them. Static libraries end in .a (for archive) and are usually named with the prefix lib-, even though that is merely a convention. At indexing a header is created within the library with the symbols from the object file. The compiler will later use this index to search for symbols within the library.

Static libraries are linked to the program at compilation, thus the resulting executable file will have its own copy of the library code. That way, once this copy is part of the executable it is only accessible by the program it was compiled with. Should you modify the library code in any shape or form, you would need to recompile it so that changes take effect.

On the other hand, dynamic libraries can be shared by multiple programs and its contents are loaded at runtime. These exist as separate files from the executable which does not store a replica of the library. Consequently, it is not necessary to recompile if the library suffers any modifications. Dynamic libraries are named with prefix -lib (which as I mentioned before is only a convention but it is widely used) and the extension .so (for shared object). Dynamic linking occurs just by including the address of the library. This way functions occupy a certain space in memory and are accessed by programs without the need of having several copies.

The static library is part of the program while the dynamic library only creates a symbols table in the program

Creating a static library in Linux

As you can see below all the files we want to include in the library are listed within the same directory. In order to do so we must compile these files but only up to the assembler stage. We can do so by adding the -c option to our compilation. Therefore, our .c files will be turned into .o files.

As you can see below, all of our .c files were turned into .o files

At this point we can archive these object files into a static library. In order to create a static library we must use the ar command which will take all the .o files and add them to an .a file thus creating our static library. Below you can see an example,

ar -rc staticlibrary.a file.o

  • the -r flag will update older files within the library
  • the -c flag will istruct ar to create the library if it doesn’t exist
  • staticlibrary is the name of the library to be created
  • file.o is the name of evert object file to be included in the static library

After creating the library you should index it. Some archivers will index it for you but better safe than sorry so it is advisable to run the command just in case. The command used in order to create the index is ranlib,

ranlib staticlibrary.a

In order to check the library content you can use the below command,

ar -t staticlibrary.a

Should you wish to check the symbols in your library you can use the nm command, which lists the symbol value of each symbol, the symbol type, and the symbol name from the object files,

nm staticlibrary.a

Thus we have created a static library. Now I will exemplify how to use it with the below program,

#include “staticlibrary.h”
void main()
{
void my_library(void);
}

We will create a file print_example.c containg the function my_library

#include <stdio.h>
void my_library(void)
{
printf(“When I have a house of my own, I shall be miserable if I have not an excellent library”);
}

Now we will compile our program up to the object file state and create print_example.o with the below command,

gcc -c print_example.c -o print_example.o

Now we will link our program to our library,

gcc -o print_example print_example.o -L. -lstaticlibrary

  • -L tells the compiler to search within the current directory for the static library
  • -l gives the name of the library

Now we can run our executable,

./print_example

When I have a house of my own, I shall be miserable if I have not an excellent library

Creating a dynamic library in Linux

In this case we also need to compile our .c files. However, as many programs can use a dynamic library they cannot store fixed addresses. This is done with the flag -fpic after the compiler has generated the object code. The compiler is instructed to pause and return object code by using the -c flag.

gcc -fpic my_library.c -c

Now these files can be compiled into a dynamic library. For this we will use the -shared flag,

gcc -shared -o library.so my_library.o

Now we have to export the path for libraries so that programs know where to look for them by executing the following command,

export LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH

Now we will compile our program and generate our executable file,

gcc print_example.c -o print_example

And now we just need to execute print_example,

./print_example

When I have a house of my own, I shall be miserable if I have not an excellent library

Generally dynamic libraries are stored within the paths /lib, /lib64, /usr/local/lib. Bear in mind that when working with dynamic libraries two commands will come in handy such as ldconfig and ldd.

ldconfig will let you update links to the latest shared libraries found in the directories specified on the command line, in the /etc/ld.so.config file, and in trusted directories (/usr/lib, /lib64 and /lib). This command will check the header and file names of the libraries it finds when determining which versions to update their bindings. This command also creates a file called /etc/ld.so.cache that is used to speed up linking.

ldd will tell you which libraries a program needs to run.

ldd /bin/ls

linux-vdso.so.1 => (0x00007fff87ffe000)

libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007ff0510c1000)

Pros and cons

The main issue with static libraries is that we end up storing the same information in several places, i.e. in the executable and the library. The larger our program the larger our executable will be and the more memory it will occupy. Additionally, if we changed one of our functions we would need to recompile the program so that those changes are implemented.

This can be avoided by using dynamic libraries since the executable file only contains the addresses of our functions. The more functions our program uses the larger the executable, of course, but it would be only a fraction of its size were we to use static libraries. Also, changes made to our functions will be applied at runtime. However, with dynamic libraries you must have access to it at run time making it slower to run.

So after all, which library type should you choose? As a very wise man once said “Figuring things out for yourself is practically the only freedom anyone really has nowadays. Use that freedom”.

--

--