
Linkers Lto
- 347 installs
- 155 repo stars
- Updated June 27, 2026
- mohitmishra786/low-level-dev-skills
Configure linker flags and LTO for faster, smaller native binaries in C, C++, and Rust release builds across GCC, Clang, and MSVC toolchains.
About
Guides configuring link-time optimization across common native toolchains so agents can choose correct LTO modes, linker flags, and release settings for smaller, faster CLI and API binaries without guesswork.
- GCC/Clang/MSVC LTO flags
- Whole-program optimization tradeoffs
- Binary size vs compile-time tuning
- Cross-language native linking
- Release-profile linker scripts
Linkers Lto by the numbers
- 347 all-time installs (skills.sh)
- +22 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #162 of 550 CLI & Terminal skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill linkers-ltoAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 347 |
|---|---|
| repo stars | ★ 155 |
| Last updated | June 27, 2026 |
| Repository | mohitmishra786/low-level-dev-skills ↗ |
What it does
Configure linker flags and LTO for faster, smaller native binaries in C, C++, and Rust release builds across GCC, Clang, and MSVC toolchains.
Files
Linkers and LTO
Purpose
Guide agents through linker selection, common linker flags, link-order issues, LTO setup, and symbol-visibility management.
Triggers
- "I'm getting
undefined referenceat link time" - "How do I enable LTO for a real project?"
- "Which linker should I use: ld, gold, or lld?"
- "How do I reduce binary size with
--gc-sections?" - "How do I write or understand a linker script?"
- "I have duplicate symbol or weak symbol issues"
Workflow
1. Linker selection
| Linker | Invocation | Strengths |
|---|---|---|
| GNU ld (BFD) | default on Linux | Universal, stable |
| gold | -fuse-ld=gold | Faster than ld for C++; supports LTO plugins |
| lld (LLVM) | -fuse-ld=lld | Fastest, parallel, required for Clang LTO |
# Use lld with GCC or Clang
gcc -fuse-ld=lld -o prog ...
clang -fuse-ld=lld -o prog ...
# Check which linker is used
gcc -v -o prog main.c 2>&1 | grep 'Invoking'2. Essential linker flags
# Pass linker flags via compiler driver:
# -Wl,flag1,flag2 (comma-separated, no spaces)
# -Wl,flag1 -Wl,flag2 (separate -Wl options)
gcc main.c -o prog \
-Wl,-rpath,/opt/mylibs/lib \ # runtime library search path
-Wl,--as-needed \ # only link libraries that are actually used
-Wl,--gc-sections \ # remove unused sections (requires -ffunction-sections -fdata-sections)
-Wl,-z,relro \ # mark relocations read-only after startup
-Wl,-z,now \ # resolve all symbols at startup (full RELRO)
-L/opt/mylibs/lib -lfoo3. Link order matters (GNU ld)
GNU ld processes archives left-to-right. A library must come after the objects that need it.
# Wrong: libfoo provides symbols needed by main.o
gcc main.o -lfoo libdep.a -o prog # can fail if libdep.a needs libfoo
# Correct: dependencies after dependents
gcc main.o -lfoo -ldep -o prog
# If there are circular deps between archives:
gcc main.o -Wl,--start-group -lfoo -lbar -Wl,--end-group -o prog
# --start-group/--end-group: repeat search until no new symbols resolved4. LTO with GCC
# Compile
gcc -O2 -flto -ffunction-sections -fdata-sections -c foo.c -o foo.o
gcc -O2 -flto -ffunction-sections -fdata-sections -c bar.c -o bar.o
# Link (must pass -flto again)
gcc -O2 -flto -Wl,--gc-sections foo.o bar.o -o prog
# Archives: must use gcc-ar / gcc-ranlib, not plain ar
gcc-ar rcs libfoo.a foo.o
gcc-ranlib libfoo.aParallel LTO:
gcc -O2 -flto=auto foo.o bar.o -o prog # uses jobserver
gcc -O2 -flto=4 foo.o bar.o -o prog # 4 parallel jobs5. LTO with Clang / lld
# Full LTO
clang -O2 -flto -fuse-ld=lld foo.c bar.c -o prog
# ThinLTO (faster, nearly same quality)
clang -O2 -flto=thin -fuse-ld=lld foo.c bar.c -o prog
# LTO with cmake: set globally
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) # enables -fltoThinLTO caches work: subsequent builds that reuse unchanged modules are faster. Cache location: specify with -Wl,--thinlto-cache-dir=/tmp/thinlto-cache.
6. Dead-code stripping
# Compile with per-function/per-data sections
gcc -O2 -ffunction-sections -fdata-sections -c foo.c -o foo.o
# Link with garbage collection
gcc -Wl,--gc-sections foo.o -o prog
# Verify what was removed
gcc -Wl,--gc-sections -Wl,--print-gc-sections foo.o -o prog 2>&1 | head -20On macOS, the linker strips dead code by default (-dead_strip).
7. Symbol visibility
Controlling visibility reduces DSO size and enables better LTO:
# Hide all symbols by default, export explicitly
gcc -fvisibility=hidden -O2 -shared -fPIC foo.c -o libfoo.so
# In source, mark exports:
__attribute__((visibility("default"))) int my_public_function(void);Or use a version script:
# foo.ver
{
global: my_public_function; my_other_public;
local: *;
};gcc -Wl,--version-script=foo.ver -shared -fPIC -o libfoo.so foo.o8. Common linker errors
| Error | Cause | Fix |
|---|---|---|
undefined reference to 'foo' | Missing library or wrong order | Add -lfoo; move after the object that needs it |
multiple definition of 'foo' | Symbol defined in two TUs | Remove duplicate; use static or extern |
cannot find -lfoo | Library not in search path | Add -L/path/to/lib; install dev package |
relocation truncated | Address overflow in relocation | Use -mcmodel=large; restructure image |
version 'GLIBC_2.33' not found | Binary needs newer glibc | Link statically or rebuild on older host |
circular reference | Archives mutually depend | Use --start-group/--end-group |
9. Linker map file
# Generate a map file (shows symbol → section → file)
gcc -Wl,-Map=prog.map -o prog foo.o bar.o
less prog.mapUseful for debugging binary size and symbol placement.
For a comprehensive linker and LTO flags reference, see references/flags.md.
Related skills
- Use
skills/binaries/elf-inspectionfor examining the resulting binary - Use
skills/compilers/gccorskills/compilers/clangfor compile-phase LTO flags - Use
skills/binaries/binutilsforar,strip,objcopy
Linker and LTO Flags Reference
Source: <https://sourceware.org/binutils/docs/ld/Options.html> Source: <https://clang.llvm.org/docs/ThinLTO.html> Source: <https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html> (LTO section) Source: <https://lld.llvm.org/>
Table of Contents
1. Linker selection 2. GNU ld / gold flags 3. lld flags 4. GCC LTO flags 5. Clang LTO flags 6. MSVC LTCG 7. Linker scripts basics 8. Common linker errors
---
Linker selection
# Via GCC/Clang driver
gcc -fuse-ld=gold main.c -o prog
gcc -fuse-ld=lld main.c -o prog
clang -fuse-ld=lld main.c -o prog
# Check which linker is in use
gcc -v main.c -o /dev/null 2>&1 | grep 'Invoking\|collect2\|ld\b'
# mold (very fast alternative to lld)
gcc -fuse-ld=mold main.c -o prog---
GNU ld / gold flags
Pass via -Wl,flag or after -Wl, prefix (no spaces).
Basics
| Flag | Effect |
|---|---|
-o file | Output filename |
-e symbol | Entry point |
-L dir | Add library search directory |
-l name | Link library libname.so or libname.a |
-rpath dir | Runtime library search path (embedded in ELF) |
-rpath-link dir | Search for indirect deps (not embedded) |
-soname name | Set the SONAME of a shared library |
-shared | Build a shared library |
-static | Force static linking |
-pie | Build a position-independent executable |
-no-pie | Force non-PIE |
Symbol control
| Flag | Effect |
|---|---|
--export-dynamic | Add all symbols to dynamic table (needed for dlopen) |
--dynamic-list=file | Specify dynamic symbol list |
--version-script=file | Control symbol versioning and visibility |
--retain-symbols-file=file | Keep only listed symbols |
--strip-all | Strip all symbols at link |
--strip-debug | Strip debug symbols at link |
Dead-code removal
# Requires -ffunction-sections -fdata-sections at compile time
-Wl,--gc-sections # remove unused sections
-Wl,--print-gc-sections # print what was removed (debug)
-Wl,--icf=safe # Identical Code Folding (gold/lld)
-Wl,--icf=all # Aggressive ICF (may affect function pointers)Hardening
-Wl,-z,relro # Mark relocations read-only after startup
-Wl,-z,now # Resolve all PLT entries at startup (full RELRO)
-Wl,-z,noexecstack # Mark stack non-executable
-Wl,-z,separate-code # Separate code from data segmentsDiagnostics
-Wl,--as-needed # Only link libs that are actually needed
-Wl,--no-as-needed # Always link specified libs (default on some systems)
-Wl,--warn-common # Warn about tentative definitions
-Wl,--warn-unresolved-symbols # Warn (not error) on unresolved symbols
-Wl,-Map=prog.map # Generate linker map file
-Wl,--print-map # Print map to stdout
-Wl,--stats # Print linker statistics (gold)
-Wl,--verbose # Verbose linker outputGroup / circular deps
-Wl,--start-group -lA -lB -Wl,--end-group
# Repeats search of A and B until no new symbols are resolved
# Resolves circular dependencies between static archives---
lld flags
lld (LLVM linker) accepts most GNU ld flags plus:
# ThinLTO cache
-Wl,--thinlto-cache-dir=/tmp/thinlto-cache
-Wl,--thinlto-cache-policy=cache_size_bytes=1g
# Parallel LTO jobs
-Wl,--thinlto-jobs=8
# ICF (Identical Code Folding)
-Wl,--icf=safe
-Wl,--icf=all
# Print statistics
-Wl,-stats
# LLD-specific symbol ordering for startup perf
-Wl,--call-graph-profile-sort # sort functions by call graph
# Reproduce a link failure
-Wl,--reproduce=repro.tar---
GCC LTO flags
# Compile phase (generates GIMPLE IR alongside object)
gcc -O2 -flto -ffunction-sections -fdata-sections -c foo.c -o foo.o
# Link phase (must repeat -flto and -O level)
gcc -O2 -flto -Wl,--gc-sections foo.o bar.o -o prog
# Parallel LTO
gcc -O2 -flto=auto ... # uses jobserver parallelism
gcc -O2 -flto=4 ... # exactly 4 threads
# Static archives: must use gcc-ar / gcc-ranlib
gcc-ar rcs libfoo.a foo.o bar.o
gcc-ranlib libfoo.a
# Diagnose LTO decisions
gcc -O2 -flto -fdump-ipa-all foo.c -o prog # dump IPA analysisLTO object files contain both machine code and GIMPLE IR. Plain ar can archive them, but only gcc-ar creates the special index needed for LTO linking.
---
Clang LTO flags
Full LTO
clang -O2 -flto -fuse-ld=lld foo.c bar.c -o progThinLTO (preferred for large projects)
# Compile
clang -O2 -flto=thin -c foo.c -o foo.o
clang -O2 -flto=thin -c bar.c -o bar.o
# Link
clang -O2 -flto=thin -fuse-ld=lld foo.o bar.o -o prog \
-Wl,--thinlto-cache-dir=/tmp/thinlto-cache
# ThinLTO cache greatly speeds up incremental linksLTO in CMake
# Enable for all targets
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
# Or per-target (CMake 3.9+)
set_target_properties(myapp PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON)---
MSVC LTCG
REM Compile with whole-program optimisation
cl /GL /O2 /c foo.cpp /Fo:foo.obj
REM Link with LTCG
link /LTCG foo.obj bar.obj /OUT:prog.exe
REM Or with MSBuild property:
<WholeProgramOptimization>true</WholeProgramOptimization>/GL at compile + /LTCG at link = MSVC equivalent of -flto.
---
Linker scripts basics
Rarely hand-written, but essential for embedded systems:
/* Minimal linker script for Cortex-M */
MEMORY {
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS {
.text : {
KEEP(*(.isr_vector)) /* interrupt vector must be first */
*(.text*)
*(.rodata*)
} > FLASH
.data : {
_sdata = .;
*(.data*)
_edata = .;
} > RAM AT > FLASH /* LMA in FLASH, VMA in RAM */
.bss : {
_sbss = .;
*(.bss*)
*(COMMON)
_ebss = .;
} > RAM
_estack = ORIGIN(RAM) + LENGTH(RAM);
}Key concepts:
MEMORY: defines address regions with permissionsSECTIONS: maps sections to regionsAT > FLASH: load address in FLASH, run address in RAM (copy at startup)KEEP(...): prevent--gc-sectionsfrom removing this
---
Common linker errors
| Error | Cause | Fix |
|---|---|---|
undefined reference to 'foo' | Missing library | Add -lfoo; move -l after objects |
cannot find -lfoo | Library not in search path | Add -L/path; install -dev package |
multiple definition of 'x' | Defined in multiple TUs | Make one static; or use extern in header |
relocation truncated | Address too far for relocation | Use -mcmodel=large; or restructure |
version GLIBC_2.33 not found | Binary needs newer glibc | Build on older host; link statically |
circular reference | Archives depend on each other | Use --start-group/--end-group |
| LTO mismatch error | Mixed LTO and non-LTO objects | Recompile all with -flto consistently |
file format not recognized | Wrong architecture object | Check cross-compiler used for all objects |