web-dev-qa-db-fra.com

Problèmes de liaison dus aux symboles avec abi :: cxx11?

Nous avons récemment intercepté un rapport à cause de GCC 5.1, libstdc ++ et Dual ABI . Il semble Clang n'est pas au courant des changements d'espace de noms en ligne de GCC , donc il génère du code basé sur un ensemble d'espaces de noms ou de symboles, tandis que GCC a utilisé un autre ensemble d'espaces de noms ou de symboles. Au moment de la liaison, il y a des problèmes dus à des symboles manquants.

Si j'analyse correctement la page Dual ABI , cela ressemble à une question de pivotement sur _GLIBCXX_USE_CXX11_ABI et abi::cxx11 avec quelques difficultés supplémentaires. Plus de lecture est disponible sur le blog de Red Hat à GCC5 et le C++ 11 ABI et Le cas de GCC-5.1 et les deux ABI C++ .

Ci-dessous, une machine Ubuntu 15. La machine fournit GCC 5.2.1.

$ cat test.cxx
#include <string>

std::string foo __attribute__ ((visibility ("default")));
std::string bar __attribute__ ((visibility ("default")));

$ g++ -g3 -O2 -shared test.cxx -o test.so

$ nm test.so | grep _Z3
...
0000201c B _Z3barB5cxx11
00002034 B _Z3fooB5cxx11

$ echo _Z3fooB5cxx11 _Z3barB5cxx11 | c++filt 
foo[abi:cxx11] bar[abi:cxx11]

Comment puis-je générer un binaire avec des symboles en utilisant les deux décorations ("coexistence" comme l'appelle le blog Red Hat)?

Ou, quelles sont les options qui s'offrent à nous?


J'essaie de réaliser un "ça marche juste" pour les utilisateurs. Je me fiche qu'il y ait deux symboles faibles avec deux comportements différents (std::string manque de copie sur écriture, tandis que std::string[abi:cxx11] permet la copie sur écriture). Ou, l'un peut être un alias pour l'autre.

Debian a une cargaison de bogues similaires à Journaux de rapports de bogues Debian: Bogues marqués libstdc ++ - cxx11 . Leur solution consistait à tout reconstruire sous le nouvel ABI, mais il ne gérait pas le cas du coin des compilateurs de mixage/appariement modulo les changements d'ABI.

Dans le monde Apple Apple, je pense que c'est proche d'un gros binaire. Mais je ne sais pas quoi faire dans le monde Linux/GCC. Enfin, nous ne contrôlons pas comment la distribution est construire la bibliothèque, et nous ne contrôlons pas quels compilateurs sont utilisés pour lier une application à la bibliothèque.

15
jww

Avertissement, ce qui suit n'est pas testé en production, utilisez à vos propres risques.

Vous pouvez vous-même libérer votre bibliothèque sous double ABI. C'est plus ou moins analogue au "gros binaire" OSX, mais entièrement construit avec C++.

La façon la plus simple de le faire serait de compiler la bibliothèque deux fois: avec -D_GLIBCXX_USE_CXX11_ABI=0 Et avec -D_GLIBCXX_USE_CXX11_ABI=1. Placez la bibliothèque entière sous deux espaces de noms différents en fonction de la valeur de la macro:

#if _GLIBCXX_USE_CXX11_ABI
#  define DUAL_ABI cxx11 __attribute__((abi_tag("cxx11")))
#else
#  define DUAL_ABI cxx03
#endif

namespace CryptoPP {
  inline namespace DUAL_ABI {
    // library goes here
  }
}

Maintenant, vos utilisateurs peuvent utiliser CryptoPP::whatever Comme d'habitude, cela correspond à CryptoPP::cxx11::whatever Ou CryptoPP::cxx03::whatever Selon l'ABI sélectionné.

Remarque, le manuel GCC indique que cette méthode changera les noms déformés de tout ce qui est défini dans l'espace de noms en ligne balisé. D'après mon expérience, cela ne se produit pas.

L'autre méthode consiste à baliser chaque classe, fonction et variable avec __attribute__((abi_tag("cxx11"))) si _GLIBCXX_USE_CXX11_ABI Est différent de zéro. Cet attribut ajoute joliment [cxx11] À la sortie du démangleur. Je pense que l'utilisation d'un espace de noms fonctionne tout aussi bien et nécessite moins de modifications du code existant.

En théorie, vous n'avez pas besoin de dupliquer la bibliothèque entière, uniquement les fonctions et classes qui utilisent std::string Et std::list, Et les fonctions et classes qui utilisent ces fonctions et classes , et ainsi de suite récursivement. Mais en pratique, cela ne vaut probablement pas la peine, surtout si la bibliothèque n'est pas très grande.

9
n.m.

Voici une façon de le faire, mais ce n'est pas très élégant. Ce n'est pas non plus clair pour moi comment faire GCC pour l'automatiser, donc je n'ai pas à faire les choses deux fois.

Tout d'abord, l'exemple qui va être transformé en bibliothèque:

$ cat test.cxx
#include <string>

std::string foo __attribute__ ((visibility ("default")));
std::string bar __attribute__ ((visibility ("default")));

Ensuite:

$ g++ -D_GLIBCXX_USE_CXX11_ABI=0 -c test.cxx -o test-v1.o
$ g++ -D_GLIBCXX_USE_CXX11_ABI=1 -c test.cxx -o test-v2.o

$ ar cr test.a test-v1.o test-v2.o
$ ranlib test.a

$ g++ -shared test-v1.o test-v2.o -o test.so

Enfin, voyez ce que nous avons obtenu:

$ nm test.a

test-v1.o:
00000004 B bar
         U __cxa_atexit
         U __dso_handle
00000000 B foo
0000006c t _GLOBAL__sub_I_foo
00000000 t _Z41__static_initialization_and_destruction_0ii
         U _ZNSsC1Ev
         U _ZNSsD1Ev

test-v2.o:
         U __cxa_atexit
         U __dso_handle
0000006c t _GLOBAL__sub_I__Z3fooB5cxx11
00000018 B _Z3barB5cxx11
00000000 B _Z3fooB5cxx11
00000000 t _Z41__static_initialization_and_destruction_0ii
         U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev
         U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev

Et:

$ nm test.so

00002020 B bar
00002018 B __bss_start
00002018 b completed.7181
         U __cxa_atexit@@GLIBC_2.1.3
         w __cxa_finalize@@GLIBC_2.1.3
00000650 t deregister_tm_clones
000006e0 t __do_global_dtors_aux
00001ef4 t __do_global_dtors_aux_fini_array_entry
00002014 d __dso_handle
00001efc d _DYNAMIC
00002018 D _edata
00002054 B _end
0000087c T _fini
0000201c B foo
00000730 t frame_dummy
00001ee8 t __frame_dummy_init_array_entry
00000980 r __FRAME_END__
00002000 d _GLOBAL_OFFSET_TABLE_
000007dc t _GLOBAL__sub_I_foo
00000862 t _GLOBAL__sub_I__Z3fooB5cxx11
         w __gmon_start__
000005e0 T _init
         w _ITM_deregisterTMCloneTable
         w _ITM_registerTMCloneTable
00001ef8 d __JCR_END__
00001ef8 d __JCR_LIST__
         w _Jv_RegisterClasses
00000690 t register_tm_clones
00002018 d __TMC_END__
00000640 t __x86.get_pc_thunk.bx
0000076c t __x86.get_pc_thunk.dx
0000203c B _Z3barB5cxx11
00002024 B _Z3fooB5cxx11
00000770 t _Z41__static_initialization_and_destruction_0ii
000007f6 t _Z41__static_initialization_and_destruction_0ii
         U _ZNSsC1Ev@@GLIBCXX_3.4
         U _ZNSsD1Ev@@GLIBCXX_3.4
         U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev@@GLIBCXX_3.4.21
         U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev@@GLIBCXX_3.4.21
5
jww