What are dynamic/shared libraries?

Benjamin Keener
6 min readDec 17, 2019

One of the most useful tools in modern computing: libraries. What is a library? A library is a collection of useful functions and methods that can be easily shared and used. In this article, we’ll be focusing on libraries in C, but most modern languages take advantage of libraries.

Usefulness of Libraries

Let’s say you are writing code and, because you are such an awesome developer, you write a bunch of nonspecific functions to help you achieve your goal. Great! Now you move on to your next program and find that you could really use some of those functions you already made. That’s where libraries come in. Instead of copying and pasting all your functions, you could create a library of your useful functions and implement it in all the programs you want!

How Do We Take Advantage of This in C?

Good question. If you’re unfamiliar with the compilation process in C using gcc, please read my previous article. In this article, I’ll be talking about shared/dynamic libraries, though there is another type of library called a static library (Which you can read about here).

Static vs. Dynamic Libraries

We’ve already talked about static libraries, so let’s get a little information on dynamic (shared) libraries to figure out when we should use which.

When dealing with a static library, all library functions are compiled into the executable. This means not only does the size of our executable increase, but it also takes longer to compile our program. With shared libraries, the library is linked to our executable at runtime, cutting down on executable size and compile time.

Another major difference between the two types of libraries is where they are stored. Static libraries, again, are compiled right into the binary, but shared libraries are stored in a common location and when they are needed, the shared library gets loaded into system memory. This means that if a shared library is already being used by another program, it can be loaded even quicker by another program which needs to use it. This is one of the reasons why shared libraries are so popular for libraries that are used by many programs, it cuts down on load time significantly.

The last major difference between static and shared libraries is library compatibility/code changes. With a static library, if you want to update a function, each program using that library needs to be recompiled in order to reflect these changes. A benefit to this, however, is that you can be confident that the library is compatible with a program using a static library, as that’s what it was compiled with. As for shared libraries, they’re much easier to update as it requires no recompilation of the programs that use them. In turn, a library may be updated in a way which makes it incompatible with some programs, effectively rendering the programs useless.

Creating a Dynamic Library With GCC

Let’s say you decide a dynamic library is best for your application. How do we make one? It’s actually relatively simple, especially compared to static libraries.

First, we need to round up all of the files which contain the functions we want to be available in our library. You could have one function per file (like pictured), all of your functions in one file, or somewhere in between. You’ll also need a header with the prototypes of all of your functions.

All of the .c files in the directory along with the header file

Now that we have all of this organized, all we need is one “simple” command: gcc -shared -fPIC -o liblibrary.so *.c

Hold on a second… What does all of this mean? Let’s break it down.

The -shared option is pretty self-explanatory. It tells gcc that we’re compiling a shared library and to handle the following options as such.

Next, the -fPIC option tells gcc to compile with relative machine code addresses rather than absolute references. This means that the code can be inserted anywhere and the assembly will still work, even if the “line numbers” have changed. This is pretty important with a shared library, as we never know what the code using it might look like.

After that, the -o liblibrary.so option lets gcc know that our output file should be named liblibrary.so. This name also follows convention for shared libraries, which should start with lib, followed by the library’s name (in our case library), and end in .so.

The last parameter tells our compiler which files to compile into this library. In our case, we want to use all files ending in .c in our current directory.

Compiling and showing output

If you’d like, you can see which functions are included in your library using: nm -D --defined-only liblibrary.so.

Showing all functions included in compiled library

Compiling a Program With GCC and a Shared Library

Example program which uses a function included in the library

In order to take advantage of this dynamic library, we have to specify that we’re using it when we compile our program. Let’s look into the command gcc program.c -L . -llibrary -o executable.

We tell gcc to compile program.c, pretty simple. Then we use the -L flag to specify that we should look in the . directory (the current directory) for libraries (however, this only happens during linking). Next, we say that we want to use a library (-l) and the name of the library is library, therefore we end up with: -llibrary. After that, we use -o to name our output executable. Cool! Let’s run it!

Error shown when trying to run the program

Uh-oh! It can’t seem to find our library! There’s one last thing that needs to be done before we can run a program which uses a shared library. We need to specify where we can find shared libraries. For this we use the environment variable $LD_LIBRARY_PATH. This is very similar to $PATH in that it is a list of directories. To add the directory that our library is in, we can use the export command. For example, I would run:

export LD_LIBRARY_PATH=/home/vagrant/dl_backup:$LD_LIBRARY_PATH

The :$LD_LIBRARY_PATH at the end means that if the $LD_LIBRARY_PATH variable already has a value, it will get appended to the end of our new path so we don’t lose any paths.

Now let’s try running our program again:

Program working using dynamic library

Awesome! But before you go, notice how no recompiling was necessary, all we had to do was add it to the $LD_LIBRARY_PATH and it worked! The principle is the same for any changes made to the library itself, no recompiling necessary.

Did You Find This Article Helpful?

Please, let me know! I’d love to hear how I can improve and what I did well. You can follow me on twitter @TheBenKeener.

--

--