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

Calling function in shared library (Linux) get Segmentation Fault

本文关键字:函数 Fault Segmentation 共享 Linux 调用      更新时间:2023-10-16

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


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<<"1n"<<flush;break;
    case 1:cout<<"2n"<<flush;break;
    case 2:cout<<"4n"<<flush;break;
    case 4:cout<<"16n"<<flush;break;}}

我用以下命令编译了两部分:

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

这是输出

Segmentation fault (core dumped)

顺便说一句,我还试着直接调用api(tmp),而不是(*api)(tmp),这也不起作用。既然api是一个指针,(*api)更有意义吗?


我不知道该怎么办。网上有很多关于在共享库中调用函数的文章,但大多数都没有完全编码,或者实际上不起作用。

此外,我也不确定该如何处理"属性((可见性("默认"))"。我应该把它写下来吗?


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]='';}
    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;}

您应该阅读dlopen(3)和dlsym的文档,并且您应该始终处理故障。所以编码

dl=dlopen("./pluginA.so",RTLD_LAZY);
if (!dl) { fprintf(stderr, "dlopen failure: %sn", dlerror()); 
           exit (EXIT_FAILURE); };
api=(API)dlsym(dl,"API");
if (!api)  { fprintf(stderr, "dlsym failure: %sn", 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链接以使插件能够访问其符号是很有用的)

你可能想在main结束前dlclose(dl)。。。(如果dlclose太早,从dlsym-ed函数调用或返回会使程序崩溃)。您甚至可以避免dlclose(即接受一些资源泄漏)。根据经验,你通常可以dlopen成千上万的共享对象(见我的manydl.c)

只有在调试完程序后,才能添加一些优化标志,如-O-O2(也许还可以删除调试标志-g,但我不建议初学者这样做)。

你也许应该阅读Drepper的论文:如何编写共享库

我修改了您的代码并使用了错误检查。尝试一下,了解发生了什么:

#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",或者将完整的路径放在你的插件上。