由于符号与 abi::cxx11 的链接问题?

Posted

技术标签:

【中文标题】由于符号与 abi::cxx11 的链接问题?【英文标题】:Linking problems due to symbols with abi::cxx11? 【发布时间】:2016-03-22 15:56:22 【问题描述】:

我们最近因为GCC 5.1, libstdc++ and Dual ABI 收到了一份报告。看起来Clang is not aware of the GCC inline namespace changes,所以它基于一组命名空间或符号生成代码,而GCC使用另一组命名空间或符号。在链接时,由于缺少符号而出现问题。

如果我正确解析了Dual ABI 页面,看起来就像是在_GLIBCXX_USE_CXX11_ABIabi::cxx11 上进行了一些额外的困难。在 Red Hat 的博客 GCC5 and the C++11 ABI 和 The Case of GCC-5.1 and the Two C++ ABIs 上可以阅读更多内容。

以下来自 Ubuntu 15 机器。本机提供GCC 5.2.1。

$ cat test.cxx
#include <string>

std::string foo __attribute__ ((visibility ("default")));
std::string bar __attribute__ ((visibility ("default")));

$ g++ -g3 -O2 -shared test.cxx -o test.so

$ nm test.so | grep _Z3
...
0000201c B _Z3barB5cxx11
00002034 B _Z3fooB5cxx11

$ echo _Z3fooB5cxx11 _Z3barB5cxx11 | c++filt 
foo[abi:cxx11] bar[abi:cxx11]

如何使用两种装饰生成带有符号的二进制文件(Red Hat 博客称之为“共存”)?

或者,我们有哪些选择?


我正在努力为用户实现“它只是工作”。我不在乎是否有两个具有两种不同行为的弱符号(std::string 缺少写时复制,而std::string[abi:cxx11] 提供写时复制)。或者,一个可以是另一个的别名。

Debian 在Debian Bug report logs: Bugs tagged libstdc++-cxx11 有大量类似的错误。他们的解决方案是在新的 ABI 下重建所有内容,但它没有处理混合/匹配编译器以 ABI 更改为模的极端情况。

在 Apple 世界中,我认为这接近于一个胖二进制文件。但我不确定在 Linux/GCC 世界中该做什么。最后,我们不控制发行版如何构建库,也不控制使用哪些编译器将应用程序与库链接。

【问题讨论】:

你的产品是什么?库还是程序? @n.m. - 产品是魏岱的Crypto++。它是一个 C++ 库。它由 Debian 构建并作为发行版的一部分提供。 【参考方案1】:

免责声明,以下内容未经生产测试,使用风险自负。

您可以自己在双 ABI 下发布您的库。这或多或少类似于 OSX “胖二进制”,但完全使用 C++ 构建。

最简单的方法是编译库两次:使用-D_GLIBCXX_USE_CXX11_ABI=0-D_GLIBCXX_USE_CXX11_ABI=1。根据宏的值将整个库放在两个不同的命名空间下:

#if _GLIBCXX_USE_CXX11_ABI
#  define DUAL_ABI cxx11 __attribute__((abi_tag("cxx11")))
#else
#  define DUAL_ABI cxx03
#endif

namespace CryptoPP 
  inline namespace DUAL_ABI 
    // library goes here
  

现在您的用户可以像往常一样使用CryptoPP::whatever,这取决于选择的ABI 映射到CryptoPP::cxx11::whateverCryptoPP::cxx03::whatever

注意,GCC 手册说此方法将更改标记的内联命名空间中定义的所有内容的重命名。根据我的经验,这不会发生。

如果_GLIBCXX_USE_CXX11_ABI 不为零,另一种方法是用__attribute__((abi_tag("cxx11"))) 标记每个类、函数和变量。这个属性很好地将[cxx11] 添加到解包器的输出中。我认为使用命名空间同样有效,并且需要对现有代码进行较少的修改。

理论上你不需要复制整个库,只需要复制使用std::stringstd::list的函数和类,以及使用这些函数和类的函数和类,等等递归地。但在实践中,这可能不值得付出努力,尤其是在库不是很大的情况下。

【讨论】:

