web-dev-qa-db-fra.com

Liaison de deux bibliothèques partagées avec certains des mêmes symboles

Je lie avec deux bibliothèques partagées différentes. Les deux bibliothèques définissent des symboles qui partagent un nom mais ont des implémentations différentes. Je ne peux pas faire en sorte que chaque bibliothèque utilise sa propre implémentation par rapport à l'autre.

Par exemple, les deux bibliothèques définissent une fonction globale bar() que chacune appelle en interne. Library one l’appelle depuis foo1() et la deuxième bibliothèque l’appelle depuis foo2().

Lib1.so:

T bar
T foo1()     // calls bar()

Lib2.so:

T bar
T foo2()     // calls bar()

Si je lie ma demande à Lib1.so puis à Lib2.so, la barre d'implémentation de Lib1.so est appelée même si vous appelez foo2(). Par contre, si je lie ma demande à Lib2.so puis à Lib1.so, alors bar est toujours appelé depuis Lib2.so.

Existe-t-il un moyen de faire en sorte qu'une bibliothèque préfère toujours sa propre implémentation à toute autre bibliothèque?

43
drewag

Il y a plusieurs façons de résoudre ce problème:

  • Passez -Bsymbolic ou -Bsymbolic-functions à l'éditeur de liens. Cela a un effet global: chaque référence à un symbole global (de type de fonction pour -Bsymbolic-functions) pouvant être résolue en un symbole de la bibliothèque est résolue en ce symbole. Avec cela, vous perdez la possibilité d'interposer des appels de bibliothèque internes à ces symboles à l'aide de LD_PRELOAD. Les symboles sont toujours exportés, ils peuvent donc être référencés de l'extérieur de la bibliothèque.

  • Utilisez un script version pour marquer les symboles en tant que local dans la bibliothèque, par exemple. utilisez quelque chose comme: {local: bar;}; et passez --version-script=versionfile à l'éditeur de liens. Les symboles sont non exportés.

  • Marquez les symboles avec une visibilité appropriée ( page d’information GCC pour visibilité ), qui sera soit masqué, interne ou protégé . protégés symboles de visibilité sont exportés en tant que .protected, masqué symboles ne sont pas exportés, et interne symboles sont non exporté et vous vous engagez à ne pas les appeler de l’extérieur de la bibliothèque, même indirectement par le biais de pointeurs de fonction.

Vous pouvez vérifier quels symboles sont exportés avec objdump -T.

44
ninjalj

Vous devrez créer deux bibliothèques partagées 'wrapper', une pour chacune de vos bibliothèques existantes. Chacun devrait être construit avec une liste --dynamic-list qui ne répertorie que quelques symboles non conflictuels qui définissent une API. Vous aurez également besoin de -Bsymbolic pour éviter toute combinaison globale.

Il pourrait être moins stressant d’avoir accès aux bibliothèques résultantes via dlopen avec des options appropriées.

3
bmargulies

Une autre façon de résoudre ce problème consiste à utiliser une macro pour changer d'espace de nom.

Conditions préalables

  • Tous les éléments (fonctions, classes, variables globales, ...) sont dans un espace de noms.
  • La bibliothèque ne dépend pas beaucoup des macros dans les en-têtes.

Solution

  • Lors de la compilation de la bibliothèque, définissez une macro avec un nom d’espace de nommage afin de la définir différemment. Par exemple, si l'espace de nom est LibNS, utilisez -DLibNS=LibNSv1 pour un cas et -DLibNS=LibNSv2 pour l'autre.
  • Lorsque vous utilisez des bibliothèques dans le code, définissez une macro en fonction de votre situation actuelle.

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

Raisons pour lesquelles utiliser ceci à la place d'autres solutions

  • Lorsque la bibliothèque problématique est utilisée (au moins partiellement) dans des fichiers d’en-tête (par exemple, modèles, inlines, ...); lorsque vous les incluez dans le code de votre exécutable, le résolveur ne sait pas si ces fonctions doivent être appelées depuis Lib1.so ou Lib2.so.
  • Votre compilateur ne prend en charge que très peu d'autres solutions (cela ne devrait pas arriver avec nos processeurs intel/AMD 32/64 bits, mais il semble, d'après la recherche Google, que d'autres plates-formes pourraient avoir le même problème).

Problèmes potentiels

  • Il peut être problématique d’utiliser les deux versions dans un fichier cpp de votre exécutable; #include "my_lib.h" utilise probablement des macros pour se protéger contre l'inclusion multiple et les indéfinir pour éviter cela pourrait causer beaucoup de problèmes différents (l'auteur de la bibliothèque pourrait changer le nom de la macro à l'avenir, l'en-tête définit d'autres macros, etc.).

Remarques

  • Cela ne vise pas à remplacer la réponse actuellement acceptée (de ninjalj; n'hésitez pas à le copier-coller), mais à l'étendre avec une autre approche.
  • La principale raison pour laquelle j'ai posté cette réponse est que j'ai rencontré ce problème aujourd'hui, mais ma réponse n'a pas aidé en raison du code problématique qui se trouvait dans les fichiers d'en-tête.
  • Ma source: https://spin.atomicobject.com/2014/06/03/static-linking-c-plus-plus/
0
Tom