C language and Static Libraries. Static.
You’ve stumbled upon this post most likely due to searching information about “Static Libraries in C”. Welcome. It’s month two at Holberton and I’ve just created my first static library. What does it mean? Well, let’s dissect this together.
What are static libraries? A static library, also know as a statically-linked library, is:
“a set of routines, external functions and variables which are resolved in a caller at compiler-time and copied into a target application by a compiler, linker, or binder, producing an object file an a stand-alone executable”. — Wikipedia.
You might be thinking “Ok…but what does that mean?”. To understand this, we need to first understand the compilation process. If you take a look at my previous blog post Gcc your .c, I go into detail about the
gcc compilation process, but let’s do a brief refresh.
The image above gives us a visual aid into understanding a compilation process. When you
gcc your program, you are compiling your program to make it into something executable — something you can use. Your source code is your
c file, for example:
#This is your program file, which contains code to execute the program called main:
When you type in the command
gcc -c main.c, you are moving your
main.c file into a preprocessing stage, which takes in your code and compiles it into
main.o. Let’s forget about
main.s for now. When creating a static library, we want to focus on the
main.o file, which are also known as the object file. An object file is essentially a file that contains object code or machine level instructions (binary) such as this:
The above are machine level instructions to give to the assembler to make sense of your program. Now, let’s forget about the rest of
gcc for now as object files are what we need to focus on when it comes to creating our static library. What do object files have to do with a Static library you may ask. Good question! Object files are important to a static library since static libraries are composed of all the object files for each program you’ve written. Before showing you how to create a static library, let’s dive a little deeper.
Dynamic vs Static
I thought there were only static libraries! You thought wrong! First, what is a library in the technical sense? A library consists of a set of related functions to perform a task; for example, a function called
isupper. A library’s filename always start with
lib and ends with
a; for example
libholberton.a — a library called
holberton. Moving on, dynamic versus static — what makes them different?
- Static — traditional libraries where your executable programs are linked by object code.
- Dynamic — libraries are of object code as well, but are shared. They are loaded either at the time when you execute your program (with a dynamic link loader) or linked during runtime while statically aware at the same time.
A static library is linked by the linker (last step of the gcc compilation process before execution) and included in the execution code. Why would this be a problem if you were to have many programs? Well, the issue you may encountered when using a static library is when, for example, you have 10 programs linked to your static library, which means each program’s
“resulting binary would include the referenced library in its programming binary” — Darmouth.
Which means huge executable files since everything is included and needs to be loaded! This is why people use a dynamic library, which is shared and do not need to be directly included to be referenced. For example:
<stdio.h> is a dynamic library that can be referenced in your header file like so:
Easy peasy! By typing
#include <stdio.h>, you now have access to the dynamic shared library, which means you can access functions like
printf() without having the program’s source code in your directory. Your CPU will thank you. Static libraries are best for programs that you wrote yourself since they are not standard, but let’s not over do them since they take up memory and slows down your load time. Now that you understand why we use libraries and the different types, I can show you how to create your own static library.
Create a Static Library
To create a static library, you need two main things:
- Header file that contains all your prototypes
.cfiles of all your programs you’ve created
#This is my directory Project_Ramen:
#Let's ls into the directory:
#All my program files, .c, are int the directory as well as my header holberton.h, containing all of my function prototypes.
abs.c isupper.c _putchar.c strlen.c holberton.h
To create our static library, we need to have all our function prototypes in our header as well as compile all of our
.c files to grab the object files,
.o to create our static library. Let’s make sure all our prototypes are in the header
#Accessing holberton.h header with vim
$ vim holberton.h
#Content of your header file should look like this:
int abs(int n);
int isupper(int c);
int _putchar(char c);
int strlen(char *s);
#endif /* HOLBERTON_H */
#We have all the function prototypes included along with our header guard which are the three lines starting with "#". A brief definition to the purpose of header guards:
"A header guard is used to ensure your code is included only once and is used to prevent an endless loop to include files if there is a circular dependency between your header files".
#Header file is ready and now time to compile all your .c files. To do this, we will use the command gcc -c *.c, which compiles (-c) all the .c files (using the wildcard * to grab all the .c files in the current directory)
$ gcc -c *.c
#Let's ls into the directory to see what we get:
-rw-rw-r-- 1 ubuntu ubuntu 89 Mar 25 02:44 abs.c
-rw-rw-r-- 1 ubuntu ubuntu 1400 Mar 25 02:47 abs.o
-rw-rw-r-- 1 ubuntu ubuntu 132 Mar 25 02:44 isupper.c
-rw-rw-r-- 1 ubuntu ubuntu 1408 Mar 25 02:47 isupper.o
-rw-rw-r-- 1 ubuntu ubuntu 132 Mar 25 02:44 _putchar.c
-rw-rw-r-- 1 ubuntu ubuntu 1408 Mar 25 02:47 _putchar.o
-rw-rw-r-- 1 ubuntu ubuntu 166 Mar 25 02:44 strlen.c
-rw-rw-r-- 1 ubuntu ubuntu 1464 Mar 25 02:47 strlen.o
-rw-rw-r-- 1 ubuntu ubuntu 162 Mar 25 02:44 holberton.h
#As you can see, each .c file is now associated with a .o file, which are your object code files.
Ok, we’ve got all our
.o object files, now we need to make the static library. How do we do that? We need to archive them using the
ar command. The
ar command creates, modifies and extract archives (static libraries), also know as the “archiver”, to archive the object files. For example:
#I am in my Project_Ramen
#ls into my directory which contains my .c files, .o files as well as my header
abs.c abs.o isupper.c isupper.o _putchar.c _putchar.o strlen.c strlen.o holberton.h
#Using the ar command to create your static library called libholberton.a
$ ar -rc libholberton.a *.o
#Overview of the options with the ar command
1. -r --> option to insert the files into the archive
2. -c --> option to create the archive silently. Without the -c option, an informational message is written in standard error when using ar.
#The command above is telling the computer to create a archive called libholberton.a with all the .o files in the current directory.
We’ve now created the static library
libholberton.a containing all the programs in our project! Now what if we want to use the static library?
Use your Static Library
Before we use our static library, let’s check to see what’s in there:
#Using ar command with option -t to see what files my libholberton.a library includes
$ ar -t libholberton.a
#Remember we created our static library by archiving our .o files? As you can see, when we list the archive, you can see all the object files.
#If you want to display the symbolic information in your library, you can use the nm command like so:
$ nm libholberton.h
#Your output would be something like this:
0000000000000000 T abs
0000000000000000 T isupper
0000000000000000 T _putchar
0000000000000000 T strlen
#The 0’s are hex digits to show you the number of bytes of each program. I am running Ubuntu Linux 64-bit, which is why the 8 digit hex is 16. To check the output, you can do objdump -td abs.o.
After checking out the contents of the static library, let’s try to run it:
#Before we run the library, we need to index it. Why? The index is "used by the compiler to speed up symbol-lookup inside the library and to make sure the order of the symbols won't matter during compilation". - docencia
#To create the index, we will use the ranlib command
$ ranlib libholberton.a
#Depending on some systems, indexing may be already done by the archiver (not necessary with ar).
#The ranlib command generates an index to the contents of an archive and stores it in the archive. The index lists each symbol defined by a member of an archive that is a relocatable object file. - ranlib manual page.
#After indexing, let's run it using the gcc command on a main.c file.
c = 'A';
printf("%c: %d\n", c, _isupper(c));
c = 'a';
printf("%c: %d\n", c, _isupper(c));
#gcc command on main.c
gcc main.c -L. -lholberton -o isupper
#Compiling the main.c file with the static library holberton and outputting it into the executable isupper.
1. -L --> option to tell gcc to search for a library in the specified directory
2. -l --> option to tell gcc to search in a specific library. In this case, holberton. We don't need to add "lib" or ".a" since the -l option shortens the name and automatically prefixes "lib" and ".a". l for lib and holberton for the library's name
3. -o --> output the executable to a specific name, isupper
#Running the isupper program
#We've successfully ran the isupper program on our main.c file.