【问题】如何解决 C++ 链接报错 Undefined Symbols: typeinfo for xxx

365 阅读2分钟

太长不看版: -fno-rtti

问题

有个朋友求助,她的 C++ 项目 Project A 引入了一个以静态链接库形式提供的 SDK B,在编译链接自己的项目时,链接器报错:

Undefined Symbols: typeinfo for BaseClassInSDKB

分析

看到这个问题,我们当然第一时间意识到是链接时静态库中的某个 symbol 丢失了,但具体是什么问题呢。通过翻阅她的代码发现,在 Project A 中,有一个 class DerivedClassInProjectA: public BaseClassInSDKB 继承了 BaseClassInSDKB,报错就报在了这一行。

熟练使用 C++ 的朋友知道,typeinfo 是 C++ 的运行时类型信息,用于实现 C++ 的 运行时类型信息(RTTI) 功能。简单说,RTTI 类似于 Java 的 instanceOf 运算符,用于在运行时(而非静态编译期)实现类型的识别和检查。C++ 的 typeid 运算符和 dynamic_cast 运算符是依赖 typeinfo 的,异常捕获也会涉及到 RTTI 能力。

如果我们的代码在编译时启用了 RTTI(默认是启用的),那么我们代码中 符合特定条件的 class 是会被编译器生成 typeinfo 的。而我们的类 DerivedClassInProjectAtypeinfo,会引用到它父类的 typeinfo

这时编译器会在我们项目的 object 文件中产生对父类 BaseClassInSDKBtypeinfo 的引用。而 BaseClassInSDKB 所在的静态库 libsdkb.a 中不存在这个 symbol,因此链接器链接我们代码产生的 object 文件和静态库时,会报找不到 typeinfo 的错误。

而之所以 libsdkb.a 中没有对应的 symbol,是因为 libsdkb.a 在生成的时候关掉了 RTTI 支持。

到这里解决方案就比较简单了:

解决方法一:关闭项目的 RTTI

如果你整个项目都用不到 RTTI,那么就在你的 makefile 或者 cmake 等文件中配置关闭 RTTI 即可。方法就是给 gcc/clang 的参数中增加 -fno-rtti

解决方法二:关闭特定文件的 RTTI

如果你的项目中其他地方用到 RTTI,那可以单独把 DerivedClassInProjectA 这个类所在文件的 RTTI 关掉。只要在 makefile 或者 cmake 等配置中单独针对该文件配置编译器参数即可。

例如在 cmake 中:

set_source_files_properties(某个文件.cpp PROPERTIES COMPILE_FLAGS -fno-rtti)

解决方法三:打开 SDK B 的 RTTI

如果你的整个项目都需要 RTTI 支持,且你所引用的 SDK 有支持 RTTI 的版本(或者是你有源码可以自己编译),那么你可以选择支持 RTTI 的 SDK。