5.1 Linuxversion

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:

www.wachtler.de