2014-10-29 106 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」或將完整路徑放到您的插件中。