欢迎光临
我们一直在努力

链接文件(.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)
    未经允许不得转载:171主机测评 » 链接文件(.ld)详解:别只怪语法,一文搞懂 MEMORY 和 SECTIONS 如何划分内存
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址