什么是 gcc 名称重整中的"destructor group"符号

What is a "destructor group" symbol in gcc name mangling

本文关键字:destructor group 符号 gcc 什么      更新时间:2023-10-16

https://stackoverflow.com/a/6614369/1091587 简要介绍了当您读取使用 "gcc3" 类型名称重整编译的程序的符号表时出现的析构函数类型(D0、D1、D2)。还有相应的构造函数 C0/C1/C2。在 g++-4.7(可能更早)中,出现了一个新的 ctor/dtor 对,即 C5/D5,但仅作为调试符号。

$ cat i.cpp 
class X { public: virtual ~X() {}; };
int main(void) { X x; return 0; };
$ g++ -c i.cpp 
$ nm i.o | grep 5
0000000000000000 n _ZN1XC5Ev
0000000000000000 n _ZN1XD5Ev
$ c++filt -n _ZN1XC5Ev _ZN1XD5Ev
X::X()
X::~X()

demangler 源称 D5 对象为"gnu_v3_object_dtor_group",但究竟什么是 dtor 组,它有什么用? clang++-3.3 不会发出它,http://gcc.gnu.org/ml/gcc-patches/2011-11/msg00383.html 表明它可能与 gcc 中的新事务内存功能有关。

这个LLVM补丁和这个GCC错误提供了更多的背景信息。通过点击链接,我发现了错误 3187 - gcc 放下了两个构造函数的副本,这似乎是这一切的起源:

构造函数和析构函数的两个(有时三个)相同副本 被放下。链接器不会失败,但生成的二进制文件 比必要的大 20%(在我们的真实示例上)。

如果你搜索"PR c c ++/3187"(例如),你可以找到许多关于gcc补丁ML的讨论。基本上,C5/D5 本身不是一个构造函数/析构函数,而是一个包含两个或多个"基本"构造函数/析构函数的 COMDAT 组。这可确保组中的函数要么全部用于最终二进制文件,要么全部丢弃(以强制执行"一个定义规则")。

上述错误中讨论的结果似乎是:

对于任何类,实现都可以选择使用每个 comdat 构造函数/析构函数或使用 C5/D5 组合。我可能会这样做 基于任何盈利标准的决策。如果使用 C5/D5 通信 规则是

  • C5 通信必须具有 C1 和 C2。
  • 如果类具有虚拟析构函数,则 D5 组合必须具有 D0、D1 和 D2
  • 如果类具有非虚拟析构函数,则 D5 组合必须只有 D1 和 D2 析构函数。即使 实现使用 D0 而不是对 D1 + _ZdlPv 的调用来实现 "删除 *x"

您可以通过例如转储带有readelf -G的文件来查看 comdats:

COMDAT group section [    1] `.group' [_ZN1XD5Ev] contains 2 sections:
   [Index]    Name
   [   10]   .text._ZN1XD2Ev
   [   12]   .text._ZN1XD0Ev
COMDAT group section [    2] `.group' [_ZN1XC5Ev] contains 1 sections:
   [Index]    Name
   [   14]   .text._ZN1XC2Ev

(这是GCC 4.6,这可能就是它与上述定义不匹配的原因)