在共享库(Linux)中调用函数会出现Segmentation Fault

时间:2014-10-29 18:44:53

标签: c++ linux

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


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 个答案:

答案 0 :(得分:3)

您应该阅读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); };

dlopen的文档说明了您希望./pluginA.so带有./前缀的原因

最后,您应该始终使用所有警告和调试信息进行编译,因此:

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 - ed函数调用或返回会导致您的程序崩溃太早了)。您甚至可以避免dlclose(即接受一些资源泄漏)。根据经验,您通常可以dlopen数十万个共享对象(请参阅我的manydl.c

只有在调试程序后,您才可以添加一些优化标记,如-O-O2(也许可以删除调试标记-g,但我不建议初学者使用)

您应该阅读Drepper's paper: How To Write Shared Libraries

答案 1 :(得分: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;

}

最后:为什么失败了?使用正确的路径:&#34; ./ pluginA.so&#34;,而不是&#34; pluginA.so&#34;或者将完整路径放到插件中。