Creating a dynamic library in C

Aug 12, 2022 by Charles Beumier | 10148 views

C

https://cylab.be/blog/234/creating-a-dynamic-library-in-c

C libraries are handy for distribution of programming work or for structuring a large development into units. In the specific case of Python programming, a C dynamic library can be a solution for a missing Python equivalent or if performance in execution time is a concern. We explain hereunder with an example the steps necessary to write a dynamic (shared) C library in Linux (tested on Ubuntu20.04). Another blog describes how to call a C dynamic library from Python ([https://cylab.be/blog/235/calling-c-from-python]).

C_Library.png

1. Dynamic and static libraries

A library is a collection of object files grouped into one unit to ease programming work distribution or to structure software development into units. You can create two kinds of C libraries.

A static library is a file (with extension .a) meant to be attached to a compiled object file to form an executable file. This latter contains all the code necessary at runtime. On the contrary, a dynamic library (also named 'shared library', with extension .so) is not part of an executable file linked with it. Executable files with dynamic libraries are thus smaller and the same dynamic library can be reused (shared) by several running programs. Such programs map the dynamic library code at some a priori different virtual address, so that the library code must avoid absolute addressing and must be 'position independent'.

2. Why creating a C dynamic library ?

As just said, a dynamic library is not added to executable files, what represents a gain in memory for each program using the library. Also, the loading time at execution is reduced if the library is already loaded. Finally, shared libraries can be accessed from Python, with little additional work to provide for function prototyping (argument and result types). This last point has motivated this blog and another one to come: 'Calling C From Python'.

However, a dynamic library may suffer some performance penalty compared to a static library since addressing may not be optimal. Much more, the modification of a dynamic library may lead to incompatibilities of the existing executable files calling it. Executable should be adapted and recompiled.

3. Building a C dynamic library

Compile the .c source files to create (machine) object codes (.o) with a command like:

gcc -c -fPIC fct.c

The -fPIC flag stands for 'Position Independent Code' generation: the code contained in the .o file is then valid whatever the virtual address occupied at runtime. This is needed for shared libraries since it is impossible to know at which address the shared library will be.

Finally, build the library (here libFct.so) from all .o with the 'shared' flag:

gcc -shared -o libFct.so fct.o

With the gcc compiler, the 'Position Independent Code' option is set by default, so gcc -c fct.c suffices. But if you launch gcc -c -fno-PIC fct.c, the code file fct.o will be position dependent and the command gcc -shared will complain and require to add the -fPIC flag.

4. Calling the C dynamic library (from C)

Let us assume the file call_fct.c is the C source file calling some functions of the libFct.so library (an example with source code is presented in section 7).

To create the executable, you first compile the source file:

gcc -c call_fct.c

and then link the created code call_fct.o with the library:

gcc -o call_dynamic call_fct.o -L./ -lFct

The flag '-L' indicates where the library is to be found and the flag '-l' specifies the library, without the prepending 'lib' and file extension '.so'.

The executable 'call_dynamic' is ready for a run.

5. Running a C executable linked to a .so file

Running the 'call_dynamic' program should be 'call_dynamic' or './call_dynamic' if '.' (the current directory where 'call_dynamic' is) is not in the PATH variable.

But the shared library will not be found at runtime if its directory is not in the variable LD_LIBRARY_PATH (probably the case by default). Type the following line in the command shell:

if sh or bash shell:         export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
if csh or tcsh shell:        setenv LD_LIBRARY_PATH .:$LD_LIBRARY_PATH

In this example the local directory ('.') is added to the search. Mention in the first case that there is no space around the '=' sign.

The new path list will be effective only in the shell where you applied the command. If you want the path to be set automatically, copy the line in your shell startup script of your home directory (e.g. ~/.bashrc if you use a bash shell).

6. Makefile

Several compiler commands are necessary to compile the C files, to create the library and to link a program with the library. We propose the following 'Makefile' to perform one of these actions at a time.

--- Makefile ------------------------------------------------------------------

# What is printed when typing 'make'
default:
    @echo \'make Lib\' to create the shared lib libFct.so
    @echo \'make ByLib\' to create call_dynamic, using shared lib
    @echo \'make Prog\' to create exec 'prog' (standalone)
    @echo \'make Clean\' to remove all .o, exec call, call_dynamic and libFct.so

# 'make Lib': Create the shared library (need Position Indep Code)
Lib:    fct.c
    gcc -fPIC -c fct.c
    gcc -shared -o libFct.so fct.o

# 'make ByLib': Create exec 'call_dynamic' with shared lib (not contained)
# in shell or in ~/.bashrc: export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
ByLib:  call_fct.c libFct.so
    gcc -c call_fct.c
    gcc -o call_dynamic call_fct.o -L./ -lFct

# 'make Prog': Create a standalone 'prog' containing fct.o: './prog' to run
# This does not use the shared lib
Call: call_fct.c fct.c
    gcc -c fct.c
    gcc -c call_fct.c
    gcc -o prog fct.o call_fct.o

# 'make Clean': remove exec, .o and .so
Clean:
    rm *.o libFct.so call_dynamic prog

By default, so when typing 'make' (in the directory where Makefile is), a list of make commands is printed thanks to @echo. It is a reminder of all possible options that we offered in the makefile.

The command 'make Lib' compiles fct.c into fct.o and creates the dynamic library 'libFct.so'. Details are given in section 3.

The command 'make ByLib' creates the executable call_dynamic that will use the dynamic library at runtime, as described in section 4.

Type 'make Call' to create the standalone program 'prog' that contains all the code (no need for the shared library). 'prog' file is larger than 'call_dynamic', but the difference is not big in the given example because the code is really small.

7. Source code example

We give in this section the source code of a C dynamic library made of fct.c and a calling program (fct_call.c). For simplicity and compactness, we dealt with only one file for the library (fct.c) and avoided a corresponding include file (fct.h).

--- fct.c ------------------------------------------------------------------------

// An example of functions for a dynamic library

#include <string.h>
#include <stdio.h>

// Open file 'filename'. Return file descriptor. Return name length by arg
FILE *FileOpen(char *filename, int *name_len)
{
    FILE *fd = fopen(filename, "rt");
    if (fd == NULL) { printf("Bad open '%s'
", filename); return(NULL); }
    *name_len = strlen(filename);
    return(fd);
}

// Give size of file specified by fd (file descriptor)
int FileSize(FILE *fd)
{
    fseek(fd, 0, SEEK_END); // Position file pointer at end of file
    return( ftell(fd) );    // return position in file (last char)
}

----- call_fct.c --------------------------------------------------------------

// Example calling functions of fct.c. (fct.h not created for compactness)
# include <stdio.h>

extern FILE *FileOpen(char *filename, int *name_len);
extern int   FileSize(FILE *fd);

int main(int argc, char *argv[])
{
    char *filename = "file.txt";
    int  name_len = 0;

    // We show that void *fd (instead of FILE *) compiles and works
    // We'll use this trick in Python (FILE not needed and too long to create)
    void *fd = FileOpen(filename, &name_len);
    printf("File '%s': name length = %d
", filename, name_len);

    int  size = FileSize(fd);
    printf("File size = %d
", size);
}

Final words

We have presented the details and a source code example for the creation of a dynamic C library. Apart from the objective to structure or distribute your development work, a C dynamic library can be accessed from Python, as explained in [https://cylab.be/blog/235/calling-c-from-python].

This blog post is licensed under CC BY-SA 4.0