【stm32】【edgetx】解析链接脚本文件(ld)

链接器脚本 (*.ld)

.ld文件是GNU Linker Script(链接脚本),用于GNU工具链(如GCC)中的链接器ld。它主要用来定义目标程序的内存布局,包括代码段、数据段、堆栈、堆等的大小和位置。

链接器脚本的两阶段处理机制

  • 符号可以在定义前使用

  • 内存区域函数在内存区域定义后才会被计算

  • 最终值在链接时确定

stm32f40x部分

layout.ld

它是整个内存布局的核心配置文件,定义了STM32F4的所有内存区域和关键参数。

cpp 复制代码
/* Highest address of the user mode stack */
STACK_ADDRESS = ORIGIN(CCM) + LENGTH(CCM); /* end of CCM */
/* Required amount of stack for interrupt stack (Main stack) */
MAIN_STACK_SIZE = 1024;

计算 : 0x10000000 + 64K = 0x10010000
作用: 栈顶指针指向CCM内存的末尾, 为主栈(中断栈)分配1KB空间

cpp 复制代码
/* Maximum bootloader code size */
BOOTLOADER_SIZE = 0x8000;

计算 : 0x8000 = 32KB
作用: 为bootloader保留32KB Flash空间

cpp 复制代码
/* Minimum Heap Size (indicative) */
MIN_HEAP_SIZE = 0;
复制代码
// 默认MIN_HEAP_SIZE = 0 的原因:
// - 嵌入式系统通常避免动态内存分配
// - 防止内存碎片和不确定性
// - 鼓励静态内存分配,提高可靠性

堆大小设置为0,意味着默认不启用动态内存分配

cpp 复制代码
/* Highest heap address */
HEAP_ADDRESS = ORIGIN(RAM) + LENGTH(RAM);

计算 : 0x20000000 + 128K = 0x20020000
作用: 堆的结束地址指向主RAM末尾

cpp 复制代码
MEMORY
{
  FLASH (rx) :
      ORIGIN = 0x08000000,
      LENGTH = DEFINED(__flash_size) ? __flash_size : 512K

  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 128K
  CCM (xrw)       : ORIGIN = 0x10000000, LENGTH = 64K
}
复制代码
# 编译时定义__flash_size覆盖默认值
gcc -D__flash_size=256K -T layout.ld ...

MEMORY定义

复制代码
FLASH (rx): 
- 起始地址: 0x08000000  
- 大小: 512KB (可通过__flash_size覆盖)
- 属性: r(读)+x(执行)
- 灵活性: 支持不同Flash大小的芯片
复制代码
RAM (xrw):
- 起始地址: 0x20000000
- 大小: 128KB  
- 属性: x(执行)+r(读)+w(写)
- 用途: 通用变量、堆空间
复制代码
CCM (xrw):
- 起始地址: 0x10000000
- 大小: 64KB
- 属性: x(执行)+r(读)+w(写)  
- 特点: 零等待状态,与CPU核心直连
cpp 复制代码
REGION_ALIAS("REGION_BOOTLOADER",   FLASH);
REGION_ALIAS("REGION_ISR_VECT",     FLASH);
REGION_ALIAS("REGION_TEXT",         FLASH);
REGION_ALIAS("REGION_TEXT_STORAGE", FLASH);

Flash区域分配:

  • 所有Flash相关段都映射到同一物理Flash

  • 通过不同别名实现逻辑分区

cpp 复制代码
REGION_ALIAS("REGION_DATA", CCM);
REGION_ALIAS("REGION_BSS", CCM);
REGION_ALIAS("REGION_RAM", RAM);

RAM区域策略:

  • .data.bss段放在高速CCM

  • 通用.ram段放在主RAM中

  • 体现性能优化的设计思想

extra_sections.ld

这是extra_sections.ld 文件,它专门处理堆和栈的内存分配

