IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

undefined symbol sur classe mère avec dlopen


Sujet :

C++

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 12
    Points : 6
    Points
    6
    Par défaut undefined symbol sur classe mère avec dlopen
    Bonjour tout le monde,

    Voilà, j'ai un petit problème avec la fonction dlopen sous linux : je rencontre un message d'erreur à l'exécution de mon programme du type : libvehiculetest.so : undefined symbol : _ZN8VehiculeD2Ev, erreur retournée par dlerror().

    libvehiculetest.so contient l'implémentation de ma classe VehiculeTest, fille de la classe abstraite Vehicule.
    Le fichier .so a été compilé avec l'option -shared et l'exécutable avec l'option -rdynamic.

    Est-ce que quelqu'un aurait une idée de ce qui peut clocher ? Si vous avez besoin de plus de précisions, n'hésitez pas à demander.

    Merci d'avance.

  2. #2
    Futur Membre du Club
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 12
    Points : 6
    Points
    6
    Par défaut
    Bonjour à tous, je me permets de faire un petit up, je suis vraiment coincé là ^^ !

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 59
    Points : 33
    Points
    33
    Par défaut
    Salut, je rencontrais le meme soucis, je l'ai résolu en compilant le .so avec le .o contenant la description de la classe, ce qui est moyen (et fait grossir le .so...).
    Je suis toujours à la recherche d'une solution plus propre.

    epo

  4. #4
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 573
    Points
    41 573
    Par défaut
    Normalement, pour faire un plug-in en C++, toutes les méthodes de la classe Vehicule doivent être virtuelles pures, donc ton .so ne doit pas avoir besoin de définitions de fonctions de la classe...

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 59
    Points : 33
    Points
    33
    Par défaut
    Toutes les méthodes de l'objet "embarqué" dans le .so doivent etre virtuelles pures ? et quand est il des méthode d'autres objet auquelles accèdent des fonction du .so ? Y a t'il des option de compilation spécifiques à avoir ?

    epo

  6. #6
    Membre éclairé Avatar de valefor
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    711
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 711
    Points : 790
    Points
    790
    Par défaut
    Quand je rencontre ce genre de problème je fait (de mémoire) :
    - cppfilt _ZN8VehiculeD2Ev
    - nm -C libvehiculetest.so
    Et je cherche les symboles présents dans ma lib et celui demandé.

    Sinon pour info :
    http://tldp.org/HOWTO/Program-Librar....html#CPP-VS-C

  7. #7
    Futur Membre du Club
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 12
    Points : 6
    Points
    6
    Par défaut
    Merci à tous pour vos réponses, malheureusement aucune d'elles n'a fonctionné dans mon cas :

    Eponyme, j'avais déjà essayé de compiler vehicule.o dans mon .so (ce qui est de toutes façons un peu trop sale), ça n'a pas fonctionné (toujours le même problème).

    Médinoc, es-tu sûr de ce que tu nous dis là ? Je n'ai entendu ça nulle part ailleurs et je ne comprends pas vraiment pourquoi il devrait en être ainsi ? Et de toutes façons, le symbol qu'il ne trouve pas semble être le constructeur de la classe mère que je ne peux pas mettre virtuel pur (si ?).

    Valefor, je ne connais pas l'utilitaire cppflit (et mon système non plus ^^), qu'est-il sensé faire ? J'avais déjà utilisé nm sur ma libvehiculetest.so, je n'ai pas compris la totalité de l'affichage mais _ZN8VehiculeD2Ev apparait bien...
    edit : et j'avais aussi essayé de mettre la déclaration de ma classe Vehicule dans un extern "C"{} sans plus de succès...

    Si vous avez d'autres idées...

  8. #8
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 573
    Points
    41 573
    Par défaut
    Eh bien, typiquement, si on charge dynamiquement une bibliothèque C++, c'est généralement un plug-in, car je ne vois aucun intéret à charger dynamiquement une bibliothèque C++ sinon.

    Alors, on se retrouve typiquement avec ceci:
    Une interface/classe abstraite:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class IUneInterface
    {
    	virtual void Delete() = 0;
     
    	virtual int FaireUntruc(int) = 0;
    	virtual void BlaBla(IUneAutreInterface *) = 0;
    };
    Une bibliothèque qui l'implémente et contient une fonction qui la retourne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    class CUneImplementation : public IUneInterface
    {
    	void Delete();
     
    	int FaireUnTruc(int);
    	void BlaBla(IUneAutreInterface *);
    };
     
    void CUneImplementation::Delete()
    {
    	delete this;
    }
     
    void CUneImplementation::FaireUnTruc(int a)
    {
    	return a+42;
    }
     
    void BlaBla(IUneAutreInterface *pObj)
    {
    	pObj->Toto();
    }
     
    //Fonction exportée
    extern "C" UNELIB_EXPORT IUneInterface * CreateUneInterfaceImpl(void)
    {
    	return new CUneImplementation();
    }
    Et l'utilisateur appelle cette lib pour utiliser l'objet:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    lib = dlopen("unelib.so");
    createFunc = dlsym(lib, "CreateUneInterfaceImpl");
    IUneInterface * iface = createFunc();
     
    ...
     
    iface->Delete();
    iface = 0;
    dlclose(lib)

  9. #9
    Futur Membre du Club
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 12
    Points : 6
    Points
    6
    Par défaut
    Je suis tout à fait d'accord sur le fait que la classe soit abstraite y tout y tout, mais je ne vois pas pourquoi cette interface ne pourrait pas définir des comportements par défaut pour certaines méthodes.
    Pour en revenir à mon cas, j'ai une classe mère Véhicule, abstraite, mais qui va implémenter certaines méthodes virtuelles comme avancer(), reculer(), etc. Ma classe VehiculeTest reprend le comportement par défaut de la classe mère, et à côté de ça j'ai une autre classe VehiculeTruc qui réimplémente toutes les méthodes.
    Il n'est donc a priori pas nécessaire d'avoir toutes les méthodes de la mère virtuelles pures...

    Il y a une chose que je ne comprend pas dans ton exemple : que signifie le UNELIB_EXPORT après ton extern "C" ?

  10. #10
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 573
    Points
    41 573
    Par défaut
    Le problème, c'est que ces comportements par défaut, ton .so doit y avoir accès.
    Donc, il faut inclure la définition de classe dans la DLL, car un .so ne peut pas charger dynamiquement des fonctions membres.

    Pour le UNELIB_EXPORT, tu peux l'ignorer, j'avais oublié qu'il n'y avait pas de définition de ce type sous nux.

  11. #11
    screetch
    Invité(e)
    Par défaut
    tu as en fait facons de faire marcher, je cite du moins portable au plus portable :

    * linker ton executable avec une option specifique -Wl,-E ou -E (depends si tu utilises gcc pour liker ou ld). cela exporte tous les symboles du programme et les rendra disponibles pour dlopen.

    * mettre ton code contenant la classe de base dans un .so, ta fonction main dans un programme qui est liée a ce .so

    * mettre ton code contenant la classe de base dans un .so, ta fonction main dans un programme qui est liée a ce .so ET linker ton plugin avec le code de base (seule version qui marchera sous windows)

    en gros, tu as :
    * main.cc contient le code qui charge le plugin
    * baseplug.cc contient le code de base du plugin
    * plug.cc contient le plugin lui meme

    premiere version :
    g++ -Wl,-E main.cc baseplug.cc
    g++ -shared plug.cc

    deuxieme :
    g++ -shared baseplug.cc -o libengine.so
    g++ main.cc -lengine
    g++ -shared plug.cc

    troisieme :
    g++ -shared baseplug.cc -o libengine.so
    g++ main.cc -lengine
    g++ -shared plug.cc -lengine

  12. #12
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 59
    Points : 33
    Points
    33
    Par défaut
    Et les -rdynamic ca ne fait pas la meme chose (rendre dispo tous les symboles) ?

    J'ai utilisé ca moi et ca semble fonctionner.

    epo

  13. #13
    Futur Membre du Club
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 12
    Points : 6
    Points
    6
    Par défaut
    Ok, merci Medinoc, je comprends mieux maintenant. C'est bizarre que lorsque j'ai testé avec ma classe Vehicule virtuelle pure le problème soit resté... Je retenterai ça un peu plus tard. Au fait, comment ça se passera avec le constructeur de la mère ? Il est forcément appelé à la construction de la fille, non ?

    screetch, merci pour tes différents exemples, mais il me faut impérativement un link dynamique (je ne sais pas quels seront les plugins présents lors de l'exécution) donc seule ta première solution m'intéresse. Et après l'avoir essayé, je me retrouve face au même message T_T.

    Cela dit je suis d'accord avec eponyme, normalement l'option -rdynamic devrait faire la même chose...

  14. #14
    screetch
    Invité(e)
    Par défaut
    les deux autres versions sont tout a fait dynamiques aussi. elles reviennent a compiler le code partagé dans une DLL, et la faire utiliser par l'executable principal. le plugin n'est pas changé!! essaye la solution numero 2 peut etre.

    l'option de gcc -rdynamic est traduite par -Wl,-E sous linux, mais est plus portable, bonne remarque!

    http://man.developpez.com/man3/dlopen.3.php
    recherche -rdynamic

    je viens de faire tous les essais sur un linux et ca a fonctionné dans les trois cas, d'ailleurs chaque executable généré, quelque soit la methode, est capable de charger tous les plugins, meme ceux qui ont été prévus pour une autre méthode (si je suis pas clair, disons que l'exe de la methode 1 avec le -rdynamic est capable de charger le plugin 3 linké avec le code dans un .so... si c'est toujours pas clair, dis toi juste que ca marche! )

  15. #15
    screetch
    Invité(e)
    Par défaut
    si ca ne marche pas, tu es sur de ne pas avoir oublier de coder une implementation ?

  16. #16
    Futur Membre du Club
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 12
    Points : 6
    Points
    6
    Par défaut
    Effectivement, j'avais lu un peu trop vite je crois, mea culpa ! Je testerai tout ça demain, je vous tiens au courant.

    Et non, toutes mes implémentations sont codées...

    Merci pour ton aide, bonne soiré.

  17. #17
    Futur Membre du Club
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 12
    Points : 6
    Points
    6
    Par défaut
    Bonsoir tout le monde !

    Bon, je me suis replongé dans mon petit problème et je l'ai réduit au plus simple afin de toruver d'où venait l'erreur... ben je n'ai pas trouvé !
    Je vous mets ici mon code, si quelqu'un voit d'où peut venir le problème ce serait fantastique !

    L'erreur retournée à l'exécution est :
    /chemin/vers/libfille.so: undefined symbol: _ZN4MereD2Ev

    Je compile avec gcc v4.1.3.

    Je joins aussi le code en PJ avec l'exécutable et le .so (des fois que ça vienne de chez moi...)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     #ifndef __MERE_HPP
    #define __MERE_HPP
     
    /**
       \file   mere.hpp
    **/
     
    #include <string>
    #include <map>
     
    using namespace std;
     
    class Mere
    {
      public:
     
        virtual ~Mere() = 0;
        virtual void doAnything() = 0;
    };
     
    typedef Mere * (*fabriqueMere) ();
    typedef void (*detruitMere) ( Mere * );
     
    extern map<string, fabriqueMere> Fabrique_Mere;
    extern map<string, detruitMere>  Detruit_Mere;
     
    #endif // __MERE_HPP
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #ifndef __FILLE_HPP
    #define __FILLE_HPP
     
    /**
       \file fille.hpp
    **/
     
    #include "mere.hpp"
     
    class Fille : public Mere
    {
      public:
     
        virtual void doAnything();
    };
     
    #endif // __FILLE_HPP
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     /**
       \file fille.cpp
    **/
     
    #include <iostream>
    #include "fille.hpp"
     
    using namespace std;
     
    void Fille::doAnything()
    {
      cout << "Je fais un truc..." << endl;
    }
     
     
    extern "C"
    {
      Mere * Create()
      { return new Fille(); }
     
      void Destroy( Mere * _m )
      { if ( _m ) delete _m; }
     
      class proxy
      {
        public:
          proxy()
          {
            Fabrique_Mere["fille"] = Create;
            Detruit_Mere["fille"]  = Destroy;
          }
      };
     
      proxy p;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     /**
       \file   main.cpp
    **/
     
    #include <iostream>
    #include <string>
     
    #include <dlfcn.h>     // chargement dynamique.
    #include "mere.hpp"
     
    using namespace std;
     
    map<string, fabriqueMere> Fabrique_Mere;
    map<string, detruitMere>  Detruit_Mere;
     
    int main( int argc, char ** argv )
    {
      void * desc = 0;
      desc = dlopen ( "/home/yann/Projet/testbench/libfille.so", RTLD_NOW );
      if ( !desc )
        cout << dlerror() << endl;
     
      return 0;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     CC = g++ 
    BIN = test
    CFLAGS = -Wall -ansi
    LIBS   = -ldl
     
    all: $(BIN) libfille.so
     
    $(BIN): main.o
        $(CC) $(CFLAGS) -rdynamic -o $(BIN) main.o $(LIBS)
     
    .cpp.o:
        $(CC) $(CFLAGS) -c $<
     
    libfille.so: fille.o
        g++ -shared -Wl,-soname,libfille.so -o libfille.so fille.o
    =================
    Voui désolé
    Fichiers attachés Fichiers attachés

  18. #18
    screetch
    Invité(e)
    Par défaut
    salut,

    le probleme vient de ton destructeur virtuel pur. tu peux le laisser virtuel pur (cela empeche d'instancier la classe) mais tu dois fournir une implementation quand meme. le symbole _ZN4MereD2Ev fait reference au destructeur (du moins le D)
    tout comme _ZN4MereC1Ev ferait reference a un constructeur.

  19. #19
    Futur Membre du Club
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 12
    Points : 6
    Points
    6
    Par défaut
    Salut screetch,

    Il semblerait que tu aies raison en effet, bien que je ne comprenne pas pourquoi... Je ne savais même pas qu'on pouvait définir une méthode qui est virtuelle pure (en tous cas on ne peut pas le faire dans le .hpp) !

    Enfin apparemment ce n'est pas le seul problème, je suis donc reparti du départ en ajoutant du code à mes classes petit à petit, et pour l'instant ça marche.

    Merci à tous de m'avoir aidé, je repasserai peut-être si j'ai d'autres problème ^^.

Discussions similaires

  1. Réponses: 2
    Dernier message: 08/03/2010, 23h46
  2. Héritage et ManyToOne sur classe mère
    Par ProximIT dans le forum JPA
    Réponses: 1
    Dernier message: 29/05/2009, 11h23
  3. Requête sur classe mère
    Par Galak extra dans le forum Hibernate
    Réponses: 7
    Dernier message: 22/08/2008, 15h19
  4. Problème sur classe Transformer avec les <!--
    Par tykool dans le forum Format d'échange (XML, JSON...)
    Réponses: 1
    Dernier message: 10/01/2006, 10h20
  5. Réponses: 3
    Dernier message: 04/12/2005, 15h08

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo