dlopen(NULL, 0)
« | 14 May 2022 | »Da Linux Distributionen hart daran arbeiten zu einander so inkompatibel wie möglich zu sein, suchte ich nach Strategien, wie man ohne Plattform-spezifische Linkereien so viel möglich an Features erhalten kann.
Aber um zu wissen, was einem überhaupt fehlt, muss man erst mal wissen,
was man schon hat … und hier kommt
dlopen
ins Spiel.
Funktionen wie backtrace()
oder getcontext()
setcontext()
und makecontext() swapcontext()
sind zwar nicht 0815, aber auch nicht extrem selten.
Schließlich helfen sie einem den Callstack “zu bearbeiten” oder
Koroutinen oder eine spezielle
Fehlerbehandlung zu implementieren.
Linux-Varianten, die mit der glibc
aufgebaut wurden, integrieren diese
Funktionen quasi in jeden Prozess, weil die glibc
sie beinhaltet.
Wie die “echte” Bibliothek heißt, ist versionsabhängig und grundsätzlich
unbekannt, weil die Standard-Bibliothek automatisch gelinkt wird.
Ganz anders Alpine Linux oder
andere Distros, die auf abgespeckte C-Libraries wie uClibc
, musl-libc
oder
dietlibc
aufbauen.
Hier braucht man z.B.: libucontext.so.1
für die Context-Funktionen.
dlopen() ohne Bibliothek
dlopen
bietet aber eine interessante Zusatzfunktion.
Ruft man es nämlich mit
auf, erhält man Zugang zu allen bereits geladenen globalen Symbolen.
dlsym
kann nun von jeder Funktion, die bereits geladen ist, den
Funktionszeigern ermitteln und zurückgeben.
Und auf diese Weise, kann man also zuerst “prüfen”, ob eine benötigte Funktion schon da ist.
Kein: if alpine, elseif debian …
Brauche ich also eine Funktion, die manchmal schon in der C-Bibliothek
integriert ist und manchmal nicht, prüfe ich das zuerst mit dlopen(NULL)
und nur wenn hier NULL
zurückkommt, lade ich andere Bibliotheken und
versuche mein Glück mit diesen.
Früher musste ich unseelige Spielereien wie
bemühen und den Code per CMake mit
Hilfs-Makros anreichern.
Heute kann ich eine Array von Bibliotheksnamen anlegen und diese dann per
Schleife durchlaufen lassen, wenn dlopen(NULL)
eben nicht erfolgreich war.
Dieser Code ist dann OS-unabhängiger und läuft auf mehr Plattformen, weil er
eben nicht per #ifdef
auf eine Plattform fixiert ist.
Fazit
Das Thema, wie man Linux-Binaries baut, die überall laufen, ist ein schwieriges. Denn leider löst der Ansatz immer noch nicht das Problem, das z.B.: Alpine Linux und Debian zwei unterschiedliche Binaries brauchen.
Ich hatte auch schon mal mit dem -static
Flag des
GCC
gespielt um absolut unabhängige Executables zu produzieren … doch bei denen funktioniert dann
dlopen
gar nicht mehr.
Womit der Ansatz leider auch wegfällt.
Zumindest wurden so wieder ein paar Makros wegrationalisiert.
Ein kleiner Schritt für die einzelne Projektdatei.
Aber ein großer für die Zukunft des Projekt.