C++ ELF目标文件符号表中同一个函数出现了两次

5
我有一个源文件,为一个非常大的类定义了移动构造函数。我在Linux系统上使用g++ 4.9.2进行编译。当我转储ELF目标文件的符号表时,我看到移动构造函数有两个列表。这两个列表具有相同的地址、大小和类型,并且链接器可以正常链接它,没有违反ODR。当我反汇编目标文件时,我只看到一个移动构造函数。我的结论是符号表有两个指向同一位置的条目。
对于此特定类的构造函数(也是在同一源文件中定义的),同样会出现这种行为。
我唯一看到的不太明白的编译标志是“-m64”,但我不知道这会如何影响符号表。
我还尝试使用g++ 9.2.0,现在符号表中有3个条目!其中两个指向相同的地址,第三个指向地址0x0,在.text.unlikely部分,并标记为[clone .cold]。
这是为什么呢?
编辑:实际上,我可以用一个非常小的类在家里重现这个问题。
// class.h
class VeryLargeClass
{
    int data;

    public:
    VeryLargeClass(VeryLargeClass&&);
};

// class.cpp
#include "class.h"

VeryLargeClass::VeryLargeClass(VeryLargeClass&& other)
{
    data = other.data;
    other.data = 0;
}

如果我使用 g++ -c -O3 class.cpp -o class.o 编译它,然后用 objdump -t class.o | c++filt 转储符号表,我得到以下结果: class.o:文件格式为elf64-x86-64。

SYMBOL TABLE:
0000000000000000 l    df *ABS*  0000000000000000 main.cc
0000000000000000 l    d  .text  0000000000000000 .text
0000000000000000 l    d  .data  0000000000000000 .data
0000000000000000 l    d  .bss   0000000000000000 .bss
0000000000000000 l    d  .note.GNU-stack    0000000000000000 .note.GNU-stack
0000000000000000 l    d  .eh_frame  0000000000000000 .eh_frame
0000000000000000 l    d  .comment   0000000000000000 .comment
0000000000000000 g     F .text  000000000000000b VeryLargeClass::VeryLargeClass(VeryLargeClass&&)
0000000000000000 g     F .text  000000000000000b VeryLargeClass::VeryLargeClass(VeryLargeClass&&)

注意移动构造函数在符号表中出现了两次?我猜我对ELF格式有些不理解。这是使用g++ 10.2完成的。


1
这些列表是在询问符号还是提供符号?移动构造函数在那个源文件中定义了吗?在多个地方询问这个问题是有意义的。 - Anonymous1847
可能是移动构造函数或复制构造函数的问题,但如果它们不是,那么这可能是编译器的错误。 - cdecompilador
1
除非您提供一个最小可重现的示例(显示您描述的行为的最少量代码),否则人们会发现这很难复制并因此难以回答。 - Anonymous1847
@Anonymous1847 我在家里成功地复现了它,我已经编辑了我的问题。 - dwwork
1个回答

4

TL;DR: 如果你想了解正在发生的事情

  1. 不要使用objdump来检查ELF文件,应改用readelf(见下文)
  2. 不要使用c++filt来还原名称 - 不同的符号可能会产生相同的还原名称(即它不是一对一的转换)。

详情:

cat foo.cc

struct Foo {
  Foo(Foo&& f);
  void *p;
};

Foo::Foo(Foo &&f) {
  p = f.p;
  f.p = nullptr;
}

g++ -c foo.cc
objdump -t foo.o | grep Foo | c++filt

0000000000000000 g     F .text  0000000000000028 Foo::Foo(Foo&&)
0000000000000000 g     F .text  0000000000000028 Foo::Foo(Foo&&)

objdump -t foo.o | grep Foo 
0000000000000000 g     F .text  0000000000000028 _ZN3FooC2EOS_
0000000000000000 g     F .text  0000000000000028 _ZN3FooC1EOS_

请注意符号不同。您可以在此处阅读有关C1C2构造函数的信息。

P.S. 为什么永远不应该使用objdump查看ELF文件?

objdumpbinutils的一部分。虽然binutils仍在积极维护,但它们是在ELF文件存在之前编写的,并且使用了libbfd。后者具有无法足够描述ELF文件格式的内部数据模型。

因此,当您在ELF文件上使用objdump时,首先libbfd将文件解析为这种内部/不充分的数据模型,然后objdump以人类可读的形式呈现此模型。在这个转化中失去了很多重要信息


受这个答案的启发,我尝试使用readelf,但失败了。我有一个符号 - 一个方法 - 我可以用objdump来调查它,但不能用readelf。第一个(命令)显示它(使用--syms),而第二个则不显示(我甚至尝试了--all)。更不用说readelf似乎没有--disassemble选项…… - Adam Badura
@AdamBadura readelf 中的一个 bug 是有可能存在的,但是你所描述的情况似乎非常不太可能。请务必提交一个包含 所有 细节的 bug 报告。 - Employed Russian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接