web-dev-qa-db-fra.com

Comment une bibliothèque partagée (.so) peut-elle appeler une fonction implémentée dans son programme de chargement?

J'ai une bibliothèque partagée que j'ai implémentée et je veux que le .so appelle une fonction implémentée dans le programme principal qui charge la bibliothèque.

Disons que j'ai main.c (exécutable) qui contient:

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

Dans my.c (le code de libmy.so), je souhaite appeler inmain_function:

inmain_function(NULL);

Comment la bibliothèque partagée peut-elle appeler inmain_function indépendamment du fait que inmain_function est défini dans le programme principal? 

Note: Je veux appeler un symbole dans main.c depuis my.c et non l'inverse qui est l'usage courant.

18
0x90

Vous aurez besoin de créer une fonction de registre dans votre .so pour que l’exécutable puisse donner un pointeur de fonction à votre .so pour une utilisation ultérieure.

Comme ça:

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 register_function doit stocker le pointeur de fonction dans une variable du fichier .so où l'autre fonction du fichier .so peut le trouver.

Votre mylib.c aurait besoin de ressembler à ceci:

void (*callback)() = NULL;

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

void function_needing_callback() 
{
     callback();
}
13
user746527

Vous avez le choix entre deux options:

Option 1: exporter tous les symboles de votre exécutable . Cette option est simple, ajoutez un indicateur -Wl,--export-dynamic lors de la création d'un exécutable. Cela rendrait toutes les fonctions disponibles pour les appels à la bibliothèque.

Option 2: créez un fichier de symboles d'exportation avec une liste de fonctions et utilisez -Wl,--dynamic-list=exported.txt. Cela nécessite un peu de maintenance, 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");
}

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;
}

Code de la 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, commencez par construire la bibliothèque (cette étape ne diffère pas):

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

Maintenant, construisez un 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;
    };
};

Construire 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
28
Valeri Atamaniouk
  1. Placez le prototype de votre fonction principale dans un fichier .h et incluez-le dans le code de votre bibliothèque principale et dynamique.

  2. Avec GCC, compilez simplement votre programme principal avec l’indicateur -rdynamic.

  3. Une fois chargée, votre bibliothèque pourra appeler la fonction à partir du programme principal.

Une petite explication supplémentaire est qu’une fois compilée, votre bibliothèque dynamique contiendra un symbole non défini pour la fonction figurant 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.

5
mshildt