cpp 复制代码
PROVIDE(_heap_start = _eram);
  • _eram.ram段的结束地址(在common_sections.ld中定义)

  • _heap_start = .ram段之后的位置

  • 这样堆就紧接在.ram段后面开始

cpp 复制代码
INCLUDE section_ccm.ld
PROVIDE(_ccm_heap_start = _eccm);
PROVIDE(_ccm_heap_end = _main_stack_start);

作用: 包含CCM专用段的定义,建立CCM内存布局,定义CCM堆的边界

堆分配段解析

cpp 复制代码
/* Reserve heap space in RAM */
._user_heap(NOLOAD) :
{
  . = ALIGN(4);                      /* 4字节对齐 */
  . = . + MIN_HEAP_SIZE;             /* 预留堆空间大小 */
  . = ALIGN(4);                      /* 再次对齐 */
} > REGION_RAM
  • (NOLOAD): 不加载初始值,运行时动态使用

  • MIN_HEAP_SIZE = 0: 默认不分配堆空间

  • 位置: REGION_RAM (主RAM)

栈分配段解析

cpp 复制代码
/* Reserve stack space in CCM */
.stack(NOLOAD) :
{
  . = ALIGN(4);                      /* 4字节对齐 */
  . = . + MAIN_STACK_SIZE;           /* 预留栈空间大小 */
  . = ALIGN(4);                      /* 再次对齐 */
} > CCM
  • (NOLOAD): 栈不需要初始值

  • MAIN_STACK_SIZE = 1024: 分配1KB栈空间

  • 位置: CCM (核心耦合内存)

section_ccm.ld

用于定义CCM(Core Coupled Memory)内存段的布局。 CCM (Core Coupled Memory) 是一块非常特殊且有用的片上 SRAM。

.ccm (NOLOAD) 的含义

  • .ccm: 段名称,程序员可以在代码中指定变量放在这个段。

  • (NOLOAD): 关键属性,表示:这个段不需要在启动时从Flash。

cpp 复制代码
.ccm (NOLOAD) :          /* 定义名为 .ccm 的段,NOLOAD表示不加载到Flash */
{
  . = ALIGN(4);          /* 当前位置对齐到4字节边界 */
  _sccm = .;             /* 定义符号 _sccm = CCM段起始地址 */
  *(.ccm)                /* 将所有目标文件中的 .ccm 段收集到这里 */
  . = ALIGN(4);          /* 再次对齐到4字节边界 */
  _eccm = .;             /* 定义符号 _eccm = CCM段结束地址 */
} > CCM                  /* 将此段放置在CCM内存区域 */

definition.ld

cpp 复制代码
INCLUDE layout.ld                      /* 包含内存布局定义文件 */
/* Entry Point */
ENTRY(Reset_Handler)                   /* 定义程序入口点为Reset_Handler */

包含layout.ld实现配置分离

作用: 引入内存区域、大小常量等基础配置。

  • 告诉链接器和调试器程序从哪里开始执行

  • Reset_Handler是启动文件中定义的函数,负责系统初始化

  • 程序复位后,CPU首先跳转到这个地址

cpp 复制代码
/* Stack address */
_estack = STACK_ADDRESS;               /* 栈顶地址 = CCM内存末尾 */
/* Main stack end */
_main_stack_start = _estack - MAIN_STACK_SIZE;  /* 栈底地址 */
/* Highest heap address */
_heap_end = HEAP_ADDRESS;              /* 堆结束地址 = RAM末尾 */

这里有堆和栈内存结尾定义。

栈:向低地址扩展

堆:向高地址扩展

栈内存布局:

复制代码
CCM内存 (64KB):
0x10000000 +-----------------+ ← _sdata/_sbss
           | .data + .bss段  | (全局变量)
           +-----------------+ ← _edata/_ebss
           |    .ccm段       | (高性能变量)
           +-----------------+ ← _eccm = _ccm_heap_start
           |   CCM堆空间     | (可用作高速堆)
           +-----------------+ ← _ccm_heap_end = _main_stack_start
           |   主栈(1KB)     | (向下增长)
           +-----------------+ ← _estack (0x10010000)

