链接文件(.ld)详解:别只怪语法,一文搞懂 MEMORY 和 SECTIONS 如何划分内存
摘要:为什么全局变量被放在了 RAM 末尾?为什么 DMA 非要访问
0x24000000?不是编译器任性,而是 链接文件(Linker Script) 决定了这一切。本文手把手拆解.ld文件,教你如何像上帝一样掌控内存布局。
一、问题现象:为什么我要改链接文件?
**DMA 传输失败,提示地址错误;
把变量定义成
const后,Flash 反而溢出了;想预留一段内存给 FPGA 用,编译器却把它占了。**
这些都不是代码问题,而是 内存规划权 的问题。
二、核心概念:链接文件是干什么的?
链接文件(.ld)告诉链接器两件事:
-
MEMORY:芯片有哪些内存块?(Flash、RAM、CCM、SDRAM)
-
SECTIONS:代码和数据分别塞进哪块内存?
1. 物理内存视图(以 STM32H7 为例)
0x0000_0000 ─┬─ Flash (ITCM) 1MB
0x0800_0000 ─┘
0x2000_0000 ─┬─ DTCM-RAM 128KB (给内核用,最快)
0x2400_0000 ─┬─ AXI-SRAM 512KB (给 DMA 用,最稳)
0x3000_0000 ─┬─ SRAM1/2/3 288KB
0x3800_0000 ─┬─ SRAM4 64KB
痛点:DMA 不能访问 DTCM,只能访问 AXI-SRAM。如果不改链接文件,DMA 必死。
三、实战拆解:一步步写 .ld文件
1. 定义 MEMORY(画地图)
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
DTCM_RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
AXI_RAM (xrw) : ORIGIN = 0x24000000, LENGTH = 512K
SDRAM (xrw) : ORIGIN = 0xC0000000, LENGTH = 8192K
}
-
rx:只读(代码、常量) -
xrw:可读可写(变量、栈、堆)
2. 定义 SECTIONS(分房子)
SECTIONS
{
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector))
. = ALIGN(4);
} > FLASH
.text :
{
. = ALIGN(4);
*(.text)
*(.text*)
. = ALIGN(4);
} > FLASH
.rodata :
{
. = ALIGN(4);
*(.rodata)
*(.rodata*)
. = ALIGN(4);
} > FLASH
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data)
*(.data*)
_edata = .;
. = ALIGN(4);
} > DTCM_RAM AT > FLASH
重点解释:
-
> DTCM_RAM:运行时在 DTCM(快)。 -
AT > FLASH:烧录时存在 Flash(省 RAM)。 -
_sdata/_edata:给启动文件用,用于上电拷贝数据。
3. 自定义段(给工程师用)
这是最实用的技巧。定义一个给 DMA 用的专用段:
.dma_buffer :
{
. = ALIGN(32); /* DMA 要求 32 字节对齐 */
_sdma = .;
*(.dma_buffer)
_edma = .;
. = ALIGN(32);
} > AXI_RAM
在 C 代码中这样用:
#pragma location = ".dma_buffer"
uint8_t dma_tx_buffer[2048];
✅ 结果:dma_tx_buffer必定在 0x24000000开始的 AXI-RAM 中,DMA 绝对不会报错。
四、工程级应用案例
案例 1:解决 DMA 地址错误
错误:HAL_DMA_Start(&hdma, (uint32_t)&var, ...)返回 HAL_ERROR。
解决:把 var放进 .dma_buffer段,映射到 AXI-RAM。
案例 2:把 GUI 帧缓冲放进 SDRAM
.frame_buffer :
{
*(.frame_buffer)
} > SDRAM
#pragma location = ".frame_buffer"
uint16_t lcd_fb[480][272];
案例 3:预留内存给 Bootloader
.boot_flag (NOLOAD) :
{
. = ALIGN(4);
*(.boot_flag)
} > DTCM_RAM
NOLOAD表示该段不初始化(断电不丢,但上电不清零)。
五、常见坑位 Checklist
-
[ ] 是否把 DMA 缓冲区放在了 DTCM 里?(必死)
-
[ ] 是否忘了
AT > FLASH导致 RAM 溢出? -
[ ] 是否定义了
_sdata/_edata但启动文件没用? -
[ ] 自定义段的地址是否对齐(通常 4 或 32 字节)?
-
[ ] 是否把中断向量表放在了 ITCM 或 Flash 开头?
六、写在最后(终极总结)
我是 gqqsherry666,一个拒绝调包、专注底层逻辑的嵌入式架构师。
如果说 Map 文件是 X 光片,那 Linker Script 就是手术刀。
不懂链接文件,你永远只是代码的搬运工;
懂了链接文件,你才是芯片的主人。
原创文章,转载请注明出处!
更多推荐


所有评论(0)