106 votes

Lier libstdc++ de manière statique : des problèmes ?

Je dois déployer une application C++ construite sur Ubuntu 12.10 avec libstdc++ de GCC 4.7 sur des systèmes fonctionnant sous Ubuntu 10.04, qui est livré avec une version considérablement plus ancienne de libstdc++.

Actuellement, je compile avec -static-libstdc++ -static-libgcc comme le suggère cet article de blog : Liaison statique de libstdc++ . L'auteur met en garde contre l'utilisation de tout code C++ chargé dynamiquement lors de la compilation statique de libstdc++, ce que je n'ai pas encore vérifié. Pourtant, tout semble se dérouler sans problème jusqu'à présent : Je peux utiliser les fonctionnalités de C++11 sur Ubuntu 10.04, ce qui est ce que je recherchais.

Je note que cet article date de 2005 et que beaucoup de choses ont peut-être changé depuis. Ses conseils sont-ils toujours d'actualité ? Y a-t-il des problèmes latents dont je devrais être conscient ?

152voto

Jonathan Wakely Points 45593

Cet article de blog est assez inexact.

Pour autant que je sache, les modifications de l'ABI C++ ont été introduites avec chaque version majeure de GCC (c'est-à-dire celles dont les composants ont un numéro de première ou de deuxième version différent).

C'est faux. Les seules modifications de l'ABI C++ introduites depuis GCC 3.4 ont été rétrocompatibles, ce qui signifie que l'ABI C++ est stable depuis près de neuf ans.

Pour aggraver les choses, la plupart des distributions Linux majeures utilisent des snapshots GCC et/ou patchent leurs versions de GCC, ce qui rend pratiquement impossible de savoir exactement quelles sont les versions de GCC auxquelles vous avez affaire lorsque vous distribuez des binaires.

Les différences entre les versions corrigées de GCC des distributions sont mineures et ne changent pas l'ABI. Par exemple, la version 4.6.3 20120306 de Fedora (Red Hat 4.6.3-2) est compatible avec l'ABI des versions 4.6.x en amont de la FSF et presque certainement avec toute version 4.6.x de n'importe quelle autre distribution.

Sous GNU/Linux, les bibliothèques d'exécution de GCC utilisent le versionnement des symboles ELF. Il est donc facile de vérifier les versions des symboles nécessaires aux objets et aux bibliothèques, et si vous avez un fichier libstdc++.so qui fournit ces symboles, il fonctionnera, peu importe s'il s'agit d'une version patchée légèrement différente d'une autre version de votre distro.

mais aucun code C++ (ou tout code utilisant le support d'exécution C++) ne peut être lié dynamiquement pour que cela fonctionne.

Ce n'est pas vrai non plus.

Cela dit, l'établissement d'un lien statique vers libstdc++.a est une option pour vous.

La raison pour laquelle cela pourrait ne pas fonctionner si vous chargez dynamiquement une bibliothèque (en utilisant dlopen ) est que les symboles de libstdc++ dont il dépend peuvent ne pas avoir été nécessaires à votre application lorsque vous l'avez liée (statiquement), donc ces symboles ne seront pas présents dans votre exécutable. Cela peut être résolu en liant dynamiquement la bibliothèque partagée à libstdc++.so (ce qui est la bonne chose à faire de toute façon s'il en dépend). L'interposition des symboles ELF signifie que les symboles présents dans votre exécutable seront utilisés par la bibliothèque partagée, mais que les autres symboles non présents dans votre exécutable seront trouvés dans n'importe quelle bibliothèque partagée. libstdc++.so vers lequel il renvoie. Si votre application n'utilise pas dlopen tu n'as pas besoin de te soucier de ça.

Une autre option (et celle que je préfère) consiste à déployer la nouvelle version de la solution libstdc++.so le long de votre application et s'assurer qu'elle est trouvée avant le système par défaut libstdc++.so ce qui peut être fait en forçant l'éditeur de liens dynamiques à chercher au bon endroit, soit à l'aide de la fonction $LD_LIBRARY_PATH au moment de l'exécution, ou en définissant une variable d'environnement RPATH dans l'exécutable au moment de la liaison. Je préfère utiliser RPATH car il n'est pas nécessaire que l'environnement soit correctement défini pour que l'application fonctionne. Si vous liez votre application avec '-Wl,-rpath,$ORIGIN' (notez les guillemets simples pour empêcher l'interpréteur de commandes d'essayer de développer $ORIGIN ), alors l'exécutable aura un RPATH de $ORIGIN qui indique à l'éditeur de liens dynamiques de rechercher les bibliothèques partagées dans le même répertoire que l'exécutable lui-même. Si vous mettez le nouveau libstdc++.so dans le même répertoire que l'exécutable, il sera trouvé au moment de l'exécution, problème résolu. (Une autre option consiste à placer l'exécutable dans le répertoire /some/path/bin/ et la plus récente libstdc++.so dans /some/path/lib/ et le lien avec '-Wl,-rpath,$ORIGIN/../lib' ou tout autre emplacement fixe relatif à l'exécutable, et définir le RPATH relatif à $ORIGIN )