将栈放在 CCM 可以显著提高函数调用、中断响应的速度(因为栈访问非常频繁)

堆内存布局:

复制代码
主RAM (128KB):
0x20000000 +-----------------+ ← _sram
           |    .ram段       | (用户定义的RAM变量)
           +-----------------+ ← _eram = _heap_start
           |   未使用区域     | 可作为堆 (但默认未启用)
           +-----------------+ ← _heap_end (0x20020000)

common_sections.ld

ARM异常处理段

cpp 复制代码
/* C/C++ Exception handling (not used) */
.ARM.extab (READONLY) :
{
  *(.ARM.extab* .gnu.linkonce.armextab.*)
} > REGION_TEXT AT> REGION_TEXT_STORAGE

.ARM (READONLY) :
{
  PROVIDE(__exidx_start = .);
  *(.ARM.exidx* .gnu.linkonce.armexidx.*)
  PROVIDE(__exidx_end = .);
} > REGION_TEXT AT> REGION_TEXT_STORAGE

引导代码段

cpp 复制代码
/* Bootstrap code to load ISR vector and code sections */
.bootstrap (READONLY) :
{
  *(.bootstrap)
} > REGION_TEXT_STORAGE

已初始化数据段 (.data)

cpp 复制代码
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
  . = ALIGN(4);
  _sdata = .;        /* create a global symbol at data start */
  *(.data .data*)    /* .data sections */
  . = ALIGN(4);
  _edata = .;        /* define a global symbol at data end */
} > REGION_DATA AT> REGION_TEXT_STORAGE

未初始化数据段 (.bss)

cpp 复制代码
/* Uninitialized data section */
.bss (NOLOAD) : AT (ADDR(.bss))
{
  . = ALIGN(4);
  _sbss = .;         /* define a global symbol at bss start */
  *(.bss .bss*)      /* .bss sections */
  *(COMMON)          /* COMMON symbols */
  . = ALIGN(4);
  _ebss = .;         /* define a global symbol at bss end */
} > REGION_BSS

通用RAM段

cpp 复制代码
/* collect all uninitialized .ram sections */
.ram (NOLOAD) : AT (ADDR(.ram))
{
  . = ALIGN(4);
  _sram = .;
  *(.ram)
  . = ALIGN(4);
  _eram = .;
} > REGION_RAM

重启缓冲区

cpp 复制代码
/* Seems to be the safest place for all targets (see AN2606) */
.reboot_buffer (ORIGIN(REGION_DATA) + LENGTH(REGION_DATA) - 4) (NOLOAD) : AT (ADDR(.reboot_buffer))
{
  KEEP(*(.reboot*))
} > REGION_DATA

库段丢弃

cpp 复制代码
/* Remove information from the standard libraries */
/DISCARD/ :
{
  libc.a ( * )
  libm.a ( * )
  libgcc.a ( * )
}

ARM属性段

cpp 复制代码
.ARM.attributes 0 : { *(.ARM.attributes) }
复制代码
Flash存储布局 (LMA):
+-----------------+
|    .text        | 程序代码
+-----------------+
|    .data初始值  | ← _sidata
+-----------------+
|    .ARM.extab   | 异常处理
+-----------------+

CCM运行布局 (VMA):
+-----------------+ ← _sdata/_sbss
|    .data        | 已初始化变量 (运行时)
|    .bss         | 未初始化变量
+-----------------+ ← _edata/_ebss

主RAM运行布局:
+-----------------+ ← _sram
|    .ram段       | 用户定义的RAM变量
+-----------------+ ← _eram

common_text.ld

专门定义代码段和C++运行时结构的布局

