交叉编译

在 x86 PC 上编译出 ARM 可运行的二进制,是嵌入式开发的日常。

一、什么是交叉编译

常见组合:

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(推荐)

3. Linaro

4. ARM 官方

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

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