编译选项导致的 undefined reference to `typeinfo XXX`

文章讲述了在使用seetaface6库时遇到的链接错误,该错误是由于项目代码启用RTTI而库未启用导致的。通过添加-fno-rtti编译选项解决当前问题,但作者认为这不是最佳方案。最终,通过去掉-fvisibility=hidden选项并重新编译库,成功解决了问题,这允许正确处理类型信息并避免了符号冲突。

在使用了第三方库seetaface6,并且自定义类继承了库中的类。

链接时报错:

/usr/bin/ld: CMakeFiles/main.dir/main.cpp.o:(.data.rel.ro._ZTI7Handler[_ZTI7Handler]+0x10): undefined reference to `typeinfo for seeta::v3::QualityOfBrightness'

反复看了库的源代码和自己的代码,确认所有函数都定义了。

绝望之时看到有说是-fno-rtti选项的原因。

g++手册:

-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.

我们平时的代码编译时是-frtti,而如果其他库使用-fno-rtti,就可能在链接时出错。

当在编译我对代码时带上-fno-rtti,确实能通过了,但是这样有很大缺陷。
所以我选择将库重新编译,带上-frtti。然而这并没有奏效,我确信-frtti在库中已经启用。

又看到其中有个-fvidibility=hidden,当将它去掉之后,重新编译库,就解决了。具体原因请看手册介绍。

-fvisibility=[default|internal|hidden|protected]
           Set the default ELF image symbol visibility to the specified
           option---all symbols are marked with this unless overridden within the
           code.  Using this feature can very substantially improve linking and
           load times of shared object libraries, produce more optimized code,
           provide near-perfect API export and prevent symbol clashes.  It is
           strongly recommended that you use this in any shared objects you
           distribute.

           Despite the nomenclature, default always means public; i.e., available
           to be linked against from outside the shared object.  protected and
           internal are pretty useless in real-world usage so the only other
           commonly used option is hidden.  The default if -fvisibility isn't
           specified is default, i.e., make every symbol public.

           A good explanation of the benefits offered by ensuring ELF symbols have
           the correct visibility is given by "How To Write Shared Libraries" by
           Ulrich Drepper (which can be found at
           <https://www.akkadia.org/drepper/>)---however a superior solution made
           possible by this option to marking things hidden when the default is
           public is to make the default hidden and mark things public.  This is
           the norm with DLLs on Windows and with -fvisibility=hidden and
           "__attribute__ ((visibility("default")))" instead of
           "__declspec(dllexport)" you get almost identical semantics with
           identical syntax.  This is a great boon to those working with cross-
           platform projects.

           For those adding visibility support to existing code, you may find
           "#pragma GCC visibility" of use.  This works by you enclosing the
           declarations you wish to set visibility for with (for example) "#pragma
           GCC visibility push(hidden)" and "#pragma GCC visibility pop".  Bear in
           mind that symbol visibility should be viewed as part of the API
           interface contract and thus all new code should always specify
           visibility when it is not the default; i.e., declarations only for use
           within the local DSO should always be marked explicitly as hidden as so
           to avoid PLT indirection overheads---making this abundantly clear also
           aids readability and self-documentation of the code.  Note that due to
           ISO C++ specification requirements, "operator new" and "operator delete"
           must always be of default visibility.

           Be aware that headers from outside your project, in particular system
           headers and headers from any other library you use, may not be expecting
           to be compiled with visibility other than the default.  You may need to
           explicitly say "#pragma GCC visibility push(default)" before including
           any such headers.

           "extern" declarations are not affected by -fvisibility, so a lot of code
           can be recompiled with -fvisibility=hidden with no modifications.
           However, this means that calls to "extern" functions with no explicit
           visibility use the PLT, so it is more effective to use "__attribute
           ((visibility))" and/or "#pragma GCC visibility" to tell the compiler
           which "extern" declarations should be treated as hidden.

           Note that -fvisibility does affect C++ vague linkage entities. This
           means that, for instance, an exception class that is be thrown between
           DSOs must be explicitly marked with default visibility so that the
           type_info nodes are unified between the DSOs.

           An overview of these techniques, their benefits and how to use them is
           at <http://gcc.gnu.org/wiki/Visibility>.


以下内容转自https://blog.csdn.net/ai2000ai/article/details/47152133

在项目中遇到了这样一个问题:C++文件编译都OK,但链接的时候报错:undefined reference to `typeinfo for xxx’。typeinfo是C++中的RTTI(RunTime Type Identification)机制中记录类型信息用的,dynamic_cast和typeid操作符会使用这些信息。

以”undefined reference to typeinfo”为关键字在网络上搜索,大多数都是说有虚函数定义了但是未实现导致的。但是我的代码显然不是这个情况。在我即将放弃的时候,终于在StackOverflow上发现有人提出,这种错误的原因也可能是混合使用了带RTTI信息和不带RTTI信息的代码导致的。对比检查,发现我的项目里的问题正是这个。最后用了一点dirty hack,解决了bug。下面就仔细分析一下”undefined reference to `typeinfo for xxx’“产生的原因。

虚函数未实现

产生”undefined reference to `typeinfo for xxx’“最常见的原因就是基类的虚函数未实现了。由于C++类的实现可以分布在多个源文件中,所以生成目标文件时,基类的虚函数没有定义是不会报错的。但是链接成可执行文件时,需要将虚函数的信息放进typeinfo中,这个时候虚函数未实现就会引发这个错误。

混用了no-RTTI代码和RTTI代码

我碰到的正是混用了no-RTTI和RTTI代码的情形。项目中我们自己写的程序必须开启RTTI,而我们使用的外部的一个库使用no-RTTI编译。我们在自己的代码中需要重载一个外部库中的带虚函数的类,结果链接的时候就出现了问题。外部库中的基类使用-fno-rtti选项编译,生成的代码没有typeinfo信息,而我们的代码使用-frtti选项编译,要求基类必须要有typeinfo信息。最后,我在编译系统中做了一些dirty hack,让那个派生类所在的源文件以-fno-rtti选项编译,解决了问题。


我遇到的问题类似,现在的项目中需要开启RTTI,链接的外部库是no-RTTI编译的,在现在的工程中重载外部库的带虚函数的类,连接的时候报错.原文说的dirty hack,是对单个文件加编译选项-fno-rtti 。因为我用的外部库是可以开启RTTI的,我用RTTI重新编译一次后,现在的工程不报错啦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

barbyQAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值