Create a simple static library in C

jeu. 17 août 2017 by Mick Cherry

I only recently learned the difference between a static and a dynamic library in C, essentially because I have so far only worked with static libraries - and very few libraries.

This article will only cover a simple example of a static library in C.

The idea of a library (in C) is a set of compiled functions that you can call from your application, and that are usually developped and released by someone else.

You get a .a file or a .so file from a vendor/third-party and a bunch of header files. Then, you can call the functions of the header file from within your own application.

Trivial example of a regular function

Let's dig in! First, we'll start with a trivial example of a main() function calling an external function.

#include <stdio.h>

int getRandomNumber(void)
    return 4; // fair dice roll

int main(void)
    int x;
    x = getRandomNumber();
    printf("A random number is %d\n", x);

We compile and run this file

$ gcc random.c 
$ ll
total 24
drwxrwxr-x 2 etienne etienne 4096 août  17 22:21 ./
drwxrwxr-x 3 etienne etienne 4096 août  17 22:21 ../
-rwxrwxr-x 1 etienne etienne 8464 août  17 22:21 a.out*
-rw-rw-r-- 1 etienne etienne  170 août  17 22:21 random.c
$ ./a.out 
A random number is 4

If we generate a MAP file we can see that getRandomNumber is 11 (0xb) bytes long:

$ gcc random.c -Xlinker
$ grep getRandomNumber -A 1
    0x00000000000006a0                getRandomNumber
    0x00000000000006ab                main

And the objdump confirms it:

$ objdump -S a.out | grep "^\S* <getRandomNumber>" -A 5
00000000000006a0 <getRandomNumber>:
 6a0:   55                      push   %rbp
 6a1:   48 89 e5                mov    %rsp,%rbp
 6a4:   b8 04 00 00 00          mov    $0x4,%eax
 6a9:   5d                      pop    %rbp
 6aa:   c3                      retq

Nothing special here, just a regular function called from the main and placed in the resulting binary with the obvious gcc random.c.

Static Library: put the function it its own file

Now, let's imagine that we want to share this awesome getRandomNumber function with someone else for them to use it from their application. We are going to make it a library!

First, let's create a new file named lib_random.c and put the function in it:

$ cat lib_random.c 
int getRandomNumber(void)
        return 4; // fair dice roll

Now we create a single line header file in order to expose the function to other applications:

$ cat lib_random.h
int getRandomNumber(void);

We remove the function definition from the initial file random.c and add the header file name. random.c now now looks like:

$ cat random.c 
#include <stdio.h>
#include "lib_random.h"

int main(void)
        int x;
        x = getRandomNumber();
        printf("A random number is %d\n", x);

If we try to compile random.c gcc complains at the link step:

$ gcc random.c 
/tmp/cc7aL5AW.o: In function ``main':
random.c:(.text+0x9): undefined reference to `getRandomNumber'
collect2: error: ld returned 1 exit status

There is no symbol named getRandomNumber available for the linker to use.

Create the lib (compile + archive)

First, we have to compile the library file:

$ gcc -c lib_random.c -o lib_random.o

The -c flag passed to gcc means "compile only, do not link".

Then we need to create an archive out of the lib_random.o object file: this seems to be the main usage of the ar tool.

$ ar rcs lib_random.a lib_random.o
$ ll
total 28
drwxrwxr-x 2 etienne etienne 4096 août  17 22:55 ./
drwxrwxr-x 4 etienne etienne 4096 août  17 22:33 ../
-rw-rw-r-- 1 etienne etienne 1400 août  17 22:52 lib_random.a
-rw-rw-r-- 1 etienne etienne   59 août  17 22:34 lib_random.c
-rw-rw-r-- 1 etienne etienne   27 août  17 22:38 lib_random.h
-rw-rw-r-- 1 etienne etienne 1248 août  17 22:48 lib_random.o
-rw-rw-r-- 1 etienne etienne  134 août  17 22:38 random.c

Link the application to the library

The last step is to compile and to manually link the application random.c against the library lib_random.a:

$ gcc -o random random.c -L. -l_random

The -L. flag tells gcc to look for library files in the current folder (.). The -l<library> option tells the compiler to look for a library named lib<library>.a.


Finally, we can run the compiled and linked binary which includes the main() and getRandomNumber() functions:

$ ./random 
A random number is 4

Awesome! The external getRandomNumber function gets properly called even though it is not in the random.c main application file and has not been compiled at the same time!

This is named a static libray because the entire library compiled code is copied into the resulting binary.

We can check that the getRandomNumber function code is embedded inside the resulting binary random:

$ objdump -S random | grep "^\S* <getRandomNumber>" -A 7
00000000000006cd <getRandomNumber>:
 6cd:   55                      push   %rbp
 6ce:   48 89 e5                mov    %rsp,%rbp
 6d1:   b8 04 00 00 00          mov    $0x4,%eax
 6d6:   5d                      pop    %rbp
 6d7:   c3                      retq
 6d8:   0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
 6df:   00

Sources: I made extensive use of this and that with a bit of that.