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

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

# 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).


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

  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.


  • C++ Compilation Process
  • C++ CRTP
  • C++ Dynamic Initialization
  • C++ static initialization order
  • C++ Initialization Order