Связывание двух разделяемых библиотек с некоторыми из тех же символов

Я связываюсь с двумя разными разделяемыми библиотеками. Обе библиотеки определяют некоторые символы, которые имеют имя, но имеют разные реализации. Я не могу заставить каждую библиотеку использовать свою собственную реализацию по сравнению с другой.

Например, обе библиотеки определяют глобальную функцию 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

Другой способ решить эту проблему - использовать макрос для изменения пространства имен.

Предпосылки

  • Все элементы (функции, классы, глобальные переменные,...) находятся в пространстве имен.
  • Библиотека не сильно зависит от макросов в заголовках.

Решение

  • При компиляции библиотеки определите макрос с именем пространства имен, чтобы определить его для чего-то другого. Например, если пространством имен является LibNS, используйте -DLibNS=LibNSv1 для одного случая и -DLibNS=LibNSv2 для другого.
  • При использовании библиотек в коде определите макрос в соответствии с текущей ситуацией;

    #define LibNS LibNSv1
    #include "my_lib.h"
    #undef LibNS
    

Причины, почему использовать это вместо других решений

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