6
votes

Dupliquer une initialisation de variable statique en C ++

Je construis une bibliothèque partagée "libmylibrary.so" avec une classe "mycass" contenant une variable statique de type "myclass". Ensuite, je construis un "MylibraryTest" exécutable que je lie contre "libmylibrary.so". Le programme principal utilise "Dlopen" pour charger de manière dynamique un ".so" donné comme un argument.

Lors de la construction, la bibliothèque et l'exécutable sont générés dans un répertoire, disent, "BuildDir / bin". Ensuite, j'installe la bibliothèque dans "installdir / lib" et l'exécutable dans "installdir / bin" (retirant le chemin d'exécution de l'exécutable). P>

Quand j'exécute "BuildDir / MyLibraryTest BuildDir / Mylibrary.so" avec ld_library_path = builddir, tout va bien. p>

mais quand je gère "builddir / mylibraryTest installdir / lib / mylibrary.so" avec ld_library_path = installdir / lib, une chose très étrange se produit: - Le constructeur de la variable statique s'appelle deux fois em> (une fois avant Dlopen, une fois pendant Dlopen) - À la fin de l'exécution, le destructeur est appelé deux fois, ce qui entraîne un crash. P>

Voici mon code: p>

myClass.h p>

MyClass::MyClass(name=myStaticObjOfMyClass, address=0x7fa710cabb40)
dlopen(/tmp/Install/MyLibraryTest/lib/libMyLibrary.so)
MyClass::MyClass(name=myStaticObjOfMyClass, address=0x7fa710cabb40)
OK, shared library /tmp/Install/MyLibraryTest/lib/libMyLibrary.so is now loaded
MyClass::~MyClass(name=myStaticObjOfMyClass, address=0x7fa710cabb40)
MyClass::~MyClass(name=��ObjOfMyClass, address=0x7fa710cabb40)
*** glibc detected *** /tmp/Build/MyLibraryTest/Release/bin/MyLibraryTest: double free or corruption (fasttop): 0x0000000000cfb330 ***
======= Backtrace: =========
/lib64/libc.so.6[0x322f275dee]
/lib64/libc.so.6[0x322f278c3d]
/lib64/libc.so.6(__cxa_finalize+0x9d)[0x322f235d2d]
/tmp/Build/MyLibraryTest/Release/bin/libMyLibrary.so(+0x1076)[0x7fa710aab076]
======= Memory map: ========
00400000-00402000 r-xp 00000000 fd:00 1325638                            /tmp/Build/MyLibraryTest/Release/bin/MyLibraryTest
00601000-00602000 rw-p 00001000 fd:00 1325638                            /tmp/Build/MyLibraryTest/Release/bin/MyLibraryTest
00ce9000-00d1b000 rw-p 00000000 00:00 0                                  [heap]
322ee00000-322ee20000 r-xp 00000000 fd:00 545634                         /lib64/ld-2.12.so
322f020000-322f021000 r--p 00020000 fd:00 545634                         /lib64/ld-2.12.so
322f021000-322f022000 rw-p 00021000 fd:00 545634                         /lib64/ld-2.12.so
322f022000-322f023000 rw-p 00000000 00:00 0 
322f200000-322f38a000 r-xp 00000000 fd:00 545642                         /lib64/libc-2.12.so
322f38a000-322f58a000 ---p 0018a000 fd:00 545642                         /lib64/libc-2.12.so
322f58a000-322f58e000 r--p 0018a000 fd:00 545642                         /lib64/libc-2.12.so
322f58e000-322f590000 rw-p 0018e000 fd:00 545642                         /lib64/libc-2.12.so
322f590000-322f594000 rw-p 00000000 00:00 0 
322fa00000-322fa02000 r-xp 00000000 fd:00 545709                         /lib64/libdl-2.12.so
322fa02000-322fc02000 ---p 00002000 fd:00 545709                         /lib64/libdl-2.12.so
322fc02000-322fc03000 r--p 00002000 fd:00 545709                         /lib64/libdl-2.12.so
322fc03000-322fc04000 rw-p 00003000 fd:00 545709                         /lib64/libdl-2.12.so
3230600000-3230683000 r-xp 00000000 fd:00 545684                         /lib64/libm-2.12.so
3230683000-3230882000 ---p 00083000 fd:00 545684                         /lib64/libm-2.12.so
3230882000-3230883000 r--p 00082000 fd:00 545684                         /lib64/libm-2.12.so
3230883000-3230884000 rw-p 00083000 fd:00 545684                         /lib64/libm-2.12.so
7fa70c000000-7fa70c021000 rw-p 00000000 00:00 0 
7fa70c021000-7fa710000000 ---p 00000000 00:00 0 
7fa7102e7000-7fa7102e9000 r-xp 00000000 fd:00 1320668                    /tmp/Install/MyLibraryTest/lib/libMyLibrary.so
7fa7102e9000-7fa7104e8000 ---p 00002000 fd:00 1320668                    /tmp/Install/MyLibraryTest/lib/libMyLibrary.so
7fa7104e8000-7fa7104e9000 rw-p 00001000 fd:00 1320668                    /tmp/Install/MyLibraryTest/lib/libMyLibrary.so
7fa7104e9000-7fa7104ed000 rw-p 00000000 00:00 0 
7fa7104ed000-7fa710503000 r-xp 00000000 fd:00 708322                     /usr/local/lib64/libgcc_s.so.1
7fa710503000-7fa710702000 ---p 00016000 fd:00 708322                     /usr/local/lib64/libgcc_s.so.1
7fa710702000-7fa710703000 rw-p 00015000 fd:00 708322                     /usr/local/lib64/libgcc_s.so.1
7fa710703000-7fa710704000 rw-p 00000000 00:00 0 
7fa710704000-7fa710883000 r-xp 00000000 fd:00 708539                     /usr/local/lib64/libstdc++.so.6.0.21
7fa710883000-7fa710a83000 ---p 0017f000 fd:00 708539                     /usr/local/lib64/libstdc++.so.6.0.21
7fa710a83000-7fa710a8d000 r--p 0017f000 fd:00 708539                     /usr/local/lib64/libstdc++.so.6.0.21
7fa710a8d000-7fa710a8f000 rw-p 00189000 fd:00 708539                     /usr/local/lib64/libstdc++.so.6.0.21
7fa710a8f000-7fa710a94000 rw-p 00000000 00:00 0 
7fa710aa8000-7fa710aaa000 rw-p 00000000 00:00 0 
7fa710aaa000-7fa710aac000 r-xp 00000000 fd:00 1325633                    /tmp/Build/MyLibraryTest/Release/bin/libMyLibrary.so
7fa710aac000-7fa710cab000 ---p 00002000 fd:00 1325633                    /tmp/Build/MyLibraryTest/Release/bin/libMyLibrary.so
7fa710cab000-7fa710cac000 rw-p 00001000 fd:00 1325633                    /tmp/Build/MyLibraryTest/Release/bin/libMyLibrary.so
7fa710cac000-7fa710cad000 rw-p 00000000 00:00 0 
7fff2fc61000-7fff2fc76000 rw-p 00000000 00:00 0                          [stack]
7fff2fde5000-7fff2fde6000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
./test_dyn_libs.sh: line 21: 30880 Abandon                 (core dumped) ${BuildDir}/MyLibraryTest ${InstallDir}/lib/libMyLibrary.so
--- End of tests


