Aarch64嵌入式Linux开发:从零搭建可靠的交叉编译环境 🛠️

你有没有试过在一块边缘计算板子上跑大模型?我最近就踩了这么一坑——手头是块 Rockchip RK3588 开发板,Aarch64 架构,跑的是 Yocto 定制的嵌入式 Linux。系统精简到连 pip 都没有,Python 是静态链接进根文件系统的,GCC 工具链也不完整。内存只有 1GB 可用,本地编译?想都别想。

于是我们自然想到:在 x86_64 主机上搞交叉编译。听起来挺简单吧?换个编译器,指定目标架构,走起!结果第一行 #include <stdlib.h> 就报错:

/usr/include/stdlib.h:103: error: expected declaration specifiers or ‘...’ before ‘__uint64_t’

🤯 崩了。查了一圈才发现,Ubuntu 自带的 gcc-aarch64-linux-gnu 虽然方便,但它的 glibc 是 2.31,而我们的设备用的是 2.35 —— 类型定义对不上,直接 GG。


这下明白了:交叉编译不是换个编译器就行,而是要复现整个目标系统的构建上下文。工具链、头文件、库版本、内核接口……差一点都不行。

一开始我们还想“偷懒”:用系统包管理器装个交叉工具链完事。结果呢?编译能过,链接失败;链接过了,运行时报 undefined reference to 'rk_mpi_vpu_encode' —— 因为缺了 Rockchip 自家打的 VPU 补丁。

说白了,通用工具链只适合“Hello World”,真项目必须定制。


后来我们翻遍 Rockchip 的 GitLab,终于在一个冷门仓库里挖到了点线索:

git clone https://github.com/rockchip-linux/prebuilts.git

里面有个 gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu,看着像那么回事。但一查 glibc 版本……2.28,还是太老。设备是 2.35,ABI 不兼容,照样翻车。

💡 看来唯一的出路是:自己造一个完全匹配的 toolchain + sysroot

最终方案:用 Buildroot 打造专属交叉编译环境

我们选了 Buildroot 2023.02.3,因为它配置灵活、文档齐全,而且支持外部配置(BR2_EXTERNAL),适合团队协作:

wget https://buildroot.org/downloads/buildroot-2023.02.3.tar.gz
tar -xf buildroot-2023.02.3.tar.gz
cd buildroot-2023.02.3
make BR2_EXTERNAL=../my-board-configs rockchip_rk3588_defconfig

关键配置如下:

BR2_aarch64=y
BR2_TOOLCHAIN_BUILDROOT_GLIBC=y
BR2_TOOLCHAIN_BUILDROOT_GLIBC_VERSION_2_35=y
BR2_KERNEL_HEADERS_5_10=y
BR2_PACKAGE_PYTHON3=y
BR2_PACKAGE_OPENSSL=y

执行 make,Buildroot 自动拉源码、编译工具链、生成根文件系统。i7-12700K 上跑了 40 分钟,换来一套完全可控的交叉环境 ✅

完成后,工具链在:

output/host/bin/aarch64-linux-gnu-gcc

sysroot 在:

output/staging/

这才是真正的“和设备一致”的构建环境。


接下来是工程化问题:我们有个 C++ 项目,依赖 OpenCV、Protobuf、spdlog,用 CMake 管理。怎么让 find_package() 正常工作?

先写个 toolchain 文件

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)

set(CMAKE_C_COMPILER /path/to/buildroot/output/host/bin/aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER /path/to/buildroot/output/host/bin/aarch64-linux-gnu-g++)

set(CMAKE_FIND_ROOT_PATH /path/to/buildroot/output/staging)
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_SYSROOT ${CMAKE_FIND_ROOT_PATH})

set(CMAKE_C_FLAGS "-march=armv8-a+crc+crypto")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")

然后编译时带上:

cmake -DCMAKE_TOOLCHAIN_FILE=aarch64-toolchain.cmake ..

搞定?没那么简单。

别忘了 pkg-config!

Buildroot 不会自动设置 PKG_CONFIG_LIBDIR,如果你不手动导出,find_package(OpenCV) 依然会失败:

