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.
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.
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
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
Shakiba Moshiri
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?
ld
LinkerLD_LIBRARY_PATH
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
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.
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.
ü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.
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