交叉编译
在 x86 PC 上编译出 ARM 可运行的二进制,是嵌入式开发的日常。
一、什么是交叉编译
- 主机(host):编译所在机器(x86)
- 目标(target):运行所在机器(ARM)
- 构建(build):编译工具链自身所在机器
常见组合:
build = x86_64-linux
host = x86_64-linux
target = aarch64-linux-gnu
二、工具链命名
典型名称:
arm-linux-gnueabi-gcc ARMv7 soft-float
arm-linux-gnueabihf-gcc ARMv7 hard-float
arm-linux-gnueabihf-gcc-12 指定版本
aarch64-linux-gnu-gcc ARMv8 64 位
riscv64-linux-gnu-gcc
mips-linux-gnu-gcc
三、获取工具链
1. 系统包管理器
# Ubuntu / Debian
sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
# Arch Linux
sudo pacman -S arm-linux-gnueabihf-gcc
2. Bootlin(推荐)
- https://toolchains.bootlin.com
- 多种架构、glibc / musl 选择
3. Linaro
- ARM 官方优化工具链
- https://www.linaro.org/downloads/
4. ARM 官方
- https://developer.arm.com/tools-and-software
5. 厂商定制(如 NXP、ST 提供的 SDK)
四、关键参数
# 显式指定
arm-linux-gnueabihf-gcc --sysroot=/path/to/rootfs \
-I/.../include \
-L/.../lib \
-march=armv7-a -mtune=cortex-a9 \
-mfpu=neon -mfloat-abi=hard \
hello.c -o hello
# 关键环境变量
CC = arm-linux-gnueabihf-gcc
CFLAGS = -march=armv7-a -mtune=cortex-a9 -O2 -Wall
LDFLAGS = --sysroot=$(SYSROOT) -L$(SYSROOT)/usr/lib
PKG_CONFIG_PATH = $(SYSROOT)/usr/lib/pkgconfig
五、Sysroot
- 目标机的根文件系统副本
- 链接器从此目录找 .so 和头文件
- 典型目录结构:
sysroot/
├── usr/
│ ├── include/
│ ├── lib/ → *.so, *.a
│ └── lib/pkgconfig/
├── lib/
└── etc/
六、CFLAGS 经典组合(嵌入式)
CFLAGS = -O2 -g -Wall \
-march=armv7-a -mtune=cortex-a9 \
-mfpu=neon -mfloat-abi=hard \
--sysroot=$(SYSROOT) \
-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
CXXFLAGS = $(CFLAGS)
LDFLAGS = --sysroot=$(SYSROOT) \
-Wl,--gc-sections -Wl,-O1
七、pkg-config 与 cmake 的交叉编译
1. pkg-config
PKG_CONFIG_PATH=$SYSROOT/usr/lib/pkgconfig \
PKG_CONFIG_SYSROOT_DIR=$SYSROOT \
pkg-config --cflags --libs openssl
2. CMake 交叉编译
cmake -DCMAKE_TOOLCHAIN_FILE=arm-linux-gnueabihf.cmake \
-DCMAKE_INSTALL_PREFIX=/usr \
..
工具链文件示例 arm-linux-gnueabihf.cmake:
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
set(CMAKE_FIND_ROOT_PATH /opt/sysroot-arm)
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)
八、踩坑
| 问题 | 解决 |
|---|---|
exec format error |
用了主机工具链或反了架构 |
| GLIBC 版本不兼容 | 工具链的 GLIBC 比目标系统新 → 用 musl 或老工具链 |
Floating point exception |
软硬浮点不匹配 |
kernel too old |
用了需要高版本内核特性的 glibc |
| 找不到 .so | 设置 LD_LIBRARY_PATH 或 sysroot |