Instalator VPN mówi 32-bit: sprawdź nagłówek ELF
Rozmiar słowa nie identyfikuje zestawu instrukcji. Procedura odczytu klasy, maszyny i interpretera, a potem żądania builda dopasowanego do hosta.
Strona pobierania mówi 32-bit, plik ląduje na Raspberry Pi, a instalator zawodzi z komunikatem, który nie nazywa żadnej użytecznej części problemu. Dwa zupełnie różne pliki wykonywalne oba liczą się jako 32-bit w sensie ELF, a tylko jeden z nich może działać na tej płytce. Pole klasy jest jednym faktem, pole maszyny drugim, a nazwa pliku wiarygodnie nie mówi ani jednego, ani drugiego. Przeczytaj nagłówek, zanim zmienisz bibliotekę albo spróbujesz innego serwera lustrzanego.
Klasa mówi o rozmiarze słowa, maszyna o zestawie instrukcji
Dokumentacja ELF w Linuksie definiuje pole klasy i pole maszyny w nagłówku pliku, a oba odpowiadają na różne pytania. Klasa mówi, czy plik używa formy 32-bit czy 64-bit; maszyna mówi, dla jakiego zestawu instrukcji go zbudowano. Build x86 32-bit i build ARM 32-bit zgadzają się co do pierwszego, a różnią co do drugiego. Tak etykieta obiecująca właściwy rozmiar słowa wciąż dostarcza kod, którego procesor nie potrafi zdekodować. Trzecią wartą odczytania wartością jest żądany interpreter, który nazywają nagłówki programu, gdy plik binarny go deklaruje.
readelf -h -- ./vendor-vpn
readelf -l -- ./vendor-vpn
readelf -d -- ./vendor-vpn | grep NEEDED
Pierwsze polecenie wypisuje nagłówek, drugie wypisuje nagłówki programu, jeśli plik je ma, a trzecie wymienia obiekty współdzielone, których będzie potrzebował loader. Zapisz klasę, maszynę i interpreter razem, bo te trzy wartości to całe zagadnienie zgodności na tej warstwie. Żadna z nich nie pojawia się na przycisku pobierania, dlatego plik trzeba obejrzeć, a nie opisać.
Jakiego interpretera żąda od jądra
Plik wykonywalny, który deklaruje interpreter, żąda od jądra jednej konkretnej ścieżki loadera. Gdy tego loadera nie ma, uruchomienie zawodzi, choć wskazany plik leży właśnie tam. To jest moment, w którym ludzie uznają, że pobranie jest uszkodzone, podczas gdy prawdziwą odpowiedzią jest loader, którego dystrybucja nie dostarcza.
Dopasuj loader do systemu, który naprawdę uruchamiasz: wydania systemu, architektury przestrzeni użytkownika i pakietu dostarczającego loader. Jądro 64-bit może uruchomić 32-bitową przestrzeń użytkownika tylko wtedy, gdy ta przestrzeń i jej loader są zainstalowane, więc możliwości procesora nie są czynnikiem decydującym. Jedno krótkie sprawdzenie nazywa interpreter w jednej linii i zajmuje mniej czasu niż zgłoszenie do wsparcia. Skrypt powłoki zawodzi jeszcze inaczej, bo jego interpreter jest nazwany w pierwszej linii, a nie w nagłówku ELF, a odczytanie tej linii nic nie kosztuje.
file --brief ./vendor-vpn
Dopasuj pobranie do architektury pakietu
Architektury pakietów dystrybucji kodują to samo rozróżnienie własnym słownictwem i na to słownictwo powołuje się oświadczenie wsparcia dostawcy. Porównaj wartość raportowaną przez menedżer pakietów z wartością udokumentowaną przez dostawcę, zamiast porównywać etykietę marketingową z nazwą pliku. Pary, które sprawiają kłopot, to amd64 wobec arm64 i armhf wobec armel, bo każda para wygląda na liście pobierania na wymienną. Nazwa pliku kończąca się na x64 lub ia32 opisuje cel builda, a nie maszynę, która go przechowuje.
| Label on the download | ELF class | e_machine | Interpreter the kernel wants | How it fails on the wrong host |
|---|---|---|---|---|
| amd64 | ELF64 | x86-64 | /lib64/ld-linux-x86-64.so.2 | Immediate exec failure on an ARM board |
| armhf | ELF32 | ARM | /lib/ld-linux-armhf.so.3 | Loads on a soft-float system, then fails on a float ABI call |
| i386 | ELF32 | Intel 80386 | /lib/ld-linux.so.2 | Exec format error where no 32-bit loader package exists |
Każdy wiersz to przypadek, w którym rozmiar słowa wygląda poprawnie, a kod nadal zachowuje się źle, albo w którym startuje i potem zawodzi o warstwę niżej. Czytaj kolumnę interpretera jako twardy wymóg, a nie podpowiedź, bo jądro rozwiązuje tę ścieżkę, zanim wykona się jakikolwiek twój kod. Emulacja to decyzja wdrożeniowa z własnymi skutkami dla wydajności i wsparcia i nigdy nie implikuje jej procesor reklamujący szerszy rozmiar słowa.
dpkg --print-architecture
ENOEXEC i ENOENT to różne błędy
Etap uruchamiania zgłasza jeden z dwóch błędów, a wskazują one na różne problemy. Odczytanie wartości błędu klasyfikuje awarię, zanim uruchomisz cokolwiek innego.
ENOEXEC oznacza, że format nie został rozpoznany: zła architektura, zła klasa albo coś, co wcale nie jest plikiem wykonywalnym. ENOENT przy pliku ELF zwykle oznacza, że brakuje interpretera, choć żądany plik istnieje. Ten sam ENOENT może też oznaczać brak interpretera skryptu, dlatego pierwsza linia skryptu ma znaczenie. Tak czy inaczej komunikat nazywa warstwę, a traktowanie każdego „nie znaleziono“ jako brakującej konfiguracji kieruje śledztwo w złą stronę. Zacytuj wartość błędu w zgłoszeniu, bo lepiej przeżywa opowiadanie niż zdanie wokół niego.
Czego nie robić, gdy plik binarny nie startuje
Przepychanie niewyjaśnionego pliku binarnego przez opakowanie powłoki nie sprawia, że zaczyna działać. Nie uruchamiaj lekkomyślnie sprawdzania zależności na pliku, któremu nie ufasz, bo jego uruchomienie może w pewnych warunkach wykonać kod z pliku binarnego. Nie dodawaj dowiązań symbolicznych loadera, nie podmieniaj bibliotek hosta i nie ściągaj starego archiwum z lustra osoby trzeciej, żeby ominąć komunikat. Każde z tych działań poszerza powierzchnię zmiany, nie dotykając niezgodności, którą nagłówek już nazwał.
Gdy zwracasz się do dostawcy, wyślij wartości, a nie historię: wydanie systemu, architekturę pakietu, ciągi klasy i maszyny, żądany interpreter oraz pierwszą linię błędu. Ten zestaw rozwiązuje pytania do wsparcia szybciej niż zrzut ekranu zawodzącego instalatora. Stare artefakty i zapamiętane wyjścia z forów nie są aktualną macierzą wsparcia, a po wczytaniu natywnego pliku binarnego profil, uwierzytelnianie i routing pozostają osobną pracą.