cpp 复制代码
/* .text sections (code) */
*(.text .text* .gnu.linkonce.t.*)        /* 收集所有代码段:主代码、编译器生成的代码、链接优化代码 */
*(.rodata .rodata* .gnu.linkonce.r.*)    /* 收集所有只读数据段:常量字符串、全局常量、编译时常量 */

*(.glue_7)  /* glue arm to thumb code */ /* ARM调用Thumb代码的粘合代码段(veneers) */
*(.glue_7t) /* glue thumb to arm code */ /* Thumb调用ARM代码的粘合代码段(veneers) */

*(.eh_frame)                             /* 异常处理帧信息,用于栈展开和调试 */
*(.data.__global_locale)                 /* C++全局区域设置数据(locale相关) */

/* Init hooks table */
. = ALIGN(4);                            /* 对齐到4字节边界 */
__init_hook_array_start = .;             /* 定义初始化钩子数组起始符号 */
KEEP (*(.init_hook))                     /* 强制保留所有.init_hook段(自定义初始化钩子) */
__init_hook_array_end = .;               /* 定义初始化钩子数组结束符号 */

/*
   C++ Runtime: initializers for static variables.
   C Runtime: designated constructors
*/
. = ALIGN(4);                            /* 对齐到4字节边界 */
KEEP(*(.init))                           /* 强制保留.init段(C运行时初始化代码) */
. = ALIGN(4);                            /* 对齐到4字节边界 */
__preinit_array_start = .;               /* 定义预初始化数组起始符号 */
KEEP (*(.preinit_array))                 /* 强制保留.preinit_array段(C++预初始化函数指针数组) */
__preinit_array_end = .;                 /* 定义预初始化数组结束符号 */

. = ALIGN(4);                            /* 对齐到4字节边界 */
__init_array_start = .;                  /* 定义初始化数组起始符号 */
KEEP (*(SORT(.init_array.*)))            /* 强制保留并排序所有.init_array.*段(优先级排序的C++构造函数) */
KEEP (*(.init_array))                    /* 强制保留.init_array段(C++全局构造函数指针数组) */
__init_array_end = .;                    /* 定义初始化数组结束符号 */

/*
   C++ runtime: destructors for static variables.
   C runtime: designated finializers
*/
. = ALIGN(4);                            /* 对齐到4字节边界 */
KEEP(*(.fini))                           /* 强制保留.fini段(C运行时终结代码) */

. = ALIGN(4);                            /* 对齐到4字节边界 */
__fini_array_start = .;                  /* 定义终结数组起始符号 */
KEEP (*(.fini_array))                    /* 强制保留.fini_array段(C++全局析构函数指针数组) */
KEEP (*(SORT(.fini_array.*)))            /* 强制保留并排序所有.fini_array.*段(优先级排序的C++析构函数) */
__fini_array_end = .;                    /* 定义终结数组结束符号 */

/*
   C++ runtime: static constructors
*/
. = ALIGN(4);                            /* 对齐到4字节边界 */
KEEP (*crtbegin.o(.ctors))               /* 强制保留crtbegin.o中的构造函数表起始部分 */
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))/* 强制保留除crtend.o外所有.ctors段(C++静态构造函数) */
KEEP (*(SORT(.ctors.*)))                 /* 强制保留并排序所有.ctors.*段(优先级排序的静态构造函数) */
KEEP (*crtend.o(.ctors))                 /* 强制保留crtend.o中的构造函数表结束部分 */
复制代码
Flash中的.text段布局:
+----------------------+
| .text (程序代码)     |
| .rodata (只读数据)   |
| .glue_7 (ARM/Thumb)  |
+----------------------+
| __init_hook_array    | ← 自定义初始化钩子
+----------------------+
| .init                | ← C运行时初始化
+----------------------+
| __preinit_array      | ← C++预初始化
+----------------------+
| __init_array         | ← C++全局构造函数
+----------------------+
| .ctors               | ← C++静态构造函数
+----------------------+
| __fini_array         | ← C++全局析构函数
+----------------------+
| .fini                | ← C运行时终结
+----------------------+

