In a previous article, we talked about static libraries in C and how they work. In this article, we are going to introduce the concept of shared, or dynamic libraries, and explain the pros and cons of both types of libraries.
Why use libraries?
As mentioned before, libraries allow us to store function definitions, aka pieces of code that will be use in our executable program. It makes the tedious task of compiling a huge number of source files together avoidable, isn’t it great? Libraries are made of object files, with the extension ‘.o’, and can be made on Linux with the gcc compiler. Then, it’s up to us if we want to create a static or shared library.
How libraries work
Once the libraries are created, they can be linked with the file containing the main function, or entry point, with gcc. Then, the code of the functions used in the program will be linked into the executable program, and it will be ready to run!
How to create libraries (Linux only)
Let’s use as an example the file “my_file.c”. In both cases, we need to create object files first. This is done with the following commands:
$ gcc -c my_file.c
$ gcc -c -fPIC my_file.c
The ‘-c’ option makes sure the compilation process stops before the linker, and creates the ‘my_file.o’ file (see my article about compilation).
We use an additional flag for dynamic libraries: ‘-fPIC’ or ‘-fpic’. This makes the code in the library position independent. PIC is code that works no matter where it is placed in memory. Why does it matter? Because several programs can use the same shared library, the library will have different addresses in memory for each program. And we still want our programs to have access to the code wherever it is stored.
Now we can create the ‘mylib’ library with our object file. Here’s how:
$ ar rc libmylib.a my_file.o
$ gcc -shared -o libmylib.so my_file.o
In the first case, we use the command ar to create a ‘.a’ file (a stands for archive), and in the second case we use gcc with flags ‘-shared’, and of course ‘-o’ because we compile from an object file, to create a ‘.so’ file (so is for shared object).
How to use libraries (Linux only)
Now that we have our library, we have to use is to compile and create our program. Let’s say our entry point is in the ‘main.c’ file, and we want to name our program ‘test’. Here’s the command to compile the program with our library in both cases:
$ gcc -L. main.c -lmylib -o test
The ‘-L’ flag tells the compiler where it needs to look for the library, so in this case where the library is in the current working directory, we just use a dot. the ‘-lmylib’ tells the compiler to link the code in main.c with the code in the library my_lib. Finally, the ‘-o’ flag allows us to give a name to the executable, in this case it will be ‘test’.
This is where dynamic libraries differ from static. With a static library, we can just run the code now. But with a dynamic library, it won’t work. A useful tool to show this is ldd, a command that prints shared libraries dependencies. In our case, the output of ‘ldd test’ will contain a line that says:
libmylib.so => not found
This means the loader will not be able to find the library at runtime, so we need to place it in a standard location. This is done by updating the LD_LIBRARY_PATH environment variable and prepending our working directory to its existing value. Here’s a simple command to do so:
$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
Let’s try ‘ldd test’ again, and the output will be:
libmylib.so => ./libmylib.so (0x00007fd4bf2d9000)
On top of ldd for the dependencies, we can use the command ‘nm’ to check the symbols of a dynamic library. These symbols include the functions that are defined in the library. For dynamic libraries, we use the -D flag, like this:
$ nm -D libmylib.so
Okay, now that we’re sure that the dynamic library has been created and is usable, we can run our program by typing ‘./test’ in our command line.
Pros and cons of the two types of libraries
The first advantage that a dynamic library has over a static one is that it takes less space in memory, because a static library will link all the function definitions in the program, whereas a dynamic one will look in the code and see which functions are called, and link only these to the program.
Another advantage of the dynamic library over the static one is that if we modify the code in one of the functions it contains, we don’t need to recompile, we can just execute the program again! This is not possible with a static library, we would have to recompile it (see the flowchart at the top of this article). Plus, several programs can use the dynamic library.
The static library also has advantages over the dynamic library. One of them is that the program using it is faster at execution time. This is because we have linked the library before runtime, whereas dynamic libraries are actually linked at runtime. Another one is that all the code of the functions is in the executable file, so there is not problem of compatibility.
While it might not be the most passioning subject in computer science, people that code in C should know the tools and methods we talked about to create libraries. Depending on the application, developers can either choose static or dynamic libraries, which are both very useful and practical program building tools.