1 commentaires

Vous n'avez pas inclus les commandes de compilation et de liaison pour MylibaryTest. Le problème pourrait être là.


3 Réponses :


3
votes

En tant que TODD FLEMING commenté, je pense probablement que vous avez probablement relié votre mylibrary.So dans votre test exécutable lorsque vous compilez l'exécutable de test.

Vous pouvez utiliser ldd pour vérifier si votre exécutable a été lié à la bibliothèque.

J'ai essayé votre code sur mon Ubuntu Linux avec exactement les mêmes options de compilation. Pour le test exécutable, que Mylibrary.SO ne doit pas être liée, donc je ne l'ai pas associé. Il s'avère que le comportement bizarre n'a pas eu lieu. Lorsque j'ai lié le test exécutable avec Mylibrary.so, le résultat est exactement ce que vous avez dit (y compris le vidage GLIBC). Ce n'est pas trop surprenant, car lorsque vous installez votre bibliothèque vers un autre chemin, le chargeur le considérera comme une autre bibliothèque totalement non pertinente, donc double-chargée.

PS: Qu'est-ce qui m'a surpris, c'est que ces 2 singletons sont placés dans la même position de mémoire, c'est un peu incroyable. Je l'ai également testé (le cas lorsque l'exécutable est lié à la bibliothèque) sur mon Mac OS X, il s'avère que sur OS X, ces 2 instances ont un emplacement de mémoire différent. C'est plus raisonnable pour moi.

