Static and Dynamic. Static or Dynamic?
A complete guide into the characteristics, usage and differences between static and dynamic libraries
Libraries in programming have a similarity with real life libraries in that they include a lot of functions (books) that can be referenced any time. There is no need for you to have every single book available at home, you can make use of the public library in order to read or reference whichever book you’re interested in. The same goes with libraries in C, they hold functions, variables and more, which you could reference and make use of at any time in your program without having to define them every time.
Writing code is all about optimization, so when creating your program or application, the more code and functions you can reuse, the better and cleaner your final product. In order to reuse functions, libraries are a must and will become your new best friends. Libraries can be standard and global — included in the operating system by default for you to reference whenever, or they can be created with whichever functions you want.
To get into a more technical language, in programming, libraries are files that contain objects that will be compiled together with a program file. Each library has many objects inside that are referenced in the same file. These objects could be functions, variables, and more, that are used within the program and defined in the header files. The word ‘object’ does not mean that it is object-oriented as in a ‘thing’. In programming, ‘object’ refers to an intermediate compiler output language, with an .o
extension.
The libraries are compiled to the program file in the Linking step of compilation. For more information on the compilation process, check out this blog entry.
See last step: Linking
There are two main types of libraries in C: Static and Dynamic. In this article we’ll focus on Static Libraries, but will define both for pure knowledge purposes:
- Static library. (lib) A Static library or statically-linked library is a set of routines, external functions and variables which are resolved in a caller at compile-time and copied into a target application by a compiler, linker, or binder, producing an object file and a stand-alone executable. This executable and the process of compiling it are both known as a static build of the program. Historically, libraries could only be static.
They are usually faster than the shared libraries because a set of commonly used object files is put into a single library executable file. One can build multiple executables without the need to recompile the file. Because it is a single file to be built, use of link commands are simpler than shared library link commands, because you specify the name of the static library. - Dynamic or Shared libraries are .so (or in Windows .dll, or in OS X .dylib) files.
These are linked dynamically simply including the address of the library. Dynamic linking links the libraries at the run-time. Therefore, all the functions are in a special place in memory space, and every program can access them, without having multiple copies of them.
Easily put, functions used in files could be defined and coded within the same program file. If only one function is used, this is no problem, but when using more than one, defining many functions within the same program could result in a long tedious and confusing code. Also, this would mean that every function needs to be defined in every program file that uses it. This is not optimal and adds up hundreds of lines of bulky code.
Why use libraries?
This is where libraries come in handy. Instead of defining functions in every file, functions can be bundled up in a library file. This file can then be called within the program once and the functions can be used as many times as needed in the program.
Libraries are like a collection of functions and objects used over and over again or that are of common use and there is no need to reinvent every time we want to make use of these functions. Instead, we use the ones already in existence in order to focus on the program we are creating with them.
As programmers, optimization is key in several aspects of our work, such as team working/sharing code, executable file sizes and program run time, use less memory, and save up on disk space.
How do they work?
Libraries can be default by the system or user created. Either way, they have to be declared in a header file and included atop the program where the library is being used.
When compiling, the static libraries are included in the executable file in the Linker phase at once, in a single file. This means that the code for these functions becomes available for the program to use. Again, this is better and faster as far as system performance, instead of compiling functions from different spaces in memory. This also means that the executable file has the function code included and does not depend on any external file to work and run.
Then, users can reference them and use as many times as needed without having to define the functions every time, instead just by declaring the library within the program.
On the other hand, dynamic libraries are created in a different way. When the program is compiled, a table containing the required symbols (e.g. function names) which it needs to run is created and stored on the compiled version of the program (the “executable”). This library is called the “dynamic symbol table” of the program. When the program is executed, a dynamic linker is invoked to link the program to the dynamic (or “shared”) libraries which contain the definitions of these symbols. It is only after the required dynamic libraries are loaded that the program actually starts running.
To detect which libraries are required for an application to start, you can use ldd
, which will print out the shared libraries used by a given file:
It may happen that one of the libraries used is not found by the dynamic linker in the standard locations it searches:
One way around this is to add the repository folder to the environment variable LD_LIBRARY_PATH
to tell the linker where to look for the correct version. LD_LIBRARY_PATH
is the predefined environmental variable in Linux/Unix which sets the path which the linker should look in to while linking dynamic libraries/shared libraries. In this case, the right version is in this folder (.), so you can export it with:
export LD_LIBRARY_PATH =.:$LD_LIBRARY_PATH
Now the dynamic linker knows where to find the library, and the application can be executed. You can rerun ldd
again to check if the previous command worked:
LD_LIBRARY_PATH
contains a colon separated list of paths and the linker gives priority to these paths over the standard library paths /lib
and /usr/lib
. The standard paths will still be searched, but only after the list of paths in LD_LIBRARY_PATH
has been exhausted, therefore it is always recommended to add the repository folder to the library path immediately before executing the program. This way the new LD_LIBRARY_PATH
isolated from the rest of your system.
—
ld-linux.so
is responsible for linking the dynamic libraries to their corresponding prgrams but, how does this work?
This is where ldconfig
enters the scene. The ldconfig
utility scans the directories where the dynamic libraries are commonly found (/lib and /usr/lib) as well as the directories specified in /etc/ld.so.conf and creates both symbolic links to these libraries and a cache (stored on /etc/ld.so.cache) containing their locations so that ld-linux.so
can quickly find them whenever necessary.
This is done when you run ldconfig
without any arguments (you can also add the -v
option to see the scanned directories and the created symbolic links).
How to create a static library?
The first step to create a static library is to gather all the .c
files we want to include in out library and place them in the same directory. Like so:
This is a big collection of functions that carry out different actions such as identifying if a letter is upper or lowercase, if a chracter is a digit, the length of a string and more. If we were to need these functions within any program, it would be time consuming to write them out every time. Instead, we will zip them in a library to reference every time we want to use them.
To do so, we have to to compile these .c
files — but stop after the Assembler phase, by adding the -c
option to our gcc
compilation. The output will be our .c
files transformed into objects (.o
files, not objects as “things” but objects as an intermediate compilation state of file in a specific language, like we said before.)
gcc -c *.c
Where the -c
flag stops after the Assembler and *.c
will compile all the files with a .c
extension. Now, we can see all our previous .c
files have an .o
extension:
Static libraries are created using the ar
command (for archiver). This command is used in Linux to create, modify, and extract files from archives. An archive is a collection of files that have a similar structure from which the individual files can be extracted.
ar
takes object files .o
, as many as we want to include in the library and mashes them up all together into a .a
file — our static library! We will run the ar
command with the following flags and options:
ar -rc mylibrary.a <filename>.o
Where:
- The
r
flag will replace older object files in the library, with the new object files, and thec
flag tells ar to create the library if it doesn’t already exist. mylibrary
is the name of the library to be created with my original functions inside. This means that if I wanted to use my functions, instead of defining every function inside my code or linking each function independently, I could use them through the library.<filename>.o
is every name of every object file I want to include in my library.
After an archive (ar) is created, or modified, it is recommended to index it. Some archivers will do so automatically, depending on the operating system, but just to be sure it is best to run the command. This index is later used by the compiler to speed up symbol-lookup inside the library, and to make sure that the order of the symbols in the library won’t matter during compilation.
The command used to create or update the index is called ranlib
, and is used as follows:
ranlib mylibrary.a
So now that we’ve created our object files from our program files, compressed them in up an library and indexed it, we can now make use of the library.
How to use a static library?
Now that the library is created with whatever functions we wanted to include, it is now available to use by any other user and program.
To do so, just run the compilation command with the specific options and flags that allow the use of the library. For example:
gcc my_program.c -L. -lmylibrary -o my_program
Where:
gcc
is the compilation commandmy_program.c
is the program I want to compliate-L.
will tell the compilator to look in this directory for library files. It opens the option that a library will be looked for.-l
(mind the caps!) will be followed by the name of the library we created (without the “lib” and the extension “.a”- -l will handle this info)-o my_program
indicates the output file name.
You can see the contents of a library(the .o files that went into it) and the defined symbols by using nm
. The nm
command provides more information about the symbols in our static library. Specifically, by default, the nm
command will show us the virtual address of the symbol, the name of the symbol, and information about the symbols type.
Usage: nm mylibrary.a | less
How to create a dynamic library?
Dynamic linking means the use of shared libraries. Shared libraries usually end with .so
(short for "shared object").
Shared libraries are the most common way to manage dependencies on Linux systems. These shared resources are loaded into memory before the application starts, and when several processes require the same library, it will be loaded only once on the system. This feature saves on memory usage by the application.
The first step to create a dynamic library is the same as the static library: All .c
files have to be in the same repository where the library will be created.
After all the files are gathered, the following command should be ran:
gcc -Wall -fPIC -c *.c
Just like before, the gcc
compiler will stop after the Assembler step, and an .o
file will be created for every .c
file in the folder.
The -fPIC
flag ensures that the code is position-independent. This means it wouldn’t matter where the computer loads the code into memory. Some operating systems and processors need to build libraries from position-independent code so that they can decide at runtime where they want to load it into memory.
Next, type in the following command: gcc *.o -shared -o liball.so
(substitute your desired library name with all
) and hit return.
In this case:
- The wildcard
*
tells the compiler to compile all the.o
files into a dynamic library which is specified by the-shared
flag. - The naming convention for dynamic libraries is such that each shared library name must start with
lib
and end with.so
. In this case, “all” between lib and .so could be replaced with any desired library name.
After creating the library it is recommended to include it in the path environmental variable, like explained above.
How to use a dynamic library?
To use a dynamic library, you can run:
gcc -L. 0-main.c -lholberton -o mylib
Where:
gcc
is the compiler, like we already know- The
-L
flag tells the to look in the current directory for the library file. 0-main.c
is the source code to which the library will be linked to-lmylibrary
is the library name that the compiler will search for- And the
-o
flag will be followed by the desired output name, in this casemylib
.
Advantages and disadvantages of static and dynamic libraries:
- When a bug is fixed in a dynamic library, every application that references this library will profit from it. On the contrary, this also means that if the bug remains undetected, each referencing application will suffer from it (if the application uses the affected parts).
- An advantage of using static libraries is that the application can be certain that all its libraries are present and that they are the correct version.
- Static linking can also allow the program to be contained in a single executable file, simplifying distribution and installation.
- In static linking, the size of the executable becomes greater than in dynamic linking, as the library code is stored within the executable rather than in separate files.
- The program and whatever library it uses are linked with symbolic link . For dynamic libraries, in case of two or more applications using shared library, the overall size taken by library will be less.
- In static libraries the library is already linked , so application load time is less.
- In dynamic, application loading time is more because of dynamic library loading and dynamic link creation activity.
:)