Да, это спланированное поведение. Когда вы связываете программу с двоичным файлом, все ссылки на именованные внешние (нестатические 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 имеет версионные символы, и каждая библиотека будет использовать соответствующую версию соответствующего символа.