3. 搭建交叉编译环境(下):下载并安装ARM交叉编译工具链(gcc-arm-none-eabi)、配置环境变量、验证工具链
上一节我们把虚拟机环境折腾好了,Ubuntu 也跑起来了。但说实话,那只是个空壳子。真正干活儿的家伙——ARM 交叉编译工具链,还没请进来呢。
我记得刚入行那会儿,以为装个编译器就是 apt-get install gcc 完事儿。结果编译出来的程序在 ARM 板子上跑不起来,折腾了两天才发现——我装的是 x86 的 gcc,不是交叉编译器。嗯,这个坑我替你们踩过了。
3.1 什么是交叉编译工具链?
说白了,交叉编译就是「在 A 平台上编译出能在 B 平台上运行的程序」。我们现在的场景是:在 x86 的 Ubuntu 虚拟机里,编译出能在 ARM Cortex-M 系列芯片上运行的二进制代码。
你想想看,STM32 那种单片机,它自己跑个 RTOS 都费劲,哪来的资源跑编译器?所以必须靠 PC 来替它编译。这就是交叉编译存在的意义。
我们选 gcc-arm-none-eabi 而不是 arm-linux-gnueabi,原因很简单:我们的目标系统是裸机或 FreeRTOS,没有 Linux 内核。eabi 后缀表示 Embedded Application Binary Interface,专为嵌入式设计。
3.2 下载工具链
我个人习惯从 ARM 官方渠道下载,虽然慢点,但版本干净、没广告。你也可以用国内镜像,后面我会给地址。
3.2.1 官方下载方式
ARM 官方把工具链托管在 Arm Developer 站点。目前稳定版本是 gcc-arm-none-eabi-10.3-2021.10,我建议就用这个版本。太新的版本可能有兼容性问题,太老的版本不支持新芯片。
# 进入工作目录
cd ~/tools
# 下载 Linux x86_64 版本
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
3.2.2 验证下载完整性
下载完别急着解压。先检查一下文件哈希,防止下载损坏。我在项目里遇到过解压到一半报错的情况,就是因为网络中断导致文件不完整。
# 计算 SHA256
sha256sum gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2
# 官方 SHA256 值(以官网公布为准)
# 输出应该类似:f8b0fd1a2b7c9d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d
如果哈希值对不上,重新下载。别偷懒,这一步省不了。
3.3 安装与目录结构
解压完其实就算「安装」了。gcc-arm-none-eabi 是绿色软件,不需要运行安装脚本。解压后的目录结构大概是这样:
| 目录/文件 | 说明 |
|---|---|
bin/ |
可执行文件,包括 arm-none-eabi-gcc、arm-none-eabi-g++、arm-none-eabi-objcopy 等 |
lib/ |
库文件,包括 libgcc、libc_nano 等 |
include/ |
头文件,包括 stdint.h、stdlib.h 等 C 标准库头文件 |
arm-none-eabi/ |
目标平台相关的库和头文件 |
share/ |
文档和 man 手册 |
我个人习惯把工具链放在 ~/tools/ 下,这样不会污染系统目录。如果你想让所有用户都能用,可以放到 /opt/ 下,但需要 root 权限。
3.4 配置环境变量
工具链装好了,但系统还不知道它在哪里。每次敲 arm-none-eabi-gcc 都要输完整路径,那也太累了。我们需要把 bin/ 目录加到 PATH 环境变量里。
3.4.1 临时生效(当前终端)
export PATH=$PATH:~/tools/gcc-arm-none-eabi-10.3-2021.10/bin
这条命令只对当前终端窗口有效。关掉终端再打开,又得重新设。所以我们需要永久配置。
3.4.2 永久生效(推荐)
我建议把配置写到 ~/.bashrc 里。这样每次打开终端都会自动加载。
# 编辑 .bashrc
vim ~/.bashrc
# 在文件末尾添加以下内容
export GCC_ARM_HOME=$HOME/tools/gcc-arm-none-eabi-10.3-2021.10
export PATH=$PATH:$GCC_ARM_HOME/bin
# 保存退出后,使配置生效
source ~/.bashrc
~/.zshrc,不是 ~/.bashrc。我曾经帮同事排查问题,发现他配了 .bashrc 但用的是 zsh,折腾了半天才找到原因。
3.5 验证工具链
配置完环境变量,我们来验证一下工具链是否正常工作。这一步很关键,别跳过。
3.5.1 检查版本
arm-none-eabi-gcc --version
如果输出类似下面的信息,说明工具链已经可以调用了:
arm-none-eabi-gcc (GNU Arm Embedded Toolchain 10.3-2021.10) 10.3.1 20210824 (release)
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
3.5.2 编译一个测试程序
写一个最简单的 C 程序,交叉编译试试:
# 创建测试文件
cat > test.c << EOF
#include <stdio.h>
int main(void) {
printf("Hello from ARM!\n");
return 0;
}
EOF
# 交叉编译
arm-none-eabi-gcc -c test.c -o test.o
# 查看生成的目标文件信息
arm-none-eabi-objdump -f test.o
输出中应该能看到 file format elf32-littlearm,说明目标文件是 ARM 格式的。如果显示的是 elf64-x86-64,那说明你用的还是本机编译器。
3.5.3 完整编译并生成 .hex 文件
对于嵌入式开发,我们通常需要生成 .hex 或 .bin 文件,而不是可执行文件。因为单片机没有操作系统来加载 ELF。
# 完整编译流程
arm-none-eabi-gcc -c test.c -o test.o
arm-none-eabi-ld -Ttext 0x08000000 test.o -o test.elf
arm-none-eabi-objcopy -O ihex test.elf test.hex
arm-none-eabi-objcopy -O binary test.elf test.bin
# 查看生成的 hex 文件
head -20 test.hex
3.6 知识体系总览
下面这张图把本章的核心逻辑串起来了。从下载到验证,每一步都有它的目的。
3.7 常见问题与避坑指南
工具链装好了,但实际用起来还会遇到各种问题。我把这些年踩过的坑整理一下:
- 「找不到命令」:检查 PATH 是否配置正确。运行
echo $PATH看看有没有包含工具链的 bin 目录。我曾经配完环境变量忘了 source,还以为是系统坏了。 - 「权限不足」:如果工具链放在
/opt/下,普通用户可能没有执行权限。用chmod +x bin/*解决。 - 「版本冲突」:系统里可能装了多个版本的交叉编译器。用
which arm-none-eabi-gcc查看当前使用的是哪个。我建议用绝对路径调用,避免混淆。 - 「32 位兼容库缺失」:如果你用的是 64 位系统,但工具链是 32 位的,需要安装 32 位兼容库。Ubuntu 下执行
sudo apt install lib32z1 lib32ncurses5。
3.8 本章小结
到这一步,交叉编译环境就算搭好了。我们完成了:
- 从 ARM 官方下载了 gcc-arm-none-eabi 工具链
- 验证了文件完整性
- 解压并配置了环境变量
- 编译了测试程序,生成了 ARM 格式的目标文件