2010-05-10 26 views
4

Dans mon projet, il existe une dépendance à une bibliothèque statique (appelée simplement libsomething à partir de maintenant) d'une tierce partie. Récemment, libsomething est devenu disponible dans une autre version. Ma tâche est de fournir à mon logiciel un support pour l'ancienne et la nouvelle version. Une seule version de libsomething est utilisée au moment de l'exécution à un moment donné, mais quelle version doit être configurable entre les exécutions de programme. J'utilise MSVC2005 sur WinXP, un objectif secondaire est de se préparer à passer à Linux et GCC.Enveloppement des différentes versions de la bibliothèque statique dans les bibliothèques dynamiques

Étant donné que les deux versions de libsomething utilisent les mêmes symboles, il est hors de question de les lier dans mon exécutable car les symboles des deux versions vont se contracter au moment de la liaison. Bien que je puisse créer deux exécutables (un lien par rapport à l'ancienne version, l'autre utilisant la nouvelle version), je ne peux pas décider quel exécutable appeler dans l'environnement de déploiement final (raisons héritées).

J'ai eu l'idée de créer un wrapper de bibliothèque dynamique pour chaque version de libsomething et de les lier à l'exécution en fonction de certains fichiers de configuration. Avec MSCV, cela signifierait que l'on va utiliser LoadLibrary(), GetProcAddress(), etc., alors que sur Linux je devrais utiliser dlopen() et dlsym().

Je comprends que l'utilisation de libtool (c'est-à-dire, libtldl) encapsule cette dépendance de plate-forme pour l'utilisation de bibliothèques partagées. Est-ce un chemin approprié à suivre? Y a-t-il de meilleures façons (ou au moins différentes)? Est-ce que des alternatives pour libtldl existent en open-source?

Répondre

0

Cela fait quelques années maintenant mais je voudrais mentionner une autre solution pour l'exhaustivité. Au lieu des manuels dlopen et dlsym, vous pouvez générer des stubs simples pour toutes les fonctions nécessaires et, au premier appel (ou au démarrage du programme), décider quelle version de la bibliothèque est nécessaire, la charger et résoudre les adresses.

Vous pouvez écrire un script conçu spécialement pour votre projet ou utilisez Implib.so outil:

# This will generate mylib.so.init.c and mylib.so.tramp.S 
# which implement stubs. These need to be linked to your 
# executable. 
$ gen-implib.py mylib.so 

Implib.so est Linux uniquement atm mais devrait être facilement adaptable à Windows.

+0

Oui, c'est à peu près ce que j'ai fini par faire en 2010. Donc, je suppose que cela en fait la solution acceptée. :-) – fawick

2

Je sais que vous avez dit que vous ne pouviez pas utiliser deux exécutables en raison de la décision d'exécution, mais que vous ne pouviez pas exec aller et retour entre les exécutables en fonction de la version sélectionnée lors de la configuration?

+0

Vous avez raison, dans ce cas, cette approche apporte une solution. Bien que je suis plus intéressé à le faire via un lien. Mais votre idée est astucieuse out-of-the-box-thinking! – fawick

2

Sur Linux, il serait plus facile pour vous de créer un lien vers la bibliothèque partagée et utiliser des liens symboliques pour version correcte - l'OMI, il est beaucoup plus facile que d'utiliser dlopen() + dlsym().

Ainsi vous créer des bibliothèques partagées pour les anciennes et les nouvelles versions de votre bibliothèque:

g++ -shared -o libshared.so.1.1 -Wl,-soname=libshared.so.1 -L. -Wl,--whole-archive libstatic.a.old -Wl,-no-whole-archive

et

g++ -shared -o libshared.so.1.2 -Wl,-soname=libshared.so.1 -L. -Wl,--whole-archive libstatic.a.new -Wl,-no-whole-archive

Créez les liens symboliques:

ln -s libshared.so.1.1 libshared.so.1 
ln -s libshared.so.1 libshared.so 

Construire votre application, en le liant à l'ancienne version de la bibliothèque. Je suppose que les deux versions sont compatibles binaires (ABI non cassé), mais le nouveau pourrait avoir de nouveaux symboles.

g++ -o myapp myapp.cpp -L. -lshared

Depuis la bibliothèque de SONAME partagée est libshared.so.1 votre application dépendra et cherchera libshared.so.1 dans les chemins de /etc/ld.so.conf ou LD_LIBRARY_PATH

Avant d'exécuter votre application vous pouvez définir le libshared.so.1 symlink pour pointer vers libshared.so.1.2 ou libshared.so.1.1.


peu d'info sur les options de l'éditeur de liens utilisés ici:

--whole-archives
Pour chaque archive mentionnée sur la ligne de commande après l'option --whole-archives, comprennent tous les fichiers objet l'archive dans le lien , plutôt que de rechercher dans l'archive les fichiers objet requis. Ceci est normalement utilisé pour transformer un fichier d'archive en une bibliothèque partagée , forçant chaque objet à être inclus dans la bibliothèque partagée résultante. Cette option peut être utilisée plus d'une fois.
Deux notes lors de l'utilisation de cette option de gcc: D'abord, gcc ne connaît pas cette option, vous devez donc utiliser -Wl, -whole-archive. Ensuite, n'oubliez pas d'utiliser -Wl, -no-whole-archive après votre liste d'archives, car gcc ajoutera sa propre liste d'archives à votre lien et vous ne voudrez peut-être pas que ce drapeau affecte également ces archives. Lors de la création d'un objet partagé ELF, définissez le champ DT_SONAME interne sur le nom spécifié. Lorsqu'un exécutable est lié à un objet partagé qui possède un champ DT_SONAME, l'éditeur de liens dynamiques tente alors de charger l'objet partagé spécifié par le champ DT_SONAME plutôt que d'utiliser le nom de fichier donné à l'éditeur de liens.

+0

Bonne réponse, merci. Malheureusement, comme vous l'avez dit, cela ne fonctionne qu'avec Linux. Par conséquent, c'est quelque chose qui vaut la peine de revenir quand je porterai finalement sur Linux, mais pas applicable avant ce jour. – fawick