1. 搭建嵌入式交叉编译环境

搭建嵌入式交叉编译环境 学习卡片

任务目标

在x86_64 Linux主机上搭建针对ARM Cortex-M系列MCU的交叉编译工具链,并验证能够将C源码编译为ARM Thumb指令集的ELF可执行文件。本案例以STM32F407(Cortex-M4F)为默认目标,使用ARM官方提供的gcc-arm-none-eabi工具链。

环境准备

宿主机要求:Linux(Ubuntu 22.04 LTS验证通过),已安装wget与make。工具链版本采用ARM官方10.3-2021.10发行包,无需root权限即可安装至用户目录。

工具链安装

# 下载ARM官方工具链(约100MB)
wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.10/gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2

# 解压至用户目录
tar xjf gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 -C ~/

# 添加至PATH(可写入~/.bashrc永久生效)
export PATH=~/gcc-arm-none-eabi-10.3-2021.10/bin:$PATH

# 验证安装
arm-none-eabi-gcc --version

若网络受限,可改用发行版软件包:sudo apt install gcc-arm-none-eabi(版本可能较旧,但功能等价)。

最小验证工程

创建以下两个文件,构成完整的编译验证闭环:

// main.c - 最小ARM裸机程序入口
#include <stdint.h>

// 栈顶地址(STM32F407的RAM起始地址+大小)
extern uint32_t _estack;

// 简单自旋延时
static void delay(volatile uint32_t count) {
    while (count--) {
        __asm volatile("nop");
    }
}

int main(void) {
    volatile uint32_t counter = 0;
    while (1) {
        counter++;
        delay(1000);
    }
}

// 启动入口:直接调用main(最小演示用)
void Reset_Handler(void) {
    main();
}

// 向量表(仅包含初始栈指针和复位向量)
__attribute__((section(".isr_vector")))
const uint32_t vector_table[] = {
    (uint32_t)&_estack,
    (uint32_t)Reset_Handler
};
# linker.ld - 最小链接脚本
MEMORY
{
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1M
    RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}

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

SECTIONS
{
    .isr_vector :
    {
        KEEP(*(.isr_vector))
    } > FLASH

    .text :
    {
        *(.text*)
        *(.rodata*)
    } > FLASH

    .data :
    {
        *(.data*)
    } > RAM AT> FLASH

    .bss :
    {
        *(.bss*)
    } > RAM
}

编译与验证

# 编译(生成Thumb指令的ELF文件)
arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 \
    -Os -Wall -Werror -nostdlib -ffreestanding \
    -T linker.ld -o firmware.elf main.c

# 验证1:检查文件类型
arm-none-eabi-readelf -h firmware.elf | grep -E "Machine|Flags"

# 验证2:检查向量表首两项(应为RAM地址和复位函数地址)
arm-none-eabi-objdump -s -j .isr_vector firmware.elf

# 验证3:确认生成Thumb指令
arm-none-eabi-objdump -d firmware.elf | grep -m1 "<Reset_Handler>:" -A2

通过条件:

  • readelf输出Machine为ARM,Flags包含Thumb和Hard float;
  • 向量表首项为20020000(RAM末尾地址),第二项指向Reset_Handler;
  • 反汇编中Reset_Handler处指令为16位或32位Thumb编码(非ARM状态下的32位指令)。

常见问题

  • 缺少_estack符号:链接脚本中已定义,若自行修改脚本需确保符号存在;
  • 浮点选项不匹配:Cortex-M4F必须使用-mfloat-abi=hard -mfpu=fpv4-sp-d16,否则链接时可能报错或生成低效软浮点调用;
  • 无法运行:本案例仅生成ELF文件,未包含烧录与调试步骤,需配合OpenOCD或ST-Link工具进行实际板级运行。