强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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-gnuARM 64-bitARMv8-A Linux
arm-linux-gnueabihfARM 32-bit 硬浮点ARMv7 Linux
arm-linux-gnueabiARM 32-bit 软浮点旧式 ARM
riscv64-linux-gnuRISC-V 64-bitRISC-V Linux
x86_64-linux-gnux86-64本机 64-bit
i686-linux-gnux86 32-bit本机 32-bit
mips-linux-gnuMIPS 32-bit路由器等
mips64-linux-gnuabi64MIPS 64-bit
powerpc64le-linux-gnuPowerPC 64-bit LEIBM POWER
arm-none-eabiARM bare-metal嵌入式无 OS
xtensa-esp32-elfXtensa (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 configurationlibtool 不认识交叉编译器设置 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 版本一致或更新。


扩展阅读


下一步

12 - 汇编输出与内联汇编:学习如何查看 GCC 生成的汇编代码,以及在 C/C++ 中使用内联汇编。