2. 链接脚本定制

任务说明

本案例要求编写GNU LD链接脚本,将Bootloader与应用程序分配到同一颗MCU的不同Flash区域,并正确设置入口地址。以STM32F103系列(512KB Flash,起始地址0x08000000)为例,规划Bootloader占用前32KB(0x08000000~0x08007FFF),应用程序从0x08008000开始。

Flash分区规划

区域起始地址大小内容
Bootloader0x0800000032KB启动代码、升级逻辑
App0x08008000480KB应用程序

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

验证方法

  1. 使用arm-none-eabi-nm bootloader.elf查看符号表,确认Reset_Handler地址位于0x08000000~0x08007FFF范围内。
  2. 使用arm-none-eabi-nm app.elf确认Reset_Handler地址位于0x08008000之后。
  3. 检查生成的hex文件:bootloader.hex首行应为:020000040800F2(表示扩展地址0x0800),app.hex首行应为:020000040800F2且数据记录起始地址不低于0x08008000。
  4. 将两个hex分别烧录到MCU后,上电复位应从Bootloader启动,Bootloader可通过跳转指令(如BX到0x08008000+4处的栈顶值)进入应用程序。

关键说明

应用程序的中断向量表偏移需在系统初始化时设置:SCB->VTOR = 0x08008000,否则中断无法正确响应。Bootloader跳转前需确认应用程序的栈指针(首4字节)和复位向量(次4字节)合法,再执行跳转。两个链接脚本的差异仅在于FLASH的ORIGIN和LENGTH,其余段布局保持一致,确保启动代码兼容。