2026-07-16
The asker is working on an embedded ARM system (looks like STM32H7-family, given the 0x24000000 RAM base — that's classic AXI SRAM territory). They've carved out a custom section (RAM1) in their linker script to hold DMA-friendly buffers, and when they inspect the resulting ELF with arm-none-eabi-readelf -Ws, they see mysterious gaps between symbols that they can't account for from the source declarations alone.
Why this is interesting. DMA buffer placement is one of those areas where several concerns collide:
ALIGN() or . = ALIGN(1 << LOG2CEIL(SIZEOF(section))) trick.std::array wrappers or types with alignas) can pull in stricter alignment than the developer expects.KEEP(), SORT_BY_ALIGNMENT, and default FILL(0) patterns can all leave gaps that look inexplicable.Direction toward a solution. I'd approach it like this:
arm-none-eabi-ld -Map=out.map --cref. The map file will show every input section placed into RAM1, including compiler-generated ones like .bss.buffer, .rodata.*, and any *fill* pseudo-symbols.arm-none-eabi-objdump -h buffer.o. If the object's .bss has an alignment of, say, 32 (0x20), but the buffer itself is only 200 bytes, the linker will round the next section up.. = ALIGN(...) directives inside the output section, and for any SUBALIGN.alignas, __attribute__((aligned)), or C++ types whose natural alignment is larger than expected.Gotchas. A common surprise: placing a variable via __attribute__((section(".ram1"))) without specifying alignment means the compiler uses the type's natural alignment, but the linker may still enforce section-level alignment inherited from the first input section it saw. Also, readelf -Ws only shows symbols — it doesn't show anonymous fill. Cross-referencing with objdump -h -j RAM1 and the map file is the only way to see where the bytes actually went.
ALIGN directives — none of which are visible from readelf -Ws alone.
