Muszę wdrożyć aplikację C ++ zbudowaną na Ubuntu 12.10 z libstdc ++ GCC 4.7 na systemy z Ubuntu 10.04, które są dostarczane ze znacznie starszą wersją libstdc ++.
Obecnie kompiluję z -static-libstdc++ -static-libgcc
, zgodnie z sugestią tego wpisu na blogu: Statyczne łączenie libstdc ++ . Autor ostrzega przed użyciem jakiegokolwiek dynamicznie ładowanego kodu C ++ podczas statycznej kompilacji libstdc ++, czego jeszcze nie sprawdziłem. Mimo wszystko wydaje się, że jak dotąd wszystko idzie gładko: mogę korzystać z funkcji C ++ 11 w Ubuntu 10.04, o co mi chodziło.
Zauważam, że ten artykuł pochodzi z 2005 roku i być może od tamtego czasu wiele się zmieniło. Czy jej rady są nadal aktualne? Czy są jakieś czające się problemy, o których powinienem wiedzieć?
-static-libstdc++
opcji, po prostu-static
kernel too old
błąd w niektórych systemach Ubuntu 1404. Plik glibc.so jest jakkernel32.dll
w oknie, jest częścią interfejsu systemu operacyjnego, nie powinniśmy go osadzać w naszym pliku binarnym. Możesz użyć,objdump -T [binary path]
aby zobaczyć,libstdc++.so
czy jest ładowany dynamicznie, czy nie. Dla programisty golang możesz dodać#cgo linux LDFLAGS: -static-libstdc++ -static-libgcc
przed importem "C"-static-libstdc++
nie-static
taklibc.so
nie będzie statycznie powiązane.Odpowiedzi:
Ten post na blogu jest dość niedokładny.
Nie prawda. Jedyne zmiany C ++ ABI wprowadzone od czasu GCC 3.4 były wstecznie kompatybilne, co oznacza, że C ++ ABI był stabilny przez prawie dziewięć lat.
Różnice między poprawionymi wersjami GCC dystrybucji są niewielkie i nie zmieniają ABI, np. Fedora 4.6.3 20120306 (Red Hat 4.6.3-2) jest kompatybilna z ABI z oryginalnymi wydaniami FSF 4.6.x i prawie na pewno z każdym 4.6. x z dowolnej innej dystrybucji.
W bibliotekach uruchomieniowych GNU / Linux GCC używają wersji symboli ELF, więc łatwo jest sprawdzić wersje symboli wymagane przez obiekty i biblioteki, a jeśli masz plik,
libstdc++.so
który zapewnia te symbole, zadziała, nie ma znaczenia, czy jest to nieco inna poprawiona wersja z innej wersji twojej dystrybucji.To też nie jest prawdą.
To powiedziawszy, statyczne linkowanie do
libstdc++.a
jest jedną z opcji dla Ciebie.Powodem, dla którego może to nie działać, jeśli dynamicznie ładujesz bibliotekę (używając
dlopen
), jest to, że symbole libstdc ++, od których ona zależy, mogły nie być potrzebne twojej aplikacji, gdy ją (statycznie) łączysz, więc te symbole nie będą obecne w twoim pliku wykonywalnym. Można to rozwiązać, dynamicznie łącząc bibliotekę współdzieloną zlibstdc++.so
(co i tak jest właściwe, jeśli to od niej zależy). Interpozycja symboli ELF oznacza, że symbole obecne w pliku wykonywalnym będą używane przez bibliotekę współdzieloną, ale inne nie. obecny w twoim pliku wykonywalnym zostanie znaleziony w miejsculibstdc++.so
, do którego prowadzi. Jeśli Twoja aplikacja nie używadlopen
, nie musisz się tym przejmować.Inną opcją (i tą, którą preferuję) jest wdrożenie nowszej
libstdc++.so
wraz z aplikacją i upewnienie się, że zostanie ona znaleziona przed domyślnym systememlibstdc++.so
, co można zrobić, zmuszając dynamiczny linker do szukania we właściwym miejscu, używając$LD_LIBRARY_PATH
zmiennej środowiskowej w run- time lub ustawiającRPATH
plik wykonywalny w czasie łącza. Wolę używać,RPATH
ponieważ nie zależy to od prawidłowego ustawienia środowiska, aby aplikacja działała. Jeśli powiązanie aplikacji z'-Wl,-rpath,$ORIGIN'
(zwróć uwagę na apostrofów aby powłoka próbuje rozwinąć$ORIGIN
), a następnie plik wykonywalny będzie miećRPATH
z$ORIGIN
których mówi linker dynamiczny szukać bibliotek dzielonych w tym samym katalogu co plik wykonywalny sama. Jeśli umieścisz nowszylibstdc++.so
w tym samym katalogu, w którym znajduje się plik wykonywalny, zostanie znaleziony w czasie wykonywania, problem rozwiązany. (Inną opcją jest umieszczenie pliku wykonywalnego w/some/path/bin/
i nowszej libstdc ++. Tak/some/path/lib/
i połączyć się z'-Wl,-rpath,$ORIGIN/../lib'
lub inną stałą lokalizacją względem pliku wykonywalnego i ustawić wartość RPATH względem$ORIGIN
)źródło
libstdc++.so.6
dowiązanie symboliczne, które jest ustawione w czasie instalacji tak, aby wskazywało na dołączoną bibliotekę lub systemową, jeśli jest nowsza. Istnieją bardziej skomplikowane modele z połączeniami mieszanymi, takie jak używane przez Red Hat DTS, ale trudno je wykonać samodzielnie.Jeden dodatek do doskonałej odpowiedzi Jonathana Wakely'ego, dlaczego dlopen () jest problematyczny:
Ze względu na nową pulę obsługi wyjątków w GCC 5 (patrz PR 64535 i PR 65434 ), jeśli dlopen i dlclose bibliotekę, która jest statycznie połączona z libstdc ++, za każdym razem otrzymasz wyciek pamięci (obiektu puli). Więc jeśli jest jakakolwiek szansa, że kiedykolwiek użyjesz dlopen, statyczne połączenie libstdc ++ wydaje się naprawdę złym pomysłem. Zauważ, że jest to prawdziwy wyciek w przeciwieństwie do łagodnego, o którym mowa w PR 65434 .
źródło
__gnu_cxx::__freeres()
wydaje się zapewniać przynajmniej pomoc w rozwiązaniu tego problemu, ponieważ zwalnia wewnętrzny bufor obiektu puli. Ale dla mnie jest raczej niejasne, jaką implikację ma wywołanie tej funkcji w odniesieniu do wyjątków przypadkowo rzuconych później.Dodatek do odpowiedzi Jonathana Wakely'ego na temat RPATH:
RPATH będzie działać tylko wtedy, gdy dany RPATH jest RPATH uruchomionej aplikacji . Jeśli masz bibliotekę, która dynamicznie łączy się z dowolną biblioteką poprzez własną RPATH, RPATH biblioteki zostanie nadpisana przez RPATH aplikacji, która ją ładuje. Jest to problem, gdy nie możesz zagwarantować, że RPATH aplikacji jest taki sam jak w Twojej bibliotece, np. Jeśli spodziewasz się, że zależności znajdują się w określonym katalogu, ale ten katalog nie jest częścią RPATH aplikacji.
Na przykład, powiedzmy, że masz aplikację App.exe, która ma dynamicznie powiązaną zależność od libstdc ++. So.x dla GCC 4.9. App.exe ma tę zależność rozwiązaną przez RPATH, tj
App.exe (RPATH=.:./gcc4_9/libstdc++.so.x)
Teraz powiedzmy, że istnieje inna biblioteka Dependency.so, która ma dynamicznie połączoną zależność od libstdc ++. So.y dla GCC 5.5. Zależność tutaj jest rozwiązywana przez RPATH biblioteki, tj
Dependency.so (RPATH=.:./gcc5_5/libstdc++.so.y)
Gdy App.exe ładuje Dependency.so, nie dołącza ani nie dołącza przedrostka RPATH biblioteki . W ogóle go nie konsultuje. Jedyną braną pod uwagę RPATH będzie ta uruchomionej aplikacji lub w tym przykładzie App.exe. Oznacza to, że jeśli biblioteka opiera się na symbolach, które są w gcc5_5 / libstdc ++. So.y, ale nie w gcc4_9 / libstdc ++. So.x, wtedy biblioteka nie zostanie załadowana.
To tylko słowo ostrzeżenia, ponieważ sam napotkałem te problemy w przeszłości. RPATH jest bardzo przydatnym narzędziem, ale jego implementacja wciąż ma pewne problemy.
źródło
Konieczne może być również upewnienie się, że nie jesteś zależny od dynamicznej biblioteki glibc. Uruchom
ldd
wynikowy plik wykonywalny i zanotuj wszelkie zależności dynamiczne (libc / libm / libpthread są podejrzanymi o używanie).Dodatkowym ćwiczeniem byłoby zbudowanie zestawu zaangażowanych przykładów C ++ 11 przy użyciu tej metodologii i wypróbowanie powstałych plików binarnych w prawdziwym systemie 10.04. W większości przypadków, chyba że zrobisz coś dziwnego z ładowaniem dynamicznym, od razu wiesz, czy program działa, czy ulega awarii.
źródło
printf
), ale tak długo, jak glibc na Ubuntu 10.04 zapewnia wszystkie funkcje wymagane przez nowszą libstdc ++, nie ma problemu z zależnością od dynamicznego glibc, w rzeczywistości zdecydowanie zaleca się, aby nigdy nie łączyć statycznie do glibcChciałbym dodać do odpowiedzi Jonathana Wakely'ego, co następuje.
Grając
-static-libstdc++
na Linuksie, napotkałem problemdlclose()
. Załóżmy, że mamy aplikację „A” połączoną statycznielibstdc++
i ładuje się ona dynamicznie połączona zlibstdc++
wtyczką „P” w czasie wykonywania. W porządku. Ale kiedy „A” zwalnia „P”, pojawia się błąd segmentacji. Moje założenie jest takie, że po wyładowaniulibstdc++.so
„A” nie może już używać symboli związanych zlibstdc++
. Zauważ, że jeśli „A” i „P” są połączone statycznielibstdc++
lub „A” jest połączone dynamicznie, a „P” statycznie, problem nie występuje.Podsumowanie: jeśli Twoja aplikacja ładuje / zwalnia wtyczki, z którymi może się łączyć dynamicznie
libstdc++
, aplikacja również musi być z nią dynamicznie połączona. To tylko moje spostrzeżenie i chciałbym otrzymać wasze komentarze.źródło
sbrk
) przyjmuje pewne założenie i prawie oczekuje, że będzie sam w ramach jednego procesu ... nie jestem pewien, czy jest to ograniczone do chociaż konkretna wersja glibc lub cokolwiek innego.