Kiedy kompiluję coś na moim komputerze z Ubuntu Lucid 10.04, zostaje to połączone z glibc. Lucid używa 2.11 glibc. Kiedy uruchamiam ten plik binarny na innym komputerze ze starszym glibc, polecenie nie powiedzie się, mówiąc, że nie ma glibc 2.11 ...
O ile wiem, glibc używa wersji symboli. Czy mogę zmusić gcc do łączenia się z określoną wersją symbolu?
W moim konkretnym zastosowaniu staram się skompilować cross toolchain gcc dla ARM.
Odpowiedzi:
Masz rację, ponieważ glibc używa wersji symboli. Jeśli jesteś ciekaw, symbol wersjonowania realizację wprowadzony w glibc 2.1 jest opisana tutaj i jest przedłużeniem symbolem Słońca schemat wersjonowania opisaną tutaj .
Jedną z opcji jest statyczne połączenie pliku binarnego. To prawdopodobnie najłatwiejsza opcja.
Możesz także zbudować swój plik binarny w środowisku kompilacji chroot lub używając starego cross-kompilatora glibc- new => glibc- old .
Zgodnie z wpisem na blogu http://www.trevorpounds.com Linking to Older Versioned Symbols (glibc) , możliwe jest wymuszenie powiązania dowolnego symbolu ze starszym, o ile jest ważny przy użyciu tego samego
.symver
pseudo -op, który służy do definiowania symboli wersjonowanych w pierwszej kolejności. Poniższy przykład pochodzi z wpisu na blogu .Poniższy przykład wykorzystuje realpath glibc, ale zapewnia, że jest on połączony ze starszą wersją 2.2.5.
źródło
libc.a
nadal istnieje, glibc obsługuje to w niektórych przypadkach, chociaż nie jest to zalecane (Drepper) . Będziesz miał problemy z nietrywialnymi programami, szczególnie ze wszystkim, co używa NSS (obejście w FAQ ).Połącz z -static . Kiedy łączysz się z -static, linker osadza bibliotekę wewnątrz pliku wykonywalnego, więc plik wykonywalny będzie większy, ale może być wykonany na systemie ze starszą wersją glibc, ponieważ program użyje własnej biblioteki zamiast biblioteki systemowej .
źródło
Konfiguracja 1: skompiluj własną bibliotekę glibc bez dedykowanego GCC i używaj jej
Ponieważ wydaje się niemożliwe, aby zrobić to samo z hackami do wersjonowania symboli, przejdźmy o krok dalej i skompilujmy glibc samodzielnie.
Ta konfiguracja może działać i jest szybka, ponieważ nie rekompiluje ponownie całego zestawu narzędzi GCC, tylko glibc.
Ale to nie jest wiarygodne, ponieważ korzysta gospodarz C Runtime obiektów takich jak
crt1.o
,crti.o
icrtn.o
dostarczane przez glibc. Wspomina się o tym na: https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Te obiekty wykonują wczesną konfigurację, na której opiera się glibc, więc nie zdziwiłbym się, gdyby coś się zawiesiło i niesamowicie subtelne sposoby.Aby uzyskać bardziej niezawodną konfigurację, zobacz Konfiguracja 2 poniżej.
Skompiluj glibc i zainstaluj lokalnie:
Konfiguracja 1: sprawdź kompilację
test_glibc.c
Skompiluj i uruchom z
test_glibc.sh
:Program daje oczekiwane:
Polecenie zaadaptowano z https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location, ale
--sysroot
zawiodło:więc go usunąłem.
ldd
dane wyjściowe potwierdzają, żeldd
biblioteki i, które właśnie zbudowaliśmy, są używane zgodnie z oczekiwaniami:Wynik
gcc
debugowania kompilacji pokazuje, że były używane obiekty wykonawcze mojego hosta, co jest złe, jak wspomniano wcześniej, ale nie wiem, jak to obejść, np. Zawiera:Konfiguracja 1: zmodyfikuj glibc
Teraz zmodyfikujmy glibc za pomocą:
Następnie ponownie skompiluj i ponownie zainstaluj glibc oraz ponownie skompiluj i ponownie uruchom nasz program:
i widzimy
hacked
wydrukowane kilka razy zgodnie z oczekiwaniami.To dodatkowo potwierdza, że faktycznie używaliśmy skompilowanego glibc, a nie hosta.
Testowane na Ubuntu 18.04.
Konfiguracja 2: nieskazitelna konfiguracja crosstool-NG
Jest to alternatywa dla konfiguracji 1, i to jest najbardziej poprawna konfiguracja Mam osiągnąć daleko: wszystko jest w porządku o ile mogę obserwować, w tym C Runtime obiektów takich jak
crt1.o
,crti.o
icrtn.o
.W tej konfiguracji skompilujemy w pełni dedykowany łańcuch narzędzi GCC, który używa potrzebnego glibc.
Jedyną wadą tej metody jest to, że kompilacja potrwa dłużej. Ale nie ryzykowałbym konfiguracji produkcyjnej z czymś mniejszym.
crosstool-NG to zestaw skryptów, które pobierają i kompilują wszystko ze źródła dla nas, w tym GCC, glibc i binutils.
Tak, system kompilacji GCC jest tak zły, że potrzebujemy do tego osobnego projektu.
Ta konfiguracja nie jest idealna tylko dlatego, że crosstool-NG nie obsługuje budowania plików wykonywalnych bez dodatkowych
-Wl
flag , co wydaje się dziwne, odkąd zbudowaliśmy samo GCC. Ale wszystko wydaje się działać, więc jest to tylko niedogodność.Zdobądź crosstool-NG i skonfiguruj go:
Jedyną obowiązkową opcją, jaką widzę, jest dopasowanie do wersji jądra hosta, aby używał poprawnych nagłówków jądra. Znajdź wersję jądra hosta za pomocą:
co pokazuje mi:
więc
menuconfig
robię:Operating System
Version of linux
więc wybieram:
która jest pierwszą równą lub starszą wersją. Musi być starszy, ponieważ jądro jest kompatybilne wstecz.
Teraz możesz budować za pomocą:
a teraz poczekaj około trzydziestu minut do dwóch godzin na kompilację.
Konfiguracja 2: opcjonalne konfiguracje
Ten
.config
, który wygenerowaliśmy,./ct-ng x86_64-unknown-linux-gnu
ma:Aby to zmienić,
menuconfig
wykonaj:C-library
Version of glibc
zapisz plik
.config
i kontynuuj tworzenie.Lub, jeśli chcesz użyć własnego źródła glibc, np. Użyć glibc z najnowszego gita, postępuj w ten sposób :
Paths and misc options
Try features marked as EXPERIMENTAL
: ustawione na trueC-library
Source of glibc
Custom location
: Powiedz takCustom location
Custom source location
: wskazuje na katalog zawierający twoje źródło glibcgdzie glibc został sklonowany jako:
Konfiguracja 2: przetestuj
Po zbudowaniu odpowiedniego łańcucha narzędzi przetestuj go za pomocą:
Wygląda na to, że wszystko działa tak, jak w Instalatorze 1, z wyjątkiem tego, że teraz zostały użyte poprawne obiekty wykonawcze:
Instalacja 2: nieudana próba wydajnej ponownej kompilacji glibc
Nie wydaje się to możliwe w przypadku crosstool-NG, jak wyjaśniono poniżej.
Jeśli po prostu przebudujesz;
wtedy twoje zmiany w niestandardowej lokalizacji źródłowej glibc są brane pod uwagę, ale buduje wszystko od zera, przez co nie nadaje się do iteracyjnego rozwoju.
Jeśli zrobimy:
daje ładny przegląd kroków kompilacji:
dlatego widzimy, że istnieją kroki glibc przeplatane z kilkoma krokami GCC, w szczególności
libc_start_files
występuje wcześniejcc_core_pass_2
, co jest prawdopodobnie najdroższym krokiem razem zcc_core_pass_1
.Aby zbudować tylko jeden krok, musisz najpierw ustawić
.config
opcję „Zapisz kroki pośrednie” dla początkowej kompilacji:Paths and misc options
Debug crosstool-NG
Save intermediate steps
a potem możesz spróbować:
ale niestety
+
wymagane, jak wspomniano na: https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536i zasadniczo nadal sprawia, że przebudowa jest zbyt wolna, aby była możliwa do opracowania, i nie widzę, jak to przezwyciężyć bez łatania crosstool-NG.
Co więcej, rozpoczęcie od
libc
kroku nieCustom source location
powodowało ponownego skopiowania źródła z , co dodatkowo czyniło tę metodę bezużyteczną.Bonus: stdlibc ++
Bonus, jeśli interesuje Cię również standardowa biblioteka C ++: Jak edytować i przebudowywać standardowe źródło biblioteki GCC libstdc ++ C ++?
źródło
musl-libc
to kolejna opcja, jeśli chodzi o środowisko wykonawcze C.Moim zdaniem najbardziej leniwe rozwiązanie (zwłaszcza jeśli nie polegasz na najnowszych funkcjach C / C ++ lub najnowszych funkcjach kompilatora) nie zostało jeszcze wspomniane, więc oto jest:
Po prostu zbuduj system z najstarszym GLIBC, który nadal chcesz obsługiwać.
W dzisiejszych czasach jest to całkiem proste dzięki technologiom takim jak chroot, KVM / Virtualbox lub docker, nawet jeśli tak naprawdę nie chcesz używać takiej starej dystrybucji bezpośrednio na dowolnym komputerze. W szczegółach, aby utworzyć maksymalnie przenośny plik binarny swojego oprogramowania, zalecam wykonanie następujących kroków:
Po prostu wybierz swoją truciznę piaskownicy / wirtualizacji / ... cokolwiek i użyj jej, aby zdobyć wirtualną starszą wersję Ubuntu LTS i skompiluj z gcc / g ++, które ma tam domyślnie. To automatycznie ogranicza GLIBC do tego dostępnego w tym środowisku.
Unikaj polegania na zewnętrznych bibliotekach poza podstawowymi: na przykład, powinieneś dynamicznie łączyć rzeczy systemowe z poziomu podstawowego, takie jak glibc, libGL, libxcb / X11 / wayland things, libasound / libpulseaudio, prawdopodobnie GTK +, jeśli tego używasz, ale poza tym najlepiej statycznie łączyć zewnętrzne libs / wyślij je razem, jeśli możesz. Szczególnie samodzielne biblioteki, takie jak programy ładujące obrazy, dekodery multimediów itp., Mogą powodować mniej awarii w innych dystrybucjach (awaria może być spowodowana np. Jeśli występuje tylko w innej głównej wersji), jeśli je wysyłasz statycznie.
Dzięki takiemu podejściu otrzymujesz plik binarny zgodny ze starym GLIBC bez żadnych ręcznych poprawek symboli, bez wykonywania w pełni statycznego pliku binarnego (co może się zepsuć w przypadku bardziej złożonych programów, ponieważ glibc tego nienawidzi i może powodować problemy z licencjonowaniem) i bez ustawiania w górę dowolnego niestandardowego łańcucha narzędzi, dowolnej niestandardowej kopii glibc lub cokolwiek innego.
źródło