2010-08-30 11 views
6

Estoy cargando dinámicamente (con dlopen()) un objeto compartido (llamado libprofile1.so) de main.restricciones al cargar dinámicamente un objeto compartido desde otro objeto compartido?

En libprofile1.so he definido la función de fábrica CreateProfile y la clase Profile. La función CreateProfile crea una instancia de la clase Profile y le devuelve un puntero. La clase Profile tiene un método pMethod.

En principal, después de la carga libprofile1.so, llamo CreateProfile método que devuelve el puntero al objeto de Profile clase (lo llaman p).
Después, estoy llamando al método pMethod contra el objeto p (p->pMethod). En este método, estoy cargando dinámicamente otro objeto compartido (libdatasources.so).

En este objeto compartido tengo una función de fábrica CreateDataSource y clase DataSource.
CreateDataSource función crea una instancia de clase DataSource y le devuelve un puntero. La clase DataSource tiene el método dsMethod.

Como puede observar, las estructuras de ambos objetos compartidos son similares.

De pMethod después de cargar libdatasources.so estoy llamando CreateDataSource método, lo que me devuelve un puntero a una instancia de la clase DataSource, llamarlo ds. Luego llamo al dsMethod del ds objeto
(ds->dsMethod).


Ahora, el problema está siguiendo.

cuando intento llamar dsMethod de ds objeto, objeto compartido que soy la primera carga (libprofile1.so) no se carga. En realidad, dlopen() devuelve NULL. Cuando leí dlerror después dlopen me sale:

./libprofile1.so: undefined symbol: _ZN18DataSource13dsMethod

Así que si tengo una llamada ds->Method, que el primer objeto compartido no se carga!
Si hago un comentario llame al ds->dsMethod desde la fuente, entonces mi libprofile1.so y libdatasources.so se cargan sin ningún problema.
No veo la conexión entre la llamada de un método desde el segundo SO, con la carga primero SO ???

Tal vez no sé, pero ¿hay alguna restricción cuando se carga dinámicamente un objeto compartido, desde un objeto compartido que también se ha cargado dinámicamente?

Btw, dlopen se utiliza con RTLD_NOW|RTLD_GLOBAL. Intenté con RTLD_LAZY, pero sigue siendo el mismo problema.

ACTUALIZACIÓN:

bibliotecas se construyen en Eclipse. Las opciones para el compilador y el enlazador G ++ son las mismas para ambas bibliotecas.
Éstos son G ++ Compiler:

-O0 -g3 -Wall -c -fmessage-length=0 

y G ++ enlazador:

-shared 

opciones, pegado de Project Properties -> Settings -> Tool Settings

Gracias de antemano.

+0

¿Puede actualizar su pregunta indicando cómo se crean libprofile1.so y libdataresources.so? –

Respuesta

3

Si carga dinámicamente una DLL, debe asegurarse de que no tenga símbolos sin resolver.

La forma más fácil de hacerlo es vincularlo con las otras DLL que necesita para que se carguen automáticamente en lugar de tener que cargar y resolver todas las dependencias manualmente.

Así que si libprofile1 siempre usa libdatasources asegúrese de que estén unidos entre sí.

Si debe hacerlo manualmente, invierta el orden en que carga las bibliotecas. De modo que cuando cargue libprofile1 las funciones que necesita ya se han cargado.

4

Como señaló Martin York, así es como funciona en Linux. Al vincular con una biblioteca, también debe vincular a todas las dependencias. Eso es diferente en Windows, las DLL se encargan de sus propias dependencias. Cuando carga dinámicamente una biblioteca que tiene otra biblioteca como dependencia, primero debe cargar esa biblioteca con el indicador RTLD_GLOBAL. Esto es bastante extraño, ya que es posible que no sepas qué dependencias necesitan otros objetos compartidos, o las dependencias pueden cambiar con una versión más nueva que sea compatible con otros binarios. Por lo que sé (y por leer las páginas de manual de g ++ y ld), no es posible crear un comportamiento similar al de las DLL de Windows. Aquí hay un poco de caso de prueba:

two.cpp:

#include <iostream> 

extern "C" 
{ 
    void bar() 
    { 
     std::cout << "bar()\n"; 
    } 
} 

one.cpp:

#include <iostream> 

extern "C" 
{ 
    void bar(); 

    void foo() 
    { 
     std::cout << "foo()\n"; 
     bar(); 
    } 
} 

test.cpp:

#include <dlfcn.h> 
#include <iostream> 

int main (int argc, char *argv[]) 
{ 
    using namespace std; 
//  void *libtwo = dlopen("./libtwo.so", RTLD_NOW | RTLD_GLOBAL); 
    void *libone = dlopen("./libone.so", RTLD_NOW); 
    if (!libone) 
    { 
     cout << "dlopen(libone.so) failed: " << dlerror() << "\n"; 
     return 1; 
    } 
    typedef void (*foo_t)(); 
    foo_t foo = reinterpret_cast<foo_t>(dlsym(libone, "foo")); 
    if (!foo) 
    { 
     cout << "dlsym(libone.so, foo) failed\n"; 
     return 2; 
    } 
} 

one.cpp se compila en libone.so y two.cpp en libtwo.so, test.cpp se compila en el test binario. Esto fallará y solo tendrá éxito cuando la línea comentada no esté comentada.

+0

ejemplo bastante similar, pero la cosa es que en 'foo()' estoy cargando 'libtwo.so' y luego obteniendo el puntero de la función a' bar() ', al cual llamo desde' foo() 'también. Estoy usando una solución para este problema con una función de envoltura 'barWrap()' para 'bar()'. 'barWrap()' no es la parte de la clase que estoy usando ('DataSource'), sino función independiente exportada. Desde esta función llamo a 'bar()' (en mi caso 'ds-> dsMethod') y funciona bien. Por cierto, utilicé 'RTLD_NOW | RTLD_GLOBAL' como el argumento de' dlopen' pero todavía nada. Es un problema bastante extraño, no puedo entenderlo. –

+0

¿Está usted vinculando libone.so contra libtwo.so (-ltwo para el enlazador)? dlopen() no debería tener problemas para cargar esto. Agregaré mis líneas de comando de gcc como un nuevo comentario. –

+1

$ g ++ -shared -fpic -o libtwo.so -Wl, -no-undefined two.cpp $ g ++ -shared -fpic -o libone.so -Wl, -no-undefined one.cpp -ltwo -L. -Wl, -ruta ,. $ g ++ test.cpp -ldl $ ./a.out –

Cuestiones relacionadas