【stm32】【edgetx】解析链接脚本文件(ld)
链接器脚本 (*.ld
)
.ld文件是GNU Linker Script(链接脚本)
,用于GNU工具链(如GCC)中的链接器ld。它主要用来定义目标程序的内存布局,包括代码段、数据段、堆栈、堆等的大小和位置。
链接器脚本的两阶段处理机制
符号可以在定义前使用
内存区域函数在内存区域定义后才会被计算
最终值在链接时确定
stm32f40x部分
layout.ld
它是整个内存布局的核心配置文件,定义了STM32F4的所有内存区域和关键参数。
/* 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空间
/* Maximum bootloader code size */
BOOTLOADER_SIZE = 0x8000;
计算: 0x8000 = 32KB
作用: 为bootloader保留32KB Flash空间
/* Minimum Heap Size (indicative) */
MIN_HEAP_SIZE = 0;
// 默认MIN_HEAP_SIZE = 0 的原因: // - 嵌入式系统通常避免动态内存分配 // - 防止内存碎片和不确定性 // - 鼓励静态内存分配,提高可靠性
堆大小设置为0,意味着默认不启用动态内存分配
/* Highest heap address */
HEAP_ADDRESS = ORIGIN(RAM) + LENGTH(RAM);
计算: 0x20000000 + 128K = 0x20020000
作用: 堆的结束地址指向主RAM末尾
MEMORY
{FLASH (rx) :ORIGIN = 0x08000000,LENGTH = DEFINED(__flash_size) ? __flash_size : 512KRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128KCCM (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核心直连
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
通过不同别名实现逻辑分区
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文件,它专门处理堆和栈的内存分配。
PROVIDE(_heap_start = _eram);
_eram
是.ram
段的结束地址(在common_sections.ld中定义)_heap_start
=.ram
段之后的位置这样堆就紧接在
.ram
段后面开始
INCLUDE section_ccm.ld
PROVIDE(_ccm_heap_start = _eccm);
PROVIDE(_ccm_heap_end = _main_stack_start);
作用: 包含CCM专用段的定义,建立CCM内存布局,定义CCM堆的边界
堆分配段解析
/* 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)
栈分配段解析
/* 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。
.ccm (NOLOAD) : /* 定义名为 .ccm 的段,NOLOAD表示不加载到Flash */
{. = ALIGN(4); /* 当前位置对齐到4字节边界 */_sccm = .; /* 定义符号 _sccm = CCM段起始地址 */*(.ccm) /* 将所有目标文件中的 .ccm 段收集到这里 */. = ALIGN(4); /* 再次对齐到4字节边界 */_eccm = .; /* 定义符号 _eccm = CCM段结束地址 */
} > CCM /* 将此段放置在CCM内存区域 */
definition.ld
INCLUDE layout.ld /* 包含内存布局定义文件 */
/* Entry Point */
ENTRY(Reset_Handler) /* 定义程序入口点为Reset_Handler */
包含layout.ld
实现配置分离
作用: 引入内存区域、大小常量等基础配置。
告诉链接器和调试器程序从哪里开始执行
Reset_Handler
是启动文件中定义的函数,负责系统初始化程序复位后,CPU首先跳转到这个地址
/* 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异常处理段
/* 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
引导代码段
/* Bootstrap code to load ISR vector and code sections */
.bootstrap (READONLY) :
{*(.bootstrap)
} > REGION_TEXT_STORAGE
已初始化数据段 (.data)
/* 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)
/* 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段
/* collect all uninitialized .ram sections */
.ram (NOLOAD) : AT (ADDR(.ram))
{. = ALIGN(4);_sram = .;*(.ram). = ALIGN(4);_eram = .;
} > REGION_RAM
重启缓冲区
/* 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
库段丢弃
/* Remove information from the standard libraries */
/DISCARD/ :
{libc.a ( * )libm.a ( * )libgcc.a ( * )
}
ARM属性段
.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++运行时结构的布局
/* .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
专门用于引导加载器的内存布局配置。
/*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
/*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+-----------------+