2009-07-01 13 views
15

Tengo una aplicación a la que uso bibliotecas compartidas. Estas bibliotecas están vinculadas en tiempo de compilación.
En tiempo de ejecución, el cargador espera que el objeto compartido esté en el LD_LIBRARY_PATH; si no se encuentra, la aplicación falla con el error "no se pueden cargar bibliotecas compartidas". Tenga en cuenta que no hay garantía de que el cliente tenga la biblioteca. caso, quiero que la aplicación deje un mensaje de error adecuado, también la parte independiente debería funcionar correctamente.Alternativas a dlsym() y dlopen() en C++

Para este propósito estoy usando dlsym() y dlopen() para usar la API en la biblioteca compartida. El problema con esto es que si tengo muchas funciones en la API, tengo que acceder a ellas individualmente usando dlsym() y ptrs que en mi caso conducen a daños en la memoria y fallas del código.

¿Hay alguna alternativa para esto?

Respuesta

29

La solución común a su problema es declarar una tabla de punteros a funciones, hacer un solo dlsym() para encontrarlo y luego llamar a todas las otras funciones mediante un puntero a esa tabla. Ejemplo (no probado):

// libfoo.h 
struct APIs { 
    void (*api1)(void); 
    void *(*api2)(int); 
    long (*api3)(int, void *); 
}; 

// libfoo.cc 
void fn1(void) { ... } 
void *fn2(int) { ... } 
long fn3(int, void *) { ... } 

APIs api_table = { fn1, fn2, fn3 }; 


// client.cc 
#include "libfoo.h" 
... 
    void *foo_handle = dlopen("libfoo.so", RTLD_LAZY); 
    if (!foo_handle) { 
    return false;   // library not present 
    } 
    APIs *table = dlsym(foo_handle, "api_table"); 
    table->api1();    // calls fn1 
    void *p = table->api2(42); // calls fn2 
    long x = table->api3(1, p); // calls fn3 

P.S. Acceder a sus funciones de API individualmente usando dlsym y punteros no hace que en sí mismo conduzca a daños en la memoria y fallas. Lo más probable es que solo tenga errores.

EDITAR:
Puede utilizar esta misma técnica exacta con una biblioteca de terceros. Crea un libdrmaa_wrapper.so y pon el api_table en él. Enlace el contenedor directamente en libdrmaa.so.

En el ejecutable principal, dlopen("libdrmaa_wrapper.so", RTLD_NOW). Este dlopen tendrá éxito si (y solo si) libdrmaa.so está presente en el tiempo de ejecución y proporciona todas las funciones de API que utilizó en el api_table. Si tiene éxito, una sola llamada dlsym le dará acceso a la API completa.

+0

y cómo llamo a dlopen() para obtener el foo_handle? Quiero decir, ¿cargará el objeto compartido de la API, digamos libAPI.so automáticamente? – sud03r

+0

Es dlopen() en realidad para abrir la biblioteca ... :) .. de todos modos tengo tu punto ... pero esto funciona solo si hay una estructura de API en la biblioteca ... que no puedes esperar con un tercero biblioteca ... Estoy tratando de utilizar sungrid api libdrmaa.so .. – sud03r

+0

En lugar de obtener un puntero a una tabla, en su lugar podría obtener un puntero a una función que cuando se llama devuelve el puntero a la tabla. Esto permite que el complemento se inicialice antes de llamar a cualquier otra función, e incluso crea la tabla de forma dinámica. – CesarB

2

Puede envolver su aplicación con otra que primero compruebe todas las bibliotecas requeridas, y si algo falta, se equivoca, pero si todo está bien, ejecuta la aplicación real.

+0

Es bastante común para una aplicación para tener un script que creó el LD_LIBRARY_PATH antes de comenzar la aplicación. –

+0

El problema no es ese ... el problema es que si la biblioteca no está presente con el cliente, ese módulo no debería funcionar, el resto del código debería funcionar correctamente ... pero en este caso si la biblioteca no se encuentra, el código falla ejecutar. – sud03r

-1

Su problema es que la resolución de los símbolos no resueltos se hace desde el principio - en Linux Creo que los símbolos de datos se resuelven al inicio del proceso, y los símbolos de las funciones se ejecutan con pereza. Por lo tanto, dependiendo de qué símbolos tenga sin resolver, y de qué tipo de inicialización estática está teniendo lugar, es posible que no tenga la oportunidad de ingresar con su código.

Mi sugerencia sería tener una aplicación contenedora que atrape el código de retorno/serie de error "no se pueden cargar bibliotecas compartidas", y luego lo convierte en algo más significativo. Si esto es genérico, no será necesario actualizarlo cada vez que agregue una nueva biblioteca compartida.

Alternativamente, podría tener su secuencia de comandos de envoltura ejecutar ldd y luego analizar la salida, ldd informará todas las bibliotecas que no se encuentran para su aplicación en particular.

0

Uso de abajo tipo de código

Class DynLib 
{ 
    /* All your functions */ 
    void fun1() {}; 
    void fun2() {}; 
    . 
    . 
    . 
} 

DynLib* getDynLibPointer() 
{ 
    DynLib* x = new Dynlib; 
    return x; 
} 

uso dlopen() para la carga de esta biblioteca en tiempo de ejecución. y use dlsym() y llame al getDynLibPointer() que devuelve el objeto DynLib. desde este objeto puede acceder a todas sus funciones jst como obj.fun1() .....

Este es, desde luego, un estilo C++ del método struct propuesto anteriormente.

0

Probablemente esté buscando algún tipo de carga de biblioteca de demora en Linux. No está disponible de fábrica, pero puede imitarlo fácilmente al crear una pequeña biblioteca de stub estática que intentaría dlopen biblioteca necesaria en la primera llamada a cualquiera de sus funciones (emitiendo un mensaje de diagnóstico y finalizando si dlopen falló) y luego reenviar todas las llamadas a él.

Dichas bibliotecas de código auxiliar pueden ser escritos a mano, generada por el proyecto/script-biblioteca específica o generado por la herramienta universal de Implib.so:

$ gen-implib.py libxyz.so 
$ gcc myapp.c libxyz.tramp.S libxyz.init.c ...