Placing Specific Objects in Custom Memory Sections
In this lesson, you will learn how to use C compiler attributes to force specific variables or functions into custom memory regions defined in your linker script.
The Problem of Default Placement
By default, your compiler and linker work together to place global variables into sections like .data (initialized) or .bss (uninitialized). While convenient, this is often insufficient for embedded systems. You might need to place a critical ISR (Interrupt Service Routine) into fast tightly-coupled memory (TCM), or store configuration data in a specific sector of external Flash. Relying on default placement leaves this critical hardware mapping to chance.
Using Attribute Syntax
To override the default behavior, we use compiler-specific attributes. While syntax varies slightly by toolchain, GCC and Clang use the __attribute__((section("name"))) syntax. This tells the compiler: "Do not put this symbol in the usual spot; put it in the section named 'name'."
Consider this example where we place a calibration table into a specific memory region defined in our linker script as CALIB_RAM:
// Define a variable and force it into the "calib_section"
__attribute__((section(".calib_section")))
const float sensor_calibration_table[10] = {0.1f, 0.2f, 0.5f, ...};
// Force a specific function to reside in a custom section
__attribute__((section(".fast_code")))
void critical_math_function(void) {
// This code will be placed in the region mapped to .fast_code
}
Linking the Pieces
Simply tagging the code is only half the battle. Your linker script must explicitly define where the .calib_section and .fast_code sections reside in your memory map. If you tag a variable with __attribute__((section(".my_data"))) but fail to define .my_data in your linker script, the linker will likely generate an error or—worse—place it in an arbitrary default location.
By combining these attributes with your linker script's MEMORY and SECTIONS commands, you gain absolute control over the physical memory footprint of your software, ensuring that time-critical functions run from the fastest memory and data is aligned exactly where your peripherals expect to find it.
Key Takeaway: Use the __attribute__((section("name"))) directive to gain surgical control over the memory placement of variables and functions, ensuring your software architecture strictly conforms to the physical requirements of your hardware.