Static and specially dynamic libraries in C…

Rolando Quiroz
5 min readSep 17, 2019
Figure 1: National Library of the Czech Republic. Image credits: Sean Yan

In a previous article, we talked about the static libraries in C and the general importance of libraries in programming. In this entry we will review some general topics about libraries and we will focus on dynamic libraries in C.

Why using libraries in general

Libraries are collection of pre-compiled and non-volatile routines used by programs as a simple and versatile way to modularize and reuse code. The objective of a library is to make the development of certain applications easier and simpler. Typically they are oriented to solve a specific problem or develop a specific task, and this feature allow us to take a more productive path in the software developing.

Figure 2: Don’t reinvent the wheel!: Use libraries!. Image credits: IBM

How dynamic libraries work

There are two types of libraries in C Linux: static and dynamic libraries in Linux. Static libraries are .a files. All the code related to the library is in this file, and is directly linked to the program at the time of compilation. A program that uses a static library makes copies of the code it uses from the static library and makes it part of the program.

On the other hand the dynamic or shared libraries (.so files) have all the code related to the library embedded itself, the programs that use it just call it at run-time. A program that uses a shared library only refers to the code it uses in the shared library. Figure 3 shows a scheme that explains the dynamic link process.

Figure 3: Dynamic link process. Image credits: Slides

How to create dynamic libraries in Linux

  1. Get the object .o files from all .c sources in our library

When creating a dynamic object, the object code must be independent of the position. To obtain this type of code, the -fPIC (Position Independent Code) option must be specified to the compiler. This flag must be indicated both in the compilation and in the assembly of the library.

Figure 4: Compilation with flag -fPIC.

2. Create the .so library

To mount the objects it is also necessary to specify the -shared option so that the result is a ‘shareable’ object file. We will call libeexample.so to our dynamic library.

Figure 5: The -shared option.

The library has as extension .so which means shared object, and we are only interested in file .so (shared object). So, we can skip creating .o file. You can merge above two commands into one, that will give you shared object without getting .o object file.

Figure 6: Skip creating .o file.

3. Compile our program, linking to the dynamic library

We have libeexample.so ready now, so just left compile our program that we called test.c

Figure 7: Compile test.c program and libexample.so dynamic library together.

How to use them in Linux

To use this library from a program you don’t have to do anything else; it is exactly the same as in the case of the static library.

When using a library, the compiler first looks for a dynamic version (.so), if it doesn’t find it then it looks for the static version. If you have both versions of a library and you want to use the static version, you must indicate the flag -static to the assembler.

When a program uses dynamic libraries, the system needs to locate them at runtime (as opposed to static libraries). The places where a program looks for dynamic libraries are as follows (in this order):

  • In the directories of the LD_LIBRARY_PATH variable.
  • In the file ld.so.cache.
  • In the /usr/lib and /lib directories.
  • In the directories contained in the file ld.so.conf.

If the program does not find the library it needs, it will print an error message with the following appearance:

vagrant@vagrant-ubuntu-trusty-64:~$ ./test
vagrant@vagrant-ubuntu-trusty-64:~$ ./test: error while loading shared libraries: libexample.so: error in loading shared libraries: libexample.so: cannot open shared object file: No such file or directory

Normally, it is best to use the environment variableLD_LIBRARY_PATH to indicate which directories to search:

$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/user/dir_lib

In this way it should work properly.

What are the differences between static and dynamic libraries

Static libraries

  • A static library is “embedded” in our executable which means that we can take it to another computer without fear of missing libraries.
  • If the libraries have a bug and a version appears that fixes that bug you have to recompile the code.
  • They’re bigger when they run the embedded libraries.
  • Faster in execution because the functions are inside the executable we don’t have to look for them.

Dynamic libraries

  • A dynamic library does not “embedded” into our executable so our executable will be smaller.
  • If we take our executable to another machine you have to go with the libraries too.
  • The execution is slower because of to go looking for the library outside the executable.
  • If there is a bug in the library is updated and fixed in all executables that use it, if it is a change in a function (more parameters, change of behavior ..) we have to recompile everything.

What are the advantages and drawbacks of static and dynamic libraries

There are advantages and disadvantages to each method.

Shared libraries reduce the amount of code that is duplicated in each program that makes use of the library, keeping the binaries small. It also allows you to replace the shared object with one that is functionally equivalent, but you may have added performance benefits without having to recompile the program that uses it. However, shared libraries will have a small additional cost for the execution of functions, as well as a load cost at runtime, since all the symbols in the library must be connected to the things they use. In addition, shared libraries can be loaded into a runtime application, which is the general mechanism for implementing binary add-in systems.

Static libraries increase the overall size of the binary, but this means that it is not necessary to carry a copy of the library being used. Since the code is connected at compile time, there are no additional load costs at run time. The code is simply there.

--

--