Erstellen eines Modulsystems (dynamisches Laden) in C

Lesezeit: 10 Minuten

Benutzeravatar von Nikron
Nikron

Wie würde man kompilierten C-Code zur Laufzeit laden und dann Funktionen darin aufrufen? Nicht wie das einfache Aufrufen von exec().

EDIT: Das Programm, das das Modul lädt, ist in C.

  • Ausgezeichnete Frage. Viele Leute wissen, wie man das macht, aber diejenigen, die es nicht tun, tun gut daran, diese wertvolle Technik zu lernen.

    – Mike Dunlavey

    31. Dezember 2008 um 18:00 Uhr

bortzmeyers Benutzeravatar
Bortzmeyer

dlopen ist der richtige Weg. Hier sind ein paar Beispiele:

Laden eines Plugins mit dlopen:

#include <dlfcn.h>
...
int
main (const int argc, const char *argv[])
{

  char *plugin_name;
  char file_name[80];
  void *plugin;
  ...
  plugin = dlopen(file_name, RTLD_NOW);
  if (!plugin)
  {
     fatal("Cannot load %s: %s", plugin_name, dlerror ());
  }

Oben kompilieren:

% cc  -ldl -o program program.o 

Nehmen Sie dann diese API für die Plugins an:

/* The functions we will find in the plugin */
typedef void (*init_f) ();
init_f init;
typedef int (*query_f) ();
query_f query;

Die Adresse von init() im Plugin finden:

init = dlsym(plugin, "init");
result = dlerror();
if (result)
{
   fatal("Cannot find init in %s: %s", plugin_name, result);
}
init();

Mit der anderen Funktion query(), die einen Wert zurückgibt:

query = dlsym (plugin, "query");
result = dlerror();
if (result)
{
    fatal("Cannot find query in %s: %s", plugin_name, result);
}
printf("Result of plugin %s is %d\n", plugin_name, query ());

Sie können das vollständige Beispiel abrufen online.

  • Würde es Ihnen etwas ausmachen, das vollständige Beispiel auf github zu stellen? Dort wäre es einfacher zu lesen.

    – Stefan Profanter

    4. September 2015 um 11:31 Uhr

  • Wenn Sie einen C++-Compiler verwenden, ist es Standard, den Namen der entstellten Zeichenfolgenfunktion zu verwenden, wenn Sie verwenden dlsym? oder extern "c" auf die Funktion, um einfach den normalen Funktionsnamen auf zu verwenden dlsym?

    – Suchmaschine27

    15. November 2019 um 16:36 Uhr


Benutzeravatar von Robert Gamble
Robert Glück

Unter Linux/UNIX können Sie die POSIX verwenden dlopen / dlsym / dlerror / dlclose Funktionen zum dynamischen Öffnen gemeinsam genutzter Bibliotheken und zum Zugreifen auf die von ihnen bereitgestellten Symbole (einschließlich Funktionen) finden Sie unter Manpage für Details.

  • Ist das Prinzip der Poco-Bibliothek so?

    – daohu527

    21. Oktober 2020 um 12:41 Uhr

Benutzeravatar von Shakiba Moshiri
Shakiba Moshiri

für GNU/Linux-Benutzer

Die dynamische Ladebibliothek ist ein Mechanismus, mit dem wir unser Programm ausführen und zur Laufzeit entscheiden können, welche Funktion wir verwenden / aufrufen möchten. Ich denke in manchen Fällen static variabel ist auch möglich.

Fang erstmal an zu sehen man 3 dlopen oder siehe online

Die erforderliche Header-Datei lautet: dlfcn und da dies nicht zum standard gehört man sollte Verknüpfen Sie es mit dieser Bibliothek mit Ihrer Objektdatei: libdl.(so/a) und deshalb brauchen Sie so etwas wie:

gcc yours.c -ldl

dann haben Sie einen Dateinamen a.out und Sie können es ausführen ABER es funktioniert nicht richtig und ich werde es erklären, warum.


Ein vollständiges Beispiel:

erste Kiste 2 Dateien func1.c und func2.c beziehungsweise. Wir wollen diese Funktionen zur Laufzeit aufrufen.

func.c

int func1(){
    return 1;
}

func2.c

const char* func2(){
    return "upgrading to version 2";
}

Jetzt haben wir 2 Funktionen, machen wir unsere Module:

ALP ❱ gcc -c -fPIC func1.c
ALP ❱ gcc -c -fPIC func2.c
ALP ❱ gcc -o libfunc.so -shared -fPIC func1.o func2.o 

zum Nachdenken über -fPIC => BILD

Jetzt haben Sie eine dynamic library Namen: libfunc.so

Erstellen wir das Hauptprogramm (= temp.c), die diese Funktionen verwenden möchte.

Header-Dateien

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

und das Hauptprogramm

int main()
{
    // pointer function to func1 and func2
    int         ( *f1ptr )();
    const char* ( *f2ptr )();

    // for pointing to the library
    void* handle = NULL;

    // for saving the error messages
    const char* error_message = NULL;

    // on error dlopen returns NULL
    handle = dlopen( "libfunc.so", RTLD_LAZY );

    // check for error, if it is NULL
    if( !handle )
    {
        fprintf( stderr, "dlopen() %s\n", dlerror() );
        exit( 1 );
    }

    /*
        according to the header file:

        When any of the above functions fails, call this function
        to return a string describing the error.  Each call resets
        the error string so that a following call returns null.

        extern char *dlerror (void) __THROW;
    */

    // So, reset the error string, of course we no need to do it just for sure
    dlerror();

    // point to func1
    f1ptr = (int (*)()) dlsym( handle, "func1" );

    // store the error message to error_message
    // because it is reseted if we use it directly
    error_message = dlerror();
    if( error_message ) //   it means if it is not null
    {
        fprintf( stderr, "dlsym() for func1 %s\n", error_message );
        dlclose( handle );
        exit( 1 );
    }

    // point the func2
    f2ptr = (const char* (*)()) dlsym( handle, "func2" );

    // store the error message to error_message
    // because it is reseted if we use it directly
    error_message = dlerror();
    if( error_message ) //   it means if it is not null
    {
        fprintf( stderr, "dlsym() for func2 %s\n", error_message );
        dlclose( handle );
        exit( 1 );
    }

    printf( "func1: %d\n", ( *f1ptr )() );
    printf( "func2: %s\n", ( *f2ptr )() );

    // unload the library
    dlclose( handle );

    // the main return value
    return 0;
}

Jetzt müssen wir nur noch diesen Code kompilieren (= temp.c), versuchen Sie also:

ALP ❱ gcc temp.c -ldl
ALP ❱ ./a.out
libfunc.so: cannot open shared object file: No such file or directory

Es funktioniert nicht! WARUM einfach; weil unsere a.out Das Programm weiß nicht, wo es die zugehörige Bibliothek finden kann: libfunc.so und deshalb sagt es uns cannot not open ...

wie man dem Programm sagt (= a.out), um seine Bibliothek zu finden?

  1. verwenden ld Linker
  2. Umgebungsvariable verwenden LD_LIBRARY_PATH
  3. über den Standardpfad

erster Weg, mit Hilfe von ld

verwenden -Wl,-rpath, und pwd und den Pfad als Argument dafür angeben

ALP ❱ gcc temp.c -ldl
ALP ❱ ./a.out
libfunc.so: cannot open shared object file: No such file or directory
ALP ❱ pwd
/home/shu/codeblock/ALP
ALP ❱ gcc temp.c -ldl -Wl,-rpath,/home/shu/codeblock/ALP
ALP ❱ ./a.out
func1: 1
func2: upgrading to version 2

zweiter Weg

ALP ❱ gcc temp.c -ldl
ALP ❱ ./a.out
libfunc.so: cannot open shared object file: No such file or direc
ALP ❱ export LD_LIBRARY_PATH=$PWD
ALP ❱ echo $LD_LIBRARY_PATH
/home/shu/codeblock/ALP
ALP ❱ ./a.out
func1: 1
func2: upgrading to version 2
ALP ❱ export LD_LIBRARY_PATH=
ALP ❱ ./a.out
libfunc.so: cannot open shared object file: No such file or 

und dritter Weg

Sie haben libfunc.so in Ihrem aktuellen Pfad, so können Sie es in einen Standardpfad für Bibliotheken kopieren.

ALP $ sudo cp libfunc.so /usr/lib
ALP ❱ gcc temp.c -ldl
ALP ❱ ./a.out
func1: 1
func2: upgrading to version 2

Sie können es entfernen /usr/lib und benutze es. Es liegt an dir.

HINWEIS

So finden Sie heraus, dass unsere a.out kennt seinen Weg?
einfach:

ALP ❱ gcc temp.c -ldl -Wl,-rpath,/home/shu/codeblock/ALP
ALP ❱ strings a.out  | grep \/
/lib/ld-linux.so.2
/home/shu/codeblock/ALP

wie können wir es in c++ verwenden?
So lange ich weiß, kannst du das nicht g++ verstümmelt die Funktionsnamen, während gcc nicht so sollten Sie verwenden: extern "C" int func1(); zum Beispiel.

Weitere Details finden Sie in den Manpages und Linux-Programmierbüchern.

  • Nett! Laut dlopen-Manpage gibt es einen vierten Weg: “Wenn der Dateiname einen Schrägstrich (“/”) enthält, wird er als (relativer oder absoluter) Pfadname interpretiert.” Also ‘handle = dlopen( “./libfunc.so”, RTLD_LAZY );’ ermöglicht es, wie beschrieben zu kompilieren und einfach “./a.out” erfolgreich auszuführen, ohne etwas anderes zu tun.

    – Hermann SW

    9. Juni 2021 um 10:53 Uhr

Benutzeravatar von erisu
Erisu

Siehe, diese Frage wurde beantwortet, aber ich dachte, andere, die an diesem Thema interessiert sind, könnten ein plattformübergreifendes Beispiel aus einer alten Plugin-basierten Anwendung zu schätzen wissen. Das Beispiel funktioniert unter Win32 oder Linux und sucht und ruft eine Funktion namens „Konstruktor“ in der dynamisch geladenen .so- oder .dll-Datei auf, die im Dateiargument angegeben ist. Das Beispiel ist in c++, aber die Prozeduren sollten für c gleich sein.

//firstly the includes
#if !defined WIN32
   #include <dlfcn.h>
   #include <sys/types.h>
#else
   #include <windows.h>
#endif

//define the plugin's constructor function type named PConst
typedef tcnplugin* (*PConst)(tcnplugin*,tcnplugin*,HANDLE);

//loads a single specified tcnplugin,allmychildren[0] = null plugin
int tcnplugin::loadplugin(char *file) {
    tcnplugin *hpi;
#if defined WIN32               //Load library windows style
    HINSTANCE hplugin=LoadLibrary(file);
    if (hplugin != NULL) {
            PConst pinconstruct = (PConst)GetProcAddress(hplugin,"construct");
#else                                   //Load it nix style
    void * hplugin=dlopen(file,RTLD_NOW);
    if (hplugin != NULL) {
            PConst pinconstruct = (PConst)dlsym(hplugin,"construct");
#endif   
            if (pinconstruct != NULL) { //Try to call constructor function in dynamically loaded file, which returns a pointer to an instance of the plugin's class
                    hpi = pinconstruct(this, this, hstdout);
            } else {
                    piprintf("Cannot find constructor export in plugin!\n");
                    return 0;
            }
    } else {
            piprintf("Cannot open plugin!\n");
#if !defined WIN32
            perror(dlerror());
#endif
            return 0;
    }
    return addchild(hpi); //add pointer to plugin's class to our list of plugins
}

Es könnte auch erwähnt werden, dass, wenn das Modul, dessen Funktionen Sie aufrufen möchten, in C++ geschrieben ist, Sie die Funktion mit extern “C” deklarieren müssen, wie zum Beispiel:

extern "C" pcparport * construct(tcnplugin *tcnptr,tcnplugin *parent) {
    return new pcparport(tcnptr,parent,"PCPARPORT",0,1);
}

Kann man sich auch anschauen cpluff. Es ist eine Plugin-Verwaltungsbibliothek auf reinem c.

Benutzeravatar von mozzbozz
mozzbozz

Wenn Sie bereit sind, das Framework in Betracht zu ziehen, bietet Qt QPluginLoader: Qt 5-Dokumente (oder für alte Qt 4.8-Dokumente siehe hier)

Wenn Sie eine feinkörnigere Steuerung benötigen/wollen, bietet Qt mit QLibrary auch eine Möglichkeit, Bibliotheken im Handumdrehen zu laden: Qt 5-Dokumente (oder für alte Qt 4.8-Dokumente siehe hier)

Noch besser, diese sind plattformübergreifend portierbar.

Benutzeravatar von skiphoppy
überspringen

Dynamische Sprachen wie Perl tun dies ständig. Der Perl-Interpreter ist in C geschrieben, und viele Perl-Module sind teilweise in C geschrieben. Wenn diese Module benötigt werden, werden die kompilierten C-Komponenten im laufenden Betrieb dynamisch geladen. Wie in einer anderen Antwort erwähnt, besteht der Mechanismus zum Speichern dieser Module aus DLLs unter Windows und gemeinsam genutzten Bibliotheken (SO-Dateien) unter UNIX. Ich glaube, der Aufruf zum Laden einer gemeinsam genutzten Bibliothek unter UNIX ist dlopen(). Sie können wahrscheinlich Hinweise finden, wie Sie dies unter UNIX erreichen können, indem Sie mit der Dokumentation für diesen Aufruf beginnen. Für Windows müssten Sie DLLs recherchieren und lernen, wie Sie sie zur Laufzeit dynamisch laden. [Or possibly go through the Cygwin UNIX emulation layer, which would probably allow you to use the same calls on Windows as you would on UNIX, but I wouldn’t recommend that unless you’re already using and compiling against Cygwin.]

Beachten Sie, dass sich dies vom einfachen Verknüpfen mit einer gemeinsam genutzten Bibliothek unterscheidet. Wenn Sie im Voraus genau wissen, welchen Code Sie aufrufen werden, können Sie für eine gemeinsam genutzte Bibliothek erstellen, und der Build wird mit dieser Bibliothek “dynamisch verknüpft”. ohne besondere Behandlung Ihrerseits werden die Routinen aus der Bibliothek nur dann in den Speicher geladen, wenn Ihr Programm sie tatsächlich aufruft. Aber Sie können das nicht tun, wenn Sie vorhaben, etwas zu schreiben, das in der Lage ist, irgendwelche zu laden willkürlich Objektcode, Code, den Sie jetzt zur Erstellungszeit nicht identifizieren können, der stattdessen darauf wartet, zur Laufzeit irgendwie ausgewählt zu werden. Dafür müssen Sie dlopen() und seine Windows-Cousins ​​verwenden.

Sie können sich die Art und Weise ansehen, wie Perl oder andere dynamische Sprachen dies tun, um einige echte Beispiele zu sehen. Die Perl-Bibliothek, die für diese Art des dynamischen Ladens verantwortlich ist, ist DynaLoader; es hat, glaube ich, sowohl eine Perl- als auch eine C-Komponente. Ich bin mir sicher, dass andere dynamische Sprachen wie Python etwas Ähnliches haben, das Sie sich eher ansehen sollten. und Parrot, die virtuelle Maschine für das unveröffentlichte Perl 6, hat sicherlich auch einen Mechanismus dafür (oder wird es in Zukunft tun).

Java erreicht dies übrigens über seine JNI-Schnittstelle (Java Native Interface), sodass Sie sich wahrscheinlich den Quellcode für OpenJDK ansehen könnten, um zu sehen, wie Java dies sowohl unter UNIX als auch unter Windows erreicht.

1402630cookie-checkErstellen eines Modulsystems (dynamisches Laden) in C

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

Privacy policy