2013-06-13 16 views
15

Ho una libreria condivisa che implemento e voglio che il .so richiami una funzione nel programma principale che carica la libreria.Come può una libreria condivisa (.so) chiamare una funzione che è implementata nel suo programma di caricamento?

Diciamo che ho main.c (eseguibile) che contiene:

void inmain_function(void*); 
dlopen("libmy.so"); 

Nel my.c (il codice per il libmy.so) voglio chiamare inmain_function:

inmain_function(NULL); 

Come può la libreria condivisa chiamare inmain_function indipendentemente dal fatto che inmain_function sia definito nel programma principale.

Nota: Voglio chiamare un simbolo in main.c da my.c non viceversa, che è l'uso comune.

risposta

15

Avrete bisogno di creare una funzione di registro nel vostro .so in modo che l'eseguibile possa dare un puntatore a funzione al vostro .so per il suo uso successivo.

Ti piace questa:

void in_main_func() { 
// this is the function that need to be called from a .so 
} 

void (*register_function)(void(*)()); 
void *handle = dlopen("libmylib.so"); 

register_function = dlsym(handle, "register_function"); 

register_function(in_main_func); 

il register_function ha la necessità di memorizzare il puntatore a funzione in una variabile nella .so in cui l'altra funzione nella .so può trovare.

tuo mylib.c sarebbe la necessità di guardare qualcosa di simile:

void (*callback)() = NULL; 

void register_function(void (*in_main_func)()) 
{ 
    callback = in_main_func(); 
} 

void function_needing_callback() 
{ 
    callback(); 
} 
+2

Preferisco fare 'if (callback) {callback(); ritorno 0; } else {return -1; } 'per indicare un errore ed evitare di chiamare' NULL' (che sarebbe fatale). – glglgl

+0

A destra, rimosso il pugno. – user746527

0

che segue può essere utilizzato per caricare una libreria dinamica nel codice (nel caso in cui qualcuno è venuto qui dopo aver guardato come fare):

void* func_handle = dlopen ("my.so", RTLD_LAZY); /* open a handle to your library */ 

void (*ptr)() = dlsym (func_handle, "my_function"); /* get the address of the function you want to call */ 

ptr(); /* call it */ 

dlclose (func_handle); /* close the handle */ 

Non dimenticare di mettere #include <dlfcn.h> e legame con l'opzione –ldl.

Si potrebbe anche voler aggiungere una logica che controlli se viene restituito NULL. Se è il caso, è possibile chiamare dlerror e dovrebbe darvi alcuni messaggi significativi che descrivono il problema.

Altri poster hanno comunque fornito risposte più adatte al tuo problema.

+0

Voglio chiamare da .so a una funzione in un file verrà caricato il .so – 0x90

+0

Sì, questo chiamerà una funzione nella libreria '.so' da un altro file (come il tuo dove è il tuo principale). È questo che volevi dire? – Nobilis

+0

Voglio chiamare la funzione in main da .so. – 0x90

22

Hai due opzioni, da cui è possibile scegliere:

Opzione 1: esportare tutti i simboli dal vostro eseguibile. Questa è un'opzione semplice, solo quando si costruisce un file eseguibile, aggiungere un flag -Wl,--export-dynamic. Ciò renderebbe tutte le funzioni disponibili per le chiamate in biblioteca.

Opzione 2: creare un file di simboli di esportazione con un elenco di funzioni e utilizzare -Wl,--dynamic-list=exported.txt. Ciò richiede una manutenzione, ma più accurata.

Per dimostrare: semplice eseguibile e libreria caricata dinamicamente.

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

void exported_callback() /*< Function we want to export */ 
{ 
    printf("Hello from callback!\n"); 
} 

viud unexported_callback() /*< Function we don't want to export */ 
{ 
    printf("Hello from unexported callback!\n"); 
} 

typedef void (*lib_func)(); 

int call_library() 
{ 
    void  *handle = NULL; 
    lib_func func = NULL; 
    handle = dlopen("./libprog.so", RTLD_NOW | RTLD_GLOBAL); 
    if (handle == NULL) 
    { 
     fprintf(stderr, "Unable to open lib: %s\n", dlerror()); 
     return -1; 
    } 
    func = dlsym(handle, "library_function"); 

    if (func == NULL) { 
     fprintf(stderr, "Unable to get symbol\n"); 
     return -1; 
    } 

    func(); 
    return 0; 
} 

int main(int argc, const char *argv[]) 
{ 
    printf("Hello from main!\n"); 
    call_library(); 
    return 0; 
} 

Codice libreria (lib.c):

#include <stdio.h> 
int exported_callback(); 

int library_function() 
{ 
    printf("Hello from library!\n"); 
    exported_callback(); 
    /* unexported_callback(); */ /*< This one will not be exported in the second case */ 
    return 0; 
} 

Quindi, prima costruire la biblioteca (questo passaggio non differisce):

gcc -shared -fPIC lib.c -o libprog.so 

Ora costruire eseguibile con tutti i simboli esportati:

gcc -Wl,--export-dynamic main.c -o prog.exe -ldl 

Run esempio:

$ ./prog.exe 
Hello from main! 
Hello from library! 
Hello from callback! 

Simboli expo ari:

$ objdump -e prog.exe -T | grep callback 
00000000004009f4 g DF .text 0000000000000015 Base  exported_callback 
0000000000400a09 g DF .text 0000000000000015 Base  unexported_callback 

Ora, con l'elenco esportato (exported.txt):

{ 
    extern "C" 
    { 
     exported_callback; 
    }; 
}; 

Corporatura & controllo simboli visibili:

$ gcc -Wl,--dynamic-list=./exported.txt main.c -o prog.exe -ldl 
$ objdump -e prog.exe -T | grep callback 
0000000000400774 g DF .text 0000000000000015 Base  exported_callback 
+0

quando si compila libprog.so in questo modo troverà che exported_callback è un simbolo mancante. – kritzikratzi

+1

ps. per rispondere alla mia domanda: si può disabilitare l'errore "simbolo non definito" e funzionerà, ad esempio in clang con '-undefined dynamic_lookup'. su Linux con gcc funziona magicamente come descritto nel post, ma non ho idea del perché. – kritzikratzi

4
  1. Mettere prototipo la funzione principale di un .h file e includerlo nel codice della libreria principale e dinamico.

  2. Con GCC, è sufficiente compilare il programma principale con il flag -rdynamic.

  3. Una volta caricata, la libreria sarà in grado di chiamare la funzione dal programma principale.

Un po 'ulteriore spiegazione è che, una volta compilato, la libreria dinamica avrà un simbolo indefinito in esso per la funzione che è nel codice principale. Quando l'app principale carica la libreria, il simbolo verrà risolto dalla tabella dei simboli del programma principale. Ho usato il modello di cui sopra numerose volte e funziona come un fascino.

+1

Solo curioso, cosa succede se un'altra lib condivisa ha anche il simbolo? – user746527

+0

Il simbolo dell'applicazione principale sostituisce/sovrascrive la libreria, a meno che la funzione della libreria non sia stata definita con la parola chiave 'static'. – mshildt

Problemi correlati