Reputation: 858
I've got a source file that defines a move constructor for a very large class. I compile it with g++ 4.9.2, on a linux system. When I dump the symbol table of the resulting ELF object file, I see 2 listings for the move constructor. Both listings have the same address, the same size, the same type, and the linker links it just fine, with no ODR violations. When I disassemble the object file, I only see one move constructor function. My conclusion is that the symbol table has two entries that point to the same location.
This same behavior also happens for the constructor of this particular class, which is defined in this same source file.
The only compile flag I see that I don't fully understand is '-m64', but I don't know how this would affect the symbol table.
I tried this with g++ 9.2.0 as well, and now I have 3 entries in the symbol table! Two of which point to the same address, and the third points to address 0x0, is in the .text.unlikely section, and is marked as [clone .cold].
Why is this?
edit: I can reproduce this at home with a very small class, actually.
// class.h
class VeryLargeClass
{
int data;
public:
VeryLargeClass(VeryLargeClass&&);
};
// class.cpp
#include "class.h"
VeryLargeClass::VeryLargeClass(VeryLargeClass&& other)
{
data = other.data;
other.data = 0;
}
If I compile this with g++ -c -O3 class.cpp -o class.o
, and then dump the symbol table with objdump -t class.o | c++filt
I get the following:
class.o: file format 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&&)
Notice how the move constructor shows up twice in the symbol table? I'm guessing I just don't understand something about the ELF format. This was done using g++ 10.2.
Upvotes: 6
Views: 2418
Reputation: 213686
TL;DR: If you want to understand what's going on
objdump
to examine ELF files, use readelf
instead (see below)c++filt
-- different symbols can produce the same demangled name (i.e. it's not a one-to-one transformation).Details:
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_
Note that the symbols are different. You can read about the C1
and C2
constructors here.
P.S. Why should you never use objdump
to look at ELF
files?
objdump
is part of binutils
. While binutils
are still actively maintained, they were written long before ELF
existed, and use libbfd
. The latter has an internal data model which can't adequately describe ELF
file format.
So when you use objdump
on an ELF
file, first libbfd
parses the file into this internal / inadequate data model, and then objdump
presents this model in a human readable form. There is a lot that gets lost in translation.
Upvotes: 4