13voto

Emil Styrke Points 485

Un complément à l'excellente réponse de Jonathan Wakely, pourquoi dlopen() est problématique :

En raison du nouveau pool de gestion des exceptions dans GCC 5 (voir PR 64535 y PR 65434 ), si vous dlopen et dlclosez une bibliothèque qui est liée statiquement à libstdc++, vous obtiendrez une fuite de mémoire (de l'objet pool) à chaque fois. Donc, s'il y a une chance que vous utilisiez dlopen, cela semble être une très mauvaise idée de lier statiquement libstdc++. Notez qu'il s'agit d'une véritable fuite, par opposition à la fuite bénigne mentionnée dans le paragraphe suivant PR 65434 .

3voto

dhammy0110 Points 61

Vous devez également vous assurer que vous ne dépendez pas de la glibc dynamique. Exécutez ldd sur votre exécutable résultant et notez toutes les dépendances dynamiques (libc/libm/libpthread sont les suspects habituels).

Un exercice supplémentaire consisterait à construire un ensemble d'exemples C++11 en utilisant cette méthodologie et à essayer les binaires résultants sur un système 10.04 réel. Dans la plupart des cas, à moins que vous ne fassiez quelque chose de bizarre avec le chargement dynamique, vous saurez tout de suite si le programme fonctionne ou s'il se plante.

3voto

Jonathan McDevitt Points 103

Complément à la réponse de Jonathan Wakely concernant le RPATH :

RPATH ne fonctionnera que si le RPATH en question est le RPATH de l'application application en cours . Si vous avez une bibliothèque qui se lie dynamiquement à n'importe quelle bibliothèque via son propre RPATH, le RPATH de la bibliothèque sera écrasé par le RPATH de l'application qui la charge. Cela pose un problème lorsque vous ne pouvez pas garantir que le RPATH de l'application est le même que celui de votre bibliothèque, par exemple si vous pensez que vos dépendances se trouvent dans un répertoire particulier, mais que ce répertoire ne fait pas partie du RPATH de l'application.

Par exemple, disons que vous avez une application App.exe qui a une dépendance liée dynamiquement à libstdc++.so.x pour GCC 4.9. Cette dépendance est résolue dans le RPATH de l'application App.exe, à savoir

App.exe (RPATH=.:./gcc4_9/libstdc++.so.x)

Maintenant, disons qu'il existe une autre bibliothèque Dependency.so, qui a une dépendance liée dynamiquement à libstdc++.so.y pour GCC 5.5. La dépendance est ici résolue à travers le RPATH de la bibliothèque, c'est-à-dire

Dependency.so (RPATH=.:./gcc5_5/libstdc++.so.y)

Quand App.exe charge Dependency.so, il n'ajoute ni ne précède le RPATH de la bibliothèque. . Il ne le consulte pas du tout. Le seul RPATH pris en compte sera celui de l'application en cours d'exécution, ou App.exe dans cet exemple. Cela signifie que si la bibliothèque repose sur des symboles qui se trouvent dans gcc5_5/libstdc++.so.y mais pas dans gcc4_9/libstdc++.so.x, le chargement de la bibliothèque échouera.

Il s'agit juste d'un avertissement, car j'ai moi-même rencontré ces problèmes dans le passé. RPATH est un outil très utile, mais son implémentation comporte encore quelques problèmes.

1voto

Fedorov7890 Points 151

J'aimerais ajouter ce qui suit à la réponse de Jonathan Wakely.

Jouer autour -static-libstdc++ sur linux, j'ai rencontré le problème avec dlclose() . Supposons que nous ayons une application 'A' liée statiquement à libstdc++ et il se charge dynamiquement lié à libstdc++ le plugin 'P' au moment de l'exécution. Tout va bien. Mais lorsque 'A' décharge 'P', une erreur de segmentation se produit. Je suppose qu'après avoir déchargé libstdc++.so le "A" ne peut plus utiliser les symboles liés à l'activité de l'entreprise. libstdc++ . Notez que si 'A' et 'P' sont tous les deux liés statiquement à libstdc++ ou si "A" est lié dynamiquement et "P" statiquement, le problème ne se pose pas.

Résumé : si votre application charge/décharge des plugins qui peuvent dynamiquement dynamiquement à libstdc++ l'application doit également y être liée de manière dynamique. Ceci n'est que mon observation et j'aimerais avoir vos commentaires.

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