链接器脚本 (*.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
+-----------------+