Czy plik wykonywalny małego, niezwykle prostego programu, takiego jak ten pokazany poniżej, który jest skompilowany na jednym systemie Linux, będzie działał w innym stylu? A może trzeba go ponownie skompilować?
Czy architektura maszyny ma znaczenie w takim przypadku?
int main()
{
return (99);
}
glibc
tam skompilowanego lub odwrotnie. To prawda, że nie jest to całkowicie niemożliwe .gcc -m32 -static
). W ten sposób każdy Linux i386 lub amd64 będzie mógł uruchomić plik wykonywalny.Odpowiedzi:
To zależy. Coś skompilowanego dla IA-32 (Intel 32-bit) może działać na amd64, ponieważ Linux na Intel zachowuje zgodność wsteczną z aplikacjami 32-bitowymi (z zainstalowanym odpowiednim oprogramowaniem). Oto twoja
code
kompilowana na 32-bitowym systemie RedHat 7.3 (około 2002, gcc wersja 2.96), a następnie plik binarny skopiowany i uruchomiony na 64-bitowym systemie Centos 7.4 (około 2017):Starożytny RedHat 7.3 do Centos 7.4 (zasadniczo RedHat Enterprise Linux 7.4) pozostaje w tej samej rodzinie „dystrybucji”, więc prawdopodobnie będzie miał lepszą przenośność niż przejście z jakiejś losowej instalacji „Linux od zera” od 2002 r. Do innej losowej dystrybucji Linuksa w 2018 r. .
Coś skompilowanego dla amd64 nie działałoby na 32-bitowych wersjach systemu Linux (stary sprzęt nie wie o nowym sprzęcie). Odnosi się to również do nowego oprogramowania skompilowanego na nowoczesnych systemach, które mają być uruchamiane na starych, starych rzeczach, ponieważ biblioteki, a nawet wywołania systemowe mogą nie być przenośne wstecz, więc mogą wymagać sztuczek kompilacyjnych lub uzyskania starego kompilatora i tak dalej, a może zamiast tego kompilacja na starym systemie. (To dobry powód, aby trzymać maszyny wirtualne starych, starych rzeczy).
Architektura ma znaczenie; amd64 (lub IA-32) znacznie różni się od ARM lub MIPS, więc nie można oczekiwać, że binarny jeden z nich będzie działał na innym. Na poziomie montażowego
main
sekcji kodu na IA-32 poprzez kompilujegcc -S code.c
sięz którymi system amd64 może sobie poradzić (w systemie Linux - w przeciwieństwie do OpenBSD na amd64 nie obsługuje 32-bitowych plików binarnych; wsteczna kompatybilność ze starymi architekturami daje atakującym swobodę , np. CVE-2014-8866 i przyjaciele). Tymczasem w systemie big-endian MIPS
main
zamiast tego kompiluje się w celu:z którym procesor Intel nie będzie miał pojęcia, co z tym zrobić, a także w przypadku zestawu Intel na platformie MIPS.
Możesz użyć QEMU lub innego emulatora do uruchomienia obcego kodu (być może bardzo, bardzo powoli).
Jednak! Twój kod jest bardzo prostym kodem, więc będzie mniej problemów z przenośnością niż cokolwiek innego; programy zwykle korzystają z bibliotek, które zmieniły się w czasie (glibc, openssl, ...); dla tych może być również konieczne zainstalowanie starszych wersji różnych bibliotek (na przykład RedHat zazwyczaj umieszcza „kompatybilność” gdzieś w nazwie pakietu dla takich)
lub ewentualnie martwić się zmianami ABI (Application Binary Interface) o stare sposoby używające glibc lub nowszymi zmianami spowodowanymi C ++ 11 lub innymi wydaniami C ++. Można również skompilować statyczne (znacznie zwiększając rozmiar pliku binarnego na dysku), aby uniknąć problemów z biblioteką, chociaż to, czy zrobił to jakiś stary plik binarny, zależy od tego, czy stara dystrybucja Linuksa kompiluje większość wszystkiego dynamicznie (RedHat: tak), czy nie. Z drugiej strony, takie rzeczy jak
patchelf
ponowne uruchamianie dynamicznycha.out
plików binarnych (ELF, ale prawdopodobnie nie formatowanie) w celu korzystania z innych bibliotek.Jednak! Możliwość uruchomienia programu to jedno, a właściwie robienie z nim czegoś pożytecznego. Stare 32-bitowe pliki binarne Intela mogą mieć problemy z bezpieczeństwem, jeśli zależą od wersji OpenSSL, która zawiera jakieś okropne i nieobsługiwane problemy bezpieczeństwa, lub program może nie być w stanie negocjować z nowoczesnymi serwerami WWW (jako nowoczesne serwery odrzucają stare protokoły i szyfry starego programu) lub protokół SSH wersja 1 nie jest już obsługiwany lub ...
źródło
for GNU/Linux 2.6.32
(lub takie) na wyjściufile /usr/bin/ls
.Krótko mówiąc: jeśli przenosisz skompilowany plik binarny z jednego hosta na inny przy użyciu tej samej (lub kompatybilnej) architektury , możesz być w porządku, przenosząc go do innej dystrybucji . Jednak wraz ze wzrostem złożoności kodu prawdopodobieństwo powiązania z biblioteką, która nie jest zainstalowana; zainstalowany w innym miejscu; lub zainstalowany w innej wersji, wzrasta. Weźmy na przykład kod, dla którego
ldd
zgłasza się następujące zależności po kompilacjigcc -o exit-test exit-test.c
na (pochodzącym z Debiana) systemie Ubuntu Linux:Oczywiście ten plik binarny nie uruchomi się, jeśli przekopię go, powiedzmy, na komputer Mac (
./exit-test: cannot execute binary file: Exec format error
). Spróbujmy przenieść go do skrzynki RHEL:O jej. Dlaczego to może być?
Nawet w tym przypadku użycia forklifting nie powiódł się z powodu brakujących bibliotek współdzielonych.
Jednak jeśli go skompiluję
gcc -static exit-test-static exit-test.c
, przeniesienie go do systemu bez bibliotek działa dobrze. Kosztem oczywiście miejsca na dysku:Innym realnym rozwiązaniem byłoby zainstalowanie wymaganych bibliotek na nowym hoście.
Podobnie jak w przypadku wielu rzeczy we wszechświecie U&L, jest to kot z wieloma skórkami, z których dwie są przedstawione powyżej.
źródło
amd64
binarnego i uruchomienie go na innejamd64
dystrybucji lub zbudowanie plikui386
binarnego i uruchomienie go na inneji386
dystrybucji.Dodanie do doskonałych odpowiedzi na @thrig i @DopeGhoti: Uniksowe lub podobne do systemów operacyjnych, w tym Linux, tradycyjnie zawsze były projektowane i dostosowywane bardziej pod kątem przenośności kodu źródłowego niż plików binarnych.
Jeśli nie masz nic specyficznego dla sprzętu lub nie jest to proste źródło, jak w twoim przykładzie, możesz go przenieść bez żadnego problemu pomiędzy prawie każdą wersją Linuksa lub architektury jako kodem źródłowym, o ile serwery docelowe mają zainstalowane pakiety programistyczne C , niezbędne biblioteki i zainstalowane odpowiednie biblioteki programistyczne.
Ponieważ daleko odsuwa się bardziej zaawansowany kod ze starszych wersji systemu Linux odległych w czasie lub bardziej szczegółowe programy, takie jak moduły jądra dla różnych wersji jądra, może być konieczne dostosowanie i zmodyfikowanie kodu źródłowego w celu uwzględnienia przestarzałych bibliotek / interfejsów API / ABI.
źródło
Przez domyślnie , to prawie na pewno napotkasz problemy z bibliotek zewnętrznych. Niektóre inne odpowiedzi zawierają więcej szczegółów na temat tych problemów, więc nie powielę ich pracy.
Państwo może jednak kompilacji wielu programów - nawet te nietrywialne - być przenośne między systemami Linux. Kluczem jest zestaw narzędzi o nazwie Linux Standard Base . LSB jest przeznaczony do tworzenia tylko tych rodzajów zastosowań przenośnych. Skompiluj aplikację dla LSB wer. 5.0 i będzie działać w dowolnym innym środowisku Linux (o tej samej architekturze), które implementuje LSB wer. Kilka dystrybucji Linuksa jest zgodnych z LSB, a inne obejmują zestawy / biblioteki LSB jako pakiet instalacyjny. Jeśli zbudujesz aplikację przy użyciu narzędzi LSB (takich jak
lsbcc
opakowaniegcc
) i połączysz się z wersją bibliotek LSB, utworzysz aplikację przenośną.źródło
qemu
niemu możesz nawet uruchamiać programy skompilowane dla różnych architektur ((nie wysoka wydajność, ale możesz je uruchomić)apt-get install
kilka pakietów.Może.
Do rzeczy, które go psują, należą.
Jeśli unikniesz używania szybko zmieniających się bibliotek, unikniesz zmian w architekturze i wykorzystasz najstarszą dystrybucję, na którą chcesz kierować, masz dużą szansę na zrobienie jednego pliku binarnego na wielu dystrybucjach.
źródło
Oprócz niektórych rzeczy wymienionych wcześniej, wprowadzono pewne zmiany w formacie pliku wykonywalnego. W przeważającej części Linux używa ELF, ale starsze wersje używały a.out lub COFF.
Początek wikihole:
https://en.wikipedia.org/wiki/Comparison_of_executable_file_formats
Może istnieć sposób, aby starsze wersje uruchamiały nowsze formaty, ale ja osobiście nigdy się tym nie zajmowałem.
źródło