Вызов функции в разделяемой библиотеке (Linux) получить ошибку сегментации

0

Я пытался написать базовый пример открытия и функции разделяемой библиотеки, требующей практики, но, оказывается, всегда возникает "ошибка сегментации", когда 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) имеет больше смысла?


Я не уверен, что мне делать. Существует много томов о вызове функции в общей библиотеке в Интернете, но большинство из них не полностью закодированы или они фактически не работают.

И также я не уверен, что мне делать с атрибутом ((видимость ("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 ответа

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 -ed приведет к сбою вашей программы, если вы слишком рано dlclose). Возможно, вы даже избежите dlclose (т. dlclose утечку ресурсов). По опыту вы обычно можете dlopen много сотен тысяч общих объектов (см. Мой manydl.c)

Только после отладки вашей программы вы можете добавить некоторый флаг оптимизации, например -O или -O2 (и, возможно, удалить флаг отладки -g, но я не рекомендую это для новичков).

Возможно, вам стоит прочитать документ Drepper: "Как писать общие библиотеки".

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" или поместите полный путь к вашему плагину.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню