Je dois déployer une application C ++ construite sur Ubuntu 12.10 avec libstdc ++ de GCC 4.7 sur des systèmes exécutant Ubuntu 10.04, qui est livré avec une version considérablement plus ancienne de libstdc ++.
Actuellement, je compile avec -static-libstdc++ -static-libgcc
, comme suggéré par ce billet de blog: Linking libstdc ++ statiquement . 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 aller bien jusqu'à présent: je peux utiliser les fonctionnalités de C ++ 11 sur Ubuntu 10.04, ce que je recherchais.
Je note que cet article date de 2005, et peut-être que beaucoup de choses ont changé depuis. Ses conseils sont-ils toujours d'actualité? Y a-t-il des problèmes cachés dont je devrais être conscient?
-static-libstdc++
option ne servirait à rien, vous utiliseriez simplement-static
kernel too old
erreur dans certains systèmes ubuntu 1404. Le glibc.so est commekernel32.dll
dans window, il fait partie de l'interface du système d'exploitation, nous ne devons pas l'intégrer dans notre binaire. Vous pouvez utiliserobjdump -T [binary path]
pour le voir chargé dynamiquementlibstdc++.so
ou non. Pour le programmeur golang, vous pouvez ajouter#cgo linux LDFLAGS: -static-libstdc++ -static-libgcc
avant l'importation "C"-static-libstdc++
pas-static
donclibc.so
ne sera pas lié statiquement.Réponses:
Ce billet de blog est assez inexact.
Pas vrai. Les seuls changements ABI C ++ introduits depuis GCC 3.4 ont été rétrocompatibles, ce qui signifie que l'ABI C ++ est stable depuis près de neuf ans.
Les différences entre les versions corrigées des distributions de GCC sont mineures et ne changent pas ABI, par exemple la version 4.6.3 20120306 de Fedora (Red Hat 4.6.3-2) est compatible ABI avec les versions amont de FSF 4.6.x et presque certainement avec n'importe quelle version 4.6. x de toute autre distribution.
Sur les bibliothèques d'exécution de GNU / Linux GCC utilisent la gestion des versions des symboles ELF, il est donc facile de vérifier les versions de symboles nécessaires aux objets et aux bibliothèques, et si vous avez un
libstdc++.so
qui fournit ces symboles, cela fonctionnera, peu importe s'il s'agit d'une version corrigée légèrement différente d'une autre version de votre distribution.Ce n'est pas vrai non plus.
Cela dit, la liaison statique vers
libstdc++.a
est une option pour vous.La raison pour laquelle cela peut ne pas fonctionner si vous chargez dynamiquement une bibliothèque (en utilisant
dlopen
) est que les symboles libstdc ++ dont elle dépend n'ont peut-être pas é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 si cela en dépend.) L'interposition de symboles ELF signifie que les symboles présents dans votre exécutable seront utilisés par la bibliothèque partagée, mais d'autres non présent dans votre exécutable se trouve dans celui vers lequellibstdc++.so
il renvoie. Si votre application ne fonctionne pas,dlopen
vous n'avez pas besoin de vous en soucier.Une autre option (et celle que je préfère) est de déployer le plus récent à
libstdc++.so
côté de votre application et de vous assurer qu'il est trouvé avant le système par défautlibstdc++.so
, ce qui peut être fait en forçant l'éditeur de liens dynamique à chercher au bon endroit, soit en utilisant$LD_LIBRARY_PATH
la variable d'environnement lors de l'exécution. time, ou en définissant unRPATH
dans l'exécutable au moment de la liaison. Je préfère utiliserRPATH
car il ne dépend pas de la configuration correcte de l'environnement pour que l'application fonctionne. Si vous liez votre application avec'-Wl,-rpath,$ORIGIN'
(notez les guillemets simples pour empêcher le shell d' essayer d'élargir$ORIGIN
) puis l'exécutable aura unRPATH
de$ORIGIN
qui raconte l'éditeur de liens de dynamique pour rechercher des bibliothèques partagées dans le même répertoire que l'exécutable lui - même. Si vous mettez le plus récentlibstdc++.so
dans le même répertoire que l'exécutable, il se trouvera au moment de l'exécution, problème résolu. (Une autre option est de mettre l'exécutable dans/some/path/bin/
et le plus récent libstdc ++. So dans/some/path/lib/
et lier avec'-Wl,-rpath,$ORIGIN/../lib'
ou tout autre emplacement fixe par rapport à l'exécutable, et définir le RPATH par rapport à$ORIGIN
)la source
libstdc++.so.6
lien symbolique qui est défini au moment de l'installation pour pointer vers la bibliothèque fournie ou vers la bibliothèque système si elle est plus récente. Il existe des modèles de liaison mixte plus compliqués, tels qu'utilisés par Red Hat DTS, mais ils sont difficiles à faire soi-même.Un ajout à 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 et PR 65434 ), si vous ouvrez et fermez une bibliothèque 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 un jour dlopen, cela semble être une très mauvaise idée de lier statiquement libstdc ++. Notez qu'il s'agit d'une vraie fuite par opposition à celle bénigne mentionnée dans le PR 65434 .
la source
__gnu_cxx::__freeres()
semble apporter au moins une aide sur ce problème, car elle libère le tampon interne de l'objet pool. Mais pour moi, il est assez difficile de savoir quelle implication un appel à cette fonction a par rapport aux exceptions lancées accidentellement par la suite.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 en cours d'exécution . 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. C'est 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 vous attendez à ce que vos dépendances soient dans un répertoire particulier, mais que ce répertoire ne fait pas partie du RPATH de l'application.
Par exemple, supposons que vous ayez une application App.exe qui a une dépendance liée dynamiquement à libstdc ++. So.x pour GCC 4.9. App.exe a cette dépendance résolue via le RPATH, c'est-à-dire
App.exe (RPATH=.:./gcc4_9/libstdc++.so.x)
Supposons maintenant 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 ici est résolue via le RPATH de la bibliothèque, c'est-à-dire
Dependency.so (RPATH=.:./gcc5_5/libstdc++.so.y)
Lorsque App.exe charge Dependency.so, il n'ajoute ni n'ajoute le RPATH de la bibliothèque . Il ne le consulte pas du tout. Le seul RPATH considéré 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, la bibliothèque échouera à se charger.
C'est juste un mot d'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 a encore quelques pièges.
la source
Vous devrez peut-être également vous assurer que vous ne dépendez pas de la glibc dynamique. Exécutez
ldd
votre exécutable résultant et notez toutes les dépendances dynamiques (libc / libm / libpthread sont habituellement suspects).Un exercice supplémentaire consisterait à construire un tas d'exemples C ++ 11 impliqués en utilisant cette méthodologie et en essayant réellement les binaires résultants sur un vrai système 10.04. Dans la plupart des cas, à moins que vous ne fassiez quelque chose de bizarre avec le chargement dynamique, vous saurez immédiatement si le programme fonctionne ou s'il plante.
la source
printf
) mais tant que la glibc sur Ubuntu 10.04 fournit toutes les fonctionnalités nécessaires à la nouvelle libstdc ++, il n'y a aucun problème avec la glibc dynamique, en fait il est fortement recommandé de ne jamais lier statiquement à la glibcJ'aimerais ajouter à la réponse de Jonathan Wakely ce qui suit.
En jouant
-static-libstdc++
sur Linux, j'ai rencontré le problème avecdlclose()
. Supposons que nous ayons une application 'A' liée statiquementlibstdc++
et qu'elle se charge dynamiquement liée aulibstdc++
plugin 'P' au moment de l'exécution. C'est très bien. Mais lorsque «A» décharge «P», une erreur de segmentation se produit. Mon hypothèse est qu'après le déchargementlibstdc++.so
, «A» ne peut plus utiliser de symboles liés àlibstdc++
. Notez que si «A» et «P» sont liés statiquement àlibstdc++
, ou si «A» est lié dynamiquement et «P» statiquement, le problème ne se produit pas.Résumé: si votre application charge / décharge des plugins qui peuvent
libstdc++
être liés dynamiquement, l'application doit également y être liée de manière dynamique. Ce n'est que mon observation et j'aimerais avoir vos commentaires.la source
sbrk
) fait certaines hypothèses et s'attend à peu près à être seul dans un processus ... pas sûr que cela se limite à un une version particulière de la glibc ou autre.