Cortex-M MCU分散加载文件与链接文件关系
分散加载文件(如 Keil 的 .scat
描述文件或 ARM-GCC 的 .sct
文件)与链接文件(如 .ld
文件)在嵌入式开发中共同协作,但它们的角色和设计目标不同。以下是两者的核心关系与区别分析:
1. 核心定义与角色
(1) 分散加载文件(Scatter Loading File)
- 作用:
定义程序在目标硬件内存(Flash/RAM)中的物理分布规则,指导链接器如何将代码和数据段分配到具体的内存区域(如 Flash 的起始地址、RAM 的分配范围)。 - 设计目标:
解决“代码和数据应该放在哪里”的问题,支持复杂的内存布局(如多 Flash Bank、外部 RAM)。
(2) 链接文件(Linker Script,如 .ld
文件)
- 作用:
定义程序的逻辑内存布局(如.text
、.data
、.bss
等段的划分),并控制符号地址分配、段合并规则等。 - 设计目标:
解决“代码和数据如何组织”的问题,确保编译后的二进制符合程序逻辑需求(如函数调用顺序、全局变量访问)。
2. 功能对比与协作关系
功能维度 | 分散加载文件 | 链接文件(.ld) |
---|---|---|
核心关注点 | 物理内存分配(Flash/RAM 的地址和大小) | 逻辑段组织(代码、数据段的划分与合并) |
地址分配 | 直接指定段在物理内存中的加载地址(如 0x08000000 ) | 定义段之间的相对地址关系(如 .text 在 .data 之前) |
内存类型支持 | 支持多内存区域(如内部 Flash、外部 RAM) | 通常针对单一内存模型(如默认的 Flash+RAM) |
典型应用场景 | Bootloader 分离、OTA 升级、多 Flash 存储 | 标准程序布局(代码+数据分离) |
工具链关联性 | Keil MDK(ARMCC)专用 | GNU 工具链(GCC)专用 |
协作流程
- 编译阶段:源代码编译为目标文件(
.o
),包含未分配地址的段(如.text
、.data
)。 - 链接阶段:
- 链接文件(.ld):定义段的逻辑组织(如
.text
放在 Flash,.data
初始值在 Flash 但运行时在 RAM)。 - 分散加载文件:在链接文件的基础上,进一步指定这些段在物理内存中的具体地址(如 Flash 的
0x08000000
开始存储.text
)。
- 链接文件(.ld):定义段的逻辑组织(如
- 生成二进制:链接器根据两者规则生成最终的可执行文件(如
.elf
),明确代码和数据的物理与逻辑地址。
3. 关键区别详解
(1) 地址分配的粒度不同
- 分散加载文件:
直接指定段在物理内存中的绝对地址(如.text
必须从0x08000000
开始)。ER_IROM1 0x08000000 0x00080000 { ; 物理地址 0x08000000 开始存储代码*.o (RESET, +First).ANY (+RO) }
- 链接文件(.ld):
定义段之间的相对地址关系(如.data
紧跟在.text
之后),但不指定物理地址。SECTIONS {.text : { *(.text*) } ; 逻辑上 .text 在前.data : { *(.data*) } ; .data 在 .text 之后(具体地址由分散加载文件决定) }
(2) 工具链的适配性
- 分散加载文件:
是 Keil MDK(ARMCC 编译器)的专属配置,通过图形界面或文本描述内存布局。 - 链接文件(.ld):
是 GNU 工具链(如arm-none-eabi-gcc
)的标准配置,需手动编写脚本。
(3) 灵活性与复杂度
- 分散加载文件:
更适合快速配置多内存区域(如 STM32 的内部 Flash + 外部 SDRAM),但灵活性较低(依赖 Keil 的规则)。 - 链接文件(.ld):
可实现更复杂的逻辑(如动态段加载、自定义符号地址),但需要深入理解链接器行为。
4. 实际开发中的选择
(1) 使用 Keil MDK(ARMCC)
- 必须使用分散加载文件(如
.scat
或 Keil 自动生成的描述文件)。 - 链接文件(.ld)通常由 Keil 隐式管理,开发者直接配置分散加载规则即可。
(2) 使用 ARM-GCC(如 arm-none-eabi-gcc
)
- 必须使用链接脚本(.ld) 定义段逻辑和内存分配。
- 分散加载功能通过
.ld
文件中的MEMORY
和SECTIONS
实现(类似 Keil 的分散加载文件,但语法不同)。
5. 典型场景示例
场景:STM32F4 的代码与数据分离存储
Keil 的分散加载文件(.scat)
LR_IROM1 0x08000000 0x00080000 { ; Flash 区域ER_IROM1 0x08000000 0x00080000 { ; 代码和只读数据在 Flash*.o (RESET, +First).ANY (+RO)}RW_IRAM1 0x20000000 0x00010000 { ; 已初始化数据在 RAM.ANY (+RW +ZI)}
}
- 作用:
- 代码(
.text
)和常量(.rodata
)必须存储在 Flash 的0x08000000
开始处。 - 已初始化的全局变量(
.data
)初始值在 Flash,运行时拷贝到 RAM 的0x20000000
。
- 代码(
ARM-GCC 的链接脚本(.ld)
MEMORY {FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K ; Flash 区域RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K ; RAM 区域
}SECTIONS {.text : { *(.text*) } > FLASH ; 代码在 Flash.rodata : { *(.rodata*) } > FLASH ; 只读数据在 Flash.data : { *(.data*) } > RAM AT> FLASH ; .data 初始值在 Flash,运行时在 RAM.bss : { *(.bss*) } > RAM ; 未初始化数据在 RAM
}
- 作用:
- 与 Keil 分散加载文件功能相同,但通过 GNU 语法实现。
6. 总结
-
分散加载文件:
- 角色:物理内存分配器(“代码和数据放在哪里”)。
- 适用工具链:Keil MDK(ARMCC)。
- 优势:快速配置多内存区域,适合嵌入式硬件约束。
-
链接文件(.ld):
- 角色:逻辑段组织者(“代码和数据如何组织”)。
- 适用工具链:ARM-GCC(GNU 工具链)。
- 优势:灵活性高,支持复杂场景(如动态加载)。
-
本质关系:
两者是同一目标的不同实现路径——分散加载文件是 Keil 对链接器物理内存分配规则的封装,而 .ld 文件是 GNU 工具链的原生实现方式。最终目的均为生成符合硬件约束的可执行文件。