交叉编译

一、什么是交叉编译(Cross Compilation)

交叉编译是指:

在一种平台上运行的编译器生成另一种平台可执行代码的过程。

  • 举个常见例子:你在 x86 的 PC(Linux)上 编写代码,但目标平台是一个 ARM 架构的嵌入式设备(如树莓派),你在 PC 上使用一个 可以生成 ARM 代码的编译器(如 arm-none-eabi-gcc),这个过程就是交叉编译。

二、核心概念

术语 含义
Build 编译器实际运行的机器(你写代码并编译的地方)
Host 编译器生成的代码最终运行在哪台机器上
Target 编译器生成的代码将用于哪个平台的 工具链目标格式(一般是 ABI + ISA)
  • 大多数时候,Host 和 Target 是一样的(比如你编译一个 Linux 上运行的程序)。
  • 而在交叉编译时,Host ≠ Target,你必须使用一个能为目标平台输出代码的编译器

三、Canadian Cross 类比讲解

让我们回顾你的例子,把它图解成如下:

机器A(慢)       机器B(快)       机器C(慢)
 ccA             无编译器          无编译器
 ┃                  ┃                  ┃
 ┃ build cc1 ──────►┃                  ┃
 ┃ cc1 生成 cc2────►┃                  ┃
 ┃                  ┃ cc2 build ccC───►┃
 ┃                  ┃                  ┃ccC在C上运行

这是一个 三阶段构建流程,用于解决目标平台(C)缺乏本地编译器、开发机器运行太慢等问题。


四、逐阶段解释

阶段 1:A 构建 cc1(A → B)

  • Build: A
  • Host: B
  • Target: C
  • 你在 A 上运行 ccA,编译出一个能在 B 上运行但为 C 输出代码的交叉编译器 cc1
  • 也就是说:cc1 是一个 build=A, host=B, target=C 的三元交叉编译器。

阶段 2:A 构建 cc2(B → C)

  • Build: A
  • Host: C
  • Target: C
  • 你用 cc1 再编译出 cc2,这个 cc2 是一个运行在 B 上、为 C 输出代码的交叉编译器。

注意:从 cc2 开始,我们就可以把编译任务交给更快的 B 了。

阶段 3:B 构建 ccC

  • Build: B
  • Host: C
  • Target: C
  • 你用 cc2,在机器 B 上为 C 编译出最终的交叉编译器 ccC,这就是我们真正要用的编译器,它将在 C 上运行。

五、如果要验证 ccC 是否正确,还需阶段 4

阶段 4:在 C 上使用 ccC

  • Build: C
  • Host: C
  • Target: C
  • 在目标机器 C 上运行 ccC 自举自己一遍,看是否能正常编译自己、运行正确。

这时候 ccC 就不再是交叉编译器了,而是一个 本地编译器


六、分类总结:不同类型的编译器

编译器 Build Host Target 类型
ccA A A A 本地编译器
cc1 A B C 三重交叉编译器
cc2 B C C 交叉编译器
ccC C C C 本地编译器

七、现实中的应用场景

  • 编译 Linux 内核、系统工具、操作系统内核到 ARM 或 RISC-V 嵌入式设备。
  • 构建操作系统发行版(Debian 的 multi-arch cross build)。
  • 构建 rustc(Rust 编译器)或 gcc 自举(bootstrap)时的多阶段构建。
  • Nix、Yocto 等构建系统中广泛使用。

八、交叉编译工具链的常见格式

  • x86_64-linux-gnu-gcc: 运行在 x86 Linux 上,为 x86 Linux 输出代码。
  • arm-none-eabi-gcc: 运行在任意平台,为 ARM 裸机输出代码。
  • riscv64-unknown-elf-gcc: 运行在任意平台,为 RISC-V 裸机生成代码。

九、如何理解 Canadian Cross 的意义?

Canadian Cross 是一种复杂但非常实用的技术手段,用于解决编译环境缺失、目标平台运行缓慢、或者需要构建多目标平台工具链的场景。