2016-03-22 28 views
10

Niedawno otrzymaliśmy zgłoszenie z powodu GCC 5.1, libstdc++ and Dual ABI. Wydaje się, że Clang is not aware of the GCC inline namespace changes generuje kod oparty na jednym zestawie przestrzeni nazw lub symboli, podczas gdy GCC używa innego zestawu przestrzeni nazw lub symboli. W czasie połączenia występują problemy z powodu brakujących symboli.Łączenie problemów z powodu symboli z abi :: cxx11?

Jeśli poprawnie analizuję stronę Dual ABI, wygląda na to, że obracanie się pod numerami _GLIBCXX_USE_CXX11_ABI i abi::cxx11 wymaga dodatkowych trudności. Więcej czytania jest dostępne na blogu Red Hata pod numerem GCC5 and the C++11 ABI i The Case of GCC-5.1 and the Two C++ ABIs.

Poniżej znajduje się na komputerze Ubuntu 15. Maszyna zapewnia 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] 

Jak mogę wygenerować binarny z symboli z wykorzystaniem zarówno dekoracje („współistnienia”, jak blog Red Hat to nazywa)?

Lub, jakie są dostępne opcje?


Próbuję osiągnąć "po prostu działa" dla użytkowników. Nie obchodzi mnie, czy istnieją dwa słabe symbole z dwoma różnymi zachowaniami (std::string brakuje kopii na zapis, a std::string[abi:cxx11] zapewnia kopiowanie przy zapisie). Albo może być alias dla drugiego.

Debian ma mnóstwo podobnych robaków pod adresem Debian Bug report logs: Bugs tagged libstdc++-cxx11. Ich rozwiązaniem było przebudowanie wszystkiego w ramach nowego ABI, ale nie poradziło sobie z narożnym przypadkiem mieszania/dopasowywania kompilatorów modulo do zmian ABI.

W świecie Apple uważam, że jest blisko grubego pliku binarnego. Ale nie jestem pewien, co zrobić w świecie Linuksa/GCC. Na koniec nie kontrolujemy sposobu, w jaki dystrybucje budują bibliotekę, i nie kontrolujemy, które kompilatory są używane do łączenia aplikacji z biblioteką.

+0

Jaki jest Twój produkt? Biblioteka lub program? –

+0

@ n.m. - Produkt jest produktem Wei Dai [Crypto ++] (http://www.cryptopp.com/). Jest to biblioteka C++. Jest zbudowany przez Debiana i dostarczany jako część dystrybucji. – jww

Odpowiedz

3

Oto jeden sposób, aby to zrobić, ale nie jest zbyt elegancka. Nie jest dla mnie jasne, jak sprawić, aby GCC ją zautomatyzował, więc nie muszę robić tego dwa razy.

pierwsze, przykład, który będzie przekształcony w bibliotece:

$ cat test.cxx 
#include <string> 

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

wówczas:

$ 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 

Wreszcie, co mamy:

$ 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 

oraz:

$ 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

Zrzeczenie się odpowiedzialności, poniższe elementy nie są testowane w produkcji, wykorzystuj na własne ryzyko.

Możesz uwolnić swoją bibliotekę pod podwójnym ABI. Jest to mniej więcej analogiczne do "grubego binarnego" OSX, ale w całości zbudowane z C++.

Najprostszym sposobem na zrobienie tego byłoby dwukrotne skompilowanie biblioteki: z -D_GLIBCXX_USE_CXX11_ABI=0 i -D_GLIBCXX_USE_CXX11_ABI=1.Umieścić całą bibliotekę w ramach dwóch różnych nazw w zależności od wartości makra:

#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 
    } 
} 

Teraz użytkownicy mogą korzystać CryptoPP::whatever jak zwykle, to mapy albo CryptoPP::cxx11::whatever lub CryptoPP::cxx03::whatever zależności od wybranego ABI.

Uwaga: instrukcja GCC mówi, że ta metoda zmieni zniekształcone nazwy wszystkich elementów zdefiniowanych w otagowanym nazewnictwie. Z mojego doświadczenia wynika, że ​​tak się nie dzieje.

Inną metodą byłoby oznaczenie każdej klasy, funkcji i zmiennej za pomocą __attribute__((abi_tag("cxx11"))), jeśli _GLIBCXX_USE_CXX11_ABI jest niezerowe. Ten atrybut ładnie dodaje [cxx11] do wyjścia demangler. Myślę, że używanie przestrzeni nazw działa równie dobrze i wymaga mniej modyfikacji istniejącego kodu.

Teoretycznie nie trzeba powielić całą bibliotekę, tylko funkcje i klas, które używają std::string i std::list, a funkcje i klas, które używają te funkcje i klasy, i tak dalej rekurencyjnie. Ale w praktyce prawdopodobnie nie jest to warte wysiłku, zwłaszcza jeśli biblioteka nie jest zbyt duża.

+0

Dzięki @ n.m. Współdzielony obiekt jest problematyczny dla nieświadomych kompilatorów, takich jak obecny Clang. Obiekt współdzielony jest odpowiedni dla świadomych kompilatorów. Dla nieświadomych kompilatorów obiekt współdzielony potrzebuje symboli zarówno dla 'CryptoPP :: cxx11 :: whatever' oraz' CryptoPP :: cxx03 :: whatever'. Rzeczy w nagłówkach powinny być w porządku, jak opisujesz. Nadal używam bitów, aby zobaczyć, co można zrobić, aby pomóc nieświadomym kompilatorom. – jww

+0

"Symbole obiektów współdzielonych" zarówno dla CryptoPP :: cxx11 :: cokolwiek, jak i CryptoPP :: cxx03 :: cokolwiek "Tak, właśnie to mówię. Łączymy obie wersje w tej samej bibliotece. –

+0

Jeśli chcesz wspierać klang lub inne kompilatory nieświadome abi_tag, po prostu nie używaj '__attribute __ ((abi_tag (" cxx11 ")))'. –