Конфликты символов общей библиотеки и связывание c (на Linux) - PullRequest
2 голосов
/ 14 июля 2020

Я столкнулся с проблемой, которая подробно описана в хорошей статье Конфликты символов общей библиотеки (на Linux) . Проблема в том, что когда выполнение и .so определили функции с одним и тем же именем, если .so вызовет это имя функции, он вызовет эту функцию при выполнении, а не эту в самом .so.

Давайте поговорим о кейсе в этой статье. Я понимаю, что функция DoLayer() в layer.o имеет зависимость от внешней функции DoThing() при компиляции layer.o.

Но при компиляции libconflict.so не следует разрешать зависимость внешней функции в -местите и просто замените адресом conflict.o/DoThing() статически?

Почему layer.o/DoLayer() все еще использует динамическое связывание c для поиска DoThing()? Это спланированное поведение?

Ответы [ 2 ]

0 голосов
/ 14 июля 2020

Это спланированное поведение?

Да.

Во время внедрения разделяемых библиотек на UNIX, целью было сделать вид, что они работают точно так же, как если бы код был в обычной (архивной) библиотеке.

Предположим, у вас есть foo(), определенный как в libfoo, так и в libbar, и bar() в libbar вызовах foo().

Цель дизайна заключалась в том, чтобы cc main.c -lfoo -lbar работал одинаково независимо от того, являются ли libfoo и libbar архивными или разделяемыми библиотеками. Единственный способ добиться этого - использовать libbar.so динамическое c связывание для разрешения вызова с bar() на foo(), несмотря на наличие локальной версии foo().

Этот дизайн делает невозможным создание автономного libbar.so - его поведение (какие функции он вызывает) зависит от того, какие другие функции связаны с процессом. Это также противоположно тому, как работают Windows DLL.

Создание автономных DSO в то время не рассматривалось, поскольку UNIX был фактически открытым исходным кодом.

Вы можете измените правила с помощью специальных флагов компоновщика, например -Bsymbolic. Но правила очень быстро усложняются, и (поскольку это не по умолчанию) вы можете столкнуться с ошибками в компоновщике или загрузчике времени выполнения.

0 голосов
/ 14 июля 2020

Да, это спланированное поведение. Когда вы связываете программу с двоичным файлом, все ссылки на именованные внешние (нестатические c) функции разрешаются и указывают на таблицу символов для двоичного файла. Любые общие библиотеки, с которыми связаны ссылки, указываются как записи DT_NEEDED.

Затем, когда вы запускаете двоичный файл, динамический c компоновщик загружает каждую требуемую общую библиотеку по подходящему адресу и преобразует каждый символ в адрес. Иногда это делается лениво, а иногда - один раз при первом запуске. Если имеется несколько символов с одним и тем же именем, один из них будет выбран компоновщиком, и ваша программа, скорее всего, выдаст ошибку sh, так как вы можете получить неправильный.

Обратите внимание, что это поведение на Linux, в котором все символы представлены в виде плоского пространства имен. Windows разрешает символы по-разному, используя топологию дерева, которая имеет как преимущества (меньше конфликтов), так и недостатки (невозможность выделить память в одной библиотеке и освободить ее в другой).

Поведение Linux является очень важно, если вы хотите, чтобы работали такие вещи, как LD_PRELOAD. Это позволяет использовать инструменты отладки, такие как Electri c Fence, и инструменты профилирования ЦП, такие как инструменты производительности Google, или заменять распределитель памяти во время выполнения. Ничего из этого не сработало бы, если бы символы были преимущественно разрешены в их двоичную или общую библиотеку.

Компоновщик GNU Dynami c, однако, поддерживает версии символов, так что можно загружать несколько версий общей библиотеки в ту же программу. Часто дистрибутивы, такие как Debian, делают это с библиотеками, которые они ожидают часто менять, например OpenSSL. Если программа использует liba, которая использует OpenSSL 1.0, и libb, которая использует OpenSSL 1.1, тогда программа все равно должна работать в таком случае, поскольку OpenSSL имеет версионные символы, и каждая библиотека будет использовать соответствующую версию соответствующего символа.

...