2008-12-25 7 views
8

Cuando se carga una biblioteca compartida se abre a través de la función dlopen(), ¿hay alguna forma de llamar funciones en el programa principal?¿Cómo llamaría una función de biblioteca cargada a un símbolo en la aplicación principal?

+0

Las respuestas a continuación hacen un buen trabajo respondiendo la pregunta, pero tengo que preguntar: ¿podría explicar el contexto más amplio de este requisito? Cuando he encontrado la necesidad de hacer esto, es para construir un modelo de extensión/complemento o porque mi programa no está muy bien factorizado. – reuben

+0

también podría usarse para control de inversión, ¿no? Defina el flujo de la aplicación en la biblioteca mientras la implementación real está en la aplicación principal – hhafez

+0

Piense en un módulo Perl XS. Necesita usar funciones de Perl de bajo nivel (digamos newSViv() para crear un SV a partir de un entero); es conveniente si el módulo usa la nueva función de Vivo() de Perl, en lugar de incrustar su propia copia en el objeto compartido del módulo. Además, el código necesita la biblioteca C estándar. –

Respuesta

17

Código de dlo.c (lib):

#include <stdio.h> 

// function is defined in main program 
void callb(void); 

void test(void) { 
    printf("here, in lib\n"); 
    callb(); 
} 

compilar con

gcc -shared -olibdlo.so dlo.c 

Aquí el código del programa principal (copiado de la página de manual de dlopen, y ajustado):

#include <stdio.h> 
#include <stdlib.h> 
#include <dlfcn.h> 

void callb(void) { 
    printf("here, i'm back\n"); 
} 

int 
main(int argc, char **argv) 
{ 
    void *handle; 
    void (*test)(void); 
    char *error; 

    handle = dlopen("libdlo.so", RTLD_LAZY); 
    if (!handle) { 
     fprintf(stderr, "%s\n", dlerror()); 
     exit(EXIT_FAILURE); 
    } 

    dlerror(); /* Clear any existing error */ 

    *(void **) (&test) = dlsym(handle, "test"); 

    if ((error = dlerror()) != NULL) { 
     fprintf(stderr, "%s\n", error); 
     exit(EXIT_FAILURE); 
    } 

    (*test)(); 
    dlclose(handle); 
    exit(EXIT_SUCCESS); 
} 

Construir con

gcc -ldl -rdynamic main.c 

Salida:

[[email protected] dlopen]$ LD_LIBRARY_PATH=. ./a.out 
here, in lib 
here, i'm back 
[[email protected] dlopen]$ 

La opción -rdynamic pone todos los símbolos en la tabla dinámica símbolo (que está asignada en la memoria), no sólo los nombres de los símbolos utilizados . Lea más al respecto here. Por supuesto, también puede proporcionar punteros a funciones (o una estructura de indicadores de función) que definan la interfaz entre la biblioteca y su programa principal. En realidad, es el método que elegiría probablemente. Escuché de otras personas que no es tan fácil hacer -rdynamic en Windows, y también haría una comunicación más clara entre la biblioteca y el programa principal (tienes un control preciso de lo que se puede llamar y no), pero también requiere más mantenimiento de la casa.

+0

Impresionante responder. Alguien ha ganado 10k rep :-) –

+0

Esa es una forma interesante de hacer el casting. ¿No es más normal convertir el valor de retorno de dlsym() al puntero para funcionar, en lugar de pretender que el puntero al que está asignando es del mismo tipo que la función que devuelve dlsym()? –

+0

C no dice qué sucede si lanza desde un vacío * a un puntero de función. lamentablemente no encontré el párrafo que establezca que es un comportamiento indefinido, pero la página de manual dice que así lo dice. –

4

Sí, si proporciona a su biblioteca un puntero a esa función, estoy seguro de que la biblioteca podrá ejecutar/ejecutar la función en el programa principal.

Aquí es un ejemplo, no han compilado así que ten cuidado;)

/* in main app */ 

/* define your function */ 

int do_it(char arg1, char arg2); 

int do_it(char arg1, char arg2){ 
    /* do it! */ 
    return 1; 
} 

/* some where else in main app (init maybe?) provide the pointer */ 
LIB_set_do_it(&do_it); 
/** END MAIN CODE ***/ 

/* in LIBRARY */ 

int (*LIB_do_it_ptr)(char, char) = NULL; 

void LIB_set_do_it(int (*do_it_ptr)(char, char)){ 
    LIB_do_it_ptr = do_it_ptr; 
} 

int LIB_do_it(){ 
    char arg1, arg2; 

    /* do something to the args 
    ... 
    ... */ 

    return LIB_do_it_ptr(arg1, arg2); 
} 
+0

do_it_ptr toma un puntero a una función que espera 3 argumentos char; usted asigna punteros a función para funciones que solo toman 2 argumentos char. La declaración externa para doit() apenas se necesita. Do_it_ptr no es necesario; simplemente puede pasar do_it por su nombre donde actualmente pasa do_it_ptr. Etc! –

+0

eso es correcto :) de hecho, también podría deshacerse de LIB_get_it() y simplemente definir un nuevo LIB_do_it (int (* do_it_ptr) (char, char, char)) {return do_it_ptr (arg1, arg2, arg3)} – hhafez

1

La función dlopen(), como se describe en @litb, se proporciona principalmente en sistemas que utilizan archivos de objetos de formato ELF. Es bastante potente y le permitirá controlar si los símbolos a los que hace referencia la biblioteca cargada se pueden satisfacer desde el programa principal y, en general, permite que se satisfagan. No todos los sistemas de carga de bibliotecas compartidas son tan flexibles, tenga en cuenta si se trata de portar su código.

El mecanismo de devolución de llamada descrito por @hhafez funciona ahora que las fallas en ese código están enderezadas.

+0

enderezó los dobleces;) gracias – hhafez

Cuestiones relacionadas