3、交叉编译工具链配置:arm-linux-gnueabihf安装、sysroot制作、环境变量设置

交叉编译工具链,说白了就是一套能在你的 x86 笔记本上生成 ARM 可执行文件的工具集。你想想看,WebRTC 这种体量的项目,直接在 ARM 板子上编译?那得等到天荒地老。所以我们得在 PC 上搞定它。

我个人习惯把工具链配置分成三步走:装工具链 → 做 sysroot → 设环境变量。每一步都有坑,我踩过不少,下面一个一个说。

3.1 安装 arm-linux-gnueabihf 交叉编译器

Ubuntu 下最省事的办法就是 apt 直接装:

sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf

装完验证一下:

arm-linux-gnueabihf-gcc --version

能看到版本号就说明装好了。不过这里有个细节——apt 源里的版本可能比较老。我在项目中遇到过,Ubuntu 18.04 默认装的是 GCC 7.x,但 WebRTC 官方推荐用 clang 或者更新的 GCC。如果你遇到编译错误,可以考虑从 Linaro 或 ARM 官网下载预编译包。

注意: 不要用 arm-linux-gnueabi(软浮点),WebRTC 大量使用 NEON 指令集,必须用 gnueabihf(硬浮点)。否则编译出来的库在 ARM 板子上跑起来会崩溃。

3.2 制作 sysroot——这一步最容易被忽略

什么是 sysroot?简单说,就是目标 ARM 板子的根文件系统的一个副本。里面包含了板子上的 /lib、/usr/lib、/usr/include 等目录。交叉编译时,编译器会去 sysroot 里找头文件和库,而不是去 PC 本机的 /usr/include。

为什么要自己做?因为 WebRTC 依赖很多系统库(libpthread、libdl、librt 等),这些库的版本必须和板子上的一致。我曾经偷懒用了一个通用 sysroot,结果编译出来的 WebRTC 在板子上运行时,报 GLIBC_2.29 not found——板子上的 glibc 版本太旧了。

制作 sysroot 的步骤:

  1. 从板子上拷贝:用 scp 或 NFS 把板子的 /lib、/usr/lib、/usr/include 拷出来。
  2. 整理目录结构:放到一个文件夹里,比如 $HOME/sysroot,保持 lib/、usr/lib/、usr/include/ 的层级。
  3. 处理符号链接:板子上的库很多是相对链接,拷贝后可能断链。用 readlink -f 检查,手动修复。

我一般用 rsync 来同步,保留软链接属性:

rsync -avz --copy-links root@<板子IP>:/lib/ $HOME/sysroot/lib/
rsync -avz --copy-links root@<板子IP>:/usr/lib/ $HOME/sysroot/usr/lib/
rsync -avz --copy-links root@<板子IP>:/usr/include/ $HOME/sysroot/usr/include/
小技巧: 如果板子上空间紧张,可以用 tar 打包再传,速度更快。我曾经在树莓派上这么干过,省了不少时间。

3.3 环境变量设置——CC/CXX/AR 的学问

环境变量是告诉构建系统「用哪个编译器」的关键。WebRTC 用的是 GN + Ninja 构建系统,它通过 gn args 来配置工具链。但如果你要手动编译某个依赖库,就得设好这些变量。

核心变量就三个:

变量名 含义 示例值
CC C 编译器 arm-linux-gnueabihf-gcc
CXX C++ 编译器 arm-linux-gnueabihf-g++
AR 静态库打包器 arm-linux-gnueabihf-ar

我习惯写一个 setenv.sh 脚本,每次编译前 source 一下:

#!/bin/bash
export CC=arm-linux-gnueabihf-gcc
export CXX=arm-linux-gnueabihf-g++
export AR=arm-linux-gnueabihf-ar
export SYSROOT=$HOME/sysroot
export CFLAGS="--sysroot=$SYSROOT -march=armv7-a -mfpu=neon"
export CXXFLAGS="$CFLAGS"
export LDFLAGS="--sysroot=$SYSROOT"

这里有几个关键点:

  • --sysroot:告诉编译器去哪里找头文件和库。不设的话,编译器会去 PC 本机的 /usr/include,那编译出来的东西肯定跑不了。
  • -march=armv7-a:指定 ARM 架构。WebRTC 一般需要 ARMv7 以上。
  • -mfpu=neon:启用 NEON 指令集。WebRTC 的编解码、降噪、回声消除都重度依赖 NEON,不开的话性能直接腰斩。
避坑指南: 我曾经在编译 OpenSSL 时忘了设 AR 变量,结果它用了 x86 的 ar 来打包静态库。编译没报错,但链接 WebRTC 时疯狂报 undefined reference。查了两天才发现是库文件格式不对。所以 AR 一定要设对。

3.4 验证工具链是否正常工作

写一个简单的测试程序:

// test.c
#include <stdio.h>
#include <arm_neon.h>

int main() {
    printf("Hello from ARM WebRTC!\n");
    // 简单 NEON 指令测试
    int32x4_t a = vdupq_n_s32(1);
    int32x4_t b = vdupq_n_s32(2);
    int32x4_t c = vaddq_s32(a, b);
    printf("NEON works: %d\n", vgetq_lane_s32(c, 0));
    return 0;
}

编译:

arm-linux-gnueabihf-gcc --sysroot=$HOME/sysroot -march=armv7-a -mfpu=neon test.c -o test_arm

然后用 file 命令检查:

file test_arm
# 输出应该类似:ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV)

如果看到 ARM 字样,说明编译成功了。把文件传到板子上运行一下,确认能正常输出。

我的习惯: 每次换板子或换系统后,我都会跑一遍这个测试。确保工具链和 sysroot 匹配,不然后面编译 WebRTC 时出问题,排查起来很痛苦。

3.5 本章知识结构图

下面这张图梳理了工具链配置的完整流程和关键节点:

交叉编译工具链配置流程 安装交叉编译器 gcc-arm-linux-gnueabihf 制作 sysroot 从板子同步 /lib /usr 设置环境变量 CC / CXX / AR 关键编译参数 --sysroot:指向板子库文件路径 -march=armv7-a:指定 ARM 架构 -mfpu=neon:启用 NEON 指令集加速 验证:编译 test.c → file 检查 → 板子运行 常见问题 • 软浮点 vs 硬浮点不匹配 → 用 gnueabihf • sysroot 版本与板子 glibc 不一致 → 重新同步