3. 交叉编译核心概念

好,咱们进入正题。上一章聊了什么是交叉编译,这一章直接动手——把工具链请进你的开发机。

说白了,交叉编译工具链就是一套能在你的 x86 电脑上生成 ARM 可执行文件的「编译器全家桶」。你不需要在 ARM 板子上装 gcc,那太慢了,也装不全。我刚开始做嵌入式那会儿,就傻乎乎地在 ARM 板子上编译内核,结果一个晚上没编译完……后来才明白,交叉编译才是正道。

3.1 从哪里下载?Linaro 预编译工具链

工具链的获取方式有好几种:

  • 芯片厂商提供:比如 NXP、TI 官方 SDK 里自带的工具链,最省心,但版本可能偏旧。
  • 自己用 crosstool-NG 编译:自由度最高,但耗时,适合有特殊需求的场景。
  • Linaro 预编译工具链:我推荐这个。Linaro 是 ARM 生态的顶级组织,他们维护的预编译工具链稳定、更新快、支持全。

我个人习惯用 Linaro 的 gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf 这个版本。为什么选 7.5.0?因为它在稳定性和新特性之间平衡得不错,而且很多 BSP(板级支持包)默认就依赖这个版本。

小提示:如果你用的是较新的内核(比如 5.10+),建议选 gcc 9.x 或 10.x 版本。太老的编译器可能不支持某些内核编译选项。

3.2 解压与安装

下载下来的文件是一个 .tar.xz 压缩包。解压命令很简单:

# 假设文件下载到了 ~/Downloads 目录
cd ~/Downloads
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz

解压后你会得到一个文件夹。我习惯把它放到 /opt 目录下,方便管理:

sudo mv gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf /opt/
cd /opt/
ls -l

你会看到类似这样的目录结构:

gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/
├── arm-linux-gnueabihf/
├── bin/
├── include/
├── lib/
├── libexec/
└── share/

其中 bin/ 目录里放的就是所有可执行工具,比如 arm-linux-gnueabihf-gcc、arm-linux-gnueabihf-g++、arm-linux-gnueabihf-ld 等等。

注意:不要直接把工具链解压到 /usr/bin 或 /usr/local/bin。那样会污染系统环境,而且多个工具链版本共存时容易冲突。我见过有人这么干,结果换项目时编译器版本对不上,折腾了一整天。

3.3 配置环境变量

解压完只是第一步。你得让系统知道这些工具在哪里。配置环境变量有两种方式:

方式一:临时生效(适合测试)

export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH

这种方式只在当前终端窗口有效。关掉终端就没了。

方式二:永久生效(推荐)

编辑 ~/.bashrc 文件(如果你用 zsh,就编辑 ~/.zshrc):

echo 'export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

这样每次打开终端,工具链就自动在 PATH 里了。

我的习惯:我一般会在 ~/.bashrc 里加一个别名,方便切换不同版本的工具链。比如:
alias arm-gcc='export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH'
这样我只需要输入 arm-gcc 就能切换到对应的工具链环境。

3.4 验证工具链

配置完环境变量,别急着写代码。先验证一下工具链是否正常工作。

3.4.1 检查编译器版本

arm-linux-gnueabihf-gcc --version

如果输出类似下面这样,说明工具链已经就绪:

arm-linux-gnueabihf-gcc (Linaro GCC 7.5.0-2019.12) 7.5.0
Copyright (C) 2017 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.4.2 编译一个简单的测试程序

写一个最简单的 C 程序:

// hello.c
#include <stdio.h>

int main() {
    printf("Hello, Embedded Linux!\n");
    return 0;
}

用交叉编译器编译:

arm-linux-gnueabihf-gcc -o hello hello.c

编译完成后,用 file 命令查看生成的可执行文件:

file hello

你应该会看到类似这样的输出:

hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, not stripped

看到 ARM 字样了吗?这说明这个程序是给 ARM 架构的 CPU 用的,不能在你的 x86 电脑上直接运行。这就是交叉编译的证明。

关键点:如果你在 x86 电脑上运行 ./hello,会报错 cannot execute binary file: Exec format error。别慌,这是正常的。这个程序只能放到 ARM 板子上跑。

3.4.3 检查工具链的完整工具集

一个完整的工具链不止有 gcc。我建议你检查一下这些工具是否都在:

工具名 作用 验证命令
arm-linux-gnueabihf-gcc C 编译器 --version
arm-linux-gnueabihf-g++ C++ 编译器 --version
arm-linux-gnueabihf-ld 链接器 --version
arm-linux-gnueabihf-ar 静态库打包工具 --version
arm-linux-gnueabihf-objdump 反汇编工具 --version
arm-linux-gnueabihf-strip 去除调试符号 --version

如果这些工具都能正常输出版本信息,那你的工具链就完全可用了。

3.5 避坑指南

我在项目中遇到过几个常见问题,这里列出来,你遇到了可以少走弯路:

  • PATH 顺序问题:如果你系统里本来就装了 ARM 版的 gcc(比如用 apt 装的),注意 PATH 里要把 Linaro 的 bin 目录放在前面。否则系统可能调用到错误的编译器。
  • 32 位库缺失:Linaro 工具链是 64 位的,但某些依赖库是 32 位的。如果你在 64 位系统上运行报错 No such file or directory,试试安装 sudo apt install lib32z1 lib32ncurses5。
  • 权限问题:放在 /opt 下的工具链,普通用户可能没有执行权限。记得 chmod +x 或者用 sudo 执行。
我曾经踩过的坑:有一次我下载了错误的工具链版本——arm-linux-gnueabi(软浮点)而不是 arm-linux-gnueabihf(硬浮点)。结果编译出来的程序在板子上跑起来就段错误。检查了三天才发现是浮点 ABI 不匹配。所以下载时一定要确认你的目标板 CPU 支持哪种浮点模式。

3.6 本章知识结构图

下面这张图帮你梳理了工具链获取与安装的完整流程:

交叉编译工具链获取与安装流程 交叉编译工具链获取与安装流程 1. 从Linaro下载 2. 解压到 /opt 3. 配置环境变量 (PATH) 4. 验证工具链 检查编译器版本 编译测试程序 关键:确认目标板 CPU 架构(ARMv7/ARMv8)和浮点模式(硬浮点/软浮点)