[2026] C++ Linking Explained: Static vs Dynamic Libraries, Symbols, and LTO
이 글의 핵심
How the linker combines object files into executables and libraries. Static vs dynamic linking, undefined reference fixes, -L/-l order, rpath, nm, ldd, and LTO basics.
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
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 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: 다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 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
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
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)
아래 코드는 makefile를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
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. |