Static vs Dynamic Libraries
One of the more common programming adage is “never code the same thing twice”. Typically referring to making functions for blocks of code that you plan to use multiple times in a program, this can be taken a step further by converting multiple blocks of code from multiple files into a library that allows you to utilize a function in other programs.
What is a library and how do we use them?
A library in computer programming is a collection of object (also known as machine) code stored in .o files that can be used in other files by including them in your code. In C this is done through #include.
This lets us make calls to functions, macros, and variables that have been defined elsewhere inside our own code, which can save time overall when writing future code. While there are several predefined libraries, often called standard libraries, out there there may come a time when you want to turn your own code into a library. If this is ever the case you first need to make the decision on whether you want to make a static or dynamic library.
What is the difference between static and dynamic?
The main difference between the two is how the library is made, stored, and eventually used. With a static library, also known as an archive library, you create an archive file, libname.a in Linux or libname.lib in Windows. When you compile your code with a static library, the compiler will copy the appropriate code from the library into the resulting executable. On the other hand a dynamic library, also known as a shared library, creates shared object files, libname.so in Linux or libname.dll in Windows. Unlike with static libraries, the code from dynamic libraries are not copied into the executable but rather symbolic links are provided to the dynamic library and the code from the dynamic libraries are copied over into the executable at runtime. This allows the executable to only load needed code when necessary instead of having it all plugged into the file at once. So lets take a look at why we would use one over the other:
Pros: The biggest pro with static libraries is independence. The executable made using a static library can be put into a brand new, isolated environment and would still run perfectly fine (assuming nothing else changes). Another benefit is runtime execution speed: since the code is already in the executable it doesn’t need to reference the library anymore and thus run speeds increase.
Cons: Both of those benefits directly tie into their biggest drawbacks. While the independence means you do not need the library itself to function, it does mean that anytime the library is improved or expanded you need to recompile the executable in order for to benefit from the updates. On the other side, the increased speed comes at the cost of file size. The more calls to library functions means that your executable will increase in size as well. This issue is compounded if multiple files use the same static library, each executable will make its own copy of the code creating multiple larger executables. One other major downside of static libraries is inter-program dependencies. Every program created from a static library will have its own instance of any variables that were stored in the library, meaning that multiple programs could not share any important variables.
Pros: The first pro of dynamic libraries is file size. Since you are not copying the code into the executable, the file can be significantly smaller than if you had used a static library, and just how a static libraries file size is compounded with multiple files, so too is the size benefit of dynamic libraries. For example, if you have a library that contained 500mb of code and several programs that had about 5mb of code in them not including the libraries, each program made using the static library would be around 505mb plus the original 500mb from the library (assuming you are storing everything in the same location) vs the dynamic library programs still only being about 5mb. The second major benefit is separate updating. In the real world sometimes a program will receive and update to its base code while the library remains the same, or vice versa, the library gets an update but the base program is still the same. With a static library you would have to recompile everything to see the library changes, but with our dynamic library your executable would access the updated library at runtime without the need to be recompiled itself. The last major advantage we will touch is inter program dependencies. Since the dynamic library is accessed at runtime rather than compilation, any variable updates made by programs can be shared with other programs rather than being instanced.
Cons: Once more the major cons are directly tied to the benefits. In return for your smaller file size run speed will decrease as you use more and more calls to library functions. And the program will not run properly if it is taken out of the environment that has the dynamic library in it.
How do I make a library?
Now that you have an idea of which type of library you wish to make its time to learn how. We will be approaching both using Linux (so making .a and .so files).
For static libraries you follow these steps:
gcc gcc -options_here -c *.c
ar -rc libname.a *.o
The first line turns all your .c files into .o files, thanks to the option -c which stops compilation part of the way through, to be bundled into your archive. The second command takes the .o files and bundles them together into your .a
Depending on the version of gcc you are running there is a third step:
That would create the symbol table that would be used by the compiler when making your executable. Newer versions have the ranlib command embedded into the ar command. You now have a functional .a file. To use the functions in your code you can now just prototype the functions in your code and then include the library name when compiling your executable like so:
gcc -options -L/path randcfile.c libname.a
and the compiler will take care of the rest. -L is used to tell your compiler where your library is located if it is not in the current directory.
For dynamic libraries we follow the following steps:
gcc -optionshere -fPIC -c *.c
gcc -shared -Wl,-soname,libname.so -o liball.so *.o
The first step should look familiar. Like with static libraries the -c stops compilation so that instead of executables we end up with .o files. The option -fPIC creates position independent code which is required for share libraries to function properly. In the second step the -shared option designates that we are creating a shared library. -Wl allows us to pass options to our linker when compiling, in this case the option -soname followed by the comma and the library name (-soname,libname.so)which is the name of our dynamic library, which we pass once more to gcc (our compiler) using the -o option which will control the name of our dynamic library .so file. Now that the file is made you can either edit your library path or create symbolic links so that the compiler can access your library when creating new executables. We will use the second option in the following example:
ln -sf /opt/lib/libname.so
These symbolic links are how your executable will access the dynamic library at runtime. You then compile your program as follows:
gcc -otheroptions -L/opt/lib prog.c -lname
where prog.c is your program name and -lname is replaced by -l(your library name minus the initial lib). The -L tag tells the compiler where your library is located. This is why we did the ln -sf step. Otherwise the compiler would tell you that it could not find the file name. If you have a file that has been compiled using dynamic libraries, you can use the command ldd to see what dynamic libraries it needs in order to run. You can also use ldconfig to update your dynamic linker with the dynamic libraries by passing it directories as so:
ldconfig -options dir1 dir2 diretc
After creating either library you can check that the library was made succefully by using the command:
nm -D libname
This command allows you to view the contents of binary files, such as libraries in this case. The -D option covers initialized global data.
And that is how you make and use both static and dynamic libraries, as well as a small conversation as to why you would use one over the other.