GNU/Linux >> Tutoriels Linux >  >> Linux

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

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

  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 le -rdynamic drapeau.

  3. 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.


Linux
  1. Linux – Comment s'assurer qu'une bibliothèque partagée aura ses pages mémoire partagées par plusieurs processus ?

  2. Chargement des bibliothèques partagées et utilisation de RAM ?

  3. Pourquoi /dev/null est-il un fichier ? Pourquoi sa fonction n'est-elle pas implémentée comme un programme simple ?

  4. Comment créer et appeler des fonctions dans Bash

  5. Comment initialiser une bibliothèque partagée sous Linux

Comment faire le versioning d'une bibliothèque partagée sous Linux ?

Comment puis-je obtenir ce que ma fonction principale a renvoyé?

Comment la fonction eval peut-elle être utilisée dans le makefile ?

Comment configurer googleTest en tant que bibliothèque partagée sous Linux

Comment puis-je créer un lien vers une ancienne version d'une bibliothèque partagée

Impossible d'appeler la fonction de bibliothèque standard C sur Linux 64 bits à partir du code d'assemblage (yasm)