clang交叉编译arm64必须显式指定-target aarch64-linux-gnu并配合--sysroot指向完整arm64根文件系统,否则默认生成x86_64代码导致exec format error;需同步配置链接器sysroot或切换ld,且sysroot须含lib/ld-linux-aarch64.so.1等运行时文件。

clang -target 参数必须显式指定 aarch64-linux-gnu
LLVM 不像 GCC 那样自带绑定好 ABI 和 sysroot 的专用交叉编译器,它靠 -target 参数驱动整个交叉行为。漏掉或写错这个参数,clang 就默认按宿主机(x86_64-linux-gnu)编译,生成的仍是 x86 代码——看着 clang 没报错,但程序在 ARM64 板子上直接 exec format error。
正确写法是:
clang -target aarch64-linux-gnu hello.c -o hello-arm64
注意三点:
-
aarch64-linux-gnu是标准三段式 target triple,不能简写成aarch64或arm64,否则头文件路径、ABI 规则全错 - 必须用
clang,不是gcc;LLVM 工具链里没有aarch64-linux-gnu-clang这种独立可执行名,全靠-target切换 - 目标 triple 中的
gnu表示使用 GNU libc(glibc),若目标系统用的是 musl(如 Alpine),得换成aarch64-linux-musl,否则链接阶段找不到libc.so
头文件和库路径不配,编译直接卡在 #include
只写 -target 还不够。LLVM 默认不会自动查找 ARM64 系统的头文件和库,它会去宿主机的 /usr/include 和 /usr/lib 找——那全是 x86_64 的,struct stat 布局、寄存器定义、__NR_write 宏全都不对。
必须手动告诉 clang 去哪找:
- 用
--sysroot=/path/to/aarch64-rootfs指向一个完整的 ARM64 根文件系统镜像(比如从 Armbian、Debian arm64 netinst 或 Linaro sysroot 下载的 tar 包) - 或分别指定:
-I/path/to/aarch64-rootfs/usr/include+-L/path/to/aarch64-rootfs/usr/lib - 常见错误:把
/usr/include直接 symlink 到宿主机目录——不行,架构不匹配,asm/unistd_64.h和asm/unistd_32.h是不同东西
完整命令示例:
clang -target aarch64-linux-gnu --sysroot=/opt/sysroot-aarch64 hello.c -o hello-arm64
CMake 项目里 clang 交叉编译要绕过隐式检测逻辑
CMake 默认会尝试调用 clang --version 并检查其输出是否含 “aarch64”,但它实际运行的是宿主机 clang,永远不匹配。结果 CMake 误判为“不支持目标平台”,后续所有 find_package()、check_include_file() 全挂掉。
正确做法是彻底关闭 CMake 的编译器探测,强制注入工具链:
- 新建
toolchain-aarch64.cmake:
set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_C_COMPILER clang) set(CMAKE_CXX_COMPILER clang++) set(CMAKE_C_FLAGS "-target aarch64-linux-gnu --sysroot=/opt/sysroot-aarch64") set(CMAKE_CXX_FLAGS "-target aarch64-linux-gnu --sysroot=/opt/sysroot-aarch64") set(CMAKE_FIND_ROOT_PATH /opt/sysroot-aarch64) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
- 然后构建:
cmake -DCMAKE_TOOLCHAIN_FILE=toolchain-aarch64.cmake .. - 关键点:
CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER防止 CMake 去 sysroot 里找ld这类宿主机工具;而LIBRARY和INCLUDE设为ONLY才能确保find_library(m)找到的是 ARM64 的libm.so
链接时找不到 crt1.o 或 _start:你缺的是 aarch64 的启动代码
即使头文件和库都对了,clang 仍可能报错:
ld.lld: error: unable to find library -lc ld.lld: error: cannot open crt1.o: No such file or directory
这不是路径问题,而是 LLVM 默认用 lld 链接器,但它不会自动加 --sysroot 到链接阶段。crt1.o、crti.o、crtn.o 这些启动对象文件都在 sysroot 的 usr/lib 下,但 lld 没被告知去哪翻。
解决方法只有两个:
- 显式传给链接器:
clang -target aarch64-linux-gnu --sysroot=/opt/sysroot-aarch64 hello.c -Wl,--sysroot=/opt/sysroot-aarch64 -o hello-arm64(注意-Wl,前缀) - 或者换回 GNU ld:
clang -target aarch64-linux-gnu --sysroot=/opt/sysroot-aarch64 -fuse-ld=gold hello.c -o hello-arm64(前提是宿主机装了binutils-aarch64-linux-gnu)
最容易被忽略的一点:sysroot 必须包含完整的 usr/lib 和 lib,尤其 lib/ld-linux-aarch64.so.1 ——它不在编译期用,但在 QEMU 模拟运行时会被动态加载,缺了就 No such file or directory。











