I think static and dynamic libraries are confusing. Let’s explore why we’d go through the trouble of creating a library in the first place.
First, imagine that it was illegal to re-use any functions in your code. This means every time you wanted to perform an operation, you had to re-write the same function for it every time. Fortunately, this is not our reality as we’re able to re-use functions we’ve already written.
While we often don’t take the time to create entire libraries whenever we want to re-use a function, libraries allow us to store code that we know we’re going to use later.
For example, if we add ten functions to a library, we can include the library in the next source code file we write and have access to each of those ten functions.
Here we’ll be discussing two different kinds of libraries — static and dynamic.
In a static library, the linker includes the object code of the library functions your source code uses. This means that if you created a static library called mylib.a in your source code and then called five functions defined in mylib.a, the implementation of those functions would be translated into object code and then included in the final executable file by the linker.
Static libraries are less susceptible to breaking then are dynamic libraries. This is because the executable code from the library, once it has been created and used, is safe inside the executable file. Additionally, since the functions are already compiled into object code and included in the executable file, multiple calls to the same function will be much faster in a static library.
Two downsides of static libraries are executable file size, and compilation. Because the actual object code of the functions used from static library get included in the final executable, static libraries make executable files longer. Additionally any changes made to the implementation of a function that’s included in the static library, will not be reflected until the static library is re-compiled with the new function.
How do we create a static library?
Let’s create a static library using all the *.c files in our current directory. First we need to compile our C files into object code. We can do that using:
Next, we need to create our static library and add files to it. We can do this with the command:
After we’ve added everything to our standard library, there’s a need to index the library. Indexing the library can be done by running the command ranlib libexample.a. Indexing the library is important as it speeds up the linking process when the library is called.
We can view our recently generated index by running the command nm libstatic.a. 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. If the symbol is lower case, it is local, if it’s written in uppercase then it’s external.
To use this static library we would create a program — lets call it my_program.c that used functions from the library we just created. To use the library functions, we need to tell the compiler where to look. We could run the command:
Now let’s take a look at dynamic libraries. Let’s imagine a familiar scenario, we have a source code file that calls a function from the mylib.so library (notice the difference in file extension. On linux, static libraries should end in “.a” and dynamic libraries should end in “.so”). Instead of actually compiling the function implementation into object code and including it in the executable file, in a dynamic library, the linker will simply include the address of the beginning of the function we’re trying to use in the executable file. This eliminates the size issue caused by static libraries because the final executable file will include only addresses instead of entire function implementations.
Additionally, you don’t need to re-compile dynamic libraries every time you change a function since the executable has a pointer to the function instead of object code.
How do we create a dynamic library?
Let’s create a dynamic library using all the *.c files in our current directory. First we need to compile our C files into object code and make them position independent. We can do that using:
Next, we need to create our dynamic library and add files to it. We can do this with the command:
Once we’ve added files to our dynamic library, we need to verify that the shared library we’ve created contains all of the necessary dependencies. We can check that by running the command:
“not found” will be displayed if your program is missing dependencies
The program needs to know where to look for library files. Therefore, we must add the location of the dynamic library to the environmental LD_LIBRARY_PATH. We could do this with the following command:
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
To use this dynamic library (liball.so) we would create a program — let’s call it my_program.c. Let’s say this program uses functions from our dynamic library. We then need to compile the program the same way we did a static library:
gcc -lliball -L. my_program.c -o program.
After having created this new shared library, we can use the ldconfig command to create the necessary links and cache (for use by the run-time linker, ld.so) to the /etc/ld.so.conf directory.
So when deciding whether to use a static or dynamic library for your next project — remember the tradeoffs. If you need to save space — you should probably use a dynamic library, if you need things to execute faster — you should probably use a static library.