使用C++静态库编译main.c失败,动态库可正常编译运行
核心问题拆解
从错误日志看,未定义的都是C标准库(比如std::vector、std::__throw_system_error)和C运行时(比如虚表vtable)的符号,根本原因在于静态链接和动态链接的机制差异,以及C/C++编译器的默认行为不同:
静态库不包含依赖的C++库符号
静态库只是编译生成的目标文件归档,不会自动链接它依赖的C标准库、oneDNN等第三方库的符号。而动态库在构建时(用Bazel的cc_binary linkshared=True)会完成全链路链接,把依赖的C标准库、oneDNN等符号的引用都处理好——要么直接打包进动态库,要么依赖系统的动态库文件,运行时自动加载。C编译器默认不链接C++标准库
你用clang-12编译main.c时,编译器以C语言模式工作,默认不会链接C标准库(libc++或libstdc++)。但静态库中的代码(比如oneDNN的实现)大量依赖C标准库,所以链接阶段找不到这些符号,报错"undefined reference"。extern "C"只解决导出函数的符号名问题
你提到的extern "C"确实能让C函数以C风格的符号名导出,但你的错误里的符号不是你的库函数,而是库依赖的第三方C代码的符号——这些符号本身就是C修饰的,extern "C"管不到它们,必须链接C标准库才能解决。
解决方法
1. 显式链接C++标准库
在编译命令中手动加入C++标准库的链接参数:
clang-12 main.c module.ll libconv2d.a -lc++
如果你的环境用的是GNU的C++标准库,换成-lstdc++。
2. 用C++编译器完成链接
直接用clang++-12执行编译链接,它会自动处理C++标准库的链接,同时因为main.c是C后缀,编译器依然会用C语言规则编译你的代码:
clang++-12 main.c module.ll libconv2d.a
3. 确保静态库包含所有依赖
如果你的静态库只打包了自己的目标文件,没包含oneDNN等依赖库的内容,需要把这些依赖的静态库也加到链接命令里。比如oneDNN的静态库是libdnnl.a,命令就改成:
clang++-12 main.c module.ll libconv2d.a libdnnl.a
为什么动态库没问题?
动态库在构建时,Bazel的cc_binary linkshared=True规则会自动完成所有依赖的链接:
- 它会把静态依赖的库(比如oneDNN)的代码打包进动态库,
- 或者链接系统的C++标准动态库(比如
libc++.so),并在动态库的依赖列表中记录,
当你链接动态库时,只需要找到动态库本身的导出符号,运行时动态库会自动加载它依赖的所有库,所以链接阶段不会出现未定义引用错误。
内容的提问来源于stack exchange,提问作者brad

