Table of Contents
KEY TAKEAWAYS
- Static libraries (
.a/.lib) are linked at compile time and become part of the executable - Dynamic/shared libraries (
.so/.dll) are loaded at runtime and shared between programs - Static linking produces larger but self-contained executables; dynamic linking saves disk space and memory
- Create static libraries with
arand dynamic libraries withgcc -shared
What are Libraries?
A library in C is a collection of pre-compiled functions and code that you can reuse in your programs. Instead of writing everything from scratch, you link against libraries that provide common functionality.For example, when you useprintf(), you are using a function from the C standard library (libc). You did not write printf yourself. It was compiled into a library file, and the linker connects your program to it.Libraries come in two types:- Static libraries (.a on Linux/macOS, .lib on Windows)
- Dynamic (shared) libraries (.so on Linux, .dylib on macOS, .dll on Windows)
Static Libraries
A static library is an archive of compiled object files (.o files). When you link your program with a static library, the linker copies the required code from the library directly into your final executable. Source Files Static Library Executable
+--------+ +----------+ +-----------+
| main.c | --compile--> | | |
+--------+ | libmath.a|--link--> | my_program|
| app.c | --compile--> | | (contains |
+--------+ +----------+ | all code) |
+-----------+Advantages of Static Libraries
- Self-contained: The executable contains everything it needs. No external dependencies at runtime.
- Faster execution: No overhead from loading external libraries at startup.
- Deterministic: The behavior does not change if system libraries are updated.
- Ideal for embedded: Microcontrollers typically use static linking because there is no OS to manage shared libraries.
Disadvantages of Static Libraries
- Larger executable size: Every program includes its own copy of the library code.
- No shared memory: If 5 programs use the same library, the code exists 5 times in memory.
- Recompilation needed: If the library is updated, every program using it must be relinked.
Creating a Static Library
Let us create a simple math library:mymath.h:#ifndef MYMATH_H #define MYMATH_H int add(int a, int b); int subtract(int a, int b); int multiply(int a, int b); float divide(int a, int b); #endifmymath.c:
#include "mymath.h"
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int multiply(int a, int b) {
return a * b;
}
float divide(int a, int b) {
if (b == 0) return 0.0f;
return (float)a / (float)b;
}Step 1: Compile the source to an object file:gcc -c mymath.c -o mymath.oStep 2: Create the static library using
ar (archiver):ar rcs libmymath.a mymath.oThe flags mean:
r– Insert files into the archive (replace if exists)c– Create the archive if it does not exists– Write an index (for faster linking)
#include <stdio.h>
#include "mymath.h"
int main() {
printf("5 + 3 = %d\n", add(5, 3));
printf("5 - 3 = %d\n", subtract(5, 3));
printf("5 * 3 = %d\n", multiply(5, 3));
printf("5 / 3 = %.2f\n", divide(5, 3));
return 0;
}Compile and link:gcc main.c -L. -lmymath -o calculatorThe flags mean:
-L.– Look for libraries in the current directory-lmymath– Link with libmymath.a (the “lib” prefix and “.a” suffix are added automatically)
Dynamic (Shared) Libraries
A dynamic library (also called a shared library) is loaded at runtime rather than being copied into the executable at compile time. The executable contains only a reference to the library, and the operating system loads the library into memory when the program starts. Executable Shared Library (in memory)
+-----------+ +------------+
| my_program|---ref--->| libmath.so |
| (small) | | (loaded |
+-----------+ | at runtime)|
+------------+
^
+-----------+ |
| other_prog|---ref---------+ (shares same copy)
+-----------+Advantages of Dynamic Libraries
- Smaller executables: The library code is not copied into the executable.
- Shared memory: Multiple programs can share a single copy of the library in memory.
- Update without recompilation: You can update the library without relinking the programs that use it.
- Plugin systems: Load and unload libraries dynamically at runtime.
Disadvantages of Dynamic Libraries
- Runtime dependency: The library must be present on the system. Missing library = program fails to start.
- Version conflicts: Different programs may need different versions of the same library (“DLL hell”).
- Slightly slower startup: The OS must locate and load the library at program start.
- Not suitable for bare-metal embedded: Requires an OS with a dynamic linker.
Creating a Dynamic Library
Using the same mymath source files:Step 1: Compile with position-independent code (PIC):gcc -c -fPIC mymath.c -o mymath.oThe
-fPIC flag generates code that can be loaded at any memory address, which is required for shared libraries.Step 2: Create the shared library:gcc -shared -o libmymath.so mymath.oStep 3: Compile and link your program:
gcc main.c -L. -lmymath -o calculatorStep 4: Run the program (Linux):
# Tell the loader where to find the library export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./calculatorOr install the library system-wide:
sudo cp libmymath.so /usr/local/lib/ sudo ldconfig
Loading Dynamic Libraries at Runtime
You can also load shared libraries programmatically usingdlopen():#include <stdio.h>
#include <dlfcn.h>
int main() {
// Load the library
void *handle = dlopen("./libmymath.so", RTLD_LAZY);
if (!handle) {
printf("Error: %s\n", dlerror());
return 1;
}
// Get function pointer
int (*add_func)(int, int) = dlsym(handle, "add");
if (!add_func) {
printf("Error: %s\n", dlerror());
dlclose(handle);
return 1;
}
// Use the function
printf("5 + 3 = %d\n", add_func(5, 3));
// Unload the library
dlclose(handle);
return 0;
}Compile with:gcc main.c -ldl -o calculatorThis pattern is used in plugin systems where you do not know at compile time which libraries will be loaded.
Static vs Dynamic: Comparison
| Feature | Static Library (.a / .lib) | Dynamic Library (.so / .dll) |
|---|---|---|
| Linking time | Compile time | Runtime |
| Executable size | Larger (includes library code) | Smaller (references only) |
| Memory usage | Each program has its own copy | Shared between programs |
| Dependencies | None (self-contained) | Library must exist at runtime |
| Updates | Must relink all programs | Replace library file only |
| Performance | Slightly faster (no runtime loading) | Slightly slower startup |
| Embedded systems | Standard choice (bare-metal) | Used on Linux-based systems (RPi, etc.) |
| File extension (Linux) | .a | .so |
| File extension (Windows) | .lib | .dll |
When to Use Which
Use static libraries when:- Building for bare-metal embedded systems (no OS)
- You need a fully self-contained executable
- Deterministic behavior is critical (no surprise library updates)
- Distributing a standalone application
- Building for Linux-based embedded systems (Raspberry Pi, BeagleBone)
- Multiple programs share the same library
- You want to update library code without rebuilding everything
- Building a plugin system where modules are loaded on demand
Libraries in Embedded Build Systems
In a typical Makefile for an embedded project, you might use both:# Static library for your own modules STATIC_LIBS = libdrivers.a libprotocol.a # Dynamic libraries (Linux-based embedded only) DYNAMIC_LIBS = -lpthread -lssl # Build static library libdrivers.a: uart.o spi.o gpio.o ar rcs $@ $^ # Link everything firmware: main.o $(STATIC_LIBS) gcc main.o -L. -ldrivers -lprotocol $(DYNAMIC_LIBS) -o firmware
Useful Commands
# List contents of a static library ar -t libmymath.a # List symbols in a library nm libmymath.a # Check which dynamic libraries a program needs ldd ./calculator # Check symbols in a shared library nm -D libmymath.so
Summary
Libraries let you organize, reuse, and share compiled code efficiently:- Static libraries (.a) are archives of object files linked at compile time. The code is copied into your executable. They are the standard choice for bare-metal embedded systems.
- Dynamic libraries (.so/.dll) are loaded at runtime by the operating system. They save memory when multiple programs share the same code. They are used on Linux-based embedded platforms.
- Use
arto create static libraries andgcc -sharedto create dynamic libraries. - Link with
-L(library path) and-l(library name).

Vivek Bhageria — Lead Firmware R&D Engineer, 12+ years. Ex-Bosch (automotive powertrain), MusicTribe (real-time audio), medical devices. M.Tech BITS Pilani. I write at NerdyElectronics — practical, register-level embedded systems for engineers who want to understand what’s actually happening under the hood.







