Vous avez deux options, parmi lesquelles vous pouvez choisir :
Option 1 :exportez tous les symboles de votre exécutable.C'est une option simple, juste lors de la construction de l'exécutable, ajoutez un indicateur -Wl,--export-dynamic
. Cela rendrait toutes les fonctions disponibles pour les appels de bibliothèque.
Option 2 :créez un fichier de symboles d'exportation avec la liste des fonctions et utilisez -Wl,--dynamic-list=exported.txt
. Cela nécessite un peu d'entretien, mais plus précis.
Pour démontrer :une bibliothèque exécutable simple et chargée dynamiquement.
#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;
}
Code bibliothèque (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;
}
Donc, construisez d'abord la bibliothèque (cette étape ne diffère pas) :
gcc -shared -fPIC lib.c -o libprog.so
Construisez maintenant l'exécutable avec tous les symboles exportés :
gcc -Wl,--export-dynamic main.c -o prog.exe -ldl
Exemple d'exécution :
$ ./prog.exe
Hello from main!
Hello from library!
Hello from callback!
Symboles exportés :
$ objdump -e prog.exe -T | grep callback
00000000004009f4 g DF .text 0000000000000015 Base exported_callback
0000000000400a09 g DF .text 0000000000000015 Base unexported_callback
Maintenant avec la liste exportée (exported.txt
):
{
extern "C"
{
exported_callback;
};
};
Créer et vérifier les symboles visibles :
$ 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
Vous aurez besoin de créer une fonction de registre dans votre .so afin que l'exécutable puisse donner un pointeur de fonction à votre .so pour une utilisation ultérieure.
Comme ceci :
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 fonction_registre doit stocker le pointeur de fonction dans une variable du .so où l'autre fonction du .so peut le trouver.
Votre mylib.c devrait ressembler à ceci :
void (*callback)() = NULL;
void register_function( void (*in_main_func)())
{
callback = in_main_func();
}
void function_needing_callback()
{
callback();
}
-
Placez le prototype de votre fonction principale dans un fichier .h et incluez-le dans le code de votre bibliothèque principale et dynamique.
-
Avec GCC, compilez simplement votre programme principal avec le
-rdynamic
drapeau. -
Une fois chargée, votre bibliothèque pourra appeler la fonction depuis le programme principal.
Une petite explication supplémentaire est qu'une fois compilée, votre bibliothèque dynamique contiendra un symbole indéfini pour la fonction qui se trouve dans le code principal. Lorsque votre application principale charge la bibliothèque, le symbole sera résolu par la table des symboles du programme principal. J'ai utilisé le modèle ci-dessus à plusieurs reprises et cela fonctionne comme un charme.