结论:宿主机是运行编译命令的机器,目标机是程序最终运行的设备;llvm不自动识别目标架构,必须显式指定--target、--sysroot和--gcc-toolchain三要素,否则编译成功但运行失败。

宿主机和目标机不搞清,LLVM交叉编译必崩
直接说结论:宿主机(Host)是你敲命令、跑 clang 的那台机器,比如你的 x86_64 Ubuntu 笔记本或 Apple Silicon Mac;目标机(Target)是程序最终要跑的设备,比如 RK3566(ARMv8-A)、Orangepi CM5(ARMv7-A)或 Cortex-M4 单片机。LLVM 本身不自动猜目标——你必须显式告诉它“我要为谁编译”,否则它默认按宿主机架构生成代码,结果就是:编译成功,运行失败,报错 Illegal instruction 或直接段错误。
常见混淆点:
- 误以为“Mac 上装了 LLVM 就能编译 ARM 程序”——其实默认
clang是x86_64-apple-darwin工具链,不带 ARM 后端支持 - 在 Ubuntu 宿主机上用
apt install clang装的 clang,--target可能只支持x86_64和aarch64,但缺armv7或thumb2支持,导致编译 Cortex-M3 失败 - 用
clang --target=arm-linux-gnueabihf却没配--sysroot,链接时找不到libc.a或stdio.h,报错fatal error: stdio.h: No such file or directory
LLVM 交叉编译三要素:--target、--sysroot、--gcc-toolchain
LLVM 不像 GNU 工具链那样靠前缀(如 aarch64-linux-gnu-gcc)区分工具,而是靠参数驱动。核心就三个:
-
--target=armv7a-unknown-linux-gnueabihf:决定生成什么指令集、ABI、调用约定。注意不是随便写,得和你目标板的 Linux 内核/用户空间 ABI 对齐(比如树莓派 3B+ 用armv7a,RK3399 用aarch64) -
--sysroot=/path/to/arm-sysroot:指定目标机的根文件系统镜像路径,里面必须有usr/include和usr/lib。没有它,clang就连stdint.h都找不到 -
--gcc-toolchain=/path/to/gcc-arm-toolchain(可选但推荐):当你要链接libgcc或libatomic这类底层运行时库时,LLVM 会去这个路径下找对应架构的lib和include,比纯裸clang更稳
举个最小可行命令:clang --target=aarch64-linux-gnu --sysroot=/opt/sysroots/rk3566 --stdlib=libc++ hello.c -o hello_arm
Clang 编译器前端 vs LLD 链接器后端,别混着用
很多人用 clang 编译完,再拿宿主机的 ld 去链接,结果报 cannot use 'aarch64' binary in 'x86_64' link。这是因为 ld 是 GNU binutils 的链接器,不认 ARM 目标;而 LLVM 自带的 lld 才是跨架构链接器。
- 编译阶段用
clang没问题,它能生成目标架构的 .o 文件 - 链接阶段必须用
lld,且要加-fuse-ld=lld参数,或者直接调用aarch64-linux-gnu-ld(如果你装了 GNU 工具链) - 验证输出架构:
file hello_arm应显示ELF 64-bit LSB pie executable, ARM aarch64,而不是x86-64
静态链接 libc++ 和 libunwind 时容易漏掉依赖
LLVM 默认用 libc++,但嵌入式环境往往没完整 C++ 运行时。若你加了 -static-libc++,还得手动补 libunwind 和 libatomic,否则运行时报 undefined symbol: __cxa_begin_catch 或 __atomic_fetch_add_4。
- 完整静态链接命令示例:
clang --target=armv7a-unknown-linux-gnueabihf --sysroot=/opt/sysroots/orangepi-cm5 -static-libc++ -static-libunwind -latomic hello.cpp -o hello_static - 注意
libunwind必须和libc++同源编译,不能混用 GNU libunwind 和 LLVM libc++ 自带的 unwind - 如果目标机是裸机(无 OS),还要加
--rtlib=compiler-rt和-nostdlib,自己提供_start入口
最常被忽略的是:LLVM 的 --target 字符串必须和 sysroot 中的库 ABI 完全匹配。差一个字母(比如 gnueabihf 写成 gnueabi),头文件可能找到,但链接时符号名对不上,报一堆 undefined reference。这种问题没法靠重装解决,只能查 readelf -A 看目标库的 ABI 标签。











