Windows用Clang++交叉编译Linux程序遇libm.so.6链接错误求助
问题背景
我正尝试用Clang从x86_64-pc-windows-msvc交叉编译至x86_64-pc-linux-gnu,测试程序是仅使用iostream的C Hello World。为了在Windows主机提供Linux环境,我从WSL2复制了头文件、.a、.so等文件到主机C:/wsl2目录。
执行以下Powershell命令时,lld链接器无法打开libm.so.6动态库:
PS C:\Users\xxx\Desktop\cmake_demo\simple> clang++ --target=x86_64-pc-linux-gnu --sysroot="C:/wsl2" -o main ./main.cpp -fuse-ld=lld -LC:/wsl2/usr/lib/x86_64-linux-gnu ld.lld: error: cannot open /lib/x86_64-linux-gnu/libm.so.6: no such file or directory clang++: error: linker command failed with exit code 1 (use -v to see invocation)
此前遇到libc_nonshared.a类似问题时,复制对应.a文件即可解决,但本次复制libm.so.6至C:/wsl2相关目录后仍无效(文件非符号链接,长度不为0):
PS C:\wsl2\usr\lib> ls C:\wsl2\usr\aarch64-linux-gnu\lib | findstr libm -a---- 2/23/2022 12:26 PM 1445378 libm.a -a---- 2/23/2022 12:26 PM 551064 libm.so -a---- 4/16/2024 3:40 PM 940560 libm.so.6 Directory: C:\wsl2\usr\aarch64-linux-gnu\lib Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 4/16/2024 3:40 PM 940560 libm.so.6 Directory: C:\wsl2\usr\lib Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 4/16/2024 3:40 PM 940560 libm.so.6 Directory: C:\wsl2\usr\lib\x86_64-linux-gnu Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 4/16/2024 3:40 PM 940560 libm.so.6 Directory: C:\wsl2\usr\lib64\x86_64-linux-gnu Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 4/16/2024 3:40 PM 940560 libm.so.6
添加-v参数查看链接详情后,发现ld.lld使用了Linux动态链接器/lib64/ld-linux-x86-64.so.2,导致在Windows主机上无法找到类Unix路径下的libm.so.6:
... C:/wsl2/usr/include/x86_64-linux-gnu C:/wsl2/usr/include End of search list. "C:\\Program Files\\LLVM\\bin\\ld.lld" --sysroot=C:/wsl2 --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o main "C:/wsl2/usr/lib/gcc/x86_64-linux-gnu/11/../../..\\crt1.o" "C:/wsl2/usr/lib/gcc/x86_64-linux-gnu/11/../../..\\crti.o" "C:/wsl2/usr/lib/gcc/x86_64-linux-gnu/11\\crtbegin.o" -LC:/wsl2/usr/lib/x86_64-linux-gnu -LC:/wsl2/usr/lib/gcc/x86_64-linux-gnu/11 -LC:/wsl2/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib64 -LC:/wsl2/usr/lib/../lib64 -LC:/wsl2/usr/lib/x86_64-linux-gnu/../../lib64 -LC:/wsl2/usr/lib/gcc/x86_64-linux-gnu/11/../../.. -LC:/wsl2/usr/lib "C:\\Users\\hzhang3\\AppData\\Local\\Temp\\main-61818f.o" -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc "C:/wsl2/usr/lib/gcc/x86_64-linux-gnu/11\\crtend.o" "C:/wsl2/usr/lib/gcc/x86_64-linux-gnu/11/../../..\\crtn.o" ld.lld: error: cannot open /lib/x86_64-linux-gnu/libm.so.6: no such file or directory clang++: error: linker command failed with exit code 1 (use -v to see invocation) ...
问题解答
1. 用Clang++从Windows交叉编译到Linux是否可行?
完全可行。Clang/LLVM本身就是跨平台的编译器,支持从Windows主机交叉编译生成Linux目标文件,只要配置好正确的目标平台sysroot、链接器和依赖库即可。
2. 此场景应使用哪种动态链接器?
交叉编译时,链接阶段不需要在Windows主机上运行Linux的动态链接器(ld-linux-x86-64.so.2),它只是被嵌入到生成的Linux可执行文件中,用于在Linux系统上加载动态库。链接阶段你需要使用的是lld(LLVM链接器),也就是你当前指定的-fuse-ld=lld,它能正确处理ELF格式的目标文件和Linux库。
3. 如何配置lld使其找到指定路径下的libm.so.6?
问题的核心不是绕过动态链接器,而是你的sysroot目录结构不完整,或者lld在解析路径时出现了混淆,可通过以下方式解决:
- 补全sysroot目录结构:在
C:/wsl2下创建lib/x86_64-linux-gnu目录,将libm.so.6复制进去;或者用Windows的mklink命令创建符号链接,指向已有的C:/wsl2/usr/lib/x86_64-linux-gnu/libm.so.6。lld会以--sysroot为根目录解析绝对路径,补全后/lib/x86_64-linux-gnu/libm.so.6会被映射到C:/wsl2/lib/x86_64-linux-gnu/libm.so.6,就能被找到。 - 明确指定库路径:添加
-LC:/wsl2/usr/lib/x86_64-linux-gnu(或你存放libm.so.6的实际目录),或者直接用-l:libm.so.6明确指定库文件名,避免链接器查找默认路径。 - 检查sysroot有效性:确保
--sysroot="C:/wsl2"参数正确,lld会将所有Linux风格的绝对路径基于这个sysroot来转换。
4. Windows到x64 Linux交叉编译推荐使用哪种Linux环境?
推荐以下几种方案:
- 直接使用WSL2编译:无需复制文件到Windows主机,直接在WSL2的Linux发行版(如Ubuntu)中用Clang或GCC编译,生成的可执行文件直接适用于x64 Linux,这是最省心的方式,避免交叉编译的路径和依赖问题。
- 使用预构建交叉工具链:通过MSYS2、Chocolatey等包管理器安装
x86_64-linux-gnu-gcc这类预编译的交叉工具链,工具链已配置好完整的sysroot和依赖库,不需要手动复制文件。 - 挂载WSL2目录作为sysroot:如果坚持在Windows主机交叉编译,直接挂载WSL2的根目录(路径通常为
\\wsl$\<你的发行版名称>),用--sysroot=\\wsl$\Ubuntu(根据实际发行版调整),这样能保证目录结构完整,避免手动复制带来的文件缺失或路径错误。
内容的提问来源于stack exchange,提问作者Houze