export PKG_CONFIG_LIBDIR=/path/to/buildroot/output/staging/lib/aarch64-linux-gnu/pkgconfig
export CC=aarch64-linux-gnu-gcc
export CXX=aarch64-linux-gnu-g++

否则 CMake 根本找不到 .pc 文件,报错 “Could NOT find OpenCV (missing: OpenCV_DIR)” —— 实际上库就在那儿,只是 pkg-config 找不着路罢了 😅


踩坑实录 🕳️

坑 1:FATAL: kernel too old

编译成功,拷到板子一运行:

./inference_engine: FATAL: kernel too old

啥情况?内核明明是 5.10.66,Buildroot 也配了 BR2_KERNEL_HEADERS_5_10,理论上兼容啊。

深入排查发现:问题出在 glibc 的符号版本控制上。我们用了 memcpy@GLIBC_2.35,但设备上的动态链接器(ld.so)太旧,解析不了这个符号。

解决办法:
- 在 make menuconfig 中关闭不必要的功能(如 BR2_INET_IPV6),减少 glibc 依赖面
- 编译时加标志降级兼容性:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -U_FORTIFY_SOURCE -D_GNU_SOURCE")
坑 2:Python binding 编译失败

更头疼的是 Python 模块。我们想给 C++ 库写个 binding,结果 setup.py build_ext 直接崩溃:

ImportError: Unable to detect the target architecture, are you running Python...

原因是 setuptools 内部调了 platform.machine(),返回的是主机的 x86_64,不是 aarch64,直接判定“这不是你的菜”。

折腾半天,最终放弃交叉编译 Python 模块,改用 Docker + QEMU 模拟编译

FROM arm64v8/ubuntu:22.04
COPY . /app
RUN apt update && apt install -y python3-dev python3-pip
WORKDIR /app
RUN pip3 wheel .

构建命令:

docker build --platform=linux/arm64 -t my-aarch64-wheel .

虽然慢(QEMU 单核模拟,编译要 20 多分钟),但胜在可靠,不会出现 ABI 错乱。


性能表现 💪

最终部署到 RK3588 板子上:

  • 二进制体积:23MB(静态链接 OpenVINO)
  • 内存占用:峰值 180MB
  • 启动时间:1.2s(SSD)
  • 推理延迟:7B 模型 Token 输出平均 80ms/token(INT8 量化)

对比 x86 主机,性能损失约 15% —— 主要是 ARM NEON 对矩阵运算的支持不如 AVX2 完善。但功耗从 65W 降到 12W,妥妥的性价比之选 ⚡


几个高频问题 🔍

Q:为啥不用 Yocto?
A:Yocto 功能强,但学习成本高,bitbake 规则复杂,构建一次两小时起步。我们是小团队,快速迭代更重要,Buildroot 更轻量、透明。

Q:能不能直接用 QEMU 模拟编译?
A:可以,但日常开发不现实。QEMU 模拟下编译一个 C++ 项目要二十多分钟,喝杯咖啡都不够等的。适合 CI 打包,不适合本地开发。

Q:怎么确保工具链和设备完全一致?
A:最保险的做法是——从设备上直接打包 /usr/include/lib 作为 sysroot。我们后期就这么干了,彻底杜绝 ABI 不兼容。

Q:有推荐的容器镜像吗?
A:强烈推荐 multiarch/qemu-user-static + arm64v8/ubuntu 组合,配合 GitHub Actions 或 Jenkins 做 CI/CD,一键构建 Aarch64 包,超顺滑 🚀


说实话,刚开始真没想到交叉编译能这么麻烦。但回头看,早点把这套流程标准化,至少能省下三天 debug 时间

现在我们的 CI 脚本已经自动化:每次提交代码,自动构建 Aarch64 wheel 包,推送到内部 Nexus 仓库。开发人员 pip install 一下就能用,效率飞起。

所以总结一句话:

别信系统自带的交叉工具链,别怕动手造轮子。真正的嵌入式开发,是从搭建可靠的构建环境开始的。

🛠️ 环境稳了,后面的一切才有可能。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