#c #linux #linker #g #elf
#c #linux #компоновщик #g #elf
Вопрос:
У меня есть исходный файл, который определяет конструктор перемещения для очень большого класса. Я компилирую ее с помощью g 4.9.2 в системе Linux. Когда я сбрасываю таблицу символов результирующего объектного файла ELF, я вижу 2 списка для конструктора перемещения. Оба списка имеют один и тот же адрес, одинаковый размер, один и тот же тип, и компоновщик связывает его просто отлично, без нарушений ODR. Когда я разбираю объектный файл, я вижу только одну функцию конструктора перемещения. Я пришел к выводу, что в таблице символов есть две записи, которые указывают на одно и то же местоположение.
Такое же поведение также происходит для конструктора этого конкретного класса, который определен в этом же исходном файле.
Единственный флаг компиляции, который я вижу, который я не до конца понимаю, это ‘-m64’, но я не знаю, как это повлияет на таблицу символов.
Я также пробовал это с g 9.2.0, и теперь у меня есть 3 записи в таблице символов! Две из которых указывают на один и тот же адрес, а третья указывает на адрес 0x0, находится в разделе .text.unlikely и помечена как [clone .cold] .
Почему это так?
редактировать: на самом деле я могу воспроизвести это дома с очень маленьким классом.
// class.h
class VeryLargeClass
{
int data;
public:
VeryLargeClass(VeryLargeClassamp;amp;);
};
// class.cpp
#include "class.h"
VeryLargeClass::VeryLargeClass(VeryLargeClassamp;amp; 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(VeryLargeClassamp;amp;)
0000000000000000 g F .text 000000000000000b VeryLargeClass::VeryLargeClass(VeryLargeClassamp;amp;)
Обратите внимание, как конструктор перемещения дважды отображается в таблице символов? Я предполагаю, что я просто чего-то не понимаю в формате ELF. Это было сделано с использованием g 10.2.
Комментарии:
1. Запрашивают ли эти списки символ или предоставляют символ? Определен ли конструктор перемещения в этом исходном файле? Для этого имело бы смысл запрашивать в нескольких местах.
2. вероятно, это конструктор перемещения или копирования, но если это не так, это может быть ошибка компилятора
3. Людям будет трудно воспроизвести это и, следовательно, трудно ответить, если вы не дадите нам минимально воспроизводимый пример (минимальный объем кода, который показывает поведение, которое вы описываете).
4. @Anonymous1847 Мне удалось воспроизвести его дома, я отредактировал свой вопрос.
Ответ №1:
TL; DR: Если вы хотите понять, что происходит
- не используйте
objdump
для проверки файлов ELF, используйтеreadelf
вместо этого (см. Ниже) - не разбирайте имена с
c filt
помощью — разные символы могут создавать одно и то же разбираемое имя (т. Е. Это не преобразование один к одному).
Подробные сведения:
cat foo.cc
struct Foo {
Foo(Fooamp;amp; f);
void *p;
};
Foo::Foo(Foo amp;amp;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(Fooamp;amp;)
0000000000000000 g F .text 0000000000000028 Foo::Foo(Fooamp;amp;)
objdump -t foo.o | grep Foo
0000000000000000 g F .text 0000000000000028 _ZN3FooC2EOS_
0000000000000000 g F .text 0000000000000028 _ZN3FooC1EOS_
Обратите внимание, что символы разные. Вы можете прочитать о конструкторах C1
and C2
здесь .
PS Почему вы никогда не должны использовать objdump
для просмотра ELF
файлов?
objdump
является частью binutils
. Хотя binutils
они все еще активно поддерживаются, они были написаны задолго до ELF
того, как существовали и использовались libbfd
. Последняя имеет внутреннюю модель данных, которая не может адекватно описать ELF
формат файла.
Поэтому, когда вы используете objdump
для ELF
файла, сначала libbfd
разбирает файл на эту внутреннюю / неадекватную модель данных, а затем objdump
представляет эту модель в удобочитаемой форме. При переводе многое теряется.
Комментарии:
1. Вдохновленный этим ответом, я попытался использовать
readelf
, но потерпел неудачу. У меня есть символ — метод, который я могу исследовать,objdump
но нетreadelf
. Первая показывает это (с--syms
), вторая — нет (я даже пробовал--all
). Не говоря уже о том, чтоreadelf
, похоже, у--disassemble
нее нет опций…2. @AdamBadura Ошибка,
readelf
безусловно, возможна, но то, что вы описали, кажется очень маловероятным. Пожалуйста, отправьте сообщение об ошибке со всеми подробностями.