gcc używa terminów „architektura” w znaczeniu „zestawu instrukcji” konkretnego procesora, a „cel” obejmuje kombinację procesora i architektury, a także inne zmienne, takie jak ABI, libc, endian-ness i więcej (ewentualnie włączając „goły metal”). Typowy kompilator ma ograniczony zestaw kombinacji docelowych (prawdopodobnie jeden ABI, jedna rodzina procesorów, ale prawdopodobnie zarówno 32-, jak i 64-bitowy). Kompilator krzyżowy zwykle oznacza albo kompilator z celem innym niż system, na którym działa, albo taki z wieloma celami lub ABI (patrz także to ).
Czy pliki binarne są przenośne w różnych architekturach procesorów?
Ogólnie nie. Plik binarny w tradycyjnym ujęciu jest rodzimym kodem obiektowym dla konkretnego procesora lub rodziny procesorów. Ale istnieje kilka przypadków, w których mogą one być średnio do bardzo przenośnych:
- jedna architektura jest nadzbiorem innej (zwykle pliki binarne x86 są skierowane na i386 lub i686, a nie najnowsze i najlepsze x86, np.
-march=core2
)
- jedna architektura zapewnia natywną emulację lub tłumaczenie innej (być może słyszałeś o Crusoe ) lub zapewnia kompatybilne koprocesory (np. PS2 )
- system operacyjny i środowisko wykonawcze obsługują wiele ścieżek (np. możliwość uruchamiania 32-bitowych plików binarnych x86 na x86_64) lub płynną pracę VM / JIT (Android przy użyciu Dalvik lub ART )
- istnieje obsługa „grubych” plików binarnych, które zasadniczo zawierają zduplikowany kod dla każdej obsługiwanej architektury
Jeśli uda ci się jakoś rozwiązać ten problem, pojawi się inny przenośny problem binarny niezliczonych wersji bibliotek (glibc patrzę na ciebie). (Większość systemów wbudowanych oszczędza przynajmniej od tego konkretnego problemu).
Jeśli jeszcze tego nie zrobiłeś, teraz jest dobry moment, aby biegać gcc -dumpspecs
i gcc --target-help
zobaczyć, co masz przeciwko.
Pliki binarne tłuszczu mają różne wady , ale wciąż mają potencjalne zastosowania ( EFI ).
Istnieją dwie dalsze kwestie, których brakuje w pozostałych odpowiedziach: ELF i interpreter ELF oraz obsługa jądra Linux dla dowolnych formatów binarnych . Nie będę tutaj wchodził w szczegóły na temat plików binarnych lub kodu bajtowego dla nierealnych procesorów, chociaż można traktować je jako „natywne” i wykonywać pliki binarne kodu bajtowego Pythona w Javie lub skompilowanym , takie pliki binarne są niezależne od architektury sprzętowej (ale zależą w odpowiedniej wersji maszyny wirtualnej, która ostatecznie uruchamia natywny plik binarny).
Każdy współczesny system Linux będzie używał plików binarnych ELF (szczegóły techniczne w tym pliku PDF ), w przypadku dynamicznych plików binarnych ELF jądro jest odpowiedzialne za ładowanie obrazu do pamięci, ale jest to zadanie „interpretera” ustawionego w ELF nagłówki do ciężkiego podnoszenia. Zwykle wymaga to upewnienia się, że wszystkie zależne biblioteki dynamiczne są dostępne (za pomocą sekcji „Dynamiczna”, która zawiera listę bibliotek i niektórych innych struktur, które zawierają wymagane symbole) - ale jest to warstwa pośrednia o prawie ogólnym przeznaczeniu.
$ file /bin/ls
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses \
shared libs), stripped
$ readelf -p .interp /bin/ls
String dump of section '.interp':
[ 0] /lib/ld-linux.so.2
( /lib/ld-linux.so.2
jest również plikiem binarnym ELF, nie ma interpretera i jest rodzimym kodem binarnym).
Problem z ELF polega na tym, że nagłówek w pliku binarnym ( readelf -h /bin/ls
) oznacza go dla konkretnej architektury, klasy (32- lub 64-bitowej), endianowości i ABI („uniwersalne” binarne pliki tłuszczu Apple używają alternatywnego formatu binarnego Mach-O zamiast tego, który rozwiązuje ten problem, pochodzi z NextSTEP). Oznacza to, że plik wykonywalny ELF musi pasować do systemu, w którym ma być uruchomiony. Jednym z nich jest interpreter, może to być dowolny plik wykonywalny (w tym taki, który wyodrębnia lub odwzorowuje specyficzne dla architektury podsekcje oryginalnie binarnego i je wywołuje), ale nadal jesteś ograniczony typami ELF, które twój system pozwoli na uruchomienie . (FreeBSD ma interesujący sposób obsługi plików ELF systemu Linux , brandelf
modyfikuje pole ELF ABI.)
Istnieje (przy użyciu binfmt_misc
) wsparcie dla Mach-O na Linuksie , jest tam przykład, który pokazuje, jak utworzyć i uruchomić gruby (32- i 64-bitowy) plik binarny. Widelce zasobów / ADS , jak pierwotnie zrobiono na Macu, mogą być obejściem, ale żaden natywny system plików Linux nie obsługuje tego.
Mniej więcej to samo dotyczy modułów jądra, .ko
pliki są również ELF (chociaż nie mają ustawionego interpretera). W tym przypadku jest dodatkowa warstwa, która używa wersji jądra ( uname -r
) na ścieżce wyszukiwania, co można teoretycznie zrobić zamiast tego w ELF z wersjonowaniem, ale podejrzewam, że przy pewnym stopniu złożoności i niewielkim zysku.
Jak wspomniano w innym miejscu, Linux nie obsługuje natywnie plików binarnych typu fat, ale istnieje aktywny projekt binarny typu fat-fat: FatELF . Istnieje od lat , nigdy nie był zintegrowany ze standardowym jądrem, częściowo z powodu (wygasłych) obaw patentowych. W tej chwili wymaga to zarówno obsługi jądra, jak i zestawu narzędzi. Nie stosuje tego binfmt_misc
podejścia, ponieważ omija problemy z nagłówkiem ELF i pozwala również na moduły grubego jądra.
- Jeśli mam aplikację skompilowaną do działania na systemie docelowym x86, Linux xyz w wersji xyz, czy mogę po prostu uruchomić ten sam skompilowany plik binarny w innym systemie „docelowym ARM, Linux xyz”?
Nie z ELF, nie pozwoli ci to zrobić.
- Jeśli powyższe nie jest prawdą, jedynym sposobem jest uzyskanie kodu źródłowego aplikacji do przebudowania / ponownej kompilacji za pomocą odpowiedniego zestawu narzędzi „na przykład arm-linux-gnueabi”?
Prostą odpowiedzią jest tak. (Skomplikowane odpowiedzi obejmują emulację, reprezentacje pośrednie, translatory i JIT; z wyjątkiem przypadku „obniżenia” wersji binarnej i686 do używania tylko kodów i386, prawdopodobnie nie są one tutaj interesujące, a poprawki ABI są potencjalnie tak trudne jak tłumaczenie kodu natywnego. )
- Podobnie, jeśli mam ładowalny moduł jądra (sterownik urządzenia), który działa na systemie docelowym x86, Linux x OS w wersji xyz, czy mogę po prostu załadować / użyć tego samego skompilowanego pliku .ko w innym systemie docelowym ARM, Linux OS w wersji xyz? ?
Nie, ELF nie pozwoli ci tego zrobić.
- Jeśli powyższe nie jest prawdą, jedynym sposobem jest uzyskanie kodu źródłowego sterownika w celu przebudowania / ponownej kompilacji za pomocą odpowiedniego zestawu narzędzi „na przykład arm-linux-gnueabi”?
Prostą odpowiedzią jest tak. Wierzę, że FatELF pozwala ci zbudować .ko
wielowątkową architekturę, ale w pewnym momencie trzeba utworzyć wersję binarną dla każdej obsługiwanej architektury. Rzeczy wymagające modułów jądra często pochodzą ze źródła i są budowane zgodnie z wymaganiami, np. Robi to VirtualBox.
To już długa, kłótliwa odpowiedź, jest tylko jeden objazd. Jądro ma już wbudowaną maszynę wirtualną, choć dedykowaną: maszynę wirtualną BPF, która służy do dopasowywania pakietów. Czytelny dla człowieka filtr „host foo, a nie port 22”) jest kompilowany do kodu bajtowego i filtr pakietów jądra go wykonuje . Nowy eBPF jest nie tylko dla pakietów, teoretycznie, że kod VM jest przenośny w dowolnym współczesnym Linuksie i lvvm obsługuje go, ale ze względów bezpieczeństwa prawdopodobnie nie będzie on odpowiedni do niczego innego niż reguły administracyjne.
Teraz, w zależności od tego, jak hojna jesteś z definicją binarnego pliku wykonywalnego, możesz (ab) użyć binfmt_misc
do implementacji obsługi grubego binarnego skryptu powłoki i plików ZIP jako formatu kontenera:
#!/bin/bash
name=$1
prog=${1/*\//} # basename
prog=${prog/.woz/} # remove extension
root=/mnt/tmpfs
root=$(TMPDIR= mktemp -d -p ${root} woz.XXXXXX)
shift # drop argv[0], keep other args
arch=$(uname -m) # i686
uname_s=$(uname -s) # Linux
glibc=$(getconf GNU_LIBC_VERSION) # glibc 2.17
glibc=${glibc// /-} # s/ /-/g
# test that "foo.woz" can unzip, and test "foo" is executable
unzip -tqq "$1" && {
unzip -q -o -j -d ${root} "$1" "${arch}/${uname_s}/${glibc}/*"
test -x ${root}/$prog && (
export LD_LIBRARY_PATH="${root}:${LD_LIBRARY_PATH}"
#readlink -f "${root}/${prog}" # for the curious
exec -a "${name}" "${root}/${prog}" "$@"
)
rc=$?
#rm -rf -- "${root}/${prog}" # for the brave
exit $rc
}
Nazwij to „wozbin” i skonfiguruj go za pomocą czegoś takiego:
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
printf ":%s:%s:%s:%s:%s:%s:%s" \
"woz" "E" "" "woz" "" "/path/to/wozbin" "" > /proc/sys/fs/binfmt_misc/register
Rejestruje .woz
pliki w jądrze, wozbin
zamiast tego wywoływany jest skrypt z pierwszym argumentem ustawionym na ścieżkę wywoływanego .woz
pliku.
Aby uzyskać przenośny (gruby) .woz
plik, po prostu utwórz test.woz
plik ZIP z hierarchią katalogów, aby:
i686/
\- Linux/
\- glibc-2.12/
armv6l/
\- Linux/
\- glibc-2.17/
W każdym katalogu arch / OS / libc (dowolny wybór) umieść specyficzne dla architektury pliki test
binarne i komponenty, takie jak .so
pliki. Po wywołaniu wymagany podkatalog jest rozpakowywany do systemu plików w pamięci tmpfs ( /mnt/tmpfs
tutaj) i wywoływany.