Hai due opzioni, tra le quali puoi scegliere:
Opzione 1:esporta tutti i simboli dal tuo eseguibile. Questa è un'opzione semplice, proprio durante la creazione dell'eseguibile, aggiungi un flag -Wl,--export-dynamic
. Ciò renderebbe tutte le funzioni disponibili per le chiamate alla libreria.
Opzione 2:creare un file di simboli di esportazione con un elenco di funzioni e utilizzare -Wl,--dynamic-list=exported.txt
. Ciò richiede un po' di manutenzione, ma più accurato.
Per dimostrare:semplice libreria eseguibile e caricata dinamicamente.
#include <stdio.h>
#include <dlfcn.h>
void exported_callback() /*< Function we want to export */
{
printf("Hello from callback!\n");
}
void 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 costruisci la libreria (questo passaggio non differisce):
gcc -shared -fPIC lib.c -o libprog.so
Ora crea un eseguibile con tutti i simboli esportati:
gcc -Wl,--export-dynamic main.c -o prog.exe -ldl
Eseguire l'esempio:
$ ./prog.exe
Hello from main!
Hello from library!
Hello from callback!
Simboli esportati:
$ 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;
};
};
Costruisci e controlla i 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
Dovrai creare una funzione di registro nel tuo .so in modo che l'eseguibile possa fornire un puntatore a funzione al tuo .so per un uso successivo.
In questo modo:
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);
la register_function deve memorizzare il puntatore di funzione in una variabile nel .so dove l'altra funzione nel .so può trovarlo.
Il tuo mylib.c dovrebbe assomigliare a questo:
void (*callback)() = NULL;
void register_function( void (*in_main_func)())
{
callback = in_main_func();
}
void function_needing_callback()
{
callback();
}
-
Inserisci il prototipo della tua funzione principale in un file .h e includilo sia nel codice della libreria principale che in quello dinamico.
-
Con GCC, compila semplicemente il tuo programma principale con
-rdynamic
bandiera. -
Una volta caricata, la tua libreria sarà in grado di chiamare la funzione dal programma principale.
Una piccola ulteriore spiegazione è che una volta compilata, la tua libreria dinamica avrà un simbolo indefinito per la funzione che si trova nel codice principale. Dopo che l'app principale ha caricato la libreria, il simbolo verrà risolto dalla tabella dei simboli del programma principale. Ho usato lo schema sopra numerose volte e funziona a meraviglia.