48 votes

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

Je fais le lien avec deux bibliothèques partagées différentes. Les deux bibliothèques définissent certains 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 plutôt que celle de l'autre.

Par exemple, les deux bibliothèques définissent une fonction globale bar() que chacun appelle en interne. La première bibliothèque l'appelle depuis foo1() et la deuxième bibliothèque l'appelle à partir de foo2() .

Lib1.so :

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

Lib2.so :

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

Si je lie mon application à Lib1.so et ensuite à Lib2.so, l'implémentation de la barre de Lib1.so est appelée même lorsque je fais appel à foo2() . Si, par contre, je lie mon application à Lib2.so et ensuite à 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 ?

54voto

ninjalj Points 22026

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

  • Passez -Bsymbolic o -Bsymbolic-functions à l'éditeur de liens. Cela a un effet global : chaque référence à un symbole global (de type fonction pour -Bsymbolic-functions ) qui peut être résolu en un symbole de la bibliothèque est résolu en ce symbole. Vous perdez ainsi la possibilité d'interposer des appels internes à la bibliothèque vers ces symboles à l'aide de LD_PRELOAD. Les symboles sont toujours exportés afin qu'ils puissent être référencés depuis l'extérieur de la bibliothèque.

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

  • Marquer les symboles avec un appropprié visibilité ( Page d'information du CCG pour la visibilité ), qui sera soit caché , interne ou protégé . protégé symboles de visibilité sont exportés en tant que .protected , caché les symboles ne sont pas exportés y interne les symboles ne sont pas exportés et vous vous engagez à ne pas les appeler depuis 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 .

1 votes

Bonjour, pouvez-vous me dire si cela s'applique également à clang ?

4voto

bmargulies Points 49855

Vous devrez créer deux librairies partagées "enveloppantes", une pour chacune de vos librairies existantes. Chacune d'entre elles doit être construite avec une --dynamic-list qui ne liste que quelques symboles non contradictoires définissant une API. Vous aurez également besoin de -Bsymbolic pour éviter toute combinaison globale.

Il peut également être moins stressant d'accéder aux librairies résultantes via dlopen avec des options appropriées.

0 votes

Merci beaucoup ! L'option -Bsymbolic (passée à l'éditeur de liens en utilisant l'option -Wl) pour les deux liaisons de bibliothèques partagées a résolu le problème pour moi.

2voto

Tom Points 145

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, ...) se trouvent 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 la macro avec le nom de l'espace de nom pour le définir à quelque chose de différent. Par exemple, si l'espace de noms 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 la macro en fonction de votre situation actuelle ;

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

Raisons pour lesquelles utiliser cette solution plutôt que d'autres

  • Lorsque la bibliothèque problématique est utilisée (au moins partiellement) dans des fichiers d'en-tête (par exemple des templates, des 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 a un support faible/inexistant pour d'autres solutions (cela ne devrait pas se produire avec nos CPU intel/amd 32/64 bits, mais il semble, d'après une recherche sur Google, que d'autres plateformes pourraient avoir ce problème).

Problèmes potentiels

  • Il pourrait être problématique d'utiliser les deux versions dans un seul fichier cpp de votre exécutable ; #include "my_lib.h" utilise probablement des macros pour se protéger contre les inclusions multiples et leur indéfinition pour éviter cela pourrait causer de nombreux 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.)
  • Le nom LibNS peut être utilisé pour autre chose dans la bibliothèque (variable, fonction, etc.) ; dans ce cas, ce nom sera également changé en LibNSv1 o LibNSv2 . Cela peut entraîner d'autres problèmes selon la bibliothèque et la façon dont elle est utilisée.

Notes

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X