跟着教程写第一个llvm pass的时候,在opt load动态库的时候遇到了下面的报错
$ opt -load ./libLLVMmypass.so
Error opening './libLLVMmypass.so': ./libLLVMmypass.so: undefined symbol: _ZTIN4llvm12FunctionPassE
-load request ignored.
$ c++filt _ZTIN4llvm12FunctionPassE
typeinfo for llvm::FunctionPass
报错的原因是动态链接器没有找到typeinfo for llvm::FunctionPass
这个类的定义。为了理解这个类,我们需要一些背景。
C++里的dynamic_cast
和typeid
运算符使用到了RTTI(run-time type identification),用于在运行时动态获取变量的类型信息。typeid
返回的是一个具有static storage duration
作用域的std::type_info
类的引用,C++中每个类型都至少有一个对应的type_info
类(可能会存在多个,考虑模板+动态库的情形,主程序实例化头文件中的某个模板类,因此主程序中有一个type_info
,主程序再load一个动态库,该动态库也用同样的方法实例化了该头文件中的同一个模板类,因此该动态库中也存在对应同一个类的type_info)。
注意不要把rtti与多态搞混了,从No RTTI but still virtual methods可以看出,在没有rrti的情况下,vtable也会正常工作。
另外从文档中可以看出,异常处理也貌似依赖于rtti,不过这部分我从来没用过,就暂时不讨论了。
gcc中关于-fno-rtti
的论述如下:
-fno-rtti
Disable generation of information about every class with virtual functions for use by the C++ run-time type identification features (“dynamic_cast” and “typeid”). If you don’t use those parts of the language, you can save some space by using this flag. Note that exception handling uses the same information, but G++ generates it as needed. The “dynamic_cast” operator can still be used for casts that do not require run-time type information, i.e. casts to “void *” or to unambiguous base classes.
Mixing code compiled with -frtti with that compiled with -fno-rtti may not work. For example, programs may fail to link if a class compiled with -fno-rtti is used as a base for a class compiled with -frtti.
最后一段解释了问题所在,表明我编译LLVM时没有保留rtti的信息。查询文档发现
LLVM_ENABLE_RTTI:BOOL
Build LLVM with run-time type information. Defaults to OFF.
这下找到了问题的根源。在LLVMConfig.cmake
定义了LLVM_ENABLE_RTTI
来表明LLVM是否包含RTTI信息。因此在cmake中可以这样写
if(NOT LLVM_ENABLE_RTTI)
target_compile_options(${mypass} PRIVATE "-fno-rtti")
endif()
通常编译LLVM时我们会设置LLVM_BUILD_LLVM_DYLIB=ON
来得到一个包含所有符号的LLVM动态库,但还有一个重要的设置是LLVM_LINK_LLVM_DYLIB
,这个变量控制生成的二进制工具是与各个components静态链接还是与这个大的LLVM动态库进行动态链接。
如果没有设置LLVM_LINK_LLVM_DYLIB
,ldd opt
会发现opt并不依赖于任何llvm相关的库(因为是静态链接的)。这时候在编译自己的pass的时候,就不要链接任何LLVM的库了(因为opt中已经静态链接过一次了),否则会得到类似于下面的报错
: CommandLine Error: Option 'help-list' registered more than once!
LLVM ERROR: inconsistency in registered CommandLine options