C Libraries and Their Types

CorleyComputerRepair
7 min readJun 14, 2022

--

In programming, one must often use the same tool to accomplish the same task, hundreds or even thousands of times. Before too long, you may start noticing you’ve written some functions that you like to use everywhere, but it takes quite a bit of time to rewrite a given function over, and over, and over again. Perhaps, instead, a friend or colleague has written this same common function and you like theirs much better! This is where the idea of libraries comes into play. If code needs to be reused in multiple places, libraries are an ideal time and space-saving tool. After all, how many different ways can one count the length of a string, or determine the absolute value of an integer?

While there are multiple types of libraries, their behavior and use are very similar. Libraries are so ubiquitous that you’re like to find them in nearly any possible programming language. Simply put, libraries are collections of small programs you can call upon any time you need them, usually to accomplish one particular task. Maybe you wrote them, maybe you didn’t, but you’re almost guaranteed to be *using* some just by reading this blog post.

There are two varieties of library, only one of which you’ll actually see if you’re casually browsing your filesystem. These two types are known as /static/ and /dynamic/ libraries. Their difference is primarily when you invoke them. Static libraries can only be used at compile time, by the program that included them, which results in the entirety of the library being included with your source. At this point, the static library is only useful to the programmer that needed it to build it. In contrast, dynamic libraries simply “hooked” into a newly-created binary and used later, when needed. Let’s first get into the process of creating and building with them, and then discuss more about the advantages and disadvantages of each.

Both types of libraries are composed of static C or C++ objects. In other words, a bunch of C/++ files are taken after the Compilation process and dumped into a new file, together. For a static library, this is simply:
`gcc -c INPUT.c`

This is also true of dynamic libraries, except that there is an additional flag added which we’ll explain a bit later.
`gcc -fPIC -c INPUT.c`

The resulting file should now be:
`INPUT.o`

Now that we’ve obtained our object files, they need to be packaged into an actual static library, or archive. Note that this use of the term “archive” really isn’t all that different from a .zip or .7z file if you’ve seen those before, except that these types of archives don’t compress the data being stored in them as a typical archive would.

`ar rcs libmy.a INPUT.o`

Here, `ar` creates the archive and the flags are as follows: `r` replaces old files, `c` creates the archive if it didn’t already exist, and `s` creates an index of the archive and places it within said archive.
Indexing is not strictly necessary, but it does improve compiler time and prevent some errors from occurring. It’s fairly typical for whatever program made the archive to handle indexing it, but there is also another alternative:

`ranlib libmy.a`

Ranlib does basically the same job as the `s` flag to `ar`, which also done by default with many archivers. Ergo, ranlib is often not useful, to the point that the occasional system even supplies a `ranlib` command that does nothing at all.

Note: to keep your index up-to-date, if it needs to be copied elsewhere, use the following command to preserve its modified date, otherwise its index will need to be regenerated:

`cp -p`

Using an example from a recent project, we can actually query the index of a library with `ar -t`. Here’s its output, listing each object used to create it:

If you wish to list the symbols of each object to get an even more detailed view, you can also use the `nm` command:

Now that our static library has been created, we just tell the linker to use it.
`gcc main.c -L. -lmy -o main`

Normally, the linker will only search for libraries in a handful of preset directories, such as `/lib` and `/usr/lib` but here we’ve used `-L.` to Locate in the current directory also.

Worth mention as well is the fact that when using a static library, the prefix “lib” and the file extension “.a” are assumed by the linker and added back onto what it actually searches for, so `-lmy` is actually looking for a file, `libmy.a`.

At this point the program is usable and the library is only needed when rebuilding the program. Whatever was built with it can be freely distributed without it. Let’s take a look at shared libraries now!

Just like with Static, Shared libraries are C/++ object files collected in a new, individual file.

`gcc -fPIC -c INPUT.c`

Here, we run into one of the most important distinction between Dynamic and Static Libraries. Dynamic libraries aren’t dumped wholesale into a new program during its /compile/ but rather at the time it actually is running, or executing. Because of this, one library can be used by multiple unique programs, which is excellent! Except, you can’t determine beforehand where each of these programs inserts functions from the called library into memory. One might have them early, another in the middle, and another later, and so on. To get around this issue, we use the `-fPIC` flag to gcc, for “Position Independent Code.” This ensures that relative memory addresses are used over absolute ones. For more detail on how this affects the linker, check the sources at the end of this document.

Now that our extra-flexible objects have been created, we need to bundle them into a library as before. Unlike static libraries, dynamic ones have a format that’s unique to the hardware they’re intended to run on, so the compiler is used to build them as well.

`gcc -shared libmy.so INPUT.o`

Now that our library is built, linking to the program during compilation is identical.

`gcc main.c -L. -lmy -o main`

This outputs the program `main` using the library `libmy.so`. In this example, we have `libmy.a` and `libmy.so`, and gcc will give preference to shared libraries by default, so `libmy.so` is always used unless we specify otherwise.

We’ve now built a program using our shared library, but now there’s an additional problem: the dynamic library is loaded when the program runs, so our system needs to be able to find it! Just like the linker looks for libraries in a specific directory during compilation, Linux does the same thing when we run the program. We can use the command `ldd` to determine what libraries a given program requires.

Highlighted in red, you can see the library isn’t found.
We *could* add the library to one of the other locations listed here, but instead of polluting our system directory we can use the environment variable, `LD_LIBRARY_PATH`.

Check it’s not set:
`echo $LD_LIBRARY_PATH`

If not, feel free to enter the location of the library and update LD_LIBRARY_PATH:

`export LD_LIBRARY_PATH=$(pwd)`

Now we’ve built a dynamic library using object files, and linked it such that the system will load it automagically at runtime. However, this isn’t the only //SCREENSHOT OF NM LIBMY.Away to use a dynamic library. It’s not at all necessary to load them immediately when the program runs, and doing so may be computationally expensive. There are three C functions we can use instead to arbitrarily determine when to load a shared library, call functions from it, and close it when we’re done.

`dlopen(“full/library/path”, RTLD_LAZY)` — Loads a dynamic library. Its first argument is the location of the library you are trying to call, the second being a flag defining whether symbols in the library are checked immediately or when needed.

`dlsym(LIB-VARIABLE, FUNCTION_NAME)` — Searches the library named in the second argument, for the function named in the second.`

`dlclose(LIB-VARIABLE)` — Closes down the library when it’s no longer needed, freeing up the resources it was using.

For more detailed information and code examples: Building and Using Static and Shared Libraries in C

man ar , man nm , man ranlib

--

--