Calling function in shared library (Linux) get Seg

2019-08-22 16:44发布

问题:

I was trying to write a basic example of shared library opening and function calling for practice, but it turns out that I always get "segmentation fault" when the exectuable is actually running. Here are the source code:


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;}}

I compiled the two part with the following command:

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

Here is the output

Segmentation fault (core dumped)

BTW, I also tried directly call api(tmp) rather than (*api)(tmp), that also don't work. Since api is a pointer, (*api) makes more sense?


I'm not sure what should I do. There are many totorials about calling function in shared library online, but most of them aren't fully coded, or they actually don't work.

And also I'm not sure what should I do with "attribute((visibility("default")))". Should I even write it down?


EDT1 Thanks for giving me so much advice. I finally find out that actually everything is a typo in compiling command... I mistakenly typed pluginA.so to pluginA.o, and that's the reason why it don't work...

Anyway, here is my revised program, with error handling added, and more "full" system added:

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;}

回答1:

You should read documentation of dlopen(3) and dlsym and you should always handle failure. So code

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); };

The documentation of dlopen is explaining why you want to pass ./pluginA.so with a ./ prefix

At last, you should always compile with all warnings and debug info, so:

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

(It is useful to link the main program with -rdynamic so that the plugin could access its symbols)

You could want to dlclose(dl) just before the end of main ... (calling or returning from a dlsym-ed function will crash your program if you dlclose too early). You might even avoid the dlclose (i.e. accept some resource leak). By experience you usually can dlopen many hundreds of thousands shared objects (see my manydl.c)

Only once your program is debugged you could add some optimization flag like -O or -O2 (and perhaps remove the debugging flag -g, but I don't recommend that for beginners).

You should perhaps read Drepper's paper: How To Write Shared Libraries.



回答2:

I correted your code a bit and use the error checking. Try that and get the idea what's going on:

#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;

}

At last: why it is failed? Use the right path: "./pluginA.so", not "pluginA.so" or put the full path to your plugin.



标签: c++ linux