Wie kann eine Shared Library (.so) eine Funktion aufrufen, die in ihrem Loader-Code implementiert ist?

Lesezeit: 5 Minuten

Benutzer-Avatar
0x90

Ich habe eine gemeinsam genutzte Bibliothek, die ich implementiert habe, und möchte, dass die .so eine Funktion aufruft, die im Hauptprogramm implementiert ist, das die Bibliothek lädt.

Nehmen wir an, ich habe main.c (ausführbare Datei), die Folgendes enthält:

void inmain_function(void*);
dlopen("libmy.so");

In der my.c (dem Code für die libmy.so) möchte ich aufrufen inmain_function:

inmain_function(NULL);

Wie kann die gemeinsam genutzte Bibliothek aufrufen inmain_function ungeachtet der Tatsache inmain_function wird im Hauptprogramm definiert.

Hinweis: Ich möchte ein Symbol in main.c von my.c aufrufen, nicht umgekehrt, was die übliche Verwendung ist.

Benutzer-Avatar
Waleri Atamaniouk

Sie haben zwei Möglichkeiten, aus denen Sie wählen können:

Option 1: Exportieren Sie alle Symbole aus Ihrer ausführbaren Datei. Dies ist eine einfache Option, nur wenn Sie eine ausführbare Datei erstellen, fügen Sie ein Flag hinzu -Wl,--export-dynamic. Dies würde alle Funktionen für Bibliotheksaufrufe verfügbar machen.

Option 2: Erstellen Sie eine Exportsymboldatei mit einer Liste von Funktionen und verwenden Sie sie -Wl,--dynamic-list=exported.txt. Dies erfordert etwas Wartung, ist aber genauer.

Zur Demonstration: einfache ausführbare und dynamisch geladene Bibliothek.

#include <stdio.h>
#include <dlfcn.h>

void exported_callback() /*< Function we want to export */
{
    printf("Hello from callback!\n");
}

void unexported_callback() /*< Function we don't want to export */
{
    printf("Hello from unexported callback!\n");
}

typedef void (*lib_func)();

int call_library()
{
   void     *handle  = NULL;
   lib_func  func    = NULL;
   handle = dlopen("./libprog.so", RTLD_NOW | RTLD_GLOBAL);
   if (handle == NULL)
   {
       fprintf(stderr, "Unable to open lib: %s\n", dlerror());
       return -1;
   }
   func = dlsym(handle, "library_function");

   if (func == NULL) {
       fprintf(stderr, "Unable to get symbol\n");
      return -1;
   }

   func();
   return 0;
}

int main(int argc, const char *argv[])
{
    printf("Hello from main!\n");
    call_library();
    return 0;
}

Bibliothekscode (lib.c):

#include <stdio.h>
int exported_callback();

int library_function()
{
    printf("Hello from library!\n");
    exported_callback();
    /* unexported_callback(); */ /*< This one will not be exported in the second case */
    return 0;
}

Erstellen Sie also zuerst die Bibliothek (dieser Schritt unterscheidet sich nicht):

 gcc -shared -fPIC lib.c -o libprog.so

Erstellen Sie nun eine ausführbare Datei mit allen exportierten Symbolen:

 gcc -Wl,--export-dynamic main.c -o prog.exe -ldl

Beispiel ausführen:

 $ ./prog.exe
 Hello from main!
 Hello from library!
 Hello from callback!

Exportierte Symbole:

 $ objdump -e prog.exe -T | grep callback
 00000000004009f4 g    DF .text  0000000000000015  Base        exported_callback
 0000000000400a09 g    DF .text  0000000000000015  Base        unexported_callback

Jetzt mit der exportierten Liste (exported.txt):

{
    extern "C"
    {
       exported_callback;
    };
};

Sichtbare Symbole bauen & prüfen:

$ gcc -Wl,--dynamic-list=./exported.txt main.c -o prog.exe -ldl
$ objdump -e prog.exe -T | grep callback
0000000000400774 g    DF .text  0000000000000015  Base        exported_callback

  • Wenn Sie libprog.so so kompilieren, wird es feststellen, dass exported_callback ein fehlendes Symbol ist.

    – Kritzikratzi

    7. Dezember 2015 um 3:54 Uhr

  • p.s. um meine eigene frage zu beantworten: man kann den fehler “undefiniertes symbol” deaktivieren und es funktioniert, zB in clang with -undefined dynamic_lookup. Unter Linux mit gcc funktioniert dies auf magische Weise wie im Beitrag beschrieben, aber ich habe keine Ahnung warum.

    – Kritzikratzi

    8. Dezember 2015 um 6:06 Uhr

  • Dies ist eine so viel bessere Antwort als die akzeptierte! Ich wünschte, es wäre oben.

    – SergejA

    28. Februar 2019 um 19:50 Uhr