bootloader.ld

专门用于引导加载器的内存布局配置。

cpp 复制代码
/*
   Generic bootloader linker script for STM32
*/

INCLUDE definitions.ld                    /* 包含基础定义:内存区域、栈地址、堆地址等 */

/* Define output sections */
SECTIONS
{
  /* ISR vector to be loaded */
  .isr_vector :
  {
    _sisr_vector = .;                    /* 定义向量表起始地址符号 */
    KEEP(*(.isr_vector))                 /* 强制保留中断向量表,防止链接器优化掉 */

    . = ALIGN(4);                        /* 对齐到4字节边界 */
    _eisr_vector = .;                    /* 定义向量表结束地址符号 */
  } > REGION_ISR_VECT AT> REGION_TEXT_STORAGE  /* VMA在ISR向量区域,LMA在代码存储区域 */

  _isr_load = LOADADDR(.isr_vector);     /* 获取向量表在Flash中的加载地址(LMA) */
  g_pfnVectors = _isr_load;              /* 创建全局符号指向向量表,供C代码使用 */

  /* The program code and other data goes into FLASH */
  .text :
  {
    FILL(0xFFFF)                         /* 未初始化区域填充0xFFFF(Flash擦除状态) */
    CREATE_OBJECT_SYMBOLS                 /* 为每个输入对象文件创建符号,用于调试 */

    _stext = .;                          /* 定义代码段起始地址符号 */
    KEEP(*(.version))                    /* 强制保留版本信息段 */
    KEEP(*(.bootversiondata))            /* 强制保留引导加载器版本数据段 */

    INCLUDE common_text.ld               /* 包含通用代码段定义:程序代码、只读数据、C++运行时等 */

    . = ALIGN(4);                        /* 对齐到4字节边界 */
    _etext = .;        /* define a global symbols at end of code */  /* 定义代码段结束地址符号 */
  } > REGION_TEXT AT> REGION_TEXT_STORAGE  /* VMA在代码区域,LMA在代码存储区域 */

  _text_load = LOADADDR(.text);          /* 获取代码段在Flash中的加载地址(LMA) */

  INCLUDE common_sections.ld             /* 包含通用数据段定义:.data, .bss, .ram等 */
}

firmware.ld

cpp 复制代码
/*
   Generic firmware linker script for STM32
*/

INCLUDE definitions.ld                    /* 包含基础定义:内存区域、栈地址、堆地址等常量 */