mise à jour:

À propos de la raison pour laquelle ces 2 instances ont la même position de mémoire, c'est une fonctionnalité de résolution de symboles sous Linux. S'il vous plaît voir les commentaires pour plus de détails. Merci N.M. pour souligner.


11 commentaires

Cela n'explique pas le phénomène. Dlopen ne doit normalement pas charger une seconde copie d'un objet partagé, quelle que soit la manière dont la première copie a été introduite.


Cela pourrait arriver si l'exécutable contient physiquement myclass.o I.e. est liée à cela de manière stable.


Oui, une deuxième copie ne sera pas chargée, ce qui signifie que si vous liez la bibliothèque et la dlopen elle, une seule copie est chargée. Mais si vous changez le chemin / le nom de la bibliothèque et le dlopen, c'est une autre histoire.


Il serait plus clair que si vous regardez la décharge glibc, il y a 2 libmymibrary.so chargé de trajectoires différents.


Hmm. La page Dlopen Man dit qu'il ne devrait pas dépendre des chemins utilisés. Le Soname devrait identifier la lib, pas le chemin. Je vais essayer de le tester ..


Ok l'a eu. Soname n'a pas d'importance, le fichier lui-même fait (Inode?) Si les deux chemins pointe vers le même fichier (par exemple avec des liens symboliques), tout fonctionne bien. Si vous avez deux fichiers distincts, même identiques, il se bloque comme décrit.


Quant pourquoi il n'y a qu'une copie de la variable statique: elle est à peu près attendue sur Linux. Le symbole est global et comme tel qu'une seule copie est utilisée, même si des copies supplémentaires sont chargées. Pour empêcher cela, il faut lier de manière aubsymbolique. C'est peut-être la valeur par défaut sur Mac OS X?


Merci pour les informations sur -Bsymbolic sur Linux. J'ai testé sur Mac OS X, un symbole dans une bibliothèque dynamique est résolu en lui-même par défaut.


Merci. J'ai essayé -bsymbolic sur Linux. La variable statique est toujours créée deux fois, mais cette fois avec deux emplacements de mémoire différents. Il n'y a plus de crash, mais je me demande s'il pourrait y avoir d'autres bugs sournois en raison de cette variable en double.


Votre bibliothèque est chargée deux fois n'a rien à voir avec -bymbolique. Avez-vous vérifié la commande LDD pour voir votre bibliothèque est déjà liée à l'exécutable?


@Conanlord Regardez attentivement votre vidage glibc, il existe 2 copies de votre bibliothèque chargée de différents chemins.



0
votes

Je pense que le problème d'initialisation statique est résolu si vous masquez votre variable statique dans une getter statique. Donc, dans l'en-tête, vous remplacez la variable de la classe statique xxx

avec une fonction de classe statique xxx

et mettez-la dans le CPP: xxx

L'objet statique est initialisé lorsque getinstance () est appelé pour la première fois.


0 commentaires

0
votes

Merci à tous pour vos réponses. Comme dit Todd, j'ai oublié d'inclure la commande de liaison pour MylibraryTest. Ici, il est:

set (CMAKE_SKIP_RPATH ON)


0 commentaires