必须用aarch64-linux-gnu-gcc编译ARM64目标,因其与arm-linux-gnueabihf完全不兼容;uname -m为aarch64时才可选用,且需配对sysroot、libc版本及CMake toolchain中SYSTEM_PROCESSOR=aarch64。

不能靠“完美”二字蒙混过关——交叉编译能跑通、链接对、ABI 匹配、不崩溃,就是实际意义上的“完美”。其余都是幻觉。
怎么选对 aarch64-linux-gnu-gcc 工具链而不是 arm-linux-gnueabihf-gcc
选错工具链是第一道坎。aarch64 和 armhf 完全不兼容:前者是 64 位 ARM(树莓派 4/5、大部分新开发板),后者是 32 位 ARM(树莓派 Zero/3B)。用 aarch64-linux-gnu-gcc 编译出来的二进制,在 arm-linux-gnueabihf 环境下连 exec format error 都不会报,直接 No such file or directory(因为解释器路径不对)。
- 查目标设备真实架构:
uname -m—— 输出aarch64就必须用aarch64-前缀工具链 - Ubuntu/Debian 用户直接装:
sudo apt install g++-aarch64-linux-gnu,对应编译器是aarch64-linux-gnu-g++ - 别信“通用工具链”宣传,
arm-none-eabi-是裸机/RTOS 用的,没有libc和pthread,Linux 应用根本跑不起来 - 验证是否真能生成 ARM64 代码:
aarch64-linux-gnu-g++ -dM -E -x c /dev/null | grep __aarch64__—— 有输出才算生效
CMake toolchain 文件里必须设对的三件事
硬编码 CMAKE_C_COMPILER 在 CMakeLists.txt 里是自找麻烦。正确做法是写独立的 toolchain 文件(比如 arm64-toolchain.cmake),然后用 -DCMAKE_TOOLCHAIN_FILE= 指定。
-
set(CMAKE_SYSTEM_NAME Linux)—— 必须写,否则 CMake 当作 host 编译 -
set(CMAKE_SYSTEM_PROCESSOR aarch64)—— 不是arm,不是ARM64,就是小写的aarch64 -
set(CMAKE_FIND_ROOT_PATH "/path/to/sysroot")—— 如果你用了 sysroot(强烈建议),这个路径必须包含usr/include和usr/lib;没设它,find_package(OpenCV)会去宿主机目录瞎翻 - 额外加一句:
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)和set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY),防止 CMake 错误地混用宿主机头文件和库
第三方库找不到?90% 是 pkg-config 路径和 sysroot 没对齐
交叉编译时 find_package(Boost) 成功,但链接时报 undefined reference to `boost::system::generic_category()',大概率是 Boost 库本身没用 ARM64 版本编译过,或者 pkg-config 返回了 x86 的 .pc 文件路径。
- 确保目标平台的第三方库(如
libjsoncpp.so)已部署到你的 sysroot 下:/path/to/sysroot/usr/lib/libjsoncpp.so - 设置环境变量让
pkg-config找对位置:export PKG_CONFIG_LIBDIR="/path/to/sysroot/usr/lib/pkgconfig" - 在 CMake 中显式禁用宿主机 pkg-config:
set(ENV{PKG_CONFIG_PATH} ""),不然它会优先读/usr/lib/pkgconfig - 如果库没提供
.pc文件,就老实用find_path+find_library手动定位,别依赖find_package自动猜
编译通过但运行段错误?检查 libc 版本和符号可见性
最隐蔽的坑:程序在 build 主机上 readelf -A 看起来一切正常,一放到树莓派上就 Segmentation fault (core dumped)。常见原因不是代码问题,而是 ABI 断层。
- 确认工具链自带的
libc版本不低于目标系统:aarch64-linux-gnu-objdump --dynamic-syms /path/to/sysroot/lib/ld-linux-aarch64.so.1 | grep GLIBC,对比目标机ldd --version - 避免使用
-fPIC以外的激进优化(如-O3 -flto),LTO 在交叉环境下容易产生不兼容符号 - 检查是否意外导出了 host-only 符号:用
aarch64-linux-gnu-readelf -d your_binary | grep NEEDED,确认所有NEEDED库名都是libc.so.6、libstdc++.so.6这类标准名,而非libstdc++.so.6.0.30这种带完整版本号的绝对路径依赖 - 调试时加
-Wl,--no-as-needed防止链接器悄悄丢掉动态库依赖
真正卡住人的从来不是“怎么启动交叉编译”,而是“为什么它看起来成功了,却在目标机上不工作”。sysroot 内容是否干净、libc 是否匹配、符号是否被意外污染——这些细节没法靠一键脚本兜底,得一条命令一条命令地验证。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











