Ein shared object wird mit der Funktion dlopen() geladen; bei Bedarf kann man sie mit dlclose() wieder freigeben.
Beim Laden wird hier der Parameter RTLD_LAZY
angegeben. Das
bedeutet, daß in der geladenen Bibliothek vorhandene Referenzen nicht
sofort befriedigt werden sollen, sondern erst später beim erstmaligen Aufruf der
einzelnen Funktionen (nicht auflösbare Referenzen führen auch erst
dann zu Fehlern). Alternativ könnte man auch RTLD_NOW
angeben; dann werden Referenzen sofort aufgelöst beziehungsweise als
Fehler erkannt, wenn sie nicht auflösbar sind.
In beiden Fällen kann man den verwendeten Wert noch mit
RTLD_GLOBAL
verODERn (also beispielsweise RTLD_NOW|RTLD_GLOBAL
);
dann werden die globalen Symbole der jetzt geladenen Bibliothek zum
Auflösen der Referenzen in zukünftig geladenen Bibliotheken
verwendet.
Wenn man die globalen Symbole des aufrufenden Programms für die
Referenzen der Bibliotheken verwenden möchtem dann muß das aufrufende
Programme mit der Option -rdynamic
gelinkt worden sein.
Wenn die geladene Bibliothek eine Funktion _init()
enthält, dann wird diese aufgerufen, bevor dlopen() beendet ist.
dlopen() liefert im Erfolgsfall einen void*
-Zeiger
ungleich NULL, der im weiteren verwendet wird, um mit
dlsym() Zeiger auf Funktionen (oder bei Bedarf auf globale Variablen)
in der eingeblendeten Bibliothek zu finden.
Die ganzen dl...()-Funktionen haben einen gemeinsamen
Fehlerindikator; jeweils der letzte Fehler kann (als String const char*
)
mit der Funktion dlerror() abgefragt werden. Allerdings wird von
dlerror() der Fehlerindikator gelöscht; weitere Aufrufe liefern
dann keine Fehlermeldung mehr (bis ein weiterer Fehler auftritt
natürlich nur).
In dem folgenden Quelltext wird für die beiden aufgerufenen Funktionen jeweils ein Typ mit typedef definiert, um die Funktionszeiger optisch etwas ansprechender handhaben zu können.
Mit dlsym() holt man sich anhand des Namens eines Objekts (Funktion, globale Variable) eine Adresse in den eigenen Adreßraum (oder NULL im Fehlerfall).
Über den von dlsym() gelieferten Zeiger kann man die Objekte der Bibliothek ansprechen; in diesem Beispiel also die Funktionen aufrufen.
/////////////////////////////////////////////////////////////////////// // // Time-stamp: "(27.05.01 19:30) dynaufrufer.cpp [Klaus Wachtler]" // // kleine Demo zum Laden und Verwenden eines shared object. // Kompilieren und Linken: // g++ -rdynamic dynaufrufer.cpp -o dynaufrufer -ldl // Zum Starten muß die libdemodll.so gefunden werden können; // siehe man dlopen. #include <stdio.h> #include <unistd.h> #include <dlfcn.h> // Typ: Zeiger auf Funktion, die int liefert, zwei int erhält: typedef int ( *int_intint_pf_t)( int, int ); // Typ: Zeiger auf Funktion, die int liefert, keine Parameter: typedef int ( *int_void_pf_t)( void ); int main() { // Handle für geladene Bibliothek: void *so_handle = NULL; // Zeiger auf die Funktion in der Bibliothek: int_intint_pf_t summe_pf = NULL; int_void_pf_t immereinsmehr_pf = NULL; // einmalig die Bibliothek laden: so_handle = dlopen( "libdemodll.so", RTLD_LAZY ); if( !so_handle ) { // Laden der Bibliothek hat nicht geklapppt! char *Fehlertext = dlerror(); fprintf( stderr, "Fehler beim Laden: %s\n", ( Fehlertext ? Fehlertext : "??????" ) ); exit( 2 ); } // Zeiger auf die darin enthaltenen Funktionen holen: summe_pf = (int_intint_pf_t)dlsym( so_handle, "summe" ); immereinsmehr_pf = (int_void_pf_t)dlsym( so_handle, "immereinsmehr" ); // Funktion gefunden? if( !summe_pf ) { char *Fehlertext = dlerror(); fprintf( stderr, "Fehler! Funktion summe() nicht gefunden (%s)\n", ( Fehlertext ? Fehlertext : "??????" ) ); exit( 2 ); } // Funktion gefunden? if( !immereinsmehr_pf ) { // Nö! char *Fehlertext = dlerror(); fprintf( stderr, "Fehler! Funktion immereinsmehr() nicht gefunden (%s)\n", ( Fehlertext ? Fehlertext : "??????" ) ); exit( 2 ); } // und beliebig oft benutzen: for( int i=0; i<10; i++ ) { try { printf( "Summe von %d und %d ist %d; immer eins mehr ist %d\n", i, i/2, (*summe_pf)( i, i/2 ), (*immereinsmehr_pf)() ); } catch( int Fehler ) { fprintf( stderr, "int-Ausnahme %d gefangen!\n", Fehler ); } catch( ... ) { fprintf( stderr, "irgendeine Ausnahme gefangen!\n" ); } sleep( 1 ); } // Mann kann die Bibliothek auch wieder entladen, wenn man sie nicht // mehr braucht. // Kann man sich aber auch sparen. dlclose( so_handle ); return 0; } // ///////////////////////////////////////////////////////////////////////
Das Beispielprogramm kann folgendermaßen übersetzt und gestartet werden:
klaus@aw33:~/db/linux > g++ -Wall -rdynamic dynaufrufer.cpp -o dynaufrufer -ldl klaus@aw33:~/db/linux > ./dynaufrufer Fehler beim Laden: libdemodll.so: cannot open shared object file: Datei oder Verzeichnis nicht gefunden klaus@aw33:~/db/linux > export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH klaus@aw33:~/db/linux > ./dynaufrufer Summe von 0 und 0 ist 0; immer eins mehr ist 0 Summe von 1 und 0 ist 1; immer eins mehr ist 1 Summe von 2 und 1 ist 3; immer eins mehr ist 2 Summe von 3 und 1 ist 4; immer eins mehr ist 3 Summe von 4 und 2 ist 6; immer eins mehr ist 4 Summe von 5 und 2 ist 7; immer eins mehr ist 5 Summe von 6 und 3 ist 9; immer eins mehr ist 6 int-Ausnahme 125 gefangen! int-Ausnahme 125 gefangen! int-Ausnahme 125 gefangen!
Tips:
Die eigentlichen Aufrufe der Funktionen in der Bibliothek sind allerdings nicht mehr zu protokollieren (zumindest nicht ohne Debugger), weil sie nur noch über Zeiger stattfinden.
Namen, die in einer Objektdatei referenziert werden, aber nicht vorhanden sind, ist ein U vorangestellt; solche unsatisfied externals müssen beim Linken beziehungsweise beim Starten eines Programms durch weitere Bibliotheken befriedigt werden.
Dagegen ist Symbolen, die in der aktuellen Objektdatei definiert sind und von anderen Objektdateien referenziert werden können, in der nm-Ausgabe ein T vorangestellt. So gekennzeichnete Funtionen sind Kandidaten für einen dlsym()-Aufruf.
www.wachtler.de