2014-10-29 37 views
0

我想写一个共享库打开和函数调用进行练习的基本示例,但事实证明,当exectuable实际运行时,我总是得到“分段错误” 。下面是源代码:共享库调用函数(Linux)获取分割错误


main.cpp中:

#include<iostream> 
#include<dlfcn.h> 

using namespace std; 

typedef void (*API)(unsigned int); 

int main(int argc,char** argv){ 

    void* dl; 
    API api; 
    unsigned int tmp; 

    //... 

    dl=dlopen("pluginA.so",RTLD_LAZY); 
    api=(API)dlsym(dl,"API"); 

    cin>>tmp; 
    (*api)(tmp); 

    dlclose(dl); 

    //... 

    return 0; 

    } 

pluginA.cpp:

#include<iostream> 
using namespace std; 
extern "C" void API(unsigned int N){switch(N){ 
    case 0:cout<<"1\n"<<flush;break; 
    case 1:cout<<"2\n"<<flush;break; 
    case 2:cout<<"4\n"<<flush;break; 
    case 4:cout<<"16\n"<<flush;break;}} 

我编译所述两个部分具有下面的命令:

g++ -shared -o pluginA.so -fPIC plugin.cpp 
g++ main.cpp -ldl 

这里是输出

Segmentation fault (core dumped) 

顺便说一句,我也试过直接调用API(TMP),而不是(* API)(TMP),也不起作用。由于api是一个指针,(* api)更有意义?


我不知道该怎么办。在线共享库中有很多关于调用函数的概念,但是其中大多数没有完全编码,或者实际上并不工作。

而且我不知道我该怎么处理“”属性((visibility(“default”)))“。我应该写下来吗?


EDT1 谢谢你给了我这么多的建议。我终于发现,实际上在编译命令时,一切都是拼写错误...我错误地将pluginA.so输入到pluginA.o,这就是它不工作的原因...

无论如何,这里是我的修改程序,错误处理加入,更加“全面”系统还说:

main.cpp中:

#include<dirent.h> 
#include<dlfcn.h> 
#include<iostream> 
#include<cstring> 
using namespace std; 

typedef bool (*DLAPI)(unsigned int); 

int main(){ 

    DIR* dldir=opendir("dl"); 
    struct dirent* dldirf; 
    void* dl[255]; 
    DLAPI dlapi[255]; 
    unsigned char i,dlc=0; 
    char dldirfname[255]="./dl/"; 
    unsigned int n; 

    while((dldirf=readdir(dldir))!=NULL){ 
     if(dldirf->d_name[0]=='.')continue; 
     strcat(dldirfname,dldirf->d_name); 
     dl[dlc]=dlopen(dldirfname,RTLD_LAZY); 
     if(!dl[dlc])cout<<dlerror()<<endl;else{ 
      dlapi[dlc]=(DLAPI)dlsym(dl[dlc],"API"); 
      if(!dlapi[dlc])cout<<dlerror()<<endl;else dlc++;} 
     dldirfname[5]='\0';} 

    if(dlc==0){ 
     cerr<<"ERROR:NO DL LOADED"<<endl; 
     return -1;} 

    while(true){ 
     cin>>n; 
     for(i=0;i<dlc;i++)if((*dlapi[i])(n))break; 
     if(i==dlc)cout<<"NOT FOUND"<<endl;} 

    for(i=0;i<dlc;i++)dlclose(dl[i]); 

    return 0;} 

回答

2

你应该阅读dlopen(3)dlsym,你应该总是手柄失败的文档。所以代码

dl=dlopen("./pluginA.so",RTLD_LAZY); 
if (!dl) { fprintf(stderr, "dlopen failure: %s\n", dlerror()); 
      exit (EXIT_FAILURE); }; 
api=(API)dlsym(dl,"API"); 
if (!api) { fprintf(stderr, "dlsym failure: %s\n", dlerror()); 
      exit (EXIT_FAILURE); }; 

为什么要传递./pluginA.so./前缀

最后,你应该总是与所有警告和调试信息编译,所以dlopen的文档解释:

g++ -Wall -Wextra -g -shared -o pluginA.so -fPIC plugin.cpp 
g++ -Wall -Wextra -g -rdynamic main.cpp -ldl 

(将主程序链接到-rdynamic以便插件可以访问其符号很有用)

你可能想要dlclose(dl)就在main结束之前...(打电话或从dlsym返回 - 如果您的dlclose太早,功能会使程序崩溃)。你甚至可以避免dlclose(即接受一些资源泄漏)。根据经验,通常可以dlopen几十万共享对象(见我manydl.c

只有当你的程序调试,你可以添加一些优化标志像-O-O2(也许删除调试标志-g,但我不”建议初学者)。您应该阅读Drepper's paper: How To Write Shared Libraries

1

我纠正了你的代码,并使用错误检查。尝试一下,并得到这个想法是怎么回事:

#include<iostream> 
#include<dlfcn.h> 

using namespace std; 

typedef void (*API)(unsigned int); 

int main(int argc,char** argv) 
{ 

    API api; 
    unsigned int tmp; 

    //... 

    void* handle = dlopen("pluginA.so", RTLD_LAZY); 
    if (!handle) 
    { 
    std::cerr << dlerror() << std::endl; 
    return 1; 
    } 

    dlerror(); 

    api = reinterpret_cast<API>(dlsym(handle, "API")); 
    if (!api) 
    { 
    std::cerr << dlerror() << std::endl; 
    return 2; 
    } 

    cin>>tmp; 
    (*api)(tmp); 

    dlclose(handle); 

    //... 

    return 0; 

} 

最后:为什么它失败了?使用正确的路径:“./pluginA.so”,而不是“pluginA.so”或将完整路径放到您的插件中。