Libraries workflows

A mention to the GNU C Compiler (gcc) is worthwhile. Therefore we few basic source file to explain few concepts.

Preprocessor directives

Preprocessor directives for libraries are used to make the code base aware of additional external code.

#include <mylib.h>

// use your library functions here

These lines are not program statements but directives for the preprocessor. The preprocessor examines the code before actual compilation of code begins and resolves all these directives before any code is actually generated by regular statements.

Source files

Create a touch ~/add.c file such as:

#include "add.h"

double add(double a, double b) {
	return(a + b)
}

With an associated header file:

echo "double add(double x, double y);" > ~/add.h

and a main source file main.c like

#include <stdio.h>
#include "add.h"

double addition_result = add(double 1., double 2.)
printf("The additon result is: %d \n", addition_result);

Compilation procedure

Compilers first transform all the program's source code files into object .o files. The compiler then links the compiled object files into an executable file.

An example is shown below that address the compiler to include header files at the set location (-I), libraries names (-l) located at a give location (-L)

gcc path/to/source.cpp -o ouputName -Ipath/to/header/directory -Lpath/to/library -llibrary_name

.o - Object file generation

An .o file is a compiled C program object. Some C compilers create .o files during the executable creation process. .o files themselves are typically not executable.

Create and use a static library

Then treat ~/add.c as a source file of the library:

gcc -c add.cpp -o add.o

The -c flag is indicating that you want to compile but not link the file to anything else.

Wrap the object as a static library.

In Linux/GNU systems, it involves using the ar application (an archive utility tool) to create the static library file (.a : archive file) such as:

ar -cr libmymath.a add.o

The -cr flag is to indicate creating a new static library file.

Notice the name of output is “libmymath.a”. It is a convention to name the file libXXX.a. The compiler relies on this convention for the linker to work properly.

Compile the main c file into an object file as follwows and link it to the static library to create final executable with the library embedded into it:

gcc main.cpp libmymath.a -o final_executable

# A more explicit and preferred  way to link a library is:
gcc main.cpp -L. -lmymath

The flag -L is used to explicitly specify the library path while -l the library name, as convetion the lib prefix is expressed in this way.

Delete the library for verification

We can verify that the library has been copied into the executable by deleting the library and running:

rm lib_mymath.a 
./a.out

Create and use a dynamic library

Similar to static library, create an object file:

gcc -c my_math.cpp -o my_math.o

Then create a shared object out of it

gcc -shared -o libmymath.so my_math.o

and either move the .so file into the library path, or enlarge the existing one:

export LD_LIBRARY_PATH="/path/to/your/project/root:$LD_LIBRARY_PATH"

Create the executable:

gcc main.o -L. -lmymath -o final_executable

Position independent code

In this example:

mylibrary.so is the name of the output shared library. mysource1.c is the source file you want to compile.

gcc -shared -fPIC -o ouputName.so mysource1.c -lmylib -L/path/to/library

The first two flags stands for:

  • shared: instructs GCC to create a shared library instead of an executable.
  • fPIC: This stands for "Position Independent Code."

When creating shared libraries, it's crucial to generate position-independent code because the library can be loaded into any part of the address space at runtime. -fPIC ensures that the generated code does not depend on a fixed memory location, making it suitable for dynamic linking.