Dlaczego plik binarny Linux nie działa: glibc vs musl
Błąd GLIBC_2.34 not found oznacza zbyt wysoką wersję biblioteki na hoście budującym. Poznaj cztery metody tworzenia przenośnych plików binarnych i uniknij problemów z wersjonowaniem.
Dlaczego plik binarny Linux nie uruchamia się na starszym serwerze
Stworzenie przenośnego pliku binarnego dla systemu Linux jest trudne, ponieważ glibc, biblioteka GNU C, gwarantuje kompatybilność tylko w jednym kierunku. Stary plik binarny działa z nową biblioteką glibc. Nowy plik binarny nie działa ze starą biblioteką glibc. Maszyna, na której przeprowadzana jest kompilacja, wyznacza minimalne wymagania dla każdej maszyny docelowej.
Komunikat o błędzie wskazuje dokładną wersję, której wymagał system:
./mytool: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found (required by ./mytool)Plik nie jest uszkodzony, a konfiguracja jest poprawna. Dynamiczny loader odczytał znacznik wersji zapisany wewnątrz pliku binarnego, wyszukał go w systemowej bibliotece libc, nie znalazł go i odmówił uruchomienia programu. Ponowne pobranie pliku oraz wykonanie chmod +x nic nie zmieni, ponieważ wymaganie jest zapisane w samym pliku. Należy zmienić sposób budowania pliku binarnego lub zmienić wersję dostarczanego oprogramowania.
Na czym polega wersjonowanie symboli w glibc
Każda funkcja eksportowana przez glibc posiada znacznik wersji. printf wewnątrz libc.so.6 to w rzeczywistości printf@@GLIBC_2.2.5. Gdy glibc zmienia zachowanie lub ABI (application binary interface) funkcji, nie zastępuje starej wersji. Zachowuje stary kod pod starym znacznikiem i dodaje nowy kod pod nowym znacznikiem, dzięki czemu pojedyncza biblioteka libc.so.6 przechowuje jednocześnie kilka wersji tego samego symbolu. Plik binarny z 2009 roku odnajduje znacznik z 2009 roku, co zapewnia wysoką kompatybilność wsteczną.
Etap linkowania rejestruje użyte zależności. Plik binarny otrzymuje sekcję .gnu.version_r, która w praktyce oznacza: „wymagam GLIBC_2.38 z libc.so.6”. Starsza wersja libc nie posiadała tego znacznika, więc loader przerywa działanie przed uruchomieniem main. Mechanizm awaryjny nie istnieje, ponieważ oznaczałby ciche podstawienie funkcji innej niż ta, z którą skompilowano program.
Najczęstszą przyczyną problemów od 2021 roku jest glibc 2.34. To wydanie scaliło libpthread oraz libdl w libc i przeniosło __libc_start_main, czyli funkcję inicjującą każdy program w języku C, do znacznika GLIBC_2.34. Program typu hello-world skompilowany w dowolnej dystrybucji z glibc 2.34 lub nowszym wymaga zatem GLIBC_2.34, nawet jeśli kod źródłowy nie korzysta z żadnych nowoczesnych funkcji. Dlatego problem pojawił się nagle u użytkowników, których własny kod nie zmieniał się od lat.
Która wersja glibc jest dostarczana z moją dystrybucją?
The data behind this chart
[
{
"distro": "CentOS 7",
"glibc": 2.17
},
{
"distro": "RHEL 8",
"glibc": 2.28
},
{
"distro": "Ubuntu 20.04",
"glibc": 2.31
},
{
"distro": "Debian 11",
"glibc": 2.31
},
{
"distro": "RHEL 9",
"glibc": 2.34
},
{
"distro": "Ubuntu 22.04",
"glibc": 2.35
},
{
"distro": "Debian 12",
"glibc": 2.36
},
{
"distro": "Ubuntu 24.04",
"glibc": 2.39
},
{
"distro": "Debian 13",
"glibc": 2.41
}
]Są to wersje dostarczane przez poszczególne dystrybucje w ich własnych repozytoriach, zgodnie z informacjami publikowanymi przez twórców dystrybucji, aktualnymi na sierpień 2026 roku. Wsparcie dla CentOS 7 dawno wygasło, jednak pozostaje on na liście, ponieważ nadal działają na nim odziedziczone serwery. Sprawdź dowolny posiadany system za pomocą ldd --version, co wyświetli wersję glibc w pierwszej linii, lub za pomocą getconf GNU_LIBC_VERSION.
Traktuj te 9 wierszy jak drabinę. Kompilacja na Debian 13 z glibc 2.41 oznacza, że wynikowy plik nie zadziała na systemach starszych niż Debian 13. Kompilacja na Ubuntu 20.04 z glibc 2.31 sprawia, że ten sam kod źródłowy zadziała w każdym wierszu powyżej tej linii, włącznie z systemem Debian 13. Najstarszy serwer, który musisz wspierać, jest jedynym istotnym hostem kompilacji, więc dystrybucja, na której się standaryzujesz, wyznacza minimalny poziom ABI dla wszystkiego, co dla niej kompilujesz. Warto podjąć tę decyzję, zanim flota serwerów zacznie istnieć, biorąc pod uwagę inne kompromisy opisane w wyborze systemu operacyjnego dla swojego VPS.
Jak sprawdzić minimalną wersję glibc wymaganą przez plik binarny?
Należy odczytać tę informację bezpośrednio z pliku. Metoda ta działa w każdym przypadku, w tym dla plików binarnych dostarczonych przez zewnętrznych dostawców bez dołączonej dokumentacji budowania.
objdump -T ./mytool | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5Ostatnia linia wskazuje na minimalną wymaganą wersję. Jeśli objdump jest niedostępne, należy zainstalować pakiet binutils. Jeśli instalacja oprogramowania na danej maszynie jest niemożliwa, polecenie strings -a ./mytool | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5 pozwoli uzyskać przybliżone informacje, ponieważ znaczniki są przechowywane wewnątrz pliku jako zwykłe ciągi znaków.
Polecenie readelf -V ./mytool przedstawia te same wymagania w ustrukturyzowany sposób. Należy szukać bloku nagłówkowego Version needs section '.gnu.version_r'. Zawiera on jedną linię Name: GLIBC_x.y dla każdego znacznika, pogrupowaną według biblioteki, która musi go dostarczyć.
Aby ustalić, która funkcja narzuca minimalną wersję, należy użyć polecenia grep dla danego znacznika: objdump -T ./mytool | grep GLIBC_2.38. Często jest to pojedynczy symbol, którego czasami można uniknąć. Jeśli objdump -T nie zwraca żadnych danych, oznacza to, że plik binarny jest skonsolidowany statycznie, nie posiada dynamicznej tablicy symboli i nie wymaga żadnej konkretnej wersji biblioteki.
Co file oraz readelf -d mówią o otrzymanym pliku binarnym
file podaje architekturę oraz typ linkowania w jednej linii.
file ./mytoolStandardowa kompilacja glibc wygląda następująco:
ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=..., for GNU/Linux 3.2.0, not strippedKompilacja statyczna zawiera informację statically linked i nie wskazuje interpretera. Kompilacja musl wskazuje inny:
ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, ...Interpreter jest kluczowym polem. Jest to dynamiczny loader, który jądro uruchamia przed właściwym programem, a wskazana ścieżka musi istnieć na docelowej maszynie. /lib/ld-musl-x86_64.so.1 nie występuje na serwerze Debian lub Ubuntu, chyba że zainstalowano tam musl.
readelf -d ./mytool wymienia biblioteki współdzielone wymagane przez plik binarny, według nazw:
Dynamic section at offset 0x2d58 contains 27 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libssl.so.3]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN/../lib]Każdy wpis NEEDED stanowi twardy wymóg, dopasowywany na podstawie soname. libssl.so.3 to OpenSSL 3, więc plik binarny nie uruchomi się na serwerze posiadającym tylko libssl.so.1.1, a przyczyną jest właśnie soname: inna nazwa soname oznacza celowo niekompatybilne ABI. RUNPATH wskazuje loaderowi, gdzie szukać w pierwszej kolejności, a $ORIGIN rozwija się do katalogu zawierającego plik binarny, co pozwala samodzielnym pakietom na odnalezienie własnych bibliotek.
Nie należy uruchamiać ldd na pliku binarnym, do którego nie ma się zaufania. W przypadku glibc, ldd może rozwiązywać zależności poprzez uruchomienie programu pod kontrolą loadera, co pozwala wrogiemu plikowi na wykonanie kodu. readelf oraz objdump jedynie odczytują bajty. Przed wykonaniem tych czynności należy potwierdzić, że pobrany plik jest tym, który faktycznie opublikował autor projektu, ponieważ weryfikacja pobranego pliku za pomocą sumy kontrolnej jest jedynym krokiem pozwalającym ustalić, czyje bajty faktycznie posiadasz.
Rzeczywiste błędy i ich znaczenie
Program ładujący wskazuje wersję GLIBC, której nie może odnaleźć. Plik binarny został skompilowany z użyciem nowszej wersji glibc niż ta dostępna w systemie docelowym. Żadna instalacja w systemie docelowym nie rozwiąże tego problemu w bezpieczny sposób. Należy wybrać jedną z czterech poniższych opcji.
Powłoka zgłasza No such file or directory dla pliku, który jest widoczny. Brakującym elementem jest interpreter, a nie sam plik binarny. execve zwraca ENOENT, gdy ścieżka do programu ładującego w nagłówku ELF jest nieobecna, a powłoka wyświetla jedyny komunikat, jakim dysponuje. Należy uruchomić file i odczytać ścieżkę interpretera. Plik binarny skompilowany z musl na serwerze z samym glibc wykazuje dokładnie ten objaw.
cannot execute binary file: Exec format error oznacza nieprawidłową architekturę: plik binarny x86-64 na serwerze arm64 lub odwrotnie. Pierwsza linia danych wyjściowych file informuje, z jakim plikiem mamy do czynienia.
error while loading shared libraries: libssl.so.3: cannot open shared object file oznacza, że biblioteka NEEDED jest nieobecna lub występuje pod inną nazwą soname. Należy zainstalować odpowiedni pakiet dystrybucyjny. Nazwy pakietów różnią się między rodzinami systemów, dlatego przed skopiowaniem linii apt z pliku README należy dokonać tłumaczenia, a odpowiedniki poleceń dnf i apt zawierają zestawienie tych mapowań.
Puste Segmentation fault w przypadku kompilacji z musl, która działa poprawnie w środowisku glibc. Zazwyczaj dotyczy to rozmiaru stosu wątku, co opisano w opcji 2.
Cztery sposoby dystrybucji przenośnego pliku binarnego Linux
Każde rozwiązanie wiąże się z określonym kosztem. Wybór należy uzależnić od sposobu działania programu w czasie wykonywania, a nie od estetyki wybranego rozwiązania.
Opcja 1: budowanie na najstarszej wspieranej dystrybucji
Jest to rozwiązanie najmniej skomplikowane i zazwyczaj najwłaściwsze. Kompilację należy przeprowadzić wewnątrz obrazu kontenera najstarszej dystrybucji, której wsparcie jest deklarowane. Konsolidator (linker) może zapisać tylko te znaczniki, które faktycznie posiada używana wersja glibc, dzięki czemu minimalna wymagana wersja biblioteki zostaje obniżona, a plik binarny pozostaje standardowym plikiem dynamicznym z zachowaniem pełnej funkcjonalności glibc.
docker run --rm -v "$PWD:/src" -w /src ubuntu:20.04 sh -c 'apt-get update && apt-get install -y build-essential && make'Wynikowy plik binarny działa na glibc 2.31 oraz wszystkich nowszych wersjach. Zamiast zakładać poprawność, należy ją zweryfikować: należy uruchomić wspomniane wcześniej polecenie objdump -T dla uzyskanego pliku i sprawdzić, czy najwyższy wykryty znacznik jest zgodny z oczekiwaniami.
Koszt tego rozwiązania stanowi łańcuch narzędzi (toolchain). Stary obraz bazowy zawiera również stary kompilator, co stanowi problem, gdy kod wymaga nowszego standardu C++. Języki Go oraz Rust w dużej mierze unikają tego problemu, ponieważ ich łańcuchy narzędzi instalują się w kontenerze niezależnie od pakietów dystrybucji. W przypadku C i C++ można dodać nowszy kompilator z kanału narzędziowego danej dystrybucji lub skorzystać z obrazów manylinux, które powstały właśnie w celu połączenia starej wersji glibc z aktualnym GCC. Dołączony do Zig kompilator C może również bezpośrednio celować w wybraną wersję glibc, jak pokazano w zig cc -target x86_64-linux-gnu.2.28, co pozwala uzyskać ten sam efekt bez konieczności utrzymywania starego obrazu.
Opcja 2: statyczne linkowanie z musl
musl to niewielka biblioteka C zaprojektowana z myślą o statycznym linkowaniu. Statyczny plik binarny musl zawiera własną bibliotekę libc, nie wymaga interpretera i działa na każdym jądrze Linux o odpowiedniej architekturze. W ten sposób tworzona jest większość narzędzi dystrybuowanych jako pojedyncze pliki.
sudo apt install -y musl-tools
musl-gcc -static -O2 hello.c -o hello
file ./hellofile powinno teraz zwracać statically linked, bez pola interpretera. W przypadku Rust należy dodać odpowiedni target i zbudować projekt z jego użyciem:
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-muslGo nie wymaga żadnych dodatkowych kroków. Dzięki CGO_ENABLED=0 go build wynikowy plik jest już statyczny i nie linkuje żadnej biblioteki libc.
Wyszukiwanie NSS ulega zmianie. glibc rozwiązuje nazwy użytkowników i hostów poprzez NSS (name service switch), który w trakcie działania programu ładuje moduły libnss_* za pomocą dlopen. Statycznie zlinkowana biblioteka glibc nie może tego wykonać, o czym linker ostrzega w następujący sposób:
warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linkingmusl omija to ostrzeżenie, ponieważ w ogóle nie implementuje NSS. Posiada własny mechanizm rozwiązywania nazw i odczytuje pliki /etc/resolv.conf oraz /etc/hosts bezpośrednio. Na typowym serwerze VPS jest to rozwiązanie poprawne i prostsze. Na hoście, gdzie konta lub nazwy pochodzą z LDAP lub SSSD, plik binarny nie będzie ich widział, podczas gdy wszystkie inne programy w systemie będą miały do nich dostęp. Mechanizm rozwiązywania nazw w musl jest również młodszy niż w glibc: obsługa fallbacku TCP dla odpowiedzi DNS większych niż 512 bajtów pojawiła się w musl 1.2.4 w 2023 roku, więc kompilacje z użyciem starszych wersji musl ucinają duże odpowiedzi.
dlopen nie działa. W statycznym pliku binarnym musl funkcja dlopen jest jedynie atrapą, która zawsze kończy się niepowodzeniem. Wszystko, co wymaga ładowania kodu w czasie wykonywania, jest wykluczone: systemy wtyczek, moduły PAM, sterowniki GPU czy moduły zestawów znaków iconv biblioteki glibc. Jeśli program wymaga dlopen, statyczne linkowanie nie wchodzi w grę i należy wybrać jedno z pozostałych trzech rozwiązań.
Aktualizacje bezpieczeństwa stają się Twoim obowiązkiem. Dynamicznie zlinkowany plik binarny otrzymuje poprawkę libc w momencie, gdy serwer wykonuje aktualizację pakietów. Plik statyczny nigdy jej nie otrzyma. Gdy pojawi się wpis CVE (common vulnerabilities and exposures) dotyczący użytej biblioteki libc lub statycznie dołączonej biblioteki OpenSSL, należy przebudować i ponownie rozprowadzić plik. Każda wdrożona kopia pozostaje podatna na ataki, dopóki ktoś ręcznie nie wymieni pliku. Należy prowadzić rejestr zlinkowanych bibliotek, ponieważ w systemie docelowym nie ma mechanizmu, który poinformowałby administratora, że pojedynczy plik binarny zawiera bibliotekę sprzed dwóch lat.
Licencja ulega zmianie. glibc jest na licencji LGPL, a statyczne linkowanie wyzwala obowiązek relinkowania: należy dostarczyć odbiorcom wszystko, co jest potrzebne do ponownego zlinkowania programu z inną wersją glibc. musl jest na licencji MIT i nie nakłada takich warunków. Jest to główny powód, dla którego projekty dystrybuujące pliki binarne jako pojedyncze pliki wybierają musl zamiast statycznego glibc.
Dwie różnice w czasie wykonywania powodują mylące awarie. Domyślny rozmiar stosu wątku w musl wynosi 128 KiB, w porównaniu do 8 MiB w glibc, więc kod umieszczający duży bufor na stosie wątku spowoduje błąd segmentacji (segfault) bez żadnego komunikatu czy wpisu w dzienniku. Należy jawnie ustawić rozmiar za pomocą pthread_attr_setstacksize lub przenieść bufor na stertę. Alokator musl został również napisany z myślą o małym rozmiarze i przewidywalnym zachowaniu, a nie o obsłudze wielu wątków alokujących pamięć jednocześnie, więc programy wielowątkowe intensywnie korzystające z alokacji mogą działać zauważalnie wolniej. Należy przeprowadzić testy wydajnościowe własnego obciążenia, zamiast polegać na reputacji którejkolwiek z bibliotek.
Opcja 3: dołączenie loadera i bibliotek
Należy umieścić biblioteki obok pliku binarnego wraz z odpowiednim loaderem, a następnie uruchomić program za pomocą tego loadera.
./ld-linux-x86-64.so.2 --library-path ./lib ./mytoolAby wprowadzić to ustawienie na stałe, należy zapisać ścieżki w pliku za pomocą patchelf:
patchelf --set-interpreter "$PWD/lib/ld-linux-x86-64.so.2" --set-rpath '$ORIGIN/lib' ./mytoolO powodzeniu tego rozwiązania decyduje jedna zasada: loader oraz libc.so.6 muszą pochodzić z tej samej kompilacji glibc. Stanowią one dopasowaną parę, a łączenie loadera hosta z dołączoną biblioteką libc powoduje awarię podczas uruchamiania zamiast czytelnego komunikatu o błędzie. Należy dołączyć oba te elementy lub żaden z nich.
AppImage jest realizacją tego wzorca w formie pakietu, w którym ładunek znajduje się w obrazie squashfs, a niewielkie środowisko uruchomieniowe odpowiada za jego montowanie. Posiada on jedną cechę, którą często pomija się w analizach: AppImage nie zawiera w sobie glibc, więc plik AppImage zbudowany na nowoczesnej dystrybucji nadal będzie generował ten sam błąd wersji na starym serwerze. Zalecenia twórców AppImage wskazują na budowanie na najstarszej wspieranej bazie, co czyni ten format sposobem dostarczania oprogramowania nałożonym na opcję 1, a nie jej zamiennikiem.
Na serwerze typu headless AppImage wymaga FUSE (filesystem in userspace) do zamontowania ładunku, a minimalne obrazy VPS często go nie zawierają. Błąd objawia się komunikatem libfuse.so.2. Można całkowicie pominąć montowanie za pomocą ./App.AppImage --appimage-extract-and-run, co powoduje rozpakowanie do katalogu tymczasowego i wykonanie programu z tej lokalizacji.
Opcja 4: dostarczenie obrazu kontenera
Przeniesienie całego środowiska użytkownika wraz z programem. Obraz zawiera własną bibliotekę libc, więc wersja glibc na hoście przestaje mieć znaczenie; zgodne muszą być jedynie jądro systemu oraz architektura. Jest to najmniej wyrafinowana opcja, która eliminuje całą klasę problemów, dlatego tak wiele oprogramowania serwerowego jest dystrybuowane w ten sposób. Uruchamianie Docker na VPS to standardowy sposób korzystania z tego rozwiązania.
Jądro systemu hosta nadal wyznacza ograniczenia. Nowsze wersje glibc korzystają z nowszych wywołań systemowych, które stary host kontenerów może blokować: glibc 2.34 i nowsze używają clone3, co jest odrzucane przez starsze domyślne profile seccomp. Objawem jest natychmiastowa awaria z błędem Operation not permitted, bez żadnej wzmianki o glibc. Aktualizacja środowiska uruchomieniowego kontenerów na hoście rozwiązuje problem, ponieważ blokada znajduje się w filtrze wywołań systemowych środowiska, a nie w samym jądrze.
Koszty są typowe. Cel wymaga środowiska uruchomieniowego kontenerów oraz uprawnień do jego użycia. Rozmiar pobieranego pliku rośnie z jednego pliku do dziesiątek lub setek megabajtów. Od tego momentu użytkownik zarządza obrazem bazowym i harmonogramem jego poprawek, więc praca nad bezpieczeństwem, której uniknięto w opcji 2, powraca w postaci konieczności przebudowy obrazów. W przypadku usługi działającej długoterminowo jest to uczciwa wymiana. W przypadku narzędzia wiersza poleceń uruchamianego jednorazowo – nie.
Którą opcję wybrać?
- Narzędzie wewnętrzne dla kontrolowanych serwerów działających w ramach jednej dystrybucji: zbuduj dynamicznie na tej dystrybucji i na tym poprzestań.
- Pojedynczy plik pobierany i uruchamiany przez nieznanych użytkowników: statyczny musl, jeśli program nie wymaga
dlopeni wyszukiwań opartych na NSS. - Program z wtyczkami lub dostępem do GPU: pozostań przy budowaniu dynamicznym, użyj starej bazy i dostarcz go zgodnie z opcją 3.
- Usługa działająca w tle na maszynie z zainstalowanym środowiskiem uruchomieniowym: dostarcz obraz.
Niezależnie od wyboru, udokumentuj go i zweryfikuj. Wersja glibc na hoście budującym staje się częścią procesu wydawniczego. Aktualizacja maszyny budującej z jednego wydania LTS do kolejnego podnosi wymagania systemowe i powoduje awarie u użytkowników, u których oprogramowanie działało poprawnie w poprzednim miesiącu. Przypnij obraz budujący za pomocą tagu i zaimplementuj w procesie budowania sprawdzenie najwyższego tagu GLIBC_ w pliku wyjściowym. Dzięki temu błąd zostanie wykryty w potoku CI/CD, a nie w terminalu użytkownika końcowego.
FAQ
Dlaczego na serwerze pojawia się błąd "version GLIBC_2.38 not found"?
Plik binarny został skompilowany na maszynie z nowszą wersją glibc niż ta zainstalowana na serwerze. Wersjonowanie symboli w glibc zapewnia kompatybilność tylko w jedną stronę: stare pliki binarne działają z nowym glibc, natomiast nowe pliki binarne nie uruchomią się na starym glibc, ponieważ loader wymaga dokładnego znacznika wersji zapisanego w pliku, którego starsza biblioteka libc nie posiada. Żadna instalacja na serwerze nie naprawi tego w bezpieczny sposób. Należy przebudować aplikację na starszej bazie, dostarczyć statyczną kompilację musl, dołączyć biblioteki wraz z odpowiednim loaderem lub dostarczyć obraz kontenera.
Jak sprawdzić, jakiej wersji glibc wymaga plik binarny?
Odczytaj znaczniki wersji z pliku za pomocą objdump -T ./mytool | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5. Ostatnia linia wskazuje najniższą wersję glibc, która może załadować dany plik. readelf -V ./mytool pokazuje te same wymagania w sekcji .gnu.version_r, pogrupowane według biblioteki, która musi je dostarczyć. Jeśli objdump -T nie zwraca żadnych danych, plik binarny jest statyczny i nie posiada żadnych wymagań względem glibc. Porównaj wynik z wersją glibc zainstalowaną na serwerze, sprawdzając ją za pomocą ldd --version.
Czy plik binarny musl jest wolniejszy od pliku binarnego glibc?
Zależy to od obciążenia, a rzetelną odpowiedzią jest wykonanie pomiarów. Statyczny plik binarny musl uruchamia się szybciej, ponieważ nie ma loadera do uruchomienia ani pracy związanej z relokacją w momencie wywołania exec. Z drugiej strony, alokator musl został stworzony z myślą o małym rozmiarze i przewidywalnym zachowaniu, a nie o obsłudze wielu wątków alokujących pamięć w tym samym czasie. Ponadto musl zawiera mniej zoptymalizowanych ręcznie procedur obsługi ciągów znaków i pamięci niż glibc, więc programy wielowątkowe intensywnie korzystające z alokacji lub operacji na ciągach znaków mogą być zauważalnie wolniejsze. Przed przyjęciem którejkolwiek z tez należy przetestować własny program na własnym serwerze.
Dlaczego bash zwraca "No such file or directory" dla pliku, który istnieje?
Brakującym plikiem jest dynamiczny loader, a nie sam plik binarny. Jądro odczytuje ścieżkę interpretera z nagłówka ELF i zwraca ENOENT, gdy ta ścieżka nie istnieje, a powłoka zgłasza to za pomocą jedynego dostępnego komunikatu. Uruchom file ./mytool i odczytaj pole interpreter. Jeśli na serwerze Debian lub Ubuntu widnieje tam /lib/ld-musl-x86_64.so.1, oznacza to, że używasz pliku binarnego skompilowanego z musl w systemie opartym na glibc; należy pobrać wersję kompatybilną z musl lub użyć kompilacji statycznej.
Czy mogę skopiować libc.so.6 z nowszego serwera, aby to naprawić?
Nie. libc.so.6 oraz ld-linux-x86-64.so.2 stanowią dopasowaną parę z jednej kompilacji glibc i każdy proces w systemie z nich korzysta. Nadpisanie systemowej kopii może sprawić, że serwer nie będzie w stanie uruchomić żadnego procesu, w tym narzędzi potrzebnych do cofnięcia zmian. Jeśli konieczne jest uruchomienie nowszego pliku binarnego na starym hoście, należy rozpakować nowsze glibc do prywatnego katalogu i uruchomić program przez jego własny loader za pomocą --library-path, co wpłynie tylko na ten konkretny proces. Przebudowanie aplikacji w oparciu o starsze glibc pozostaje rozwiązaniem, które nie spowoduje problemów w przyszłości.