Linux-Binary läuft nicht: glibc oder musl?
Bei GLIBC_2.34 not found hat der Build-Host die Mindestversion festgelegt. Prüfen Sie die Anforderungen der Binary und wählen Sie eine von vier portablen Lösungen.
Warum eine Linux-Binärdatei auf einem älteren Server nicht ausgeführt wird
Eine portable Linux-Binärdatei ist schwer zu erstellen, weil glibc, die GNU-C-Bibliothek, Kompatibilität nur in eine Richtung zusichert. Eine alte Binärdatei läuft weiterhin mit einer neuen glibc. Eine neue Binärdatei läuft nicht mit einer alten glibc. Das System, auf dem Sie kompilieren, legt die Mindestversion für jedes System fest, auf dem Sie die Binärdatei bereitstellen.
Die Fehlermeldung nennt die exakt benötigte Version:
./mytool: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found (required by ./mytool)Die Datei ist nicht beschädigt, und normalerweise ist auch nichts falsch konfiguriert. Die einzige Ausnahme ist eine LD_LIBRARY_PATH-Einstellung, die den Loader auf eine ältere libc verweist. Der Pfad in der Meldung zeigt, welche libc tatsächlich gelesen wurde. Der dynamische Loader las ein im Inneren der Binärdatei gespeichertes Versions-Tag, suchte dieses Tag in der libc, fand es dort nicht und verweigerte den Programmstart. Die Datei erneut herunterzuladen und chmod +x auszuführen, ändert nichts, weil die Anforderung in der Datei selbst gespeichert ist. Sie müssen entweder ändern, wie die Binärdatei erstellt wird, oder ändern, was Sie ausliefern.
Was die glibc-Symbolversionierung tatsächlich bewirkt
Jede von glibc exportierte Funktion trägt ein Versions-Tag. printf innerhalb von libc.so.6 ist tatsächlich printf@@GLIBC_2.2.5. Wenn glibc das Verhalten oder die ABI (Application Binary Interface) einer Funktion ändert, ersetzt glibc die alte Funktion nicht. Der alte Code bleibt unter dem alten Tag erhalten, und der neue Code kommt unter einem neuen Tag hinzu. Ein einzelnes libc.so.6 enthält dadurch gleichzeitig mehrere Versionen desselben Symbols. Ein Binary aus dem Jahr 2009 findet den Tag aus dem Jahr 2009 weiterhin vor. Deshalb funktioniert die Vorwärtskompatibilität so zuverlässig.
Beim Linken wird festgehalten, welche Version verwendet wurde. Ihr Binary erhält einen Abschnitt .gnu.version_r, der sinngemäß angibt: „Ich benötige GLIBC_2.38 aus libc.so.6.“ Eine ältere libc hatte diesen Tag nie. Deshalb beendet der Loader den Vorgang, bevor main ausgeführt wird. Es gibt kein Fallback. Ein Fallback würde bedeuten, dass das Programm stillschweigend eine andere Funktion erhält als die, gegen die es kompiliert wurde.
Der häufigste Auslöser ist seit 2021 glibc 2.34. Diese Version führte libpthread und libdl in libc zusammen und verschob __libc_start_main, die Funktion, die jedes C-Programm startet, auf den Tag GLIBC_2.34. Ein Hello-World-Programm, das auf einer Distribution mit glibc 2.34 oder neuer kompiliert wurde, benötigt deshalb GLIBC_2.34, obwohl der Quellcode keine moderne Funktion aufruft. Deshalb trat das Problem plötzlich bei Anwendern auf, deren eigener Code seit Jahren unverändert war.
Welche glibc-Version liefert meine Distribution aus?
Die Daten hinter diesem Diagramm
[
{
"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
}
]Das sind die Versionen, die die einzelnen Distributionen in ihren eigenen Paketquellen ausliefern, wie von den Distributionen veröffentlicht und im August 2026 aktuell. CentOS 7 hat das Ende seiner Lebensdauer längst erreicht. Es bleibt in der Liste, weil geerbte Server weiterhin damit betrieben werden. Prüfen Sie jedes System mit ldd --version. Der Befehl gibt die glibc-Version in der ersten Zeile aus. Alternativ können Sie getconf GNU_LIBC_VERSION verwenden.
Lesen Sie diese 9 Zeilen als Stufenfolge. Wenn Sie auf Debian 13 mit glibc 2.41 bauen, läuft das Ergebnis auf keinem älteren System als Debian 13. Wenn Sie auf Ubuntu 20.04 mit glibc 2.31 bauen, läuft derselbe Quellcode auf jeder darüber aufgeführten Zeile, einschließlich des Debian-13-Systems. Der älteste Server, den Sie unterstützen müssen, ist der einzige relevante Build-Host. Die von Ihnen standardisierte Distribution legt daher die ABI-Untergrenze für alles fest, was Sie dafür kompilieren. Diese Entscheidung sollten Sie treffen, bevor die Serverflotte existiert, zusammen mit den anderen Abwägungen bei der Auswahl des Betriebssystems für Ihren VPS.
Wie finde ich die minimale glibc-Version, die ein Binary benötigt?
Lesen Sie die Information aus der Datei aus. Das funktioniert mit jeder Datei, auch mit einem Binary, das Sie von einem Anbieter ohne Build-Dokumentation erhalten haben.
objdump -T ./mytool | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5Die letzte Zeile gibt die Mindestversion an. Wenn objdump fehlt, installieren Sie das Paket binutils. Wenn Sie auf diesem System nichts installieren können, liefert strings -a ./mytool | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5 ein nahezu gleichwertiges Ergebnis, weil die Tags als einfache Zeichenfolgen in der Datei gespeichert sind.
readelf -V ./mytool zeigt dieselbe Anforderung strukturiert an. Suchen Sie nach dem Block mit der Überschrift Version needs section '.gnu.version_r'. Er enthält pro Tag eine Zeile mit Name: GLIBC_x.y und gruppiert die Einträge unter der Bibliothek, die den jeweiligen Tag bereitstellen muss.
Um herauszufinden, welche Funktionsmenge die Mindestversion bestimmt, suchen Sie nach dem Tag: objdump -T ./mytool | grep GLIBC_2.38. Häufig handelt es sich um ein einzelnes Symbol, das Sie unter Umständen vermeiden können. Wenn objdump -T überhaupt keine Ausgabe erzeugt, ist das Binary statisch gelinkt. Es besitzt daher keine dynamische Symboltabelle und keine auszugebende Versionsanforderung.
Was file und readelf -d über eine übergebene Binärdatei aussagen
file zeigt Architektur und Link-Typ in einer Zeile.
file ./mytoolEin gewöhnlicher glibc-Build sieht so aus:
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 strippedEin statischer Build meldet statically linked und nennt keinen Interpreter. Ein musl-Build nennt einen anderen:
ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, ...Der Interpreter ist das entscheidende Feld. Er ist der dynamische Loader, den der Kernel vor Ihrem Programm startet. Dieser genaue Pfad muss auf dem Zielsystem vorhanden sein. /lib/ld-musl-x86_64.so.1 ist auf einem Debian- oder Ubuntu-Server nicht vorhanden, sofern dort niemand musl installiert hat.
readelf -d ./mytool listet die gemeinsam genutzten Bibliotheken auf, die die Binärdatei namentlich benötigt:
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]Jeder Eintrag in NEEDED ist eine zwingende Abhängigkeit, die über den Sonamen aufgelöst wird. libssl.so.3 steht für OpenSSL 3. Daher wird diese Binärdatei auf einem Server nicht geladen, auf dem nur libssl.so.1.1 vorhanden ist. Der Soname ist der Grund dafür: Ein anderer Soname bedeutet eine absichtlich inkompatible ABI. RUNPATH ergänzt Verzeichnisse, die der Loader nach den direkten Abhängigkeiten der Binärdatei durchsucht. Das geschieht nach einer eventuellen Einstellung von LD_LIBRARY_PATH, aber vor dem Systembibliotheks-Cache und den Standardpfaden. $ORIGIN wird zum Verzeichnis erweitert, in dem sich die Binärdatei befindet. So findet ein eigenständiges Bundle seine eigenen Bibliotheken.
Führen Sie ldd nicht für eine Binärdatei aus, der Sie nicht vertrauen. Unter glibc kann ldd Abhängigkeiten auflösen, indem es das Programm unter dem Loader ausführt. Dadurch kann eine schädliche Datei Code ausführen. readelf und objdump lesen nur Bytes. Prüfen Sie vorher, ob es sich bei dem Download um die Datei handelt, die das Projekt tatsächlich veröffentlicht hat. Die Prüfung eines Downloads anhand seiner veröffentlichten Prüfsumme ist der einzige Schritt, der Ihnen mitteilt, wessen Bytes Sie in Händen halten.
Die Fehler, die tatsächlich auftreten, und ihre jeweilige Bedeutung
Der Loader nennt eine GLIBC-Version, die er nicht finden kann. Das Binary wurde gegen eine neuere glibc-Version kompiliert, als auf dem Zielsystem vorhanden ist. Nichts, was Sie auf dem Zielsystem installieren, behebt das sicher. Wählen Sie eine der vier folgenden Optionen.
Die Shell meldet No such file or directory für eine Datei, die eindeutig vorhanden ist. Es fehlt der Interpreter, nicht das Binary. execve liefert ENOENT zurück, wenn der Loader-Pfad im ELF-Header fehlt. Die Shell gibt dann die einzige verfügbare Meldung aus. Führen Sie file aus und lesen Sie den Interpreter-Pfad ab. Ein musl-gelinktes Binary auf einem reinen glibc-Server verursacht genau dieses Symptom.
cannot execute binary file: Exec format error bedeutet normalerweise, dass die Architektur nicht stimmt: ein x86-64-Binary auf einem arm64-Server oder umgekehrt. Es kann auch bedeuten, dass die Datei überhaupt kein gültiges ausführbares Programm ist, beispielsweise wegen eines abgebrochenen oder beschädigten Downloads. Die erste Zeile der Ausgabe von file zeigt, welche Architektur vorliegt oder dass die Datei kein verwendbares ELF-Executable ist.
error while loading shared libraries: libssl.so.3: cannot open shared object file bedeutet, dass eine NEEDED-Bibliothek fehlt oder unter einem anderen Soname vorhanden ist. Installieren Sie das passende Distributionspaket. Die Paketnamen unterscheiden sich je nach Distributionfamilie. Übersetzen Sie sie daher, bevor Sie eine apt-Zeile aus einer README-Datei übernehmen. Die entsprechenden dnf- und apt-Befehle zeigen diese Zuordnung.
Ein unverändertes Segmentation fault aus einem musl-Build, das unter glibc problemlos läuft. Meistens ist die Thread-Stackgröße die Ursache, die unter Option 2 behandelt wird.
Vier Möglichkeiten, eine portable Linux-Binärdatei bereitzustellen
Jede Option hat reale Nachteile. Wählen Sie anhand dessen, was Ihr Programm zur Laufzeit benötigt, und nicht danach, welche Option am saubersten klingt.
Option 1: Auf der ältesten unterstützten Distribution bauen
Die unspektakulärste Antwort und meistens die richtige. Kompilieren Sie innerhalb eines Container-Images der ältesten Distribution, die Sie unterstützen möchten. Der Linker kann nur Tags aufzeichnen, die diese alte glibc tatsächlich enthält. Dadurch wird die Untergrenze auf diese Version gesetzt, während die Binärdatei eine normale dynamische Binärdatei bleibt und das gesamte Verhalten von glibc beibehält.
docker run --rm -v "$PWD:/src" -w /src ubuntu:20.04 sh -c 'apt-get update && apt-get install -y build-essential && make'Die Ausgabe läuft auf glibc 2.31 und allen höheren Versionen. Verlassen Sie sich nicht auf Annahmen, sondern prüfen Sie dies: Führen Sie den objdump -T-Einzeiler von weiter oben für das Ergebnis aus und prüfen Sie, ob der höchste Tag dem erwarteten Wert entspricht.
Der Nachteil ist die Toolchain. Ein altes Basis-Image enthält auch einen alten Compiler. Das führt zu Problemen, wenn Ihr Code einen aktuellen C++-Standard benötigt. Go und Rust umgehen dieses Problem weitgehend, weil ihre Toolchains unabhängig von den Distributionspaketen im Container installiert werden. Für C und C++ können Sie einen neueren Compiler aus dem eigenen Toolchain-Kanal der Distribution hinzufügen oder eines der manylinux-Images verwenden. Diese Images existieren genau dafür, eine veraltete glibc mit einem aktuellen GCC zu kombinieren. Der gebündelte C-Compiler von Zig kann ebenfalls direkt auf eine ausgewählte glibc zielen, wie in zig cc -target x86_64-linux-gnu.2.28. Damit erhalten Sie dieselbe Untergrenze, ohne ein altes Image dauerhaft bereithalten zu müssen.
Option 2: statisches Linken gegen musl
musl ist eine kleine C-Bibliothek, die für statisches Linken entwickelt wurde. Ein statisch gelinktes musl-Binary enthält seine eigene libc, benötigt keinen Interpreter und läuft auf jedem Linux-Kernel der passenden Architektur. So werden die meisten Downloads von Einzeldatei-Tools erstellt.
sudo apt install -y musl-tools
musl-gcc -static -O2 hello.c -o hello
file ./hellofile sollte jetzt statically linked lauten und kein Interpreter-Feld enthalten. Für Rust fügen Sie das Target hinzu und erstellen Sie das Binary dafür:
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-muslGo benötigt nichts davon. Mit CGO_ENABLED=0 go build ist das Ergebnis bereits statisch und linkt überhaupt keine libc.
NSS-Abfragen ändern sich. glibc löst Benutzer und Hostnamen über NSS (Name Service Switch) auf. Dabei lädt sie libnss_*-Module mit dlopen, während das Programm läuft. Eine statisch gelinkte glibc kann das nicht. Der Linker gibt dann diese Warnung aus:
warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linkingmusl umgeht die Warnung, indem es NSS überhaupt nicht implementiert. Die Bibliothek verfügt über einen eigenen Resolver und liest /etc/resolv.conf und /etc/hosts direkt. Auf einem gewöhnlichen VPS ist das problemlos und einfacher. Auf einem Host, auf dem Konten oder Namen über LDAP oder SSSD bereitgestellt werden, sieht Ihr Binary diese Einträge nicht, während alle anderen Programme auf dem System darauf zugreifen können. Der Resolver von musl ist außerdem jünger als der von glibc: Der TCP-Fallback für DNS-Antworten, die zu groß für eine UDP-Antwort sind, wurde in musl 1.2.4 im Jahr 2023 eingeführt. Mit älteren musl-Versionen erstellte Binaries können keine großen DNS-Einträge abfragen. Ihre Abfragen können außerdem bei rekursiven Nameservern fehlschlagen, die bei einer abgeschnittenen UDP-Antwort keine Teilergebnisse zurückgeben.
dlopen funktioniert nicht. In einem statischen musl-Binary ist dlopen ein Stub, der immer fehlschlägt. Alles, was zur Laufzeit Code lädt, ist damit ausgeschlossen: Plugin-Systeme, PAM-Module, GPU-Treiber und die iconv-Zeichensatzmodule von glibc. Wenn Ihr Programm dlopen benötigt, kommt statisches Linken nicht infrage. Sie benötigen dann eine der anderen drei Lösungen.
Sicherheitsupdates werden zu Ihrer Aufgabe. Ein dynamisch gelinktes Binary übernimmt eine Korrektur in der libc, sobald der Server sein Paket-Upgrade ausführt. Ein statisches Binary tut das nie. Wenn ein CVE-Eintrag (Common Vulnerabilities and Exposures) für Ihre libc oder für ein von Ihnen gebündeltes statisches OpenSSL veröffentlicht wird, müssen Sie das Binary neu erstellen und verteilen. Jede bereits bereitgestellte Kopie bleibt verwundbar, bis jemand die Datei ersetzt. Dokumentieren Sie, was Sie gelinkt haben. Auf dem Zielsystem kann sonst niemand erkennen, dass Ihr Einzeldatei-Binary eine zwei Jahre alte Bibliothek enthält.
Die Lizenzbedingungen ändern sich. glibc steht unter der LGPL. Statisches Linken löst die Relinking-Pflicht der LGPL aus: Sie müssen den Empfängern alles bereitstellen, was sie benötigen, um Ihr Programm gegen eine andere glibc neu zu linken. musl steht unter der MIT-Lizenz und enthält keine solche Bedingung. Das ist der Hauptgrund, warum Projekte für Einzeldatei-Binaries musl statt statischer glibc verwenden.
Zwei Laufzeitunterschiede verursachen schwer verständliche Abstürze. Der Standard-Thread-Stack von musl ist 128 KiB groß. glibc übernimmt den Standardwert aus dem Stack-Limit, mit dem das Programm gestartet wurde. Dieser liegt normalerweise zwischen 2 und 10 MB. Wenn das Limit unbegrenzt ist, verwendet glibc auf den meisten Architekturen 2 MB. Code, der einen großen Puffer auf dem Thread-Stack anlegt, kann unter musl daher ohne Meldung und ohne Logeintrag mit einem Segmentation Fault abbrechen. Legen Sie die Größe mit pthread_attr_setstacksize explizit fest oder verschieben Sie den Puffer auf den Heap. Der Allocator von musl ist außerdem auf kleine Größe und vorhersehbares Verhalten ausgelegt, nicht auf viele Threads, die gleichzeitig Speicher reservieren. Programme mit vielen speicherintensiven Threads können daher messbar langsamer laufen. Benchmarken Sie Ihre eigene Arbeitslast, statt sich auf den Ruf einer der beiden Bibliotheken zu verlassen.
Option 3: Loader und Bibliotheken bündeln
Stellen Sie die Bibliotheken zusammen mit der passenden Loader-Datei neben der Binärdatei bereit. Starten Sie das Programm anschließend über diesen Loader.
./ld-linux-x86-64.so.2 --library-path ./lib ./mytoolDamit die Einstellung dauerhaft gilt, schreiben Sie die Pfade mit patchelf in die Datei:
patchelf --set-interpreter "$PWD/lib/ld-linux-x86-64.so.2" --set-rpath '$ORIGIN/lib' ./mytoolEine Regel entscheidet darüber, ob dies funktioniert: Der Loader und libc.so.6 müssen aus demselben glibc-Build stammen. Sie bilden ein zusammengehöriges Paar. Wenn Sie den Loader des Hosts mit einer gebündelten libc mischen, stürzt das Programm beim Start ab, statt eine verständliche Fehlermeldung auszugeben. Bündeln Sie beide Komponenten oder keine von ihnen.
AppImage verpackt dieses Verfahren. Die Nutzdaten liegen in einem squashfs-Image, das ein kleines Runtime-Programm einbindet. Eine wichtige Eigenschaft wird häufig übersehen: Die meisten AppImages verwenden glibc vom Host, statt glibc mitzuliefern. Ein auf einer aktuellen Distribution auf diese Weise erstelltes AppImage schlägt daher auf einem alten Server weiterhin mit demselben Versionsfehler fehl. Die Best-Practice-Empfehlung von AppImage lautet, auf einer Basis zu bauen, die nicht neuer als das älteste unterstützte System ist. Ein solches AppImage ist daher ein Bereitstellungsformat auf Basis von Option 1 und kein Ersatz dafür. Sie können glibc ebenfalls bündeln: appimage-builder und go-appimage können glibc in das AppImage aufnehmen. Ein auf diese Weise erstelltes AppImage kann auf Distributionen laufen, die älter oder neuer als das Build-System sind. Der Nachteil sind eine größere Datei und Bibliotheken wie libssl, die im Bundle eingefroren sind.
Auf einem Headless-Server benötigt AppImage FUSE (filesystem in userspace), um seine Nutzdaten einzubinden. Ein minimales VPS-Image enthält FUSE häufig nicht. Mit einem älteren Runtime-Programm erstellte AppImages benötigen auf dem Host die FUSE-2-Bibliothek. Unter Debian und Ubuntu wird sie als libfuse2 paketiert und in Ubuntu 24.04 in libfuse2t64 umbenannt. Fehlt sie, werden sie mit AppImages require FUSE to run. beendet. Neuere AppImages unterstützen FUSE 3 bereits integriert und benötigen libfuse nicht mehr auf dem Host. Mit ./App.AppImage --appimage-extract-and-run können Sie das Einbinden vollständig überspringen. Dabei werden die Nutzdaten in ein temporäres Verzeichnis entpackt und von dort ausgeführt.
Option 4: Container-Image bereitstellen
Verschieben Sie den gesamten Userland zusammen mit dem Programm. Das Image enthält seine eigene libc. Deshalb ist die glibc des Hosts nicht mehr relevant. Nur der Kernel und die Architektur müssen kompatibel sein. Dies ist die unkomplizierteste Option. Sie beseitigt die gesamte Problemklasse. Deshalb wird so viel Serversoftware auf diese Weise verteilt. Docker auf einem VPS ausführen ist der übliche Weg, das Image zu verwenden.
Der Kernel des Hosts setzt weiterhin eine Grenze. Neuere glibc-Versionen verwenden neuere Systemaufrufe. Ein alter Container-Host kann diese blockieren: glibc 2.34 und später verwenden clone3. Ältere Standard-seccomp-Profile lehnen diesen Systemaufruf ab. Das äußert sich durch einen sofortigen Fehler mit Operation not permitted, ohne dass glibc irgendwo erwähnt wird. Eine Aktualisierung der Container-Laufzeitumgebung auf dem Host behebt das Problem. Die Blockierung befindet sich im Syscall-Filter der Laufzeitumgebung und nicht im Kernel.
Die Kosten sind üblich. Auf dem Zielsystem werden eine Container-Laufzeitumgebung und die erforderlichen Berechtigungen benötigt. Der Download wächst von einer Datei auf mehrere Dutzend oder mehrere hundert Megabyte. Sie sind nun für ein Basis-Image und dessen Patch-Zeitplan verantwortlich. Damit kehrt die Sicherheitsarbeit, die Sie in Option 2 vermieden haben, in Form von Image-Neuerstellungen zurück. Für einen dauerhaft laufenden Dienst ist das ein vertretbarer Kompromiss. Für ein Kommandozeilenprogramm, das jemand einmal ausführt, ist es das nicht.
Welche Option sollten Sie wählen?
- Ein internes Tool für Server, die Sie kontrollieren und auf denen dieselbe Distribution läuft: Erstellen Sie das Programm dynamisch auf dieser Distribution und belassen Sie es dabei.
- Eine einzelne Datei, die Unbekannte herunterladen und ausführen: Verwenden Sie statisches musl, wenn das Programm weder
dlopennoch NSS-gestützte Lookups benötigt. - Ein Programm mit Plugins oder GPU-Zugriff: Bleiben Sie bei einer dynamischen Erstellung, verwenden Sie eine alte Basis und nutzen Sie Option 3 für die Auslieferung.
- Ein dauerhaft laufender Dienst auf einem Rechner, auf dem bereits eine Laufzeitumgebung vorhanden ist: Liefern Sie das Image aus.
Unabhängig von Ihrer Entscheidung sollten Sie sie dokumentieren und überprüfen. Die glibc des Build-Hosts ist nun Bestandteil Ihres Release-Prozesses. Wird eine Build-Maschine von einem LTS-Release auf das nächste aktualisiert, steigt die Mindestanforderung unbemerkt. Dadurch fallen Benutzer aus, bei denen das Programm im letzten Monat noch funktioniert hat. Pinnen Sie das Build-Image anhand des Tags. Prüfen Sie außerdem den höchsten GLIBC_-Tag in der Ausgabe als Build-Schritt. So schlägt die Prüfung in Ihrer Pipeline fehl und nicht erst im Terminal eines anderen Benutzers.
FAQ
Warum erhalte ich auf meinem Server „version GLIBC_2.38 not found“?
Das Binary wurde auf einem Rechner mit einer neueren glibc-Version kompiliert, als sie auf Ihrem Server vorhanden ist. Die Symbolversionierung von glibc bietet nur Vorwärtskompatibilität: Alte Binaries laufen auf neuer glibc, neue Binaries jedoch nicht auf alter glibc, weil der Loader exakt den im File gespeicherten Versions-Tag benötigt und eine ältere libc diesen Tag nie hatte. Nichts, was Sie auf dem Server installieren, behebt das sicher. Erstellen Sie das Binary auf einer älteren Basis neu, stellen Sie einen statischen musl-Build bereit, bündeln Sie die Libraries mit dem passenden Loader oder verwenden Sie ein Container-Image.
Wie finde ich heraus, welche glibc-Version ein Binary benötigt?
Lesen Sie die Versions-Tags mit objdump -T ./mytool | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -5 aus dem File aus. Die letzte Zeile nennt die niedrigste glibc-Version, die das Binary laden kann. readelf -V ./mytool zeigt dieselben Anforderungen im Abschnitt .gnu.version_r, gruppiert nach der Library, die sie bereitstellen muss. Wenn objdump -T keine Ausgabe erzeugt, ist das Binary statisch gelinkt und hat überhaupt keine glibc-Anforderung. Vergleichen Sie das Ergebnis mit der Version auf Ihrem Server, die ldd --version ausgibt.
Ist ein musl-Binary langsamer als ein glibc-Binary?
Das hängt von der Auslastung ab. Die zuverlässige Antwort liefert eine Messung. Ein statisches musl-Binary startet schneller, weil kein Loader ausgeführt werden muss und beim Exec keine Relokationsarbeit anfällt. Dafür ist der Allocator von musl auf geringe Größe und vorhersehbares Verhalten ausgelegt, nicht auf viele Threads, die gleichzeitig Speicher reservieren. Außerdem enthält musl weniger manuell optimierte Routinen für Strings und Speicher als glibc. Threaded-Programme mit vielen Speicherreservierungen oder umfangreicher String-Verarbeitung können daher spürbar langsamer sein. Benchmarken Sie Ihr eigenes Programm auf Ihrem eigenen Server, bevor Sie eine der beiden Aussagen übernehmen.
Warum meldet bash „No such file or directory“, obwohl ein File vorhanden ist?
Das fehlende File ist der dynamische Loader, nicht Ihr Binary. Der Kernel liest den Interpreter-Pfad aus dem ELF-Header und gibt ENOENT zurück, wenn dieser Pfad nicht vorhanden ist. Die Shell meldet das mit der einzigen Fehlermeldung, die ihr zur Verfügung steht. Führen Sie file ./mytool aus und lesen Sie das Feld interpreter. Wenn dort /lib/ld-musl-x86_64.so.1 auf einem Debian- oder Ubuntu-Server steht, verwenden Sie ein musl-gelinktes Binary auf einem glibc-System. Installieren Sie die musl-Runtime der Distribution, auf Debian und Ubuntu das Paket musl, oder verwenden Sie stattdessen einen glibc-Build oder einen statischen Build des Programms.
Kann ich libc.so.6 von einem neueren Server kopieren, um das Problem zu beheben?
Nein. libc.so.6 und ld-linux-x86-64.so.2 bilden ein zusammengehöriges Paar aus demselben glibc-Build. Jedes dynamisch gelinkte glibc-Programm auf dem Rechner verwendet diese Dateien. Wenn Sie die Systemkopie überschreiben, kann der Server daher fast keine Programme mehr ausführen, auch nicht die Tools, die Sie zum Rückgängigmachen der Änderung benötigen. Wenn Sie ein einzelnes neueres Binary auf einem alten Host ausführen müssen, entpacken Sie die neuere glibc in ein privates Verzeichnis. Starten Sie das Programm anschließend mit seinem eigenen Loader über --library-path. Das betrifft nur diesen Prozess. Ein Neubau gegen die ältere glibc bleibt jedoch die Lösung, die Sie nicht sechs Monate später überrascht.