交叉编译器命名规则为[arch]-[vendor]-[system]-[abi]-gcc,其中arch指目标架构(如aarch64)、vendor常为none或linux、system决定c库类型(gnu对应glibc,eabi多用于newlib或裸机)、abi定义应用二进制接口(如gnueabihf表示硬浮点)。

直接用编译器前缀名 + --target 或 CMake 的 CMAKE_SYSTEM_NAME 就能指定目标平台,但光写对名字不等于真适配——关键在工具链路径、sysroot 和 ABI 三者必须对齐。
交叉编译器名字本身已隐含目标平台
比如 aarch64-linux-gnu-g++ 这个名字就说明:目标架构是 aarch64,操作系统是 linux,C 库是 gnu(即 glibc),ABI 是默认的 lp64。同理,arm-none-eabi-gcc 表示目标无 OS、用 Newlib、硬浮点 ABI。
常见命名结构:[arch]-[vendor]-[system]-[abi]-gcc,其中 vendor 经常是 none 或 linux,system 决定 libc 类型(gnu 对应 glibc,eabi 常对应 Newlib 或裸机)。
不要试图用 x86_64-linux-gnu-gcc --target=aarch64-linux-gnu 强行切换——原生 GCC 不支持跨后端生成,必须用预编译好的专用工具链。
用 CMake 指定目标平台时,CMAKE_SYSTEM_NAME 不够
只设 CMAKE_SYSTEM_NAME=Linux 和 CMAKE_SYSTEM_PROCESSOR=aarch64 只是告诉 CMake “我要编译到哪儿”,但不会自动找对编译器或头文件路径。
必须配合 toolchain 文件显式指定:
-
CMAKE_C_COMPILER指向aarch64-linux-gnu-gcc路径 -
CMAKE_SYSROOT指向目标根文件系统(如/opt/sysroots/aarch64-linux),否则会误用宿主机的/usr/include -
CMAKE_FIND_ROOT_PATH需包含 sysroot 和工具链 bin 目录,否则find_package()找不到目标平台的库
漏掉 CMAKE_SYSROOT 是最常见错误:编译能过,但链接时符号缺失,或运行时报 undefined symbol: __cxa_begin_catch —— 因为混用了宿主机的 libstdc++ 头和目标平台的库。
--sysroot 和 -I/-L 手动指定路径的风险
单文件编译时可用 aarch64-linux-gnu-g++ --sysroot=/path/to/sysroot -I/sysroot/usr/include ...,但容易出错:
-
--sysroot会自动把/usr/include和/usr/lib映射到该路径下;而单独加-I不会覆盖默认搜索路径,可能造成头文件版本冲突 - 如果 sysroot 里没放完整的 libc++ 或 libstdc++,
-static-libstdc++也救不了——链接器仍会去宿主机找动态库 - 第三方库(如 OpenSSL、Boost)必须用同一套 sysroot 重新编译,不能直接复制 x86_64 的 .a 文件
真正安全的做法:所有依赖都用相同工具链 + 相同 sysroot 从源码构建,而不是拼凑二进制。
验证目标平台是否真被识别
编译后别急着烧写,先看 ELF 头和动态依赖:
-
file program输出应含ELF64-aarch64或ARM aarch64,不是ELF64-x86-64 -
aarch64-linux-gnu-readelf -h program | grep -i class确认是ELFCLASS64 -
aarch64-linux-gnu-readelf -d program | grep NEEDED查看依赖库名,如libstdc++.so.6是对的,libc.so.6表明用了 glibc;若看到libc.musl则说明工具链配置错了
最容易被忽略的是 ABI 兼容性:aarch64-linux-gnu 默认用 lp64,而 aarch64-linux-android 用 ilp32 变体,两者二进制不兼容,光看架构名一样也不行。











