2009-07-01 18 views
15

Ich habe eine Anwendung, von der ein Teil gemeinsame Bibliotheken verwendet. Diese Bibliotheken sind zur Kompilierzeit verknüpft.
Zur Laufzeit erwartet der Loader das gemeinsame Objekt in der LD_LIBRARY_PATH, wenn nicht gefunden, stürzt die gesamte Anwendung mit Fehler "nicht in der Lage, gemeinsame Bibliotheken zu laden." Beachten Sie, dass es keine Garantie gibt, dass Client die Bibliothek haben würde Falls die Anwendung eine entsprechende Fehlermeldung hinterlassen soll, sollte auch der unabhängige Teil korrekt funktionieren.Alternativen zu dlsym() und dlopen() in C++

Zu diesem Zweck verwende ich dlsym() und dlopen(), um die API in der gemeinsam genutzten Bibliothek zu verwenden. Das Problem damit ist, wenn ich viele Funktionen in der API habe, muss ich auf sie einzeln mit dlsym() und ptrs zugreifen, die in meinem Fall zu Speicherbeschädigung und Codeabstürzen führen.

Gibt es dafür Alternativen?

Antwort

29

Die übliche Lösung für Ihr Problem besteht darin, eine Tabelle mit Funktionszeigern zu deklarieren, einen einzelnen dlsym() zu finden und alle anderen Funktionen über einen Zeiger auf diese Tabelle aufzurufen. Beispiel (nicht getestet):

// libfoo.h 
struct APIs { 
    void (*api1)(void); 
    void *(*api2)(int); 
    long (*api3)(int, void *); 
}; 

// libfoo.cc 
void fn1(void) { ... } 
void *fn2(int) { ... } 
long fn3(int, void *) { ... } 

APIs api_table = { fn1, fn2, fn3 }; 


// client.cc 
#include "libfoo.h" 
... 
    void *foo_handle = dlopen("libfoo.so", RTLD_LAZY); 
    if (!foo_handle) { 
    return false;   // library not present 
    } 
    APIs *table = dlsym(foo_handle, "api_table"); 
    table->api1();    // calls fn1 
    void *p = table->api2(42); // calls fn2 
    long x = table->api3(1, p); // calls fn3 

P.S. Zugriff auf Ihre API-Funktionen einzeln mit dlsym und Zeiger nicht in sich führen zu Speicherbeschädigung und Abstürze. Höchstwahrscheinlich haben Sie nur Fehler.

EDIT:
Sie können diese genau gleiche Technik mit einer 3rd-Party-Bibliothek verwenden. Erstellen Sie eine libdrmaa_wrapper.so und setzen Sie die api_table hinein. Verknüpfen Sie den Wrapper direkt mit libdrmaa.so.

In der Hauptdatei, dlopen("libdrmaa_wrapper.so", RTLD_NOW). Diese dlopen wird gelingen, wenn (und nur wenn) libdrmaa.so zur Laufzeit vorhanden ist und alle API-Funktionen zur Verfügung stellt, die Sie in api_table verwendet haben. Wenn es erfolgreich ist, erhalten Sie einen einzigen Aufruf dlsym Zugriff auf die gesamte API.

+0

und wie rufe ich dlopen() auf, um den foo_handle zu bekommen? Ich meine, wird es das API Shared Object sagen libAPI.so automatisch laden ?? – sud03r

+0

Es ist dlopen() eigentlich zum Öffnen der Bibliothek ... :) .. nyways ich habe ur Punkt .. aber das funktioniert nur, wenn es eine APIs Struktur in der Bibliothek gibt .. die Sie nicht mit einer dritten Partei erwarten können Bibliothek .. Ich versuche, Sungrid api libdrmaa.so .. – sud03r

+0

Statt einen Zeiger auf eine Tabelle zu bekommen, könnten Sie stattdessen einen Zeiger auf eine Funktion, die beim Aufruf den Zeiger auf die Tabelle zurückgibt. Dadurch kann sich das Plugin selbst initialisieren, bevor eine andere Funktion aufgerufen wird, und die Tabelle sogar dynamisch erstellen. – CesarB

2

Sie können Ihre Anwendung mit einer anderen umbrechen, die zuerst nach allen erforderlichen Bibliotheken sucht, und wenn etwas fehlt, ist es fehlerfrei, aber wenn alles in Ordnung ist, führt es die eigentliche Anwendung aus.

+0

Es ist durchaus üblich, eine Anwendung, ein Skript zu haben, die die LD_LIBRARY_PATH einrichten bevor Sie die Anwendung starten. –

+0

Das Problem ist nicht, dass .. das Problem ist, wenn die Bibliothek nicht mit Client vorhanden ist, dass Modul nicht funktionieren sollte, sollte der Rest des Codes ordnungsgemäß funktionieren .. aber in diesem Fall, wenn die Bibliothek nicht gefunden wird, schlägt der Code fehl ausführen. – sud03r

-1

Ihr Problem ist, dass die Auflösung von ungelösten Symbolen sehr früh erfolgt - unter Linux glaube ich, dass die Datensymbole beim Prozessstart aufgelöst werden, und die Funktionssymbole sind träge gemacht. Je nachdem, welche Symbole Sie nicht gelöst haben und auf welche Art von statischer Initialisierung Sie dabei kommen, haben Sie möglicherweise keine Chance, mit Ihrem Code zu arbeiten.

Mein Vorschlag wäre, eine Wrapper-Anwendung zu haben, die den Rückgabecode/die Fehlerzeichenfolge "nicht in der Lage, gemeinsame Bibliotheken zu laden" abfängt und diese dann in etwas Sinnvolleres umwandelt. Wenn dies generisch ist, muss es nicht jedes Mal aktualisiert werden, wenn Sie eine neue gemeinsame Bibliothek hinzufügen.

Alternativ können Sie Ihre Wrapper-Skript ldd ausgeführt haben und dann die Ausgabe analysieren, ldd werden alle Bibliotheken berichten, die für Ihre Anwendung zu finden sind.

0

Verwenden unten Codeart

Class DynLib 
{ 
    /* All your functions */ 
    void fun1() {}; 
    void fun2() {}; 
    . 
    . 
    . 
} 

DynLib* getDynLibPointer() 
{ 
    DynLib* x = new Dynlib; 
    return x; 
} 

dlopen() Verwendung für diese Bibliothek zur Laufzeit geladen werden. und verwenden Sie dlsym() und rufen Sie getDynLibPointer(), die DynLib-Objekt zurückgibt. von diesem Objekt können Sie auf alle Ihre Funktionen zugreifen jst als obj.fun1() .....

Dies ist ofcource ein C++ - Stil der Struktur-Methode, die zuvor vorgeschlagen.

0

Sie suchen wahrscheinlich nach einer Form von Delay-Bibliothek laden auf Linux. Es ist nicht sofort verfügbar, aber Sie können es leicht nachahmen, indem Sie eine kleine statische Stub-Bibliothek erstellen, die dlopen benötigte Bibliothek beim ersten Aufruf einer seiner Funktionen versuchen würde (Diagnosemeldung ausgeben und beenden, wenn dlopen fehlgeschlagen ist) und dann Weiterleiten aller Anrufe an es.

Solche Stub-Bibliotheken können Implib.so von Hand, die durch Projekt/bibliotheksspezifischen Script oder erzeugt durch universelles Werkzeug geschrieben werden:

$ gen-implib.py libxyz.so 
$ gcc myapp.c libxyz.tramp.S libxyz.init.c ...