3 votes

C++ : Symboles non définis lors du chargement d'une bibliothèque partagée avec dlopen()

Je rencontre un problème lorsque j'essaie d'utiliser dlopen() pour charger une bibliothèque partagée dans une autre bibliothèque partagée. J'ai consulté tous les tutoriels sur la façon d'utiliser dlopen() correctement. Voici donc le code simplifié :

La bibliothèque partagée principale contient une classe avec des fonctions virtuelles pures que la bibliothèque partagée secondaire (ou plug-in) doit mettre en œuvre. De plus, elle possède d'autres fonctions qui sont implémentées avec un comportement par défaut. J'ai créé une macro qui est ajoutée à chaque plug-in afin d'avoir un symbole pour charger et créer la classe.

Bibliothèque principale partagée

plugin.h :

Class A {
public:
  virtual int func1() = 0;
  virtual bool func2() const;
}

#define CREATE_CLASS(cls) extern "C" void *CreateClass(void) { return new cls; }

plugin.cpp :

bool A::func2() const {   return true; }

Construire et lier le fichier main.so

g++ -g -Wall -Woverloaded-virtual -Wno-parentheses -O2 -fPIC -c -o plugin.o plugin.cpp
g++ -g -Wall -Woverloaded-virtual -Wno-parentheses -O2 -fPIC  -rdynamic -shared plugin.o -ldl -o main-plugin.so

La bibliothèque partagée ne met en œuvre que les fonctions virtuelles pures. Les autres fonctions peuvent être remplacées, bien qu'elles soient facultatives.

Sous-bibliothèque partagée

plugin-impl.cpp

Class B : public A {
public:
  virtual int func1() { return 0; }
}

CREATE_CLASS(B)

Il s'agit des lignes permettant de construire et de lier la bibliothèque partagée.

Construire et lier sub.so

g++ -g -O3 -Wall -Werror=overloaded-virtual -Wno-parentheses -fPIC -c -o subPlugin.o subPlugin.cpp
g++ -g -O3 -Wall -Werror=overloaded-virtual -Wno-parentheses -fPIC  -shared subPlugin.o  -o subPlugin.so

Il s'agit de la ligne permettant d'ouvrir la bibliothèque partagée. J'ai essayé LAZY, NOW, NOW | GLOBAL et ainsi de suite, sans aucun effet.

dlopen() quelque part dans la bibliothèque principale partagée :

  handle = dlopen(file.c_str(), RTLD_LAZY);

La plupart de ces mesures fonctionnent très bien. Cependant, lorsque j'essaie de charger la bibliothèque partagée secondaire dans la bibliothèque partagée principale, dlopen se plaint du symbole non défini de bool A::func2() const . La fonction n'existe que dans la bibliothèque partagée principale, donc je suppose qu'elle doit être exportée de toute façon. Merci de m'aider ! Je suis très confus !

SOLUTION

Comme je ne peux pas modifier l'exécutable, je dois lier la bibliothèque principale partagée avec les bibliothèques secondaires en ajoutant les options suivantes à g++ :

-L$(PLUGINDIR) -Wl,-R$(PLUGINDIR) -lmain-shared

Dans ce cas, il n'est pas nécessaire de régler le paramètre LD_LIBRARY_PATH .

3voto

jpalecek Points 31928

Étant donné que votre sous-bibliothèque a besoin des symboles de la bibliothèque principale, je pense que vous voulez qu'elle soit liée à cette dernière. Essayez de la lier comme suit :

g++ -g -O3 -Wall -Werror=overloaded-virtual -Wno-parentheses -fPIC  -shared 
    -lmain-plugin subPlugin.o  -o subPlugin.so

Vous devrez probablement jouer avec -L également.

Voici ce que j'ai essayé :

jirka@debian:/tmp$ cat executable.cpp 
#include <dlfcn.h>
#include <stdio.h>
int main()
{
  dlopen("./main-library.so", RTLD_NOW);
  void* handle=dlopen("./sub-library.so", RTLD_LAZY);
  printf("%x %s", dlsym(handle, "CreateClass"), dlerror());
}
jirka@debian:/tmp$ cat main-library.cpp 
class A {
public:
  virtual int func1() = 0;
  virtual bool func2() const;
};

#define CREATE_CLASS(cls) extern "C" void *CreateClass(void) { return new cls; }

bool A::func2() const {   return true; }
jirka@debian:/tmp$ cat sub-library.cpp 
class A {
public:
  virtual int func1() = 0;
  virtual bool func2() const;
};

#define CREATE_CLASS(cls) extern "C" void *CreateClass(void) { return new cls; }

class B : public A {
public:
  virtual int func1() { return 0; }
};

CREATE_CLASS(B)

jirka@debian:/tmp$ g++ -g -Wall -Woverloaded-virtual -Wno-parentheses -O2 -fPIC  -rdynamic -shared main-library.cpp -ldl -o main-library.so
jirka@debian:/tmp$ g++ -g -O3 -Wall -Werror=overloaded-virtual -Wno-parentheses -fPIC  -shared -l:main-library.so sub-library.cpp  -o sub-library.so
jirka@debian:/tmp$  g++ -ldl executable.cpp -o executable
jirka@debian:/tmp$ LD_LIBRARY_PATH=. ./executable 
b7713740 (null)

Une autre possibilité consiste à ajouter RTLD_GLOBAL lors du chargement main-library :

jirka@debian:/tmp$ cat executable.cpp 
#include <dlfcn.h>
#include <stdio.h>
int main()
{
  dlopen("./main-library.so", RTLD_LAZY | RTLD_GLOBAL);
  void* handle=dlopen("./sub-library.so", RTLD_LAZY);
  printf("%x %s", dlsym(handle, "CreateClass"), dlerror());
}

De cette façon, vous n'avez pas besoin de lier quoi que ce soit avec main-library.so .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X