2. 实现Bootloader与应用程序的内存布局划分
任务目标
在嵌入式系统中,Bootloader与应用程序共存于同一颗Flash芯片。若两者地址重叠,应用程序升级时可能覆盖Bootloader,导致设备变砖。本案例通过GNU ld链接脚本,将Bootloader固定在Flash起始区域(0x08000000),应用程序紧随其后(0x08008000),并分别配置独立的向量表与RAM区间,实现物理隔离。
环境与依赖
- 工具链:arm-none-eabi-gcc(含ld)
- 目标芯片:STM32F103系列(Flash 512KB,RAM 64KB,起始地址0x20000000)
- 工程文件:
bootloader.ld、app.ld、common.ld(公共定义)
关键完整代码
以下为最小可复现的链接脚本与启动验证代码。三个文件需放置于同一目录。
// common.ld —— 公共芯片定义
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
}
// bootloader.ld —— Bootloader区域划分
INCLUDE common.ld
/* Bootloader占用Flash前32KB */
MEMORY
{
BOOT_FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
APP_FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 480K
}
REGION_ALIAS("TEXT_FLASH", BOOT_FLASH);
REGION_ALIAS("DATA_RAM", RAM);
INCLUDE stm32f1_layout.inc
// app.ld —— 应用程序区域划分
INCLUDE common.ld
MEMORY
{
APP_FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 480K
}
REGION_ALIAS("TEXT_FLASH", APP_FLASH);
REGION_ALIAS("DATA_RAM", RAM);
INCLUDE stm32f1_layout.inc
// stm32f1_layout.inc —— 段布局模板(两个脚本共用)
SECTIONS
{
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector))
. = ALIGN(4);
} > TEXT_FLASH
.text :
{
. = ALIGN(4);
*(.text*)
*(.rodata*)
. = ALIGN(4);
_etext = .;
} > TEXT_FLASH
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data*)
. = ALIGN(4);
_edata = .;
} > DATA_RAM AT> TEXT_FLASH
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} > DATA_RAM
_estack = ORIGIN(DATA_RAM) + LENGTH(DATA_RAM);
}
编译时分别指定链接脚本:
# Bootloader编译
arm-none-eabi-gcc -T bootloader.ld -nostdlib -Wl,--no-warn-rwx-segments \
boot_startup.s main.c -o boot.elf
# 应用程序编译(需在代码中定义APP_BASE=0x08008000)
arm-none-eabi-gcc -T app.ld -nostdlib -Wl,--no-warn-rwx-segments \
-DAPP_BASE=0x08008000 app_startup.s main.c -o app.elf
落地与验证方法
- 检查地址分配:编译后执行
arm-none-eabi-nm boot.elf | grep _estack应输出20010000(RAM顶端);arm-none-eabi-nm app.elf | grep _etext应大于0x08008000。 - 确认镜像边界:
arm-none-eabi-objdump -h boot.elf查看.isr_vector的VMA应为08000000;app.elf的VMA应为08008000。 - 烧录验证:将
boot.bin烧录至0x08000000,app.bin烧录至0x08008000。复位后若Bootloader能跳转至APP_BASE且应用程序正常执行,则划分成功。 - 互不干扰测试:仅擦除并重写应用程序区(0x08008000之后),Bootloader区数据保持不变;反之亦然。
常见问题
- 链接报错“region FLASH overflowed”:Bootloader代码超过32KB,需增大
BOOT_FLASH长度并同步调整APP_FLASH起始地址。 - 应用程序无法中断响应:需在应用程序启动代码中设置
SCB->VTOR = APP_BASE,否则向量表仍指向Bootloader区域。 - RAM重叠风险:两个程序各自独立编译,若都使用
0x20000000起始的RAM,运行时需确保Bootloader在跳转前不占用应用程序所需RAM区间。