Course contentsShow
← Rust
Lesson 1855 of 1,91016. Specialized Runtime TargetsFree lesson

Linker-Level Dead Code Elimination

Configure the linker to strip unused symbols from final binaries more aggressively than default settings.

In this lesson, you will learn how to configure your linker to perform "Function Sections" and aggressive garbage collection to strip unused code from your final binary.

The Mechanics of Bloat

By default, the compiler (LLVM) often places entire source files or translation units into a single chunk in the object file. If you use one function from a 500KB module, the linker often has to pull in the entire chunk, including dozens of functions you never actually call. This is the primary culprit behind "binary bloat" in embedded and Wasm targets.

Function Sections

To enable surgical precision, we instruct the compiler to place each function into its own unique section. When the linker sees these tiny, individual sections, it can analyze the call graph and discard anything that isn't reachable from the entry point (e.g., main or your exported Wasm functions).

In your Cargo.toml, you can toggle this behavior through the rustflags in your profile:

[profile.release]
# Place each function in its own section
opt-level = "z"
lto = true
codegen-units = 1

[target.wasm32-unknown-unknown]
rustflags = ["-C", "link-arg=-Wl,--gc-sections"]

How the Linker Strips Code

When you pass -Wl,--gc-sections (Garbage Collect Sections) to the linker, it performs a two-pass process:

  1. Mark Phase: The linker starts at your defined entry point and traverses the call graph, "marking" every function it visits.
  2. Sweep Phase: Any function section not marked during the traversal is physically excluded from the final binary.

When combined with lto = true (Link-Time Optimization), the linker can even inline and prune across crate boundaries, removing unused code that might have otherwise been "hidden" behind trait objects or generic instantiations. This is essential for resource-constrained targets like Wasm, where every byte impacts your startup latency and memory footprint.

Key Takeaway: By enabling function-sections and using the --gc-sections linker flag, you transform your binary from a collection of opaque blobs into a collection of individual units that the linker can surgically prune.