Связывание двух разделяемых библиотек с некоторыми из тех же символов
Я связываюсь с двумя разными разделяемыми библиотеками. Обе библиотеки определяют некоторые символы, которые имеют имя, но имеют разные реализации. Я не могу заставить каждую библиотеку использовать свою собственную реализацию по сравнению с другой.
Например, обе библиотеки определяют глобальную функцию bar()
, которая каждый вызывает внутренне. Библиотека вызывает его из foo1()
, а библиотека два вызывает его из foo2()
.
Lib1.so:
T bar
T foo1() // calls bar()
Lib2.so:
T bar
T foo2() // calls bar()
Если я привяжу свое приложение к Lib1.so, а затем Lib2.so, то вызов строки из Lib1.so вызывается даже при вызове foo2()
. Если, с другой стороны, я связываю свое приложение с Lib2.so, а затем Lib1.so, тогда bar всегда вызывается из Lib2.so.
Есть ли способ, чтобы библиотека всегда предпочитала свою собственную реализацию над любой другой библиотекой?
Ответы
Ответ 1
Существует несколько способов решить эту проблему:
-
Передать -Bsymbolic
или -Bsymbolic-functions
в компоновщик. Это имеет глобальный эффект: каждая ссылка на глобальный символ (типа функции для -Bsymbolic-functions
), который может быть разрешен для символа в библиотеке, разрешена для этого символа. При этом вы теряете возможность вставлять вызовы внутренней библиотеки на эти символы с помощью LD_PRELOAD. Символы по-прежнему экспортируются, поэтому на них можно ссылаться извне библиотеки.
-
Используйте версию script для обозначения символов как локальных для библиотеки, например. используйте что-то вроде: {local: bar;};
и передайте --version-script=versionfile
в компоновщик. Символы не экспортируются.
-
Отметьте символы с соответствующей видимой видимостью (информационной страницей GCC для видимости), которая будет либо скрытой, либо внутренней, либо защищенный. защищенные символы видимости экспортируются как .protected
, скрытые символы не экспортируются, а внутренние символы не экспортируются и вы не соглашаетесь не вызывать их из-за пределов библиотеки, даже косвенно с помощью указателей функций.
Вы можете проверить, какие символы экспортированы с помощью objdump -T
.
Ответ 2
Вам нужно будет создать две "обертки" общих libs, по одному для каждой из ваших существующих библиотек. Каждый из них должен быть построен с помощью -динамического списка, в котором перечислены только несколько неконфликтных символов, которые определяют API. Вам также понадобится -Bsymbolic, чтобы избежать любой глобальной комбинации.
Возможно, было бы менее сложно получить доступ к полученным libs через dlopen с подходящими параметрами.
Ответ 3
Другой способ решить эту проблему - использовать макрос для изменения пространства имен.
Предпосылки
- Все элементы (функции, классы, глобальные переменные,...) находятся в пространстве имен.
- Библиотека не сильно зависит от макросов в заголовках.
Решение
Причины, почему использовать это вместо других решений
- Когда проблемная библиотека используется (хотя бы частично) в заголовочных файлах (например, шаблоны, встроенные строки,...); когда вы включаете их в свой исполняемый код, resolver не имеет представления, должны ли эти функции вызываться из Lib1.so или Lib2.so.
- У вашего компилятора плохая поддержка/нет поддержки других решений (это не должно происходить с нашими 32/64-битными процессорами Intel/AMD, но, похоже, из поиска Google может возникнуть проблема с некоторыми другими платформами).
Потенциальные проблемы
- Может быть проблематично использовать обе версии в одном файле cpp вашего исполняемого файла;
#include "my_lib.h"
вероятно, использует макрос для защиты от многократного включения, а отмена их определения во избежание этого может вызвать множество различных проблем (автор библиотеки может изменить имя макроса в будущем, заголовок определяет некоторые другие макросы и т.д.). - Имя
LibNS
может использоваться для чего-то еще в библиотеке (переменная, функция и т.д.); в этом случае это имя также будет изменено на LibNSv1
или LibNSv2
. Это может привести к другим проблемам в зависимости от библиотеки и способа ее использования.
Заметки
- Это не предназначено для замены принятого в настоящее время ответа (от ninjalj; не стесняйтесь копировать и вставлять его), но расширяет его другим подходом.
- Основная причина, по которой я опубликовал этот ответ, заключается в том, что я столкнулся с этой проблемой сегодня, но ответ не помог, поскольку проблемный код находится в заголовочных файлах.
- Мой источник: https://spin.atomicobject.com/2014/06/03/static-linking-c-plus-plus/