2. 链接脚本定制
任务说明
本案例要求编写GNU LD链接脚本,将Bootloader与应用程序分配到同一颗MCU的不同Flash区域,并正确设置入口地址。以STM32F103系列(512KB Flash,起始地址0x08000000)为例,规划Bootloader占用前32KB(0x08000000~0x08007FFF),应用程序从0x08008000开始。
Flash分区规划
| 区域 | 起始地址 | 大小 | 内容 |
|---|---|---|---|
| Bootloader | 0x08000000 | 32KB | 启动代码、升级逻辑 |
| App | 0x08008000 | 480KB | 应用程序 |
Bootloader链接脚本(bootloader.ld)
/* bootloader.ld - Bootloader区域链接脚本 */
ENTRY(Reset_Handler)
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
}
_estack = ORIGIN(RAM) + LENGTH(RAM);
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
_sidata = LOADADDR(.data);
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data)
*(.data*)
. = ALIGN(4);
_edata = .;
} > RAM AT> FLASH
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} > RAM
}
应用程序链接脚本(app.ld)
/* app.ld - 应用程序区域链接脚本 */
ENTRY(Reset_Handler)
MEMORY
{
FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 480K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
}
_estack = ORIGIN(RAM) + LENGTH(RAM);
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
_sidata = LOADADDR(.data);
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data)
*(.data*)
. = ALIGN(4);
_edata = .;
} > RAM AT> FLASH
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} > RAM
}
编译与链接命令
# Bootloader编译(假设startup_bootloader.o和main_bootloader.o已生成)
arm-none-eabi-ld -T bootloader.ld -o bootloader.elf \
startup_bootloader.o main_bootloader.o
# 应用程序编译(假设startup_app.o和main_app.o已生成)
arm-none-eabi-ld -T app.ld -o app.elf \
startup_app.o main_app.o
# 生成hex烧录文件
arm-none-eabi-objcopy -O ihex bootloader.elf bootloader.hex
arm-none-eabi-objcopy -O ihex app.elf app.hex
验证方法
- 使用
arm-none-eabi-nm bootloader.elf查看符号表,确认Reset_Handler地址位于0x08000000~0x08007FFF范围内。 - 使用
arm-none-eabi-nm app.elf确认Reset_Handler地址位于0x08008000之后。 - 检查生成的hex文件:
bootloader.hex首行应为:020000040800F2(表示扩展地址0x0800),app.hex首行应为:020000040800F2且数据记录起始地址不低于0x08008000。 - 将两个hex分别烧录到MCU后,上电复位应从Bootloader启动,Bootloader可通过跳转指令(如
BX到0x08008000+4处的栈顶值)进入应用程序。
关键说明
应用程序的中断向量表偏移需在系统初始化时设置:SCB->VTOR = 0x08008000,否则中断无法正确响应。Bootloader跳转前需确认应用程序的栈指针(首4字节)和复位向量(次4字节)合法,再执行跳转。两个链接脚本的差异仅在于FLASH的ORIGIN和LENGTH,其余段布局保持一致,确保启动代码兼容。