How do you place a function or variable at a fixed memory address?
Placing code or data at a specific address requires coordination between the C source file and the linker script. In C, you use __attribute__((section(".my_section"))) to assign a function or variable to a named section. For example: const uint32_t firmware_version __attribute__((section(".fw_version"))) = 0x01020003; places the version number in a section called .fw_version. For a function: void bootloader_entry(void) __attribute__((section(".bootloader")));. The attribute alone does not determine the address — it only ensures the symbol ends up in the named section rather than the default .text or .data.
In the linker script, you create a corresponding section entry and pin it to a fixed address. For example:
.fw_version 0x0800FF00 :{KEEP(*(.fw_version))} > FLASH
This places the .fw_version section at exactly 0x0800FF00 in Flash. The KEEP() directive is essential — without it, the linker's --gc-sections optimization will discard the section if no code references the variable (it may be read by an external tool, a bootloader, or via JTAG). The fixed address must not overlap with any other section; if it falls in the middle of .text, the linker will either error out or silently corrupt the layout depending on the toolchain.
Common use cases include: firmware version or CRC fields at known offsets (so a bootloader can read them without parsing the firmware), calibration data stored in a dedicated Flash page (so it survives firmware updates), peripheral register overlays (mapping a struct to the peripheral's base address), and shared memory regions between a bootloader and application. A dangerous pitfall: using __attribute__((at(address))) (an ARM Compiler 5 extension) — this is not portable to GCC. The section attribute plus linker script approach works across all major embedded toolchains (GCC, Clang, IAR, ARM Compiler 6).
Source: Boot & Startup Q&A
