[2026] C++ Linking Explained: Static vs Dynamic Libraries, Symbols, and LTO

[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

아래 코드는 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).

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

  1. Linking joins object files.
  2. Static bundles code; dynamic references shared libs.
  3. Symbol resolution matches calls to definitions.
  4. LTO can improve performance at build cost.

Static vs dynamic

StaticDynamic
Files.a / .lib.so / .dll
SizeLarger exeSmaller exe
SpeedNo load stepLoad at startup
UpdatesRelinkOften replace .so only
Next: Name mangling, Compilation process, Makefile.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3