Partage
  • Partager sur Facebook
  • Partager sur Twitter

Exécuter une fonction C externe dans le programme

Sujet résolu
    12 août 2019 à 12:58:12

    Bonjour :)

    Je souhaiterais, dans l'intérêt d'un interpréteur de ma création, pourvoir utiliser des fonctions externes et "inconnues"; en gros faire une FFI.

    Naïvement, je me dis qu'il suffit d'exécuter dans le programme actuel (donc dans mon interpréteur) une fonction en provenance d'un fichier C externe, par exemple :

    // FonctionExterne.c
    
    #include <stdio.h>
    #include <stdlib.h>
    
    int test(int x) {
        printf("'test' va renvoyer %d.", x * x); // Il faudrait évidemment pouvoir exécuter n'importe quoi
        return x * x;
    }

    J'aimerai si possible ne pas dépendre d'un compilateur C externe, comme GCC ou CLang pour la portabilité.

    Voici comment j'imagine l'appel dans mon interpréteur :

    // Dans mon interpréteur
    
    
    void executeExternC(FILE file, char* fname, int* args) {
        FROM file EXECUTE fname WITH args;
    }

    Mais je ne sais pas

    • Si c'est possible ;
    • Comment faire
    • Si c'est une bonne façon de faire

    Pour mon interpréteur, cela ressemble à faire en C

    extern "C" int test(int);

    mais évidemment, on ne peut pas faire ça de manière dynamique, au runtime.

    Avez-vous une idée ? :(

    • Partager sur Facebook
    • Partager sur Twitter
    Le doute est le commencement de la sagesse
      12 août 2019 à 13:45:38

      Salut !

      Ce que tu veux faire, c'est le rôle d'une dll (windows) ou d'un .so (Linux).

      En fait, si tu as déjà compilé un .c avec ta fonction devant en exe, c'est trop tard : la fonction n'existe plus. c'est devenu juste une adresse. Donc tu ne pourras jamais l'appeler de dehors.

      La dll, le principe, c'est que c'est comme un programme sans main, mais avec une ou plusieurs fonctions dites "exportées" (qu'il faudra définir comme exportées) et qui seront un (ou plusieurs) point d'entrée que tu pourras du coup appeler depuis un autre programme.

      • Partager sur Facebook
      • Partager sur Twitter

      Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

        12 août 2019 à 14:54:29

        D'accord, je vois, merci pour ta réponse :)

        Si je comprend bien, je devrais dès lors compiler FonctionExtern.c au format DLL, pour ensuite pouvoir utiliser cette DLL dans mon interpréteur, et donc avoir accès à ma fonction test ?

        • Partager sur Facebook
        • Partager sur Twitter
        Le doute est le commencement de la sagesse
          14 août 2019 à 20:17:29

          J'ai retrouvé comment on fait (sous Linux)

          Exemple : imaginons des plugins, qui ont chacun une fonction hello () qui affiche un message dans une langue différente

          // french.c
          
          #include <stdio.h>
          
          void hello() {
              printf("Bonjour, monde !\n");
          }
          


          On le compile avec les options qui vont bien (attention à l'ordre -fPIC -shared)

          cc  -fPIC -shared  french.c -o french.so
          

          et ça nous fabrique une bibliothèque dynamique

          $ ls -l french.so 
          -rwxr-xr-x 1 billaud billaud 15976 août  14 20:06 french.so
          $ file french.so 
          french.so: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=22a4c72e36a5316334c6de7020b66b0553b0283b, not stripped
          

          Maintenant, on veut un exécutable  capable de charger ce plugin ou un du même genre, et surtout d'exécuter la fonction qui est dedans.

          Le voili :

          // main.c
          #include <stdio.h>
          #include <dlfcn.h>
          
          int main() {
              // on charge le plugin
              void * plugin = dlopen("french.so", RTLD_LAZY);   // chemin d'acces du .so
              
              if (! plugin) {
          	printf("Cannot load library: %s\n", dlerror());
          	return 1;
              }
          
              // on va chercher la fonction "hello" du plugin
              void (*greetings)() = dlsym(plugin, "hello");
          
              greetings();              // exécution
          
              dlclose(plugin);          // on ferme
              return 0;
          }
          
          


          On compile avec l'option qui va bien

          cc  -ldl  main.c   -o main
          

          et quand on fait exécuter : ça marche

          $ ./main 
          Bonjour, monde !
          


          Le Makefile complet :

          CFLAGS =  -Wall -Wextra
          
          all: main french.so
          
          main:   LDFLAGS += -ldl
          
          %.so:  %.c
          	$(LINK.c) -fPIC -shared   $< -o $@
          
          clean:
          	$(RM) main french.so *~
          

          EDIT: en complément, on peut aussi accéder à des variables propres à chaque module.

          Dans french.c

          char *lang = "french";
          

          la variable lang contient l'adresse d'une chaine. On récupère l'adresse de cette adresse dans main par :

              char **p_lang = dlsym (plugin, "lang");
              printf("Langage = %s\n", *p_lang);
          







          -
          Edité par michelbillaud 15 août 2019 à 12:14:27

          • Partager sur Facebook
          • Partager sur Twitter
            15 août 2019 à 13:20:23

            Merci beaucoup :D

            Entre temps j'avais trouvé cette solution, mais pour la plateforme Windows (avec LoadLibrary et GetProcess), je comprend mieux à présent avec ton exemple, et en prime je vais pouvoir rendre ça portable :)

            Merci à vous, je passe le sujet en résolu.

            • Partager sur Facebook
            • Partager sur Twitter
            Le doute est le commencement de la sagesse
              15 août 2019 à 14:21:02

              Une autre source d'info, regarder comment c'est fait dans un vrai soft qui marche avec des plugins, et qui est installable sur plusieurs systèmes, omme PHP ou Apache.

              -
              Edité par michelbillaud 15 août 2019 à 14:22:31

              • Partager sur Facebook
              • Partager sur Twitter

              Exécuter une fonction C externe dans le programme

              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
              • Editeur
              • Markdown