由于 abi::cxx11 的符号而导致的链接问题?

2024-11-04 08:43:00
admin
原创
37
摘要:问题描述:我们最近收到一份有关GCC 5.1、libstdc++ 和 Dual ABI的报告。看来Clang 不知道 GCC 内联命名空间更改,因此它根据一组命名空间或符号生成代码,而 GCC 使用了另一组命名空间或符号。在链接时,由于缺少符号而出现问题。如果我正确解析了Dual ABI页面,它看起来就像是一...

问题描述:

我们最近收到一份有关GCC 5.1、libstdc++ 和 Dual ABI的报告。看来Clang 不知道 GCC 内联命名空间更改,因此它根据一组命名空间或符号生成代码,而 GCC 使用了另一组命名空间或符号。在链接时,由于缺少符号而出现问题。

如果我正确解析了Dual ABI页面,它看起来就像是一个转向的问题_GLIBCXX_USE_CXX11_ABI,并且abi::cxx11会有一些额外的困难。有关更多阅读材料,请参阅 Red Hat 博客GCC5 和 C++11 ABI以及GCC-5.1 和两个 C++ ABI 的案例。

下面是来自 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 报告日志中有大量类似的错误:标记为 libstdc++-cxx11 的错误。他们的解决方案是在新的 ABI 下重建所有内容,但它没有处理混合/匹配编译器模数 ABI 更改的极端情况。

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


解决方案 1:

免责声明,以下内容尚未在生产中测试,请自行承担风险。

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

最简单的方法是编译库两次:with-D_GLIBCXX_USE_CXX11_ABI=0和 with -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照常使用,这将映射到CryptoPP::cxx11::whateverCryptoPP::cxx03::whatever取决于所选的 ABI。

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

另一种方法是用__attribute__((abi_tag("cxx11")))if _GLIBCXX_USE_CXX11_ABIis nonzero 标记每个类、函数和变量。此属性很好地添加到了[cxx11]分解器的输出中。我认为使用命名空间同样有效,并且对现有代码的修改较少。

理论上,您不需要复制整个库,只需复制使用std::string和的函数和类std::list,以及使用这些函数和类的函数和类,依此类推。但实际上,这可能不值得付出努力,尤其是在库不是很大的情况下。

解决方案 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
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用