谢谢@n.m。共享对象对于非感知编译器来说是有问题的,比如当前的 Clang。共享对象对于感知编译器来说是可以的。对于非感知编译器,共享对象需要CryptoPP::cxx11::whateverCryptoPP::cxx03::whatever 的符号。如您所描述的,标题中的内容应该没问题。我仍在玩弄比特,看看可以做些什么来帮助不知道的编译器。 "共享对象需要 CryptoPP::cxx11::whatever 和 CryptoPP::cxx03::whatever 的符号" 是的,这就是我要说的。您将两个版本组合在同一个库中。 如果你想支持clang或其他不知道abi_tag的编译器,就不要使用__attribute__((abi_tag("cxx11")))【参考方案2】:

这是一种方法,但它不是很优雅。我也不清楚如何让 GCC 自动化它,这样我就不必做两次了。

首先,将要变成库的示例:

$ cat test.cxx
#include <string>

std::string foo __attribute__ ((visibility ("default")));
std::string bar __attribute__ ((visibility ("default")));

然后:

$ g++ -D_GLIBCXX_USE_CXX11_ABI=0 -c test.cxx -o test-v1.o
$ g++ -D_GLIBCXX_USE_CXX11_ABI=1 -c test.cxx -o test-v2.o

$ ar cr test.a test-v1.o test-v2.o
$ ranlib test.a

$ g++ -shared test-v1.o test-v2.o -o test.so

最后,看看我们得到了什么:

$ nm test.a

test-v1.o:
00000004 B bar
         U __cxa_atexit
         U __dso_handle
00000000 B foo
0000006c t _GLOBAL__sub_I_foo
00000000 t _Z41__static_initialization_and_destruction_0ii
         U _ZNSsC1Ev
         U _ZNSsD1Ev

test-v2.o:
         U __cxa_atexit
         U __dso_handle
0000006c t _GLOBAL__sub_I__Z3fooB5cxx11
00000018 B _Z3barB5cxx11
00000000 B _Z3fooB5cxx11
00000000 t _Z41__static_initialization_and_destruction_0ii
         U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev
         U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev

还有:

$ nm test.so

00002020 B bar
00002018 B __bss_start
00002018 b completed.7181
         U __cxa_atexit@@GLIBC_2.1.3
         w __cxa_finalize@@GLIBC_2.1.3
00000650 t deregister_tm_clones
000006e0 t __do_global_dtors_aux
00001ef4 t __do_global_dtors_aux_fini_array_entry
00002014 d __dso_handle
00001efc d _DYNAMIC
00002018 D _edata
00002054 B _end
0000087c T _fini
0000201c B foo
00000730 t frame_dummy
00001ee8 t __frame_dummy_init_array_entry
00000980 r __FRAME_END__
00002000 d _GLOBAL_OFFSET_TABLE_
000007dc t _GLOBAL__sub_I_foo
00000862 t _GLOBAL__sub_I__Z3fooB5cxx11
         w __gmon_start__
000005e0 T _init
         w _ITM_deregisterTMCloneTable
         w _ITM_registerTMCloneTable
00001ef8 d __JCR_END__
00001ef8 d __JCR_LIST__
         w _Jv_RegisterClasses
00000690 t register_tm_clones
00002018 d __TMC_END__
00000640 t __x86.get_pc_thunk.bx
0000076c t __x86.get_pc_thunk.dx
0000203c B _Z3barB5cxx11
00002024 B _Z3fooB5cxx11
00000770 t _Z41__static_initialization_and_destruction_0ii
000007f6 t _Z41__static_initialization_and_destruction_0ii
         U _ZNSsC1Ev@@GLIBCXX_3.4
         U _ZNSsD1Ev@@GLIBCXX_3.4
         U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev@@GLIBCXX_3.4.21
         U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev@@GLIBCXX_3.4.21

【讨论】:

以上是关于由于符号与 abi::cxx11 的链接问题?的主要内容,如果未能解决你的问题,请参考以下文章

如何链接到共享库中定义的 [abi:cxx11] 函数?

Makefile 报告 `func[abi:cxx11]()' 的多个定义

创建后符号链接断开

如何在GDB中调用C ++函数?

与 libOmnitureAppMeasurement-iPhoneSimulator.a 链接的 iOS 未定义符号错误

由于 VC9 编译器符号名称不匹配而导致无法解析的外部符号