C++ Linking Explained: Static vs Dynamic Libraries, Symbols, and LTO
이 글의 핵심
Linking joins .o files into executables or libraries. Covers symbol resolution, relocation, static (.a) vs shared (.so/.dll), and common linker errors.
Introduction
Linking combines object files (.o) into an executable or library—the last stage of the build. The compiler turns each .cpp into machine code independently; the linker connects them into the final image.
1. Linking basics
Compile vs link
# 1) Compile only (no link)
g++ -c main.cpp -o main.o
g++ -c util.cpp -o util.o
# 2) Link objects into executable
g++ main.o util.o -o myapp
# Or one step (compile + link)
g++ main.cpp util.cpp -o myapp
Layout:
// util.h
#pragma once
int add(int a, int b);
// util.cpp
#include "util.h"
int add(int a, int b) {
return a + b;
}
// main.cpp
#include <iostream>
#include "util.h"
int main() {
std::cout << add(10, 20) << std::endl;
return 0;
}
What the linker does
1. Symbol resolution — connect call sites to definitions
2. Relocation — fix addresses/offsets for the final layout
3. Final image — produce executable or .so/.dll
2. Static linking
Building a static library
g++ -c lib.cpp -o lib.o
ar rcs libmylib.a lib.o
g++ main.cpp -L. -lmylib -o myapp
Traits:
- Simple deployment (single binary option)
- No runtime library load
- Larger binary
- Library updates require relink
3. Dynamic linking
Linux/macOS shared libraries
g++ -fPIC -c lib.cpp -o lib.o
g++ -shared lib.o -o libmylib.so
g++ main.cpp -L. -lmylib -Wl,-rpath,. -o myapp
./myapp
# Or use LD_LIBRARY_PATH
LD_LIBRARY_PATH=. ./myapp
Windows DLL (conceptual)
g++ -shared lib.cpp -o mylib.dll
g++ main.cpp -L. -lmylib -o myapp.exe
Traits:
- Smaller binaries; shared memory across processes
- Runtime loader must find the library
- Versioning and deployment are trickier
4. Common problems
Problem 1: undefined reference
undefined reference to `add(int, int)'
Fix: add the .o/.cpp or -l library that defines the symbol.
Problem 2: Library order
Dependencies go after dependents on many Unix linkers:
g++ main.o -lA -lB # if main needs A and A needs B, order may matter
Problem 3: Library search path
g++ main.o -L./lib -lmylib -o myapp
Problem 4: Shared library not found at run time
g++ main.o -L./lib -lmylib -Wl,-rpath,./lib -o myapp
# or LD_LIBRARY_PATH, or install to system lib path + ldconfig
5. Inspecting symbols
nm
Lists symbols in objects or executables. T = defined in text, U = undefined.
ldd (Linux)
Lists shared library dependencies of an executable.
objdump
Disassembly (-d), symbol tables (-t), dynamic symbols (-T).
6. Link-time optimization (LTO)
g++ -flto main.cpp util.cpp -o myapp
g++ -flto -O3 main.cpp util.cpp -o myapp
Effects: whole-program optimization across TUs; longer builds; harder debugging.
7. Build examples
Makefile (sketch)
CXX = g++
CXXFLAGS = -std=c++17 -Wall -g
OBJS = main.o util.o
myapp: $(OBJS)
$(CXX) $(OBJS) -o myapp
CMake (sketch)
add_library(mylib STATIC lib.cpp)
add_executable(myapp main.cpp util.cpp)
target_link_libraries(myapp mylib)
Summary
- Linking joins object files.
- Static bundles code; dynamic references shared libs.
- Symbol resolution matches calls to definitions.
- LTO can improve performance at build cost.
Static vs dynamic
| Static | Dynamic | |
|---|---|---|
| Files | .a / .lib | .so / .dll |
| Size | Larger exe | Smaller exe |
| Speed | No load step | Load at startup |
| Updates | Relink | Often replace .so only |
Next: Name mangling, Compilation process, Makefile.
Related posts
- C++ Compilation Process
- C++ CRTP
- C++ Dynamic Initialization
- C++ static initialization order
- C++ Initialization Order