Benutzer-Avatar
Benutzer746527

Sie müssen eine Registerfunktion in Ihrer .so-Datei erstellen, damit die ausführbare Datei Ihrer .so-Datei einen Funktionszeiger für die spätere Verwendung geben kann.

So was:

void in_main_func () {
// this is the function that need to be called from a .so
}

void (*register_function)(void(*)());
void *handle = dlopen("libmylib.so");

register_function = dlsym(handle, "register_function");

register_function(in_main_func);

die register_function muss den Funktionszeiger in einer Variablen in der .so-Datei speichern, wo ihn die andere Funktion in der .so-Datei finden kann.

Ihre mylib.c müsste in etwa so aussehen:

void (*callback)() = NULL;

void register_function( void (*in_main_func)())
{
    callback = in_main_func;
}

void function_needing_callback() 
{
     callback();
}

  • Ich würde lieber tun if (callback) { callback(); return 0; } else { return -1; } um einen Fehler anzuzeigen und einen Anruf zu vermeiden NULL (was fatal wäre).

    – glglgl

    13. Juni 2013 um 8:18 Uhr

  • Richtig, die Faustlinie entfernt.

    – Benutzer746527

    14. Juni 2013 um 10:25 Uhr

  1. Platzieren Sie den Prototyp Ihrer Hauptfunktion in einer .h-Datei und fügen Sie ihn sowohl in Ihren Haupt- als auch in Ihren dynamischen Bibliothekscode ein.

  2. Mit GCC kompilieren Sie Ihr Hauptprogramm einfach mit der -rdynamic Flagge.

  3. Nach dem Laden kann Ihre Bibliothek die Funktion vom Hauptprogramm aus aufrufen.

Eine kleine weitere Erklärung ist, dass Ihre dynamische Bibliothek nach dem Kompilieren ein undefiniertes Symbol für die Funktion enthält, die sich im Hauptcode befindet. Nachdem Ihre Haupt-App die Bibliothek geladen hat, wird das Symbol von der Symboltabelle des Hauptprogramms aufgelöst. Ich habe das obige Muster unzählige Male verwendet und es funktioniert wie ein Zauber.

  • Nur neugierig, was passiert, wenn eine andere gemeinsam genutzte Bibliothek das Symbol ebenfalls hat?

    – Benutzer746527

    14. Juni 2013 um 10:27 Uhr

  • Das Symbol der Hauptanwendung ersetzt/überschreibt das der Bibliothek, es sei denn, die Funktion der Bibliothek wurde mit definiert static Stichwort.

    – mshildt

    14. Juni 2013 um 12:51 Uhr


Benutzer-Avatar
Nobilis

Folgendes kann verwendet werden, um eine dynamische Bibliothek zu laden und sie aus dem Ladeaufruf aufzurufen (falls jemand hierher kam, nachdem er nach dem Laden und Aufrufen einer Funktion in einer .so-Bibliothek gesucht hatte):

void* func_handle = dlopen ("my.so", RTLD_LAZY); /* open a handle to your library */

void (*ptr)() = dlsym (func_handle, "my_function"); /* get the address of the function you want to call */

ptr(); /* call it */

dlclose (func_handle); /* close the handle */

Vergessen Sie nicht zu setzen #include <dlfcn.h> und verlinke mit der –ldl Möglichkeit.

Möglicherweise möchten Sie auch eine Logik hinzufügen, die überprüft, ob NULL ist zurück gekommen. Wenn das der Fall ist, können Sie anrufen dlerror und es sollte Ihnen einige aussagekräftige Meldungen geben, die das Problem beschreiben.

Andere Poster haben jedoch passendere Antworten auf Ihr Problem gegeben.

1365780cookie-checkWie kann eine Shared Library (.so) eine Funktion aufrufen, die in ihrem Loader-Code implementiert ist?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy