Course contentsShow
← Embedded Systems & Computer Architecture
Lesson 450 of 9366. Embedded Software Construction and ToolchainsFree lesson

Handling Discarded Sections and Garbage Collection

You will use linker flags to remove unused code sections, reducing the final binary size for memory-constrained embedded systems.

In this lesson, you will learn how to shrink your final firmware binary by using linker flags to automatically identify and discard unused code and data.

When you compile a project, the compiler typically creates a unique section for every function and data object (e.g., .text.my_function). Even if your code never calls my_function, the linker, by default, will include it in your final executable simply because it exists in the object file. In memory-constrained systems, this "bloat" can be significant.

Enabling Linker Garbage Collection

To reclaim this wasted space, we use two complementary stages:

  1. Compiler Flags: You must instruct the compiler to place each function and data item into its own individual section. Use -ffunction-sections and -fdata-sections. This granularity allows the linker to evaluate each piece independently.
  2. Linker Flags: You then tell the linker to perform "Garbage Collection" (GC) of these sections. Use --gc-sections. When the linker performs this, it starts at your entry point (usually Reset_Handler) and performs a dependency analysis. It traces every symbol, keeping only those that are reachable. Anything else—unused libraries, debug utilities, or legacy drivers—is discarded.

Handling Critical Sections

A common pitfall occurs when you have code or data that is never "called" by the main program but must remain in memory (like an Interrupt Vector Table or a specific memory-mapped structure). Because the linker sees no references to these, it will try to discard them. You must use the KEEP() command in your linker script to prevent this:

SECTIONS {
    .vectors : {
        KEEP(*(.isr_vector)) /* Protect from GC */
    } > FLASH
    .text : {
        *(.text*)             /* Normal code is subject to GC */
    } > FLASH
}

By combining compiler-level section isolation with linker-level collection, you ensure your binary contains only the logic strictly required by your application. This not only saves precious Flash memory but also allows you to include large peripheral libraries without bloating your production image.

Key Takeaway: Use -ffunction-sections and -fdata-sections during compilation, combined with the --gc-sections linker flag, to automatically prune unreachable code, while using KEEP() in your linker script to protect vital structures from being discarded.