Building and Managing Static and Dynamic Libraries in C
Static Libraries
A static library essentially functions as an archive of object files (ending in .o). During the linking process, the specific code required from the library is directly embedded into the final executable binary.
Key Characteristics
- Large Disk Footprint: Since the library code is copied into the executable, the resulting binary size increases.
- High Execution Speed: External dependencies are resolved at compile time, allowing for immediate execution.
- Maintenance Overhead: Any updates to the library require the dependent applications to be recompiled and relinked.
Creation Workflow
1. Compile Source to Object Files
First, compile the source code without linking to generate an object file. For example, using a file named math_ops.c:
gcc -c math_ops.c -o math_ops.o
2. Package Objects into a Static Library
Use the ar utility to bundle the object files into a static archive. The convention is to name the library starting with lib and ending with .a:
ar rcs libmathcalc.a math_ops.o
In this command, libmathcalc.a is the output library. The flags rcs stand for replace, create, and write index.
3. Link the Static Library
Compile the main application and link it against the static library using the -L (library path) and -l (library name) flags:
gcc main.c -o app_static -L. -lmathcalc
Here, -L. indicates the linker should look in the current directory, and -lmathcalc references libmathcalc.a.
Dynamic Libraries
Also known as shared objects, dynamic libraries are not copied into the executable during the build phase. Instead, the operating system loads them into memory at runtime, allowing multiple programs to utilize the same library file simultaneously.
Key Characteristics
- Compact Storage: Executables remain small as they do not contain the library code.
- Resource Sharing: A single instance of the library in memory can serve multiple running processes.
- Easy Deployment: Libraries can be updated independently of the applications that use them without requiring recompilation.
Creation Workflow
1. Compile with Position Independent Code (PIC)
To generate code suitable for a shared library, the -fPIC flag is mandatory. This ensures the code can be loaded at any memory address:
gcc -fPIC -c string_util.c -o string_util.o
2. Generate the Shared Object
The -shared flag instructs the compiler to produce a dynamic library (typically with a .so extension):
gcc -shared -o libutils.so string_util.o
This process can be consolidated into a single command:
gcc -fPIC -shared -o libutils.so string_util.c
3. Link the Dynamic Library
The syntax for linking a dynamic library during compilation is similar to that of a static library:
gcc main.c -o app_dynamic -L. -lutils
Runtime Loading Issues
Attempting to run an executable linked against a dynamic library may result in an error similar to:
error while loading shared libraries: libutils.so: cannot open shared object file: No such file or directory
By default, the dynamic linker looks in system directories like /usr/lib or /lib. It does not automatically search the current working directory.
Solution 1: System Directory Installation
Copy the library to a standard system path (requires root privileges):
sudo cp libutils.so /usr/local/lib
Solution 2: Environment Variable Configuration
Set the LD_LIBRARY_PATH to include the directory containing the library. Use the absolute path:
export LD_LIBRARY_PATH=/absolute/path/to/library:$LD_LIBRARY_PATH
To make this change permanent across terminal sessions, append the export command to your shell configuration file:
- Open the configuration file:
vi ~/.bashrc - Add the path to the end of the file.
- Apply the changes immediately:
source ~/.bashrc