2. 链接脚本定制

任务目标

编写GNU LD链接脚本,将Bootloader与应用程序分别映射到独立Flash区域,并确保Bootloader入口地址与向量表位置一致。本案例面向STM32F103系列(Flash起始0x08000000,容量512KB),Bootloader占用前32KB,应用程序紧随其后。

环境依赖

  • ARM交叉编译器:arm-none-eabi-gcc(含GNU LD)
  • 目标芯片:STM32F103C8T6(Flash 64KB)或兼容型号
  • 工程文件:bootloader.ld、main.c、startup.s(最小启动文件)

链接脚本实现

以下链接脚本将Flash划分为两个区域:FLASH_BL(0x08000000,长度32KB)与FLASH_APP(0x08008000,长度32KB)。Bootloader的入口地址通过ENTRY(Reset_Handler)指定,向量表首地址与_bl_start对齐。

/* bootloader.ld */
ENTRY(Reset_Handler)

MEMORY
{
  FLASH_BL (rx)  : ORIGIN = 0x08000000, LENGTH = 32K
  FLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 32K
  RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 20K
}

_estack = ORIGIN(RAM) + LENGTH(RAM);

SECTIONS
{
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector))
    . = ALIGN(4);
  } > FLASH_BL

  .text :
  {
    . = ALIGN(4);
    *(.text)
    *(.text*)
    . = ALIGN(4);
  } > FLASH_BL

  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)
    *(.rodata*)
    . = ALIGN(4);
  } > FLASH_BL

  _bl_end = .;

  .data :
  {
    . = ALIGN(4);
    _sdata = .;
    *(.data)
    *(.data*)
    _edata = .;
  } > RAM AT> FLASH_BL

  .bss :
  {
    . = ALIGN(4);
    _sbss = .;
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    _ebss = .;
    __bss_end__ = _ebss;
  } > RAM

  . = ALIGN(4);
  _end = .;
}

最小启动与主程序

/* 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:
    ldr r0, =_sbss
    ldr r1, =_ebss
    movs r2, #0
bss_loop:
    cmp r0, r1
    bge data_init
    str r2, [r0], #4
    b bss_loop
data_init:
    ldr r0, =_sdata
    ldr r1, =_edata
    ldr r2, =_bl_end
data_loop:
    cmp r0, r1
    bge main_call
    ldr r3, [r2], #4
    str r3, [r0], #4
    b data_loop
main_call:
    bl main
    b .

/* main.c */
#include <stdint.h>

void main(void)
{
    volatile uint32_t *bl_flag = (uint32_t *)0x20000000;
    *bl_flag = 0xDEADBEEF;  /* Bootloader运行标志 */
    while (1) { }
}

编译与链接命令

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-objcopy -O binary bootloader.elf bootloader.bin
arm-none-eabi-objdump -h bootloader.elf

验证方法与判定标准

  1. 区域隔离验证:执行arm-none-eabi-objdump -h bootloader.elf,确认.isr_vector与.text的VMA(虚拟地址)均在0x08000000~0x08007FFF范围内,且.data的LMA(加载地址)位于FLASH_BL区域,VMA位于RAM。
  2. 入口地址验证:执行arm-none-eabi-nm bootloader.elf | grep Reset_Handler,预期输出地址为08000000或08000004(取决于向量表对齐),与链接脚本中ENTRY声明一致。
  3. 边界检查:_bl_end符号值应小于0x08008000(FLASH_APP起始地址),可通过arm-none-eabi-nm bootloader.elf | grep _bl_end确认。
  4. 烧录验证:将bootloader.bin烧录至芯片0x08000000地址,复位后观察RAM地址0x20000000处数值为0xDEADBEEF(通过调试器读取),证明Bootloader正常启动。

常见问题

  • 链接错误“region FLASH_BL overflowed”:Bootloader代码超过32KB,需减小LENGTH或优化代码体积。
  • 应用程序无法跳转:若后续实现跳转功能,需在应用程序链接脚本中将FLASH_APP的ORIGIN设为0x08008000,并确保应用程序向量表偏移(通过SCB->VTOR设置)。
  • 启动文件缺失:本案例使用最小启动文件,实际工程中需补充系统时钟初始化与C库启动代码(如__libc_init_array)。