/* Define output sections */
SECTIONS
{
  .bootloader :
  {
    FILL(0xFFFF)                         /* 未初始化区域填充0xFFFF(Flash擦除状态) */
    KEEP(*(.bootloader))                 /* 强制保留.bootloader段内容 */
    . = BOOTLOADER_SIZE;                 /* 当前位置前进到BOOTLOADER_SIZE(32KB)处 */
  } > REGION_BOOTLOADER                  /* 放置在引导加载器区域(Flash开头) */

  /* Used only with H7 */
  .firmware_header (READONLY) :          /* 固件头段(仅H7系列使用,F4可能忽略) */
  {
    KEEP(*(.fwdescription))              /* 强制保留固件描述信息段 */
  } > REGION_TEXT AT> REGION_TEXT_STORAGE  /* VMA在代码区域,LMA在代码存储区域 */

  /* ISR vector to be loaded */
  .isr_vector :                          /* 中断向量表段 */
  {
    . = ALIGN(4);                        /* 对齐到4字节边界 */
    _sisr_vector = .;                    /* 定义向量表起始地址符号 */
    KEEP(*(.isr_vector))                 /* 强制保留中断向量表,防止链接器优化掉 */

    . = ALIGN(4);                        /* 对齐到4字节边界 */
    _eisr_vector = .;                    /* 定义向量表结束地址符号 */
  } > REGION_ISR_VECT AT> REGION_TEXT_STORAGE  /* VMA在ISR向量区域,LMA在代码存储区域 */

  _isr_load = LOADADDR(.isr_vector);     /* 获取向量表在Flash中的加载地址(LMA) */

  /* Main code section */
  .text (READONLY) :                     /* 主代码段(只读属性) */
  {
    FILL(0xFFFF)                         /* 未初始化区域填充0xFFFF */
    CREATE_OBJECT_SYMBOLS                 /* 为每个输入对象文件创建符号,用于调试 */

    _stext = .;                          /* 定义代码段起始地址符号 */
    KEEP(*(.fwversiondata))              /* 强制保留固件版本数据段 */

    INCLUDE common_text.ld               /* 包含通用代码段定义:程序代码、只读数据、C++运行时等 */

    . = ALIGN(4);                        /* 对齐到4字节边界 */
    _etext = .;                          /* 定义代码段结束地址符号 */
  } > REGION_TEXT AT> REGION_TEXT_STORAGE  /* VMA在代码区域,LMA在代码存储区域 */

  _text_load = LOADADDR(.text);          /* 获取代码段在Flash中的加载地址(LMA) */

  INCLUDE common_sections.ld             /* 包含通用数据段定义:.data, .bss, .ram, 堆栈等 */

  .text_end_section : {} > REGION_TEXT AT > REGION_TEXT_STORAGE  /* 文本结束标记段(空段,用于计算长度) */
  _firmware_length = LOADADDR(.text_end_section) - LOADADDR(.firmware_header);  /* 计算固件总长度:从固件头到代码结束 */
  _firmware_version = _text_load;        /* 固件版本信息指向代码段开始位置 */
}

Flash物理布局

复制代码
0x08000000 +-----------------+ ← REGION_BOOTLOADER
           |   Bootloader    | 32KB (保留区域)
0x08008000 +-----------------+ ← REGION_ISR_VECT
           |  Firmware Header| (H7专用,F4可能为空)
           +-----------------+ ← _sisr_vector
           |  中断向量表     | ← _isr_load (加载地址)
           +-----------------+ ← _eisr_vector
           |                 |
           |   主程序代码    | ← _stext, _text_load
           |   (.text等)     |
           +-----------------+ ← _etext
           |  数据初始值     | (.data段初始值)
           +-----------------+
           |  固件版本数据   | ← _firmware_version
           +-----------------+
相关推荐
云山工作室4 小时前
基于单片机的三相逆变电源设计
单片机·嵌入式硬件·毕业设计·毕设
国科安芯6 小时前
ASP3605电源芯片关键指标测试说明
功能测试·单片机·嵌入式硬件·pcb工艺
SundayBear7 小时前
LVGL 开发指南:从入门到精通的嵌入式 GUI 实战心法
单片机·嵌入式·lvgl
弘毅 失败的 mian9 小时前
利用 VsCode + EIDE 进行嵌入式开发(保姆级教程)
经验分享·笔记·vscode·stm32·单片机·嵌入式硬件·eide
小龙报10 小时前
《KelpBar海带Linux智慧屏项目》
linux·c语言·vscode·单片机·物联网·ubuntu·学习方法
沐欣工作室_lvyiyi10 小时前
基于单片机的环境监测智能报警系统的设计(论文+源码)
单片机·嵌入式硬件·物联网·毕业设计
充哥单片机设计11 小时前
【STM32项目开源】基于STM32的智能宠物防丢监控系统
stm32·嵌入式硬件·宠物
mc235612 小时前
以江协科技STM32入门教程的方式打开FreeRTOS——STM32C8T6如何移植FreeRTOS
科技·stm32·嵌入式硬件
v_for_van13 小时前
STM32简单的串口Bootloader入门
笔记·stm32·单片机·嵌入式硬件·物联网·学习