Я замечаю повторяющиеся символы, присутствующие в .so файле
Как это?
$ cat foo.c
int foo(void)
{
return 42;
}
Компиляция:
$ gcc -Wall -fPIC -c foo.c
Проверьте символыв объектном файле для foo
:
$ readelf -s foo.o | grep foo
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS foo.c
8: 0000000000000000 11 FUNC GLOBAL DEFAULT 1 foo
Один удар.
Создание общей библиотеки:
$ gcc -Wall -shared -o libfoo.so foo.o
Проверка символов в общей библиотеке для foo
:
$ readelf -s libfoo.so | grep foo
5: 000000000000057a 11 FUNC GLOBAL DEFAULT 9 foo
29: 0000000000000000 0 FILE LOCAL DEFAULT ABS foo.c
44: 000000000000057a 11 FUNC GLOBAL DEFAULT 9 foo
Теперь два попадания.
Здесь все в порядке.См. Еще несколько рисунков:
$ readelf -s foo.o | egrep '(foo|Symbol table|Ndx)'
Symbol table '.symtab' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS foo.c
8: 0000000000000000 11 FUNC GLOBAL DEFAULT 1 foo
Объектный файл имеет одну таблицу символов, свою статическую таблицу символов .symtab
, которая используется компоновщиком для разрешения символов во время соединения.Но:
$ readelf -s libfoo.so | egrep '(foo|Symbol table|Ndx)'
Symbol table '.dynsym' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
5: 000000000000057a 11 FUNC GLOBAL DEFAULT 9 foo
Symbol table '.symtab' contains 48 entries:
Num: Value Size Type Bind Vis Ndx Name
29: 0000000000000000 0 FILE LOCAL DEFAULT ABS foo.c
44: 000000000000057a 11 FUNC GLOBAL DEFAULT 9 foo
общая библиотека имеет две таблицы символов: статическая таблица символов .symtab
, как объектный файл, плюс таблица динамических символов, .dynsym
, используемаязагрузчик для разрешения символов во время выполнения.
Когда вы связываете объектные файлы в общей библиотеке, компоновщик по умолчанию транскрибирует символы GLOBAL
из их .symtab
в .symtab
и .dynsym
общей библиотеки, за исключением тех символов, которые имеют HIDDEN
видимость в объектных файлах (которые они получают, будучи определенными с атрибутом скрытой видимости при компиляции).
Любые GLOBAL
символы с HIDDEN
видимостью в объектных файлах транскрибируются как LOCAL
символы с DEFAULT
видимостью в .symtab
общей библиотеки и не транскрибируютсяв .dynsym
общей библиотеки вообще.Поэтому, когда совместно используемая библиотека связана с чем-либо еще, ни компоновщик, ни загрузчик не могут видеть глобальные символы, которые были HIDDEN
при компиляции.
Но кроме скрытых символов, которых часто нет,такие же глобальные символы появятся в таблицах .symtab
и .dynsym
общей библиотеки.Каждый определенный символ, который появляется в обеих таблицах, обращается к одному и тому же определению.
Позже, комментарии OP
Я взял таблицу символов, выполнив команду objdump -T,который в идеале должен перечислять символы, присутствующие только в динамической таблице символов.
Это приводит нас к другому объяснению, потому что objdump -T
действительно сообщает только динамическую таблицу символов (например, readelf --dyn-syms
).
Обратите внимание, что символ сообщается дважды:
...
00aab4d0 w DF .text 0000003c Base boost::asio::error::detail::misc_category::~misc_category()
...
00aab50c w DF .text 0000003c Base boost::asio::error::detail::misc_category::~misc_category()
...
классифицируется w
в столбце 2 (как и все другие символы в вашем фрагменте).Что означает objdump
, так это то, что символ слабый .
Давайте повторим наблюдение:
foo.hpp
#pragma once
#include <iostream>
struct foo
{
explicit foo(int i)
: _i{i}
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
~foo()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int _i = 0;
};
bar.cpp
#include "foo.hpp"
foo bar()
{
return foo(2);
}
gum.cpp
#include "foo.hpp"
foo gum()
{
return foo(1);
}
Скомпилируйте и сделайтеобщая библиотека:
$ g++ -Wall -Wextra -c -fPIC bar.cpp gum.cpp
$ g++ -shared -o libbargum.so bar.o gum.o
Посмотрите, что динамические символы objdump
отчеты из struct foo
:
$ objdump -CT libbargum.so | grep 'foo::'
00000000000009bc w DF .text 0000000000000046 Base foo::foo(int)
00000000000009bc w DF .text 0000000000000046 Base foo::foo(int)
Повторяющиеся слабые экспорты конструктора foo::foo(int)
,Точно так же, как вы заметили.
Повесьте на галочку, хотя.foo::foo(int)
является сигнатурой метода C ++, но на самом деле не является символом , который может распознать компоновщик.Давайте сделаем это снова, на этот раз без разбивки:
$ objdump -T libbargum.so | grep 'foo'
00000000000009bc w DF .text 0000000000000046 Base _ZN3fooC1Ei
00000000000009bc w DF .text 0000000000000046 Base _ZN3fooC2Ei
Теперь мы видим символы, которые видит компоновщик, и дублирования больше не видно: _ZN3fooC1Ei
! = _ZN3fooC2Ei
, хотя оба символаимеют один и тот же адрес и
$ c++filt _ZN3fooC1Ei
foo::foo(int)
$ c++filt _ZN3fooC2Ei
foo::foo(int)
они оба деформируются на одну и ту же вещь, foo::foo(int)
.На самом деле существует 5 различных символов - _ZN3fooC
N Ei
, для 1 <= <em>N <= 5 - которые превращаются в <code>foo::foo(int).(И g++
фактически использует _ZN3fooC1Ei
, _ZN3fooC2Ei
и _ZN3fooC5Ei
в объектных файлах bar.o
и gum.o
).
Таким образом, на самом деле в динамической таблице символов нет дублирующихся символов: хитрая природа сопоставления имен и имен позволяет просто так выглядеть.
Но почему?
Боюсь, ответ на этот вопрос здесь слишком длинный и сложный.
Резюме
TheКомпилятор GCC C ++ использует два слабых символа, которые идентично разделяются, чтобы по-разному ссылаться на глобальный встроенный метод класса , как частьего фондовая формула для обеспечения возможности успешного связывания глобальных встроенных методов класса в позиционно-независимом коде.Это неотъемлемая проблема для любого компилятора, и формула GCC для нее не единственно возможная.У Clang есть другое решение, которое включает в себя использование синонимичных, но различных символов, и поэтому не приводит к иллюзорному «дублированию» символов, которые вы видели.