Nik*_*ron 36 c frameworks module
如何在运行时加载编译的C代码,然后调用其中的函数?不像简单地调用exec().
编辑:加载模块的程序在C中.
bor*_*yer 41
dlopen是要走的路.这里有一些例子:
使用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 ());
}
Run Code Online (Sandbox Code Playgroud)
编译以上内容:
% cc -ldl -o program program.o
Run Code Online (Sandbox Code Playgroud)
然后,假设插件的API:
/* The functions we will find in the plugin */
typedef void (*init_f) ();
init_f init;
typedef int (*query_f) ();
query_f query;
Run Code Online (Sandbox Code Playgroud)
在插件中查找init()的地址:
init = dlsym(plugin, "init");
result = dlerror();
if (result)
{
fatal("Cannot find init in %s: %s", plugin_name, result);
}
init();
Run Code Online (Sandbox Code Playgroud)
使用另一个函数query(),它返回一个值:
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 ());
Run Code Online (Sandbox Code Playgroud)
您可以获取完整的例子就行.
Sha*_*iri 11
动态加载库是一种机制,我们可以用它来运行我们的程序,并在运行时决定我们要使用/调用什么函数。我认为在某些情况下static
变量也是可能的。
首先开始查看man 3 dlopen
或在线查看
所需的头文件是:dlfcn
并且由于这不是标准的一部分,因此您应该使用此库将其链接到您的目标文件:libdl.(so/a)
因此您需要类似的内容:
gcc yours.c -ldl\n
Run Code Online (Sandbox Code Playgroud)\n那么你就有了一个文件名a.out
,你可以运行它,但它不能正常工作,我会解释原因。
一个完整的例子:
\n首先分别创建 2 个文件func1.c
和func2.c
。我们想在运行时调用这些函数。
函数c
\nint func1(){\n return 1;\n}\n
Run Code Online (Sandbox Code Playgroud)\n函数2.c
\nconst char* func2(){\n return "upgrading to version 2";\n}\n
Run Code Online (Sandbox Code Playgroud)\n现在我们有 2 个函数,让我们来制作我们的模块:
\nALP \xe2\x9d\xb1 gcc -c -fPIC func1.c\nALP \xe2\x9d\xb1 gcc -c -fPIC func2.c\nALP \xe2\x9d\xb1 gcc -o libfunc.so -shared -fPIC func1.o func2.o \n
Run Code Online (Sandbox Code Playgroud)\n询问关于-fPIC
=> PIC 的想法
现在你有一个dynamic library
名字:libfunc.so
temp.c
让我们创建想要使用这些函数的主程序 (= )。
头文件
\n#include <stdio.h>\n#include <stdlib.h>\n#include <dlfcn.h> \n
Run Code Online (Sandbox Code Playgroud)\n和主程序
\nint main()\n{\n // pointer function to func1 and func2\n int ( *f1ptr )();\n const char* ( *f2ptr )();\n\n // for pointing to the library\n void* handle = NULL;\n\n // for saving the error messages\n const char* error_message = NULL;\n\n // on error dlopen returns NULL\n handle = dlopen( "libfunc.so", RTLD_LAZY );\n\n // check for error, if it is NULL\n if( !handle )\n {\n fprintf( stderr, "dlopen() %s\\n", dlerror() );\n exit( 1 );\n }\n\n /*\n according to the header file:\n\n When any of the above functions fails, call this function\n to return a string describing the error. Each call resets\n the error string so that a following call returns null.\n\n extern char *dlerror (void) __THROW;\n */\n\n // So, reset the error string, of course we no need to do it just for sure\n dlerror();\n\n // point to func1\n f1ptr = (int (*)()) dlsym( handle, "func1" );\n\n // store the error message to error_message\n // because it is reseted if we use it directly\n error_message = dlerror();\n if( error_message ) // it means if it is not null\n {\n fprintf( stderr, "dlsym() for func1 %s\\n", error_message );\n dlclose( handle );\n exit( 1 );\n }\n\n // point the func2\n f2ptr = (const char* (*)()) dlsym( handle, "func2" );\n\n // store the error message to error_message\n // because it is reseted if we use it directly\n error_message = dlerror();\n if( error_message ) // it means if it is not null\n {\n fprintf( stderr, "dlsym() for func2 %s\\n", error_message );\n dlclose( handle );\n exit( 1 );\n }\n\n printf( "func1: %d\\n", ( *f1ptr )() );\n printf( "func2: %s\\n", ( *f2ptr )() );\n\n // unload the library\n dlclose( handle );\n\n // the main return value\n return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n现在我们只需要编译这段代码(= temp.c
),然后尝试:
ALP \xe2\x9d\xb1 gcc temp.c -ldl\nALP \xe2\x9d\xb1 ./a.out\nlibfunc.so: cannot open shared object file: No such file or directory\n
Run Code Online (Sandbox Code Playgroud)\n这是行不通的!为什么容易;因为我们的a.out
程序不知道在哪里可以找到相关的库:libfunc.so
因此它告诉我们cannot not open ...
如何告诉程序(= a.out
)找到它的库?
ld
链接器LD_LIBRARY_PATH
第一种方式,在帮助下ld
使用-Wl,-rpath,
andpwd
并把路径作为它的参数
ALP \xe2\x9d\xb1 gcc temp.c -ldl\nALP \xe2\x9d\xb1 ./a.out\nlibfunc.so: cannot open shared object file: No such file or directory\nALP \xe2\x9d\xb1 pwd\n/home/shu/codeblock/ALP\nALP \xe2\x9d\xb1 gcc temp.c -ldl -Wl,-rpath,/home/shu/codeblock/ALP\nALP \xe2\x9d\xb1 ./a.out\nfunc1: 1\nfunc2: upgrading to version 2\n
Run Code Online (Sandbox Code Playgroud)\n第二种方式
\nALP \xe2\x9d\xb1 gcc temp.c -ldl\nALP \xe2\x9d\xb1 ./a.out\nlibfunc.so: cannot open shared object file: No such file or direc\nALP \xe2\x9d\xb1 export LD_LIBRARY_PATH=$PWD\nALP \xe2\x9d\xb1 echo $LD_LIBRARY_PATH\n/home/shu/codeblock/ALP\nALP \xe2\x9d\xb1 ./a.out\nfunc1: 1\nfunc2: upgrading to version 2\nALP \xe2\x9d\xb1 export LD_LIBRARY_PATH=\nALP \xe2\x9d\xb1 ./a.out\nlibfunc.so: cannot open shared object file: No such file or \n
Run Code Online (Sandbox Code Playgroud)\n和第三条路
\n您当前路径中有libfunc.so
,因此您可以将其复制到库的标准路径中。
ALP $ sudo cp libfunc.so /usr/lib\nALP \xe2\x9d\xb1 gcc temp.c -ldl\nALP \xe2\x9d\xb1 ./a.out\nfunc1: 1\nfunc2: upgrading to version 2\n
Run Code Online (Sandbox Code Playgroud)\n您可以将其删除/usr/lib
并使用它。它是由你决定。
笔记
\n如何知道我们a.out
知道它的路径?
\n简单:
ALP \xe2\x9d\xb1 gcc temp.c -ldl -Wl,-rpath,/home/shu/codeblock/ALP\nALP \xe2\x9d\xb1 strings a.out | grep \\/\n/lib/ld-linux.so.2\n/home/shu/codeblock/ALP\n
Run Code Online (Sandbox Code Playgroud)\n我们如何在C++中使用它?
\n只要我知道你不能,因为g++
会破坏函数名称,而gcc
不会,因此你应该使用:extern "C" int func1();
例如。
有关更多详细信息,请参阅手册页和 Linux 编程书籍。
\n看到这个问题已得到解答,但认为对此主题感兴趣的其他人可能会欣赏来自旧的基于插件的应用程序的跨平台示例.该示例适用于win32或linux,并在文件参数中指定的动态加载的.so或.dll中调用并调用名为"constructor"的函数.示例是在c ++中,但c的过程应该相同.
//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
}
Run Code Online (Sandbox Code Playgroud)
可能还会提到,如果要调用的函数模块是用c ++编写的,则必须使用extern"C"声明函数,例如:
extern "C" pcparport * construct(tcnplugin *tcnptr,tcnplugin *parent) {
return new pcparport(tcnptr,parent,"PCPARPORT",0,1);
}
Run Code Online (Sandbox Code Playgroud)