3. 搭建交叉编译环境:工具链选择与安装

交叉编译环境,说白了就是让一台 x86 电脑给 ARM 板子编译程序。你想想看,嵌入式设备资源有限,跑个编译器都费劲,更别说编译整个 Linux 内核了。所以我们在 PC 上装一套交叉工具链,生成目标板能跑的二进制文件。

我刚开始做 BSP 移植时,觉得这步很简单——不就是装个 gcc 吗?结果第一次编译内核就报了一堆莫名其妙的链接错误。后来才发现,工具链版本不对,跟内核要求的 glibc 版本不匹配。嗯,这里面的坑不少。

3.1 工具链的选择策略

工具链不是越新越好,也不是越全越好。我个人的经验是:匹配优先。内核版本、glibc 版本、编译器版本,这三者必须对齐。

工具链类型 适用场景 我的建议
Linaro GCC ARM Cortex-A 系列,主流 BSP 首选,社区活跃,更新及时
Buildroot 生成 定制化需求,需要控制 glibc 版本 适合深度定制,但编译耗时
芯片厂商 SDK 特定 SoC,如 NXP、TI、Rockchip 最省事,但版本可能老旧
Yocto/OpenEmbedded 企业级项目,需要完整 Linux 发行版 功能强大,学习曲线陡峭
我的经验: 对于大多数 BSP 移植场景,直接用 Linaro 的预编译工具链就够了。我曾经花了一周时间自己从源码编译工具链,结果发现跟 Linaro 的版本性能差不多。除非你有特殊需求(比如必须用 uclibc 或 musl),否则别重复造轮子。

3.2 安装交叉工具链

以 ARM 64 位平台为例,我通常用 Linaro 的 aarch64-linux-gnu 工具链。安装步骤其实就三步:下载、解压、配置路径。

# 下载 Linaro 工具链(以 gcc-13.2 为例)
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/gcc-linaro-13.2.1-2023.11-x86_64_aarch64-linux-gnu.tar.xz

# 解压到 /opt 目录
sudo tar -xvf gcc-linaro-13.2.1-2023.11-x86_64_aarch64-linux-gnu.tar.xz -C /opt/

# 重命名方便管理
sudo mv /opt/gcc-linaro-13.2.1-2023.11-x86_64_aarch64-linux-gnu /opt/gcc-aarch64
注意: 解压路径建议放在 /opt 或 /usr/local 下,不要放 /home 目录。我曾经因为换了用户目录,导致整个团队的构建环境全部失效。另外,工具链的权限要设置好,建议 chmod 755。

3.3 配置环境变量

环境变量配置是新手最容易出错的地方。我个人习惯把工具链路径加到 ~/.bashrc 或 ~/.zshrc 里,而不是每次手动 export。

# 编辑 ~/.bashrc,在末尾添加
export PATH=/opt/gcc-aarch64/bin:$PATH
export CROSS_COMPILE=aarch64-linux-gnu-
export ARCH=arm64

# 使配置生效
source ~/.bashrc

这里解释一下三个变量的作用:

  • PATH:让系统能找到交叉编译器
  • CROSS_COMPILE:内核编译时自动加上这个前缀,比如调用 aarch64-linux-gnu-gcc
  • ARCH:指定目标架构,内核会根据这个选择对应的配置和头文件

避坑指南: 我曾经遇到过一个问题——明明配置了 PATH,但编译内核时还是调用了本机的 gcc。后来发现是因为 CROSS_COMPILE 变量没导出。记住,export 关键字不能省!另外,如果你同时装了多个工具链,PATH 的顺序决定了用哪个版本。

3.4 验证工具链

工具链装好没?跑几个命令验证一下。我一般做三步检查:

  1. 检查编译器版本:确认是交叉编译器,不是本机 gcc
  2. 编译测试程序:生成一个简单的 hello world,验证链接正常
  3. 检查文件类型:用 file 命令确认生成的是 ARM 架构的 ELF
# 第一步:检查版本
aarch64-linux-gnu-gcc --version
# 输出应该包含 "aarch64-linux-gnu-gcc (Linaro GCC 13.2.1)"

