交叉编译指南:为 ARM64 与 RISC-V 目标板编译程序

在嵌入式开发、物联网设备(如树莓派、开发板)以及云原生异构算力集群中,我们常常遇到目标设备(Target)CPU 算力有限、无法直接在本地流畅完成编译的情况。这时就需要借助高性能的主机(Host,如 x86_64 服务器)为异构 Target 平台编译出可直接运行的二进制文件——这一过程即为交叉编译 (Cross-Compilation)

本文将拆解交叉编译的技术要素,并覆盖 C/C++、Go 及 Rust 的实际交叉编译工作流。


1. 交叉编译的核心概念与工具链

1.1 三元组 (Target Triplet) 标识符

在交叉编译领域,CPU 架构与操作系统环境通过 GNU Target Triplet 进行定位,其格式一般为:

<arch>-<vendor>-<os>-<abi>

常见的三元组示例:

  • x86_64-pc-linux-gnu: 标准 x86_64 PC / Linux 环境。
  • aarch64-linux-gnu: ARM64 (64位 ARM) Linux 环境 (如树莓派 4/5 运行 64 位系统)。
  • riscv64-unknown-linux-gnu: 64位 RISC-V 架构 Linux 环境。
  • arm-linux-gnueabihf: 32位 ARM 硬浮点架构。

1.2 交叉编译工具链 (Cross Toolchain)

标准编译中使用 gcc;而在为 ARM64 交叉编译时,需要安装并调用带前缀的交叉编译器:aarch64-linux-gnu-gcc

在 Debian/Ubuntu 主机上安装交叉工具链:

# 安装 ARM64 交叉工具链
sudo apt install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
 
# 安装 RISC-V 交叉工具链
sudo apt install -y gcc-riscv64-linux-gnu g++-riscv64-linux-gnu

2. C/C++ 交叉编译实战 (CMake Toolchain)

在 C/C++ 项目中,直接调用 aarch64-linux-gnu-gcc main.c 可以编译单文件。但在包含第三方库依赖的大型 CMake 项目中,推荐编写 CMake 工具链文件 (Toolchain File)

2.1 编写 toolchain-aarch64.cmake

在项目根目录下创建 cmake/toolchain-aarch64.cmake

# 指定目标操作系统与 CPU 架构
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
 
# 指定交叉编译器
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
 
# 指定目标板 Sysroot 根目录路径(若需要链接 ARM64 系统的库)
# set(CMAKE_SYSROOT /path/to/arm64-sysroot)
 
# 调整搜索模式:头文件与库在 Sysroot / 交叉环境中查找,程序仅在 Host 环境查找
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

2.2 执行构建与产物校验

# 传入工具链文件
cmake -B build-arm64 -S . -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-aarch64.cmake
cmake --build build-arm64
 
# 使用 file 命令校验可执行文件架构
file build-arm64/my_app
# 输出示例: build-arm64/my_app: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV)...

3. 现代化语言交叉编译

与 C/C++ 复杂的库依赖相比,Go 和 Rust 等现代语言在交叉编译方面提供了极致的体验。

3.1 Go 语言交叉编译

Go 官方工具链天然内置交叉编译能力,只需通过环境变量切换目标操作系统 (GOOS) 与架构 (GOARCH):

# 交叉编译为 ARM64 Linux 可执行程序 (关闭 CGO 静态编译)
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o app-arm64 main.go
 
# 交叉编译为 RISC-V 64位 Linux 程序
CGO_ENABLED=0 GOOS=linux GOARCH=riscv64 go build -o app-riscv64 main.go

如需启用 CGO 依赖第三方 C 库,则需要额外指定交叉 C 编译器 CC

CGO_ENABLED=1 GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc go build -o app-cgo-arm64

3.2 Rust 语言交叉编译

Rust 借助 rustupcargo 实现了模块化的交叉编译支持。

步骤 1:添加目标架构 Target

rustup target add aarch64-unknown-linux-gnu
rustup target add riscv64gc-unknown-linux-gnu

步骤 2:配置链接器 (Linker)

在项目根目录下的 .cargo/config.toml 文件中指定链接器:

[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
 
[target.riscv64gc-unknown-linux-gnu]
linker = "riscv64-linux-gnu-gcc"

步骤 3:构建目标工程

cargo build --target aarch64-unknown-linux-gnu --release
# 生成的程序位于: target/aarch64-unknown-linux-gnu/release/my_project

4. QEMU 用户态仿真验证 (QEMU User Emulation)

在 x86_64 主机上完成交叉编译后,如果没有物理目标板,该如何验证程序正确性?Linux 提供了 QEMU 用户态仿真 + binfmt_misc 技术,允许在 x86_64 上像运行原生程序一样直接运行 ARM64 或 RISC-V 的二进制程序。

4.1 安装 QEMU 仿真器

sudo apt install -y qemu-user-static binfmt-support

4.2 直接透明运行异构二进制

安装完成后,内核 binfmt_misc 模块会自动根据二进制 ELF 头部的架构标识调用 qemu-aarch64-static

# 在 x86_64 机器上直接运行 ARM64 编译产物!
./build-arm64/my_app

系统内部的转换路径: ./my_app (ARM64) ──> binfmt_misc 内核识别 ──> qemu-aarch64-static ──> 输出结果


5. 总结

语言/工具链交叉编译难度核心配置要素
C / C++中度 / 偏繁琐需安装 gcc-<arch>-linux-gnu,编写 toolchain.cmake 并配置 Sysroot
Go极低 (纯 Go)设置环境变量 GOOS=linux GOARCH=arm64 CGO_ENABLED=0
Rustrustup target add + .cargo/config.toml 配置指定 linker

通过掌握交叉编译技术与 QEMU 仿真调试,能够极大提升在物联网、边缘计算以及嵌入式 Linux 系统开发中的构建与测试效率。

Navigation