GCC 完全指南 / 11 - 交叉编译
11 - 交叉编译
深入学习交叉编译的原理、工具链配置、target/host/build 三元组和 sysroot 管理。
11.1 交叉编译概念
交叉编译(Cross Compilation)是指在一种架构(Host)上编译出在另一种架构(Target)上运行的代码。
┌───────────────┐
│ 源代码 (.c) │
└───────┬───────┘
│
┌─────────────────────────┼─────────────────────────┐
│ 交叉编译器(运行在 Host 上) │
│ 为 Target 架构生成代码 │
└─────────────────────────┬─────────────────────────┘
│
┌───────▼───────┐
│ Target 可执行 │
│ 文件或库 │
└───────────────┘
三个关键术语
| 术语 | 说明 | 示例 |
|---|---|---|
| Build | 编译器运行的机器 | x86-64 Ubuntu 桌面 |
| Host | 编译出的程序运行的机器 | ARM64 树莓派 |
| Target | 编译器生成代码的目标架构 | 通常与 Host 相同 |
典型交叉编译场景:
Build: x86-64 Linux(你的开发机)
Host: aarch64 Linux(目标设备,如树莓派4)
Target: aarch64 Linux(与 Host 相同)
Canadian Cross 场景:
Build: x86-64 Linux(编译机器 A)
Host: ARM Linux(编译器运行在机器 B)
Target: MIPS Linux(生成 MIPS 代码)
11.2 交叉编译工具链命名规则
工具链命名遵循 {target-triplet}-{tool} 的格式。
常见目标三元组
| 三元组 | 架构 | 说明 |
|---|---|---|
aarch64-linux-gnu | ARM 64-bit | ARMv8-A Linux |
arm-linux-gnueabihf | ARM 32-bit 硬浮点 | ARMv7 Linux |
arm-linux-gnueabi | ARM 32-bit 软浮点 | 旧式 ARM |
riscv64-linux-gnu | RISC-V 64-bit | RISC-V Linux |
x86_64-linux-gnu | x86-64 | 本机 64-bit |
i686-linux-gnu | x86 32-bit | 本机 32-bit |
mips-linux-gnu | MIPS 32-bit | 路由器等 |
mips64-linux-gnuabi64 | MIPS 64-bit | |
powerpc64le-linux-gnu | PowerPC 64-bit LE | IBM POWER |
arm-none-eabi | ARM bare-metal | 嵌入式无 OS |
xtensa-esp32-elf | Xtensa (ESP32) | ESP32 系列 |
三元组结构
<arch>-<vendor>-<os>-<abi>
aarch64-linux-gnu
│ │ │
│ │ └── ABI: GNU (glibc)
│ └──────── OS: Linux
└──────────────── Architecture: AArch64
arm-linux-gnueabihf
│ │ │ │ │
│ │ │ │ └── Float: hard-float
│ │ │ └──── Endianness: (默认 little)
│ │ └─────── ABI: GNU EABI
│ └─────────── OS: Linux
└─────────────────── Architecture: ARM
11.3 安装交叉编译工具链
Ubuntu/Debian
# ARM 64-bit
sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
# ARM 32-bit (hard-float)
sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
# RISC-V 64-bit
sudo apt install gcc-riscv64-linux-gnu g++-riscv64-linux-gnu
# MIPS
sudo apt install gcc-mips-linux-gnu g++-mips-linux-gnu
# ARM bare-metal (Cortex-M)
sudo apt install gcc-arm-none-eabi
# 对应的 binutils
sudo apt install binutils-aarch64-linux-gnu
# 对应的 C 库头文件(sysroot 的一部分)
sudo apt install libc6-dev-arm64-cross
sudo apt install libc6-dev-armhf-cross
# 验证
aarch64-linux-gnu-gcc --version
crosstool-NG(自定义工具链)
# 安装 crosstool-NG
git clone https://github.com/crosstool-ng/crosstool-ng.git
cd crosstool-ng
./bootstrap && ./configure --prefix=$HOME/ct-ng
make && make install
# 创建工具链
mkdir ~/ct-build && cd ~/ct-build
~/ct-ng/bin/ct-ng aarch64-unknown-linux-gnu
~/ct-ng/bin/ct-ng menuconfig # 自定义配置
~/ct-ng/bin/ct-ng build # 编译工具链(可能需要 30+ 分钟)
# 工具链安装在 ~/x-tools/aarch64-unknown-linux-gnu/
export PATH=~/x-tools/aarch64-unknown-linux-gnu/bin:$PATH
Bootlin 预编译工具链
# 从 https://toolchains.bootlin.com/ 下载预编译的工具链
# 解压即可使用
wget https://toolchains.bootlin.com/downloads/releases/toolchains/aarch64/tarballs/aarch64--glibc--bleeding-edge-2024.02-1.tar.bz2
tar xf aarch64--glibc--bleeding-edge-2024.02-1.tar.bz2
export PATH=$(pwd)/aarch64--glibc--bleeding-edge-2024.02-1/bin:$PATH
11.4 Sysroot 管理
Sysroot 是目标平台的文件系统镜像,包含头文件和库。
sysroot/
├── usr/
│ ├── include/ ← 目标平台的头文件
│ ├── lib/ ← 目标平台的库
│ └── lib64/
├── lib/
└── etc/
指定 Sysroot
# 使用 --sysroot 选项
aarch64-linux-gnu-gcc --sysroot=/path/to/sysroot -o hello main.c
# 查看默认 sysroot
aarch64-linux-gnu-gcc -print-sysroot
# 查看默认搜索路径
aarch64-linux-gnu-gcc -print-search-dirs
从目标设备获取 Sysroot
# 方法 1: rsync 从运行目标系统的设备同步
rsync -a --delete root@target:/usr/include/ sysroot/usr/include/
rsync -a --delete root@target:/usr/lib/ sysroot/usr/lib/
rsync -a --delete root@target:/lib/ sysroot/lib/
# 方法 2: 从根文件系统镜像提取
mkdir sysroot
sudo mount -o loop rootfs.img /mnt
cp -a /mnt/* sysroot/
sudo umount /mnt
# 方法 3: 从 Docker 镜像提取
docker create --name tmp ubuntu:22.04
docker cp tmp:/usr/include sysroot/usr/include
docker cp tmp:/usr/lib sysroot/usr/lib
docker rm tmp
11.5 交叉编译实战
简单示例
# 创建测试文件
cat > hello.c << 'EOF'
#include <stdio.h>
int main(void) {
printf("Hello from cross-compiled binary!\n");
return 0;
}
EOF
# ARM64 交叉编译
aarch64-linux-gnu-gcc -o hello_arm64 hello.c
# 验证文件格式
file hello_arm64
# hello_arm64: ELF 64-bit LSB pie executable, ARM aarch64, ...
# 无法在本机运行
./hello_arm64
# bash: ./hello_arm64: cannot execute binary file: Exec format error
# 使用 QEMU 模拟运行
qemu-aarch64 hello_arm64
# Hello from cross-compiled binary!
交叉编译带依赖的项目
# 交叉编译依赖库
aarch64-linux-gnu-gcc -fPIC -shared -o libmylib.so mylib.c -I/path/to/sysroot/usr/include
# 链接时指定 sysroot
aarch64-linux-gnu-gcc \
--sysroot=/path/to/sysroot \
-L/path/to/sysroot/usr/lib \
-L. \
-o hello_arm64 main.c -lmylib
# 如果需要链接第三方库,需要先为目标架构编译它们
交叉编译 OpenSSH(实际场景)
# 假设已为目标架构编译好 zlib 和 openssl
export SYSROOT=/path/to/aarch64-sysroot
export CROSS=aarch64-linux-gnu-
# 配置 OpenSSH
./configure \
--host=aarch64-linux-gnu \
--build=x86_64-linux-gnu \
--prefix=/usr \
--sysconfdir=/etc/ssh \
--with-zlib=$SYSROOT/usr \
--with-ssl=$SYSROOT/usr \
CC="${CROSS}gcc" \
LD="${CROSS}ld"
make
11.6 QEMU 用户模式模拟
# 安装 QEMU 用户模式
sudo apt install qemu-user qemu-user-static
# 注册 binfmt(允许直接执行目标架构的二进制)
sudo apt install binfmt-support
# 直接运行 ARM64 二进制
./hello_arm64 # 如果 binfmt 已注册,可以直接运行
# 或者显式使用 qemu
qemu-aarch64 -L /path/to/sysroot ./hello_arm64
在 Docker 中使用 QEMU 进行交叉编译
# 注册多架构 binfmt
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
# 运行目标架构的容器
docker run --rm -it arm64v8/ubuntu:22.04 bash
# 在容器内可以直接运行 ARM64 程序
11.7 交叉编译常见问题
| 问题 | 原因 | 解决 |
|---|---|---|
fatal error: stdio.h: No such file | 缺少目标平台的头文件 | 安装 sysroot 或 libc6-dev-*-cross |
cannot find -lc | 缺少目标平台的 C 库 | 安装 sysroot 或交叉编译 glibc |
| 链接时使用了主机库 | -L 路径错误 | 确保 -L 指向 sysroot 的 lib 目录 |
| configure 检测失败 | configure 运行了交叉编译的二进制 | 设置 --host 和 --build |
libtool: link: unable to infer tagged configuration | libtool 不认识交叉编译器 | 设置 CC 和相关变量 |
autoconf/automake 项目交叉编译
# 关键: --host 和 --build 必须正确设置
./configure \
--host=aarch64-linux-gnu \
--build=x86_64-linux-gnu \
--prefix=/usr \
--with-sysroot=/path/to/sysroot
# 设置环境变量
export CC=aarch64-linux-gnu-gcc
export CXX=aarch64-linux-gnu-g++
export AR=aarch64-linux-gnu-ar
export STRIP=aarch64-linux-gnu-strip
export RANLIB=aarch64-linux-gnu-ranlib
export LD=aarch64-linux-gnu-ld
make
make install DESTDIR=/path/to/install
要点回顾
| 要点 | 核心内容 |
|---|---|
| 交叉编译 | 在 Host 上为 Target 架构编译代码 |
| 三元组 | arch-vendor-os-abi,如 aarch64-linux-gnu |
| Sysroot | 目标平台的头文件和库的集合 |
| 工具链 | 发行版包、crosstool-NG、Bootlin 预编译 |
| QEMU | 用户模式模拟,测试交叉编译产物 |
| configure | --host + --build 正确设置 |
注意事项
不要混用主机和目标的库: 交叉编译时最常见的错误是链接到了主机的库。始终检查
-L和--sysroot路径。
configure 需要
--host和--build: 没有这两个选项,configure 会尝试运行交叉编译的二进制文件进行检测,导致失败。
库也需要交叉编译: 如果程序依赖第三方库(如 zlib、openssl),这些库也需要先为目标架构编译。
Sysroot 版本匹配: Sysroot 中的 glibc 版本应与目标设备的 glibc 版本一致或更新。
扩展阅读
- crosstool-NG — 自定义交叉编译工具链
- Bootlin Toolchains — 预编译工具链
- QEMU User Mode — QEMU 用户模式文档
- Musl Libc — 轻量级 C 库,适合静态链接和嵌入式
- Buildroot — 嵌入式 Linux 构建系统
下一步
→ 12 - 汇编输出与内联汇编:学习如何查看 GCC 生成的汇编代码,以及在 C/C++ 中使用内联汇编。