# 第二步:编译测试程序
echo '#include <stdio.h>
int main() {
    printf("Hello, BSP World!\n");
    return 0;
}' > test.c

aarch64-linux-gnu-gcc -o test test.c

# 第三步:检查文件类型
file test
# 输出应该包含 "ELF 64-bit LSB executable, ARM aarch64"

如果 file 命令显示的是 x86-64,说明你用的是本机编译器。嗯,这个问题我见过不下十次了。

3.5 工具链验证的完整流程

下面这张图展示了从下载到验证的完整流程,我建议你把它打印出来贴在工位上。

交叉编译环境搭建与验证流程 选择工具链 下载 & 解压 配置环境变量 验证编译器版本 编译测试程序 检查文件类型 常见问题 1. 工具链版本不匹配 导致链接错误 2. PATH 未正确配置 调用了本机 gcc 3. CROSS_COMPILE 未 export 内核编译找不到编译器 4. 文件类型检查失败 生成了 x86 可执行文件 5. glibc 版本冲突 运行时提示版本过低 6. 权限问题 工具链不可执行

3.6 多版本工具链管理

实际项目中,你可能需要同时维护多个内核版本。比如我手头有个项目,内核 4.19 用 gcc-7,内核 5.10 用 gcc-9,内核 6.1 用 gcc-13。怎么管理?

我的做法是用 环境变量切换脚本,而不是改全局 PATH。

# 创建切换脚本 set-env.sh
#!/bin/bash

case "$1" in
  "4.19")
    export PATH=/opt/gcc-aarch64-7/bin:$PATH
    export CROSS_COMPILE=aarch64-linux-gnu-
    export ARCH=arm64
    echo "Switched to gcc-7 for kernel 4.19"
    ;;
  "5.10")
    export PATH=/opt/gcc-aarch64-9/bin:$PATH
    export CROSS_COMPILE=aarch64-linux-gnu-
    export ARCH=arm64
    echo "Switched to gcc-9 for kernel 5.10"
    ;;
  "6.1")
    export PATH=/opt/gcc-aarch64-13/bin:$PATH
    export CROSS_COMPILE=aarch64-linux-gnu-
    export ARCH=arm64
    echo "Switched to gcc-13 for kernel 6.1"
    ;;
  *)
    echo "Usage: source set-env.sh [4.19|5.10|6.1]"
    ;;
esac

# 使用方式
source set-env.sh 6.1
小技巧: 注意我用的是 source 而不是直接执行脚本。因为 source 会在当前 shell 生效,而直接执行会开子 shell,环境变量传不回来。这个坑我踩过好几次。

3.7 验证工具链的完整测试

除了简单的 hello world,我建议你做一个更全面的测试,验证工具链的各个组件是否正常。

# 测试汇编器
aarch64-linux-gnu-as --version

# 测试链接器
aarch64-linux-gnu-ld --version

# 测试库管理器
aarch64-linux-gnu-ar --version

# 测试反汇编器
aarch64-linux-gnu-objdump --version

# 测试二进制工具
aarch64-linux-gnu-objcopy --version

# 编译一个带数学库的程序
echo '#include <stdio.h>
#include <math.h>
int main() {
    double x = sin(3.14159/4);
    printf("sin(45°) = %f\n", x);
    return 0;
}' > math_test.c

aarch64-linux-gnu-gcc -o math_test math_test.c -lm

# 检查动态链接库依赖
aarch64-linux-gnu-readelf -d math_test | grep NEEDED

如果所有命令都正常输出,说明你的交叉编译环境已经搭建好了。接下来就可以开始真正的 BSP 移植工作了。

最后提醒: 工具链搭建完成后,建议把整个 /opt/gcc-aarch64 目录备份一份。我曾经因为硬盘故障,重新下载工具链花了半天时间。另外,不同版本的 Linux 发行版对工具链的兼容性也不同——Ubuntu 20.04 和 22.04 的 glibc 版本不同,可能导致工具链运行异常。如果你遇到 "cannot execute binary file" 的错误,先检查一下本机的 glibc 版本。