Linux hasst static binaries
« | 12 Jun 2022 | »Ich staune immer wieder darüber, wie etwas so “einfaches” wie statisch Abhängigkeiten unter Linux so schwer bzw. unmöglich gemacht werden.
Denn eigentlich sind Programme mit dem Compiler Flag -static
etwas ganz
Tolles … denn sie laufen unter jedem Linux-System (naja, zumindest unter
vielen davon) ohne dass man Bibliotheken nachinstallieren muss.
… doch dann fangen die Probleme an.
Es geht wieder mal um die alte Geschichte:
Ich hätte gerne (so wie unter Windows) ein Programm, dass ich einfach auf ein Zielsystem kopiere und es dort ohne weitere Schritte läuft.
In Windows kompiliert man eine solche EXE gegen die statische Version der C-Runtime und achtet darauf, andere Nicht-OS-Libs ebenso statisch zu linken.
Am Ende hat man eine etwas größere, dafür aber unabhängige EXE, die in jeder Windows-Variante läuft.
In der Linux GCC-Welt gibt es ein solches Compiler-Feature auch, und es heißt
-static
. Mit diesem Flag werden alle sonst dynamisch gelinkten Bibliotheken
wie pthread
, dl
oder libc
statisch in das Binary eingebunden.
Theoretisch kann man ein solches auf Ubuntu erstelltest Binary nehmen und auf OpenSUSE oder Alpine ausführen. Und bei einfachen Hello-World Apps funktioniert das auch wunderbar.
Problem “dl”: Dynamic Linker
dlopen
und dlsym
sind in Linux kein
Betriebssystem-Feature wie es
LoadLibrary
und GetProcAddress
unter Windows sind, sondern werden von der Bibliothek dl
nachgeliefert.
Zusätzlich ist der Startup-Code der C-Bibliothek darauf ausgerichtet,
Datenblöcke beim Start durchzulaufen, die der Compiler/Linker eingefügt hat,
um dynamische Bibliotheken zu öffnen (open
) und in den Prozessraum zu
laden (mmap
).
Mit -static
wird diese Prozedur umgangen und der Compiler sucht von allen
notwendigen Bibliotheken die statische Variante und fügt sie in die finale
Binärdatei ein.
In einem solchen Binary sind “dynamische” Features nicht mehr vorhanden und
auch dlopen
wird zu einer Funktion, die nichts mehr tun kann und mit einer
“not-supported” Meldung abbricht.
Bislang ist es mir nicht gelungen eine allgemeine Verwendung von dlopen()
mit -static
zu finden, die auf allen Plattformen lauffähig wäre.
Meine Debian-Derivate melden dann durch eine Warnung, dass man jetzt nur noch
jene Bibliotheken dynamisch laden kann, die mit der gleichen Version der
glibc
erstellt wurden.
Und Alpine Linux liefert den erwähnten Not-Supported
Errorcode.
Nicht -static
aber -BStatic
Alternativ kann man ein Binary auch -dynamic
kompilieren (der Default-Fall
und das Gegenteil von -static
) und dann aber beim Linken auf statische
Versionen der notwendigen Bibliotheken verweisen.
In CMake ging das mit Linker-Flags wie:
-Wl,-Bstatic -lc
linkt die C-Runtime statisch-Wl,-Bstatic -lpthread
linktpthread
statisch-Wl,-Bstatic -ldl
linktdl
statisch
Hier steckt der Teufel wieder im Detail, was man wo darf und was wo nicht.
Am Ende fand ich aber auch hier keine Variante, die “portable” Binaries für mehrere Linux-Varianten ermöglichte.
Fazit
Ich bleibe also bei “dynamischen” Binaries, weil das GATE Framework eben von
anderen externen Bibliotheken abhängig ist.
Ginge es ausschließlich um C-Runtime Funktionen wäre vielleicht ein Möglichkeit
offen reine statische und portable Binaries zu erzeugen, doch die Kombination
mit dl*
Funktionen schließt diese Option (aus meiner aktuellen Sicht) aus.
Gelernt habe ich allerdings, dass die glibc
einigermaßen kompatibel
geblieben ist und so kann ich ein Binary, das mit GCC5
unter Ubuntu 16.04
gebaut wurde, auch in den Folgeversion (Ubuntu 22.04), Debian und OpenSUSE
ohne weitere Installationen nutzen.
Ich muss daher in Zukunft bei Linux nicht Distributionen unterscheiden,
sondern deren Bindung an lib-c Implementierungen.
Und da kenne ich aktuell eben nur glibc
und musl-libc
.