The Linking Process Exposed — Static vs Dynamic Libraries

Derek Kwok
8 min readApr 17, 2018

--

What Are Libraries?

In real life, a library is a place you can go to find books about specific subjects. The books are usually organized in alphabetical order or by Dewey Decimal number which allows the user to effectively interface with the library and effectively find books that fulfill certain tasks in their life whether that be learning how to build a table out of wood or learning calligraphy.

In computer science, libraries are analagous to this concept. They are databases of binary (object) code that allow implementation of functions referenced by header files. Thus, header files define the interface and help with implementing library code correctly. They are implemented into the compilation process during the linking stage because they provide a list of indexed symbols and definitions that are either statically or dynamically linked to an executable.

Libraries are useful for a variety of reasons that include improvements in portability, efficiency, and productivity. In terms of portability, library functions help prevent duplication of code and allow it to be reused so that it does not have to be rewritten over and over again in the main function files. Just as there are more books in a library than you’ll probably ever read, there are more functions and definitions in a CS library than you’ll need at any one time so that only code that is needed is present in executables. In terms of efficiency, the indexing of library symbols and definitions decreases the compilation and linking time since the compiler only has to search and open a few files in the same general directories rather than on the various places functions could be stored on the computer. Finally, in terms of productivity, when projects get extremely large, compilation time usually ends up being the rate-limiting step — thus, with too many function files and source code files, project structure becomes extremely confusing and time-consuming. Libraries help offset a lot of this chaos!

In computer science, there are two types of libraries:

  1. Static libraries

These libraries are archives of binary code that are physically linked into an executable file at specific places that reference certain symbols and definitions. They behave similarly to finding a correct puzzle piece that fits a certain space. Static library contents physically exist in an executable that is linked to them.

2. Dynamic (Shared libraries)

Dynamic libraries are archives of binary code that are not physically linked into an executable file. The libraries are physically loaded into the computer’s memory instead and during the linking stage of compilation, only the address in the memory of the library function is added in the final executable file. The physical code itself is located at that address.

Creating a Static and Dynamic Library

Static Libraries

For a static library, the object code from binary files (extension .o) are combined into a single archive file with an extension .a. This archive is similar to a toolbox that contains all the tools that might be used in programs that call that library during linking. The tools are inserted in places where they are needed. In the image below, I have a series of .c files.

Since a library is an archive of object code, I need to convert all of these .c files into .o files using the following command:

gcc -Wall -Wextra -Werror -pedantic -c *.c

The -c option stops the compilation process right before linking after object code files are generated. I now have all of the object code files:

If I run the command ar -rc libholbertonschool.a *.o, I can archive these files into a single .a library file. The ar command is for archiving, the -r option is for moving members that have extension .o, and the -c option is for creating the archive if it does not exist. A library file starts with a prefix lib and has a .a extension. The final step that is necessary on some systems is to index the archive contents to speed up linking. This can be done with ranlib libholbertonschool.a. I now have a library that contains object code for all of the functions in the image above.

Using the Static Library

To use this library with some test file, I have to compile the test file with the library using either of the following two commands:

gcc test.c ./libholbertonschool.a -o exename

gcc test.c -L. -lholbertonschool -o exename

Both of these do the same thing, but the second is slightly more cryptic. The -L. option searches for libraries in the current directory and the -l which is connected to holbertonschool searches for the library file implicitly where a lib prefix and .a suffix are assumed. Without compiling with a library file, gcc will throw an error since the functions it is trying to link are not provided. By providing a library in the compilation, the compiler can now search the library for the functions it needs and insert them where they are needed.

Dynamic Libraries

Creating dynamic libraries is slightly different from static libraries, but the concepts are generally the same. I have the same number of .c files and the header file that defines their prototypes in a file called lists.h.

To create the dynamic library, which has a .so (standing for shared object), the following compilation command can be entered:

gcc -fPIC -Wall -Werror -Wextra -pedantic *.c -shared -o libholberton.so

The -fPIC flag stands for Position Independent Code which is required for dynamic linking. The meaning of this is exactly what it sounds like — since dynamically linked libraries can be stored anywhere in memory where they fit, this flag allows the library file to be used regardless of where it is stored.

The -shared flag allows a shared archive to be created and the -o option allows us to rename the output file to libholberton.so.

After running this command, I now have a shared library!

Using the Dynamic Library

Let’s say I have the following code:

#include "holberton.h"
#include <stdlib.h>
#include <stdio.h>
/**
* main - check the code for Holberton School students.
*
* Return: Always EXIT_SUCCESS.
*/
int main(void)
{
printf("%d\n", _strlen("Holberton"));
return (EXIT_SUCCESS);
}

I can compile the program with the following code:

gcc -Wall -pedantic -Werror -Wextra -L. 0-main.c -lholberton -o len 

Just like the static libraries, the -L. options searches the directory for library files and the -l that is appended at the beginning of holberton is an implicit search for library files that have a prefix lib, a suffix .so, and holberton in between the two.

To use the library file I just created, I need to make sure the compiler is capable of finding all library files in memory and that they are properly loaded in memory to begin with. If I run ldd len now, I can see the following:

The line libholberton.so => not found tells me that the dependency on the dynamic library I just created is not fulfilled. To fix this, the environmental variable $LD_LIBRARY_PATH must be understood. In Linux, this variable is a colon separated list of directories that the system looks into for library files. In the image above, you can see that echoing this variable at the current state shows that it is empty and thus, my attempt to run the program (len) does not work because the shared library cannot be loaded.

My current directory looks like this:

As you can see, the library file is located in the same directory as my main file and header file (current working directory). Observe the following image:

The command export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH updates the $LD_LIBRARY_PATH environmental variable with a .: which indicates that the system should search the current working directory for libraries. The export command makes this change global so that any shell can access the same value. Thus, when I echo the variable, I see my updates. Running ldd len again, I see that libholberton.so is at address location 0x00007fb9216b9000 which is likely its location in the current working directory in memory. Thus, the executable’s dependency on the shared library is now fulfilled and the program returns 9 which is the length of the word “Holberton”.

Pros and Cons of Static and Dynamic Libraries

When it comes to static libraries, the object code from the library is physically placed into the executable file’s code so that no additional files need to be loaded into the system memory before running. Thus, run time is faster because the executable generally only accesses one memory location — the location of the executable file.

Apart from this, static libraries do not appear to be as robust as dynamic libraries. One of the main reasons is due to the fact that changing any library function code will not update the executable file’s linked code for those functions. Thus, for efficiency’s sake, static libraries have a drawback in that any change in the code of a library function requires the whole program to be recompiled. For compilations that take a long time, this can be an issue. Dynamic libraries solve this issue because the code at the memory address is changed and the executable only points to the memory address. Thus, recompilation is not necessary.

Additionally, statically linked files tend to be a much larger file size compared to dynamically linked files because there are more bytes of data in their executable files. For small executables, this is perfectly fine. In terms of scale-up, this can make all the difference. File size can affect the usability of software as they take up more space on the system and make it more difficult and time-inconvenient for users to implement onto their system. Thus, the generally accepted opinion is that dynamic libraries are superior to static libraries. This is why the installation of programs tend to include so many other folders and files in addition to the main exe file for typical software packages available on computers.

--

--

Derek Kwok

BS in Materials Engineering from the University of Illinois. Software Engineering Student at Holberton School. Foreign language enthusiast. Musician. Engineer.