C Static Libraries — What, Why, and How?

Brennan D Baraban
8 min readOct 7, 2018

--

Ah, libraries, those paradises of imagination, annals of human history, keys to public knowledge, and collections of object files.

Ok, I admit the ladder description refers to something separate from the brick-and-mortar collection of books traditionally referenced by the term library. Still, collections of object files, also called libraries within the scope of the C programming language, are powerful tools in their own right. What exactly are C libraries, why are they useful, and how can we use them?

Source: https://media.giphy.com/media/JfjG4OkvYsirK/giphy.gif

Fortunately, C libraries can be thought of quite similarly to the common understanding of the term. A library on an Unix-like system is an archive file. Archive files, specified by the extension .a, are files containing object files. Just as books in a public library are sorted by such categories as author and genre, object files in an Unix library are typically indexed, permitting search by symbols such as functions and variables.

Object files consist of object code, relocatable format machine code. There are several different object file formats. The standard for Unix systems is Executable and Linkable Format (ELF), a cross-platform format that can be used as both linker input and output, thus allowing object files to work for both libraries and executables.

It is in regards to this feature of ELF object files, linking, that libraries are useful (for more on where linking fits into the compilation process, see my previous blog). When tasked with linking together C object files into an executable, a machine can more easily and more quickly filter through a library than if the object files were located separately on the hardware.

To extend the public library metaphor — imagine that you were at a library and wanted to check out a Harry Potter book as well as a biography on Abraham Lincoln. Now, imagine that the library was not organized, and instead of being able to go first to the comics section, and then the nonfiction section, you had to randomly sort through the entire collection of books until you found the two you wanted. Think about the time that would take, then think about the Harry Potter book and Lincoln biography as analogous to object files. Be nice to your machine. Create a library.

Source: https://media.giphy.com/media/y3uIVeRWg5SSY/giphy.gif

So, say that we have a collection of C programs, are nice people, and want to create a library to store them. How do we go about doing so? Unix systems permit the creation of two types of libraries — static and shared. Both have their merits and drawbacks; for the purposes of this article, I will exemplify the usage of static libraries.

Static libraries are collections of object files that are linked into a program during the linking phase of compilation, and thus are not needed during runtime (in contrast, object files in shared libraries are linked in a two-stage process that finishes at runtime). In other words, when a C program is compiled using a static library, during the linking phase, the corresponding machine code for all functions used in the program is included directly in the resulting executable. This process is called static linking.

I can’t get over my library metaphor, so for our example, we’ll be in a directory staticlibrary containing C files named after some of my favorite books:

As you can see, our directory contains six .c files — gameofthrones.c, greatgatsby.c,harrypotter.c, hungergames.c, prideandprejudice.c, and soundandthefury.c. Additionally, it contains a header file bookheader.h containing the function prototypes for all six C files.

Before we can create our static library, we need to convert our C programs into object files. To do so, we’ll use GNU’s GCC compiler with the -c option along with the wildcard character * to match all six .c files simultaneously:

Now that we have compiled .o object files for each of our C files, we can store them in a static library. To do so, we’ll use another GNU program — the binary utility ar, a convenient shorthand for “archiver.” Here, we’ll be using ar to create a library, but the program is actually much more versatile — additionally, it can be used to modify and extract object files from a given archive file.

To be thorough, and as good practice, we’ll run ar with two options — r, which will replace any older object files located in the directory with our new ones, and c, which instructs ar to create our library only if it does not already exist. I’m going to call my library libfavbooks.a (the lib prefix being standard naming practice for libraries). Again, I’ll use the * character to match all six object files simultaneously:

Great! So we now have a static library libfavbooks.a containing the object files gameofthrones.o, greatgatsby.o, harrypotter.o, hungergames.o, prideandprejudice.o, and soundandthefury.o. Don’t take my word for it, however; to list the contents of our new library, run ar again with a new option, -t:

In case you needed a reading list for next summer.

Now, how can we actually use our fancy new library?

First things first — let’s refresh our memory. Remember when we had to randomly search through an entire unorganized library to locate a Harry Potter book and Abraham Lincoln biography? Let’s not do that. Before we do anything with our new library, we need to index it.

To index, we’ll use the command ranlib. Afterwards, we can view our newly-indexed library using the command nm:

As you can see, the nm command lists the symbols contained in each object file. The letters next to each symbol indicate its type. In this example, we see two letters — T, indicating that a symbol is located in the text section of the file, and U, indicating that the symbol is undefined.

The ranlib command is not always necessary — on some systems, ar features built-in indexing. Also, note that compilers will raise errors when trying to use an archive file with a last modification date newer than the index generation date — to overcome this, re-ranlib your library.

Now that we have converted our C programs into object files, stored them in a static library, and indexed our library, we can finally, actually, conveniently use the library to give the linker access to all the files contained within it. For example, let’s say we now have a main C program novels.c that uses the functions defined within greatgatsby.c, prideandprejudice.c, and soundandthefury.c:

Manually, we could compile the program by specifically naming all relevant files in our compilation command. With our new library, however, this process becomes more compact. Instead of listing each file, we’ll simply include the static library name prefixed by the -l option. After successful compilation, we can run our a.out executable:

Note that in addition to the -l option, I included -L., which specifically instructs the linker that libraries might be found in ., the current directory. Note also that I left the lib prefix and .a extension off of the library name — the linker automatically attaches these strings to the file name when it searches for the library.

Nice and easy, right?

Let’s think about what exactly happened in the above execution. At the linking phase of compilation, the linker checks and adds files to the executable left to right, one at a time. In our above compilation of novels.c, the linker first adds the assembled main function, then enters libfavbooks.a, then checks the object files contained within the library in the order they are indexed. As the linker locates object files corresponding to functions used within the main program, it adds them directly into the resulting executable. This static linking process can be visualized thusly:

As exemplified, static libraries permit faster linking. However, the static linking process involved comes with two drawbacks. First, since static linking directly inserts machine code into the executable, C programs involving many functions can result in large, unwieldy executables. Secondly, for this same reason, static linking is not flexible when it comes to editing programs after-the-fact. With static linking, any changes made to original programs after compilation will not register in existing executables — recompilation is required.

The solution to these drawbacks is dynamic linking; however, that is a topic for another time. For now, static libraries are useful tools in their own right, archive files that permit us to more quickly and efficiently link C programs. Now, get off the screen and go get lost in a real library! (Although you shouldn’t, if it’s been properly indexed 😉)

Source: https://gph.is/1sFEqnb

--

--