Beginner’s guide to Creating Static and Shared Libraries in Linux. Learn differences, build steps with GCC, and when to use each in real projects.
You’ve just finished writing a small program in C. It works fine, but then your friend asks, “Hey, can I also use that add() function you wrote?” You copy-paste it into their code. Then another friend asks. Before you know it, you have the same function floating around in three, four, maybe ten different projects. Every time you fix a bug, you have to update it everywhere.
That’s when you realize — “There has to be a better way.”
This is exactly why libraries exist. Instead of duplicating code across projects, you can put your reusable logic into one neat package and simply “plug it in” whenever you need it. In Linux, these packages come in two flavors: static libraries and shared libraries. Both solve the duplication problem, but they work in very different ways — one bakes the code directly into your program, the other stays outside as a separate file that your program borrows at runtime.
In this guide, we’ll walk step by step through creating both types of libraries, linking them with a program, and understanding when to use which — all explained in plain, beginner-friendly language with real commands you can try out right now.
Static and Shared Libraries in Linux
What is a library?
A library is a collection of precompiled code (functions, classes, etc.) that programs can use.
- Static library (.a): The code is copied into the final executable at link time. Result: bigger executable, no runtime dependency on that library file.
- Shared (dynamic) library (.so): Code stays in a separate file and is linked at runtime (or sometimes at link time referencing a shared object). Result: smaller executables, multiple programs share the same loaded code, updates to the library can affect all programs that use it.
Why use libraries?
- Reusability: Put common code in a library and reuse it across projects.
- Memory & disk efficiency (shared libs): multiple processes use the same code in memory.
- Updatability (shared libs): fix a bug in one library file and many programs benefit (if ABI-compatible).
- Distribution: ship stable APIs to other developers via headers + library files.
Tradeoffs:
- Static: simpler distribution (no runtime .so load problems) but larger binaries and harder to update.
- Shared: smaller binaries and easier updates, but you must manage ABI compatibility and runtime search paths.
A Tiny Example Project of Static and Shared Libraries in Linux
We’ll implement a tiny add() function in libadd and use it in main.
Files:
add.h— headeradd.c— library implementationmain.c— program using the library
add.h
#ifndef ADD_H
#define ADD_H
int add(int a, int b);
#endif // ADD_H
add.c
#include "add.h"
int add(int a, int b) {
return a + b;
}
main.c
#include
#include "add.h"
int main(void) {
printf("2 + 3 = %d\n", add(2, 3));
return 0;
}
Place these files in one directory for following commands.
Create a static library (.a)
Steps:
- Compile
add.cto an object file:
gcc -c add.c -o add.o
- Create the static archive:
ar rcs libadd.a add.o
# or: ar rcu libadd.a add.o && ranlib libadd.a
ar rcsadds the object and builds an index.ranlibis sometimes used to create/refresh the index; modernar rcsusually does it already.
- Link
mainwith the static library (explicit .a is simple & unambiguous):
gcc main.c ./libadd.a -o main_static
(You can also use -L. -ladd, but if libadd.so exists the linker might pick the .so instead.)
- Run:
./main_static
# Output: 2 + 3 = 5
Notes
- Static linking copies the needed code into
main_static. The binary is self-contained (nolibadd.sorequired at runtime). - Order matters: object files (or
main.c) must come before libraries on the command line if you use-lstyle.
Create a shared library (.so)
Shared libraries require position-independent code (PIC) on many platforms (e.g., x86_64).
- Compile with
-fPIC:
gcc -fPIC -c add.c -o add.o
- Create a versioned shared library and set a SONAME:
gcc -shared -Wl,-soname,libadd.so.1 -o libadd.so.1.0.0 add.o
- Create the conventional symlinks (so compilers/linkers find
libadd.soand run-time uses SONAME):
ln -s libadd.so.1.0.0 libadd.so.1
ln -s libadd.so.1 libadd.so
Now you have:
libadd.so.1.0.0— actual filelibadd.so.1→libadd.so.1.0.0libadd.so→libadd.so.1
Why SONAME?
- The ELF SONAME (set with
-soname,) is embedded inside the.so. Programs record the SONAME they were linked against (e.g.,libadd.so.1). This helps manage ABI compatibility and versioning.
- Link
mainagainst the shared lib:
gcc main.c -L. -ladd -o main_shared
- Run it:
By default, the dynamic loader searches standard locations (/lib,/usr/lib,/usr/local/lib) — our.directory is not standard. Options:
- Quick (not recommended for production):
LD_LIBRARY_PATH=. ./main_shared
- Better: set rpath at link time so the binary knows where to look:
gcc main.c -L. -ladd -Wl,-rpath,'$ORIGIN' -o main_shared_rpath
# $ORIGIN tells the loader to look in the directory containing the binary
- Install system-wide:
sudo cp libadd.so.1.0.0 /usr/local/lib/
sudo cp add.h /usr/local/include/
sudo ldconfig
# then you can run ./main_shared (no LD_LIBRARY_PATH)
- Inspect dependencies:
ldd ./main_shared
# shows which shared libs are required and where they're found
Useful inspection tools
ldd— show shared library dependencies and where loader finds them.readelf -d libadd.so.1.0.0 | grep SONAME— inspect SONAME.nm -C libadd.a— list symbols in a static archive.-Cdemangles C++ names.nm -D --defined-only libadd.so.1.0.0— list dynamic symbols defined by a shared library.objdump -p libadd.so.1.0.0— show dynamic section.file libadd.so.1.0.0— show file type and architecture.
Linking details & common gotchas
- Order when using
-l: Put the object files or source first, libraries after:gcc main.c -L. -ladd -o appIf you use-lstyle, the linker resolves references left-to-right; unresolved references in earlier objects must be satisfied by later libraries. - Forcing static/dynamic:
- To force using the static lib for a specific
-l:-Wl,-Bstatic -ladd -Wl,-Bdynamic(advanced). - To simply use a
.a, put the.afile directly:gcc main.c ./libadd.a -o main_static.
- To force using the static lib for a specific
- Undefined references at link time: Means the linker couldn’t find the function implementation. Check you linked the proper library and that the library defines the symbol (use
nmto check). - “cannot open shared object file: No such file or directory” at runtime:
- Run
lddto see “not found”. - Use
LD_LIBRARY_PATH, or install library to/usr/local/liband runldconfig, or embedrpath.
- Run
- ABI breaks: If a shared library changes in an incompatible way, programs linked to the previous ABI may break. That’s why SONAME versioning (e.g.,
.so.1) is important.
Versioning conventions (simple overview)
Common pattern:
- Actual file:
libfoo.so.1.2.3 - SONAME:
libfoo.so.1(ABI major version) - Symlinks:
libfoo.so.1.2.3 libfoo.so.1 -> libfoo.so.1.2.3 libfoo.so -> libfoo.so.1 - If ABI changes incompatibly, bump the major SONAME (e.g.,
libfoo.so.2) so old programs still link tolibfoo.so.1.
Quick command cheat-sheet
Compile object:
gcc -c add.c -o add.o
Static library:
ar rcs libadd.a add.o
gcc main.c ./libadd.a -o main_static
Shared library:
gcc -fPIC -c add.c -o add.o
gcc -shared -Wl,-soname,libadd.so.1 -o libadd.so.1.0.0 add.o
ln -s libadd.so.1.0.0 libadd.so.1
ln -s libadd.so.1 libadd.so
gcc main.c -L. -ladd -o main_shared
LD_LIBRARY_PATH=. ./main_shared
Install to system:
sudo cp libadd.so.1.0.0 /usr/local/lib/
sudo cp add.h /usr/local/include/
sudo ldconfig
Inspect:
ldd ./main_shared
nm -C libadd.a
readelf -d libadd.so.1.0.0 | grep SONAME
Best practices & tips
- Build shared libraries with
-fPIC. - Use SONAME to manage ABI compatibility. Change SONAME when you break the API/ABI.
- Prefer versioned
.sofiles and proper symlink structure. - Keep header files stable and document the API.
- Use
pkg-configfor complex libraries so downstream build systems can find include/lib flags automatically. - For C++ libraries, pay attention to name mangling and symbol visibility (
-fvisibility=hidden) to avoid leaking internal symbols. - Use
rpathor$ORIGINcarefully —$ORIGINis great for relocatable installs but mind security and packaging implications.
Summary & next steps
You now know:
- The conceptual difference between static (.a) and shared (.so) libraries.
- How to create both with
gcc,ar, andln. - How to link programs to them and how to handle runtime lookup (
LD_LIBRARY_PATH,rpath,ldconfig). - Tools to inspect libraries (
ldd,nm,readelf).
Next steps I suggest:
- Try the example above and experiment with removing
.aor.soto see linker/loader errors. - Learn
pkg-configand create a.pcfile for your library. - Explore building libraries with
makeorCMakefor bigger projects.
Applications, Advantages & Disadvantages of Static & Shared Libraries in Linux
Applications of Static & Shared Libraries in Linux
- Static Libraries
- Embedded systems where self-contained executables are preferred.
- Small utilities that must run without external dependencies.
- Distributing software in environments without package managers.
- Shared Libraries
- Large software projects (e.g., browsers, databases) where multiple executables use the same code.
- Operating system components like
glibcorlibpthread. - Applications that need frequent updates without recompiling everything.
Advantages of Static Libraries
- No dependency issues at runtime (everything is inside the executable).
- Easier to distribute as a single binary.
- Faster loading since no dynamic linking is needed at startup.
- Good for embedded/portable applications.
Disadvantages of Static Libraries
- Executable size is larger.
- Updating the library requires recompiling all dependent programs.
- Memory usage increases if multiple processes use the same static code.
Advantages of Shared Libraries
- Saves disk space and memory (multiple programs share one library).
- Easier to update and patch — only update the
.sofile. - Smaller executables since code is not duplicated.
- Widely used in Linux distributions for system and user applications.
Disadvantages of Shared Libraries
- Runtime dependency — if the
.sofile is missing or incompatible, the program fails. - Slightly slower startup due to dynamic linking.
- ABI compatibility must be maintained, or programs may break.
- Managing library paths (
LD_LIBRARY_PATH,rpath) can be tricky for beginners.
Interview Questions on Creating Static & Shared Libraries in Linux
Basic Questions (for beginners)
- What is a static library in Linux?
- What is a shared (dynamic) library in Linux?
- How do you create a static library using
gcc? - How do you create a shared library using
gcc? - What are the file extensions for static and shared libraries in Linux?
- What is the difference between static and shared libraries?
- Which command is used to list shared library dependencies of a program?
- Where are libraries stored in a Linux system?
- What is the difference between
.aand.sofiles? - Why do we use libraries instead of copy-pasting code?
Intermediate Questions (hands-on knowledge)
- How do you link a static library while compiling a program?
- How do you link a shared library while compiling a program?
- What is the role of
arin static library creation? - What is position-independent code (PIC) and why is it required for shared libraries?
- What is the role of the
-fPICflag in gcc? - What does the
-sharedoption in gcc do? - What are SONAME and RPATH in the context of shared libraries?
- How do you update the library path so that Linux can find your shared library?
- What does
LD_LIBRARY_PATHdo? - How do you check which shared libraries a binary depends on?
Advanced Questions (conceptual & real-world)
- What are the advantages and disadvantages of static libraries?
- What are the advantages and disadvantages of shared libraries?
- Which library type is better for embedded systems? Why?
- How does Linux dynamic linker (
ld.so) work with shared libraries? - How does versioning work in shared libraries?
- What happens if a shared library version changes but the SONAME stays the same?
- How do package managers like
aptoryumhandle shared libraries? - What is the difference between compile-time linking and run-time linking?
- Can we mix static and shared libraries in the same program?
- In a production environment, when would you prefer static linking over shared linking?
FAQ: Creating Static & Shared Libraries in Linux
Q1: What are static and shared libraries in Linux?
Answer: Static libraries are .a files linked at compile time, while shared libraries are .so files linked at runtime.
Q2: How do I create a static library in Linux?
Answer: Compile with gcc -c file.c to make object files, then use ar rcs libname.a file.o to create the static library.
Q3: How do I create a shared library in Linux?
Answer: Compile with gcc -fPIC -c file.c for position-independent code, then gcc -shared -o libname.so file.o to build the shared library.
Q4: What is the main difference between static and shared libraries?
Answer: Static libraries increase binary size but are self-contained, while shared libraries keep binaries smaller and reusable across programs.
Q5: Why use static libraries in Linux?
Answer: Static libraries make deployment simple because no external .so files are required at runtime — everything is inside the executable.
Q6: Why use shared libraries in Linux?
Answer: Shared libraries save memory and disk space, allow multiple programs to share the same code, and make updates easier without recompiling executables.
Q7: How can I check which shared libraries a program uses in Linux?
Answer: Use the ldd program_name command to list all dynamic libraries linked to the program.
Q8: What is the role of SONAME in shared libraries?
Answer: SONAME ensures versioning in shared libraries. Programs record the SONAME they are built against, helping manage compatibility between updates.
Q9: How can I force GCC to use a static library instead of a shared one?
Answer: Use -Wl,-Bstatic -lname -Wl,-Bdynamic when linking, or link directly with the .a file.
Q10: Which is better for beginners: static or shared libraries in Linux?
Answer: Beginners often start with static libraries for simplicity, then move to shared libraries once they need flexibility, smaller binaries, and easier updates.
You can also Visit other tutorials of Embedded Prep
- Multithreading in C++
- Multithreading Interview Questions
- Multithreading in Operating System
- Multithreading in Java
- POSIX Threads pthread Beginner’s Guide in C/C++
- Speed Up Code using Multithreading
- Limitations of Multithreading
- Common Issues in Multithreading
- Multithreading Program with One Thread for Addition and One for Multiplication
- Advantage of Multithreading
- Disadvantages of Multithreading
- Applications of Multithreading: How Multithreading Makes Modern Software Faster and Smarter”
- Master CAN Bus Interview Questions 2025
- What Does CAN Stand For in CAN Bus?
- CAN Bus Message Filtering Explained
- CAN Bus Communication Between Nodes With Different Bit Rates
- How Does CAN Bus Handle Message Collisions
- Message Priority Using Identifiers in CAN Protocol

Mr. Raj Kumar is a highly experienced Technical Content Engineer with 7 years of dedicated expertise in the intricate field of embedded systems. At Embedded Prep, Raj is at the forefront of creating and curating high-quality technical content designed to educate and empower aspiring and seasoned professionals in the embedded domain.
Throughout his career, Raj has honed a unique skill set that bridges the gap between deep technical understanding and effective communication. His work encompasses a wide range of educational materials, including in-depth tutorials, practical guides, course modules, and insightful articles focused on embedded hardware and software solutions. He possesses a strong grasp of embedded architectures, microcontrollers, real-time operating systems (RTOS), firmware development, and various communication protocols relevant to the embedded industry.
Raj is adept at collaborating closely with subject matter experts, engineers, and instructional designers to ensure the accuracy, completeness, and pedagogical effectiveness of the content. His meticulous attention to detail and commitment to clarity are instrumental in transforming complex embedded concepts into easily digestible and engaging learning experiences. At Embedded Prep, he plays a crucial role in building a robust knowledge base that helps learners master the complexities of embedded technologies.











I’ve read a few just right stuff here. Certainly price bookmarking for revisiting. I wonder how so much attempt you set to create the sort of excellent informative website.