(This is an update of an article I wrote about static libraries. You can find the original article here.)
What is a Library?
At its core, a library is an organizational tool. It stores information (e.g. books) and ideally gives individuals some method for accessing that information. Libraries in computer programming work in a very similar way to libraries in the real world. The big difference is about what kind of information they store. Whereas physical libraries store books, programming libraries store functions. They are simply a way of holding a bunch of functions together in one file. In programming, libraries are useful because they allow us to use functions in a program without explicitly linking them during the compilation process. There are two main types of libraries: static and dynamic.
Static libraries have a .a or a .lib extension. Static libraries are added into a program during the linking phase of compilation. Afterwards, the static library is not needed for the program to run. Using a static library is great, because it can produce a completely stand-alone program. If you use a static library during the compilation process, you can run the compiled program on other computers without having to worry about installing specific libraries. Of course, there are tradeoffs to using static libraries. For one, static libraries are much more memory intensive than shared libraries. They also produce bigger executables, which could be problematic, depending on your situation. Finally, if you have multiple executables that need to access the same library, it wouldn’t be very economical to compile each one separately with the same library. Enter the dynamic library.
Dynamic libraries (also known as shared libraries) have a .so, .dll, or a .dylib extension. In contrast with static libraries, dynamic libraries are not added to an executable during compilation. Instead, the compiler checks the dynamic library for functions, but doesn’t insert them. The executable access the library during runtime. Using a dynamic library can make your executable much more streamlined. Additionally, the library can be shared between multiple executables. This can be useful if you have a bunch of programs which are using the same set of functions. On the downside, dynamic libraries aren’t as portable as static libraries. Programs which have dynamic libraries cannot be run stand-alone; they need access to those libraries. If you’re running a program on a different operating system, this could present a real problem!
How to Create and Use Libraries
Now that we know about the advantages and disadvantages of static and dynamic libraries, let’s look at how we can create and use them, step by step.
Creating a Static Library
First off, we need some object files to start with. Let’s say we want to include all functions in our current working directory in our library. First let’s type this into the command line:
gcc -c *.c
GCC (Gnu Compiler Collection) will compile all c files here, but won’t link anything, because of that c flag. Each c file in our directory will now have a corresponding object (.o) file.
Now, we want to interact with those object files we just created:
ar rc staticlib.a *.o
ar is a shell command which has the ability to create, modify, and extract from archives. There are two flags for ar, r and c. r will update and replace a function if there is a newer version of it. c will create the library if it doesn’t exist yet. Next is our library name, which ends with the .a extension. Finally, we use *.o, to say that we want to include all object files in our directory.
After we create our archive, we want to index it, using ranlib. This makes the linking process more efficient, by keeping a record of what’s included in the library.
Using a Static Library
Now that we have our static library, let’s compile it with our program.
gcc main.c -L. -lstatic -o program
We compile our main file with some important flags: L. and l. L tells the compiler to look for library files in the current directory. As long as libstatic.a is in the current directory it will be included in the process. Next, l marks our library files, letting the compiler know that libstatic.a is a library. Notice that we’ve excluded the whole library file name (“libstatic.a”) and just wrote “static”. Both the “lib” and “.a” are redundant because we’ve already said that it’s a library file. Finally, we use our -o flag and our desired program name, which we can execute without the need for the library to be present.
Creating a Dynamic Library
Now let’s look at creating dynamic libraries. To start, we need to compile our .c files:
gcc -c -fpic *.c
Here, we use the same -c flag that we were using earlier, to make object files, but we also add a new flag: -fpic. This flag tells the compiler to generate position independent code (PIC). We won’t necessarily know where our shared library is, so this flag will allow our program to still access it during runtime.
gcc -shared -o libdynamic.so *.o
Finally, we can finish up the compiling process. The -shared flag creates the shared library, with a .so extension.
Using a Dynamic Library
Okay, now let’s use the library we created!
gcc -L. test.c -ldynamic -o program
Above, we’re linking our test.c file with libdynamic.so, again, omitting lib and .so because of the -l flag. The -L. flag at the beginning tells us where to find our shared library (in the current working directory). We still have a problem, however. Our program doesn’t know where to find the dynamic library, so it won’t work at runtime.
Here, we added our current directory to LD_LIBRARY_PATH. Now our program knows where to look in order to find our dynamic library!