2. Bootloader中实现内存映射与链接脚本定制
任务目标
为Bootloader编写定制链接脚本,将启动代码固定在0x08000000(Flash起始地址),中断向量表紧随其后,并将可读写数据与堆栈放置在内部SRAM(起始0x20000000)。最终通过编译产物映射文件验证各段地址符合预期。
环境与依赖
- ARM交叉编译器:
arm-none-eabi-gcc(含ld、objdump) - 目标MCU:STM32F103系列(Cortex-M3,Flash 0x08000000,SRAM 0x20000000)
- 文件清单:
startup.s(启动代码)、main.c(主函数)、bootloader.ld(链接脚本)
关键实现
链接脚本是核心。通过MEMORY命令定义物理存储区域,SECTIONS命令将各段映射到指定地址。启动代码必须位于Flash起始地址,因此.isr_vector段排在最前;.text紧随其后;.data的LMA(加载地址)在Flash,VMA(运行地址)在SRAM,由启动代码完成拷贝;.bss和堆栈直接在SRAM。
完整代码
bootloader.ld
/* 内存区域定义 */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}
/* 栈顶地址 = SRAM起始 + SRAM大小 */
_estack = ORIGIN(SRAM) + LENGTH(SRAM);
SECTIONS
{
/* 中断向量表:必须从Flash起始地址开始 */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector))
. = ALIGN(4);
} > FLASH
/* 代码段 */
.text :
{
. = ALIGN(4);
*(.text*)
*(.rodata*)
. = ALIGN(4);
_etext = .;
} > FLASH
/* 初始化数据:LMA在Flash,VMA在SRAM */
_sidata = LOADADDR(.data);
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data*)
. = ALIGN(4);
_edata = .;
} > SRAM AT> FLASH
/* 零初始化数据 */
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} > SRAM
/* 堆(可选) */
.heap :
{
. = ALIGN(4);
_sheap = .;
. = . + 2K;
_eheap = .;
} > SRAM
}
startup.s(关键片段,演示向量表与段拷贝)
.syntax unified
.cpu cortex-m3
.thumb
.section .isr_vector
.word _estack /* 栈顶 */
.word Reset_Handler /* 复位向量 */
.section .text
.thumb_func
.global Reset_Handler
Reset_Handler:
/* 拷贝.data段从Flash到SRAM */
ldr r0, =_sdata
ldr r1, =_edata
ldr r2, =_sidata
copy_loop:
cmp r0, r1
bge copy_done
ldr r3, [r2], #4
str r3, [r0], #4
b copy_loop
copy_done:
/* 清零.bss段 */
ldr r0, =_sbss
ldr r1, =_ebss
movs r2, #0
zero_loop:
cmp r0, r1
bge zero_done
str r2, [r0], #4
b zero_loop
zero_done:
bl main
b .
main.c
void main(void) {
volatile int counter = 0;
while (1) {
counter++;
}
}
编译与链接命令
arm-none-eabi-as -mcpu=cortex-m3 -mthumb startup.s -o startup.o
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c main.c -o main.o
arm-none-eabi-ld -T bootloader.ld startup.o main.o -o bootloader.elf
arm-none-eabi-objdump -h bootloader.elf
验证方法与判定标准
- 地址验证:执行
objdump -h,检查输出中:
-.isr_vector的VMA为0x08000000
-.text紧随其后(VMA > 0x08000000且连续)
-.data的VMA在0x20000000区域,LMA在Flash区域
-.bss的VMA在SRAM区域 - 符号验证:
arm-none-eabi-nm bootloader.elf | grep _estack,应输出20005000(0x20000000+20K)。 - 反汇编验证:
arm-none-eabi-objdump -d bootloader.elf,确认Reset_Handler位于Flash地址(0x08000000+偏移),且第一条指令为栈指针初始化。
若以上地址均符合预期,则链接脚本定制成功,启动代码将在指定地址正确运行。