2016-03-22 22 views
10

Recentemente abbiamo rilevato un rapporto a causa di GCC 5.1, libstdc++ and Dual ABI. Sembra Clang is not aware of the GCC inline namespace changes, quindi genera codice basato su un set di spazi dei nomi o simboli, mentre GCC usa un altro set di spazi dei nomi o simboli. Al momento del collegamento, ci sono problemi dovuti a simboli mancanti.Collegamento di problemi dovuti ai simboli con abi :: cxx11?

Se sto analizzando correttamente la pagina Dual ABI, sembra una questione di rotazione su _GLIBCXX_USE_CXX11_ABI e abi::cxx11 con alcune difficoltà aggiuntive. Ulteriori letture sono disponibili sul blog di Red Hat allo GCC5 and the C++11 ABI e allo The Case of GCC-5.1 and the Two C++ ABIs.

Di seguito è riportato da una macchina Ubuntu 15. La macchina fornisce 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] 

Come faccio a generare un file binario con i simboli che utilizzano entrambe le decorazioni ("coesistenza", come il blog di Red Hat chiama)?

Oppure, quali sono le opzioni disponibili?


Sto cercando di ottenere un "funziona solo" per gli utenti. Non mi interessa se ci sono due simboli deboli con due comportamenti diversi (std::string manca copy-on-write, mentre std::string[abi:cxx11] fornisce copy-on-write). Oppure, uno può essere un alias per l'altro.

Debian ha un carico di errori simili a Debian Bug report logs: Bugs tagged libstdc++-cxx11. La loro soluzione consisteva nel ricostruire tutto sotto il nuovo ABI, ma non gestiva il caso d'angolo dei compilatori di compilazione/adattamento che l'ABI cambiava.

Nel mondo Apple, penso che questo sia vicino a un binario grasso. Ma non sono sicuro di cosa fare nel mondo Linux/GCC. Infine, non controlliamo il modo in cui la distribuzione genera la libreria e non controlliamo quali compilatori vengono utilizzati per collegare un'applicazione alla libreria.

+0

Qual è il tuo prodotto? Una biblioteca o un programma? –

+0

@ n.m. - Il prodotto è di Wei Dai [Crypto ++] (http://www.cryptopp.com/). È una libreria C++. È costruito da Debian e fornito come parte della distribuzione. – jww

risposta

3

Ecco un modo per farlo, ma non è molto elegante. Inoltre, non mi è chiaro come far sì che GCC lo automatizzi, quindi non devo fare cose due volte.

In primo luogo, l'esempio che sta per essere trasformato in una libreria:

$ cat test.cxx 
#include <string> 

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

Poi:

$ 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 

Infine, vedere quello che abbiamo ottenuto:

$ 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 

E:

$ nm test.so 

00002020 B bar 
00002018 B __bss_start 
00002018 b completed.7181 
     U [email protected]@GLIBC_2.1.3 
     w [email protected]@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 [email protected]@GLIBCXX_3.4 
     U [email protected]@GLIBCXX_3.4 
     U [email protected]@GLIBCXX_3.4.21 
     U [email protected]@GLIBCXX_3.4.21 
5

Declinazione di responsabilità, quanto segue non è testato in produzione, l'uso è a proprio rischio.

È possibile rilasciare la libreria con doppio ABI. Questo è più o meno analogo al "fat binary" di OSX, ma costruito interamente con C++.

Il modo più semplice per farlo sarebbe compilare la libreria due volte: con -D_GLIBCXX_USE_CXX11_ABI=0 e con -D_GLIBCXX_USE_CXX11_ABI=1.Posizionare l'intera libreria in due spazi dei nomi differenti a seconda del valore della 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 
    } 
} 

Ora gli utenti possono utilizzare CryptoPP::whatever come al solito, questo associa a uno o CryptoPP::cxx11::whateverCryptoPP::cxx03::whatever a seconda della ABI selezionato.

Nota, il manuale GCC dice che questo metodo cambierà i nomi storti di tutto ciò che è definito nello spazio dei nomi inline con tag. Nella mia esperienza questo non succede.

L'altro metodo codifica ogni classe, funzione e variabile con __attribute__((abi_tag("cxx11"))) se _GLIBCXX_USE_CXX11_ABI è diverso da zero. Questo attributo aggiunge piacevolmente [cxx11] all'output del demangler. Penso che l'uso di un namespace funzioni altrettanto bene e richiede meno modifiche al codice esistente.

In teoria non c'è bisogno di duplicare l'intera libreria, solo le funzioni e le classi che utilizzano std::string e std::list, e le funzioni e le classi che utilizzano queste funzioni e classi, e così via in modo ricorsivo. Ma in pratica probabilmente non ne vale la pena, specialmente se la biblioteca non è molto grande.

+0

Grazie @ n.m. L'oggetto condiviso è problematico per i compilatori non consapevoli, come l'attuale Clang. L'oggetto condiviso è OK per i compilatori consapevoli. Per i compilatori non consapevoli, l'oggetto condiviso necessita dei simboli sia per 'CryptoPP :: cxx11 :: whatever' che per' CryptoPP :: cxx03 :: whatever'. Le cose nelle intestazioni dovrebbero essere OK come descrivi. Sto ancora girando i bit per vedere cosa si può fare per aiutare i compilatori non consapevoli. – jww

+0

"l'oggetto condiviso ha bisogno di simboli sia per CryptoPP :: cxx11 :: qualunque sia per CryptoPP :: cxx03 :: qualunque cosa" Sì, è quello che sto dicendo. Combini entrambe le versioni nella stessa libreria. –

+0

Se si desidera supportare clang o altri compilatori inconsapevoli di abi_tag, non utilizzare '__attribute __ ((abi_tag (" cxx11 ")))'. –