De cómo se podría ir sobre la carga compilado el código C en tiempo de ejecución y, a continuación, llamar a funciones dentro de ella? No es tan simple como llamar a exec().

EDIT: El programa que se carga el módulo está en C.

  • Excelente pregunta. Muchas personas saben cómo hacer esto, pero aquellos que no haría bien en aprender esta técnica valiosa.
InformationsquelleAutor Nikron | 2008-12-21

9 Comentarios

  1. 32

    En Linux/UNIX puede utilizar el POSIX dlopen /dlsym /dlerror /dlclose funciones de abrir dinámicamente las bibliotecas compartidas y acceder a los símbolos (incluyendo las funciones) que proporcionan, consulte la el hombre de la página para más detalles.

  2. 40

    dlopen es el camino a seguir. Aquí están algunos ejemplos:

    Cargar un plugin con dlopen:

    #include <dlfcn.h>
    ...
    int
    main (const int argc, const char *argv[])
    {
    
      char *plugin_name;
      char file_name[80];
      void *plugin;
      ...
      plugin = dlopen(file_name, RTLD_NOW);
      if (!plugin)
      {
         fatal("Cannot load %s: %s", plugin_name, dlerror ());
      }

    La compilación de la anterior:

    % cc  -ldl -o program program.o 

    Entonces, asumiendo que esta API para los plugins de:

    /* The functions we will find in the plugin */
    typedef void (*init_f) ();
    init_f init;
    typedef int (*query_f) ();
    query_f query;

    Encontrar la dirección de init() en el plugin:

    init = dlsym(plugin, "init");
    result = dlerror();
    if (result)
    {
       fatal("Cannot find init in %s: %s", plugin_name, result);
    }
    init();

    Con la otra función query(), que devuelve un valor:

    query = dlsym (plugin, "query");
    result = dlerror();
    if (result)
    {
        fatal("Cannot find query in %s: %s", plugin_name, result);
    }
    printf("Result of plugin %s is %d\n", plugin_name, query ());

    Puede recuperar el ejemplo completo en línea.

    • ¿Te importaría poner el ejemplo completo a github? Sería más fácil de leer.
  3. 8

    Ver esta pregunta ha sido respondida, pero pensé que los demás interesados en este tema puede apreciar una plataforma cruzada ejemplo de un antiguo plugin basado en la aplicación. El ejemplo funciona en win32 o linux, y seaches y llama a una función llamada ‘constructor’ en la carga dinámica .así que o .dll especificada en el archivo como argumento. El ejemplo está en c++ pero los procedimientos debe ser el mismo para c.

    //firstly the includes
    #if !defined WIN32
       #include <dlfcn.h>
       #include <sys/types.h>
    #else
       #include <windows.h>
    #endif
    
    //define the plugin's constructor function type named PConst
    typedef tcnplugin* (*PConst)(tcnplugin*,tcnplugin*,HANDLE);
    
    //loads a single specified tcnplugin,allmychildren[0] = null plugin
    int tcnplugin::loadplugin(char *file) {
        tcnplugin *hpi;
    #if defined WIN32               //Load library windows style
        HINSTANCE hplugin=LoadLibrary(file);
        if (hplugin != NULL) {
                PConst pinconstruct = (PConst)GetProcAddress(hplugin,"construct");
    #else                                   //Load it nix style
        void * hplugin=dlopen(file,RTLD_NOW);
        if (hplugin != NULL) {
                PConst pinconstruct = (PConst)dlsym(hplugin,"construct");
    #endif   
                if (pinconstruct != NULL) { //Try to call constructor function in dynamically loaded file, which returns a pointer to an instance of the plugin's class
                        hpi = pinconstruct(this, this, hstdout);
                } else {
                        piprintf("Cannot find constructor export in plugin!\n");
                        return 0;
                }
        } else {
                piprintf("Cannot open plugin!\n");
    #if !defined WIN32
                perror(dlerror());
    #endif
                return 0;
        }
        return addchild(hpi); //add pointer to plugin's class to our list of plugins
    }

    También se podrían mencionar que si el módulo que las funciones que desea llamar está escrito en c++ debe declarar la función con extern «C», tales como:

    extern "C" pcparport * construct(tcnplugin *tcnptr,tcnplugin *parent) {
        return new pcparport(tcnptr,parent,"PCPARPORT",0,1);
    }
    • Que los encabezados son necesarios para que se ejecute en Linux? Y el ‘::’ significa que es C++, en lugar de C, ¿no es así?
  4. 4

    para los usuarios de GNU/Linux

    de la carga dinámica de la biblioteca es un mecanismo que, con la ayuda de que podemos ejecutar nuestro programa, y en tiempo de ejecución, decida cuál es la función que queremos utilizar. Creo que en algunos casos static variable también es posible.

    Primer lugar, comenzar con ver man 3 dlopen o ver online

    El archivo de encabezado que se requiere es: dlfcn y ya que esto no es parte de la norma que debe como a su objeto en el archivo de la biblioteca: libdl.(so/a) y por lo tanto se necesita algo como:

    gcc yours.c -ldl

    entonces usted tiene un nombre de archivo a.out y se puede ejecutar PERO no funciona correctamente y voy a explicar por qué.


    Un ejemplo completo:

    primer cajón 2 archivos func1.c y func2.c respectivamente. Queremos llamar a estas funciones en tiempo de ejecución.

    func.c

    int func1(){
        return 1;
    }

    func2.c

    const char* func2(){
        return "upgrading to version 2";
    }

    Ahora tenemos 2 funciones, vamos a hacer nuestros módulos:

    ALP  gcc -c -fPIC func1.c
    ALP  gcc -c -fPIC func2.c
    ALP  gcc -o libfunc.so -shared -fPIC func1.o func2.o 

    para la mente inquisitiva acerca de -fPIC => PIC

    Ahora usted tiene un dynamic library nombres: libfunc.so

    Vamos a crear el programa principal (= temp.c) que quiere usar esas funciones.

    archivos de encabezado

    #include <stdio.h>
    #include <stdlib.h>
    #include <dlfcn.h> 

    y el programa principal

    int main()
    {
        //pointer function to func1 and func2
        int         ( *f1ptr )();
        const char* ( *f2ptr )();
    
        //for pointing to the library
        void* handle = NULL;
    
        //for saving the error messages
        const char* error_message = NULL;
    
        //on error dlopen returns NULL
        handle = dlopen( "libfunc.so", RTLD_LAZY );
    
        //check for error, if it is NULL
        if( !handle )
        {
            fprintf( stderr, "dlopen() %s\n", dlerror() );
            exit( 1 );
        }
    
        /*
            according to the header file:
    
            When any of the above functions fails, call this function
            to return a string describing the error.  Each call resets
            the error string so that a following call returns null.
    
            extern char *dlerror (void) __THROW;
        */
    
        //So, reset the error string, of course we no need to do it just for sure
        dlerror();
    
        //point to func1
        f1ptr = (int (*)()) dlsym( handle, "func1" );
    
        //store the error message to error_message
        //because it is reseted if we use it directly
        error_message = dlerror();
        if( error_message ) //  it means if it is not null
        {
            fprintf( stderr, "dlsym() for func1 %s\n", error_message );
            dlclose( handle );
            exit( 1 );
        }
    
        //point the func2
        f2ptr = (const char* (*)()) dlsym( handle, "func2" );
    
        //store the error message to error_message
        //because it is reseted if we use it directly
        error_message = dlerror();
        if( error_message ) //  it means if it is not null
        {
            fprintf( stderr, "dlsym() for func2 %s\n", error_message );
            dlclose( handle );
            exit( 1 );
        }
    
        printf( "func1: %d\n", ( *f1ptr )() );
        printf( "func2: %s\n", ( *f2ptr )() );
    
        //unload the library
        dlclose( handle );
    
        //the main return value
        return 0;
    }

    Ahora sólo tenemos que compilar el presente código (= temp.c), por lo tanto probar:

    ALP  gcc temp.c -ldl
    ALP  ./a.out
    libfunc.so: cannot open shared object file: No such file or directory

    No funciona! Por QUÉ fácil; porque nuestra a.out programa no saben dónde se encuentran los relacionados con la biblioteca: libfunc.so y por lo tanto nos dice cannot not open ...

    cómo decirle al programa (= a.out) para encontrar su biblioteca?

    1. utilizando ld enlazador
    2. utilizando la variable de entorno LD_LIBRARY_PATH
    3. la ruta de acceso estándar

    primera forma, con la ayuda de ld

    uso -Wl,-rpath, y pwd y poner la ruta de acceso como un argumento para que

    ALP  gcc temp.c -ldl
    ALP  ./a.out
    libfunc.so: cannot open shared object file: No such file or directory
    ALP  pwd
    /home/shu/codeblock/ALP
    ALP  gcc temp.c -ldl -Wl,-rpath,/home/shu/codeblock/ALP
    ALP  ./a.out
    func1: 1
    func2: upgrading to version 2

    segunda forma

    ALP  gcc temp.c -ldl
    ALP  ./a.out
    libfunc.so: cannot open shared object file: No such file or direc
    ALP  export LD_LIBRARY_PATH=$PWD
    ALP  echo $LD_LIBRARY_PATH
    /home/shu/codeblock/ALP
    ALP  ./a.out
    func1: 1
    func2: upgrading to version 2
    ALP  export LD_LIBRARY_PATH=
    ALP  ./a.out
    libfunc.so: cannot open shared object file: No such file or 

    y tercera forma

    tiene libfunc.so en que ruta de acceso actual, por lo tanto se puede copiar en un camino estándar para las bibliotecas.

    ALP $ sudo cp libfunc.so /usr/lib
    ALP  gcc temp.c -ldl
    ALP  ./a.out
    func1: 1
    func2: upgrading to version 2

    usted puede eliminar de /usr/lib y utilizarla. Es hasta usted.

    NOTA

    cómo saber que nuestro a.out sabe acerca de su camino?

    fácil:

    ALP  gcc temp.c -ldl -Wl,-rpath,/home/shu/codeblock/ALP
    ALP  strings a.out  | grep \/
    /lib/ld-linux.so.2
    /home/shu/codeblock/ALP

    cómo la podemos usar en ?

    Mientras yo sé que no, porque g++ destroza los nombres de función, mientras que gcc no así que se debe utilizar: extern "C" int func1(); por ejemplo.

    Para más detalles, consulte las páginas man de Linux y programación de los libros.

  5. 2

    Si usted está dispuesto a considerar el marco, Qt proporciona QPluginLoader: Qt 5 docs (o para los viejos Qt 4.8 docs ver aquí)

    Si usted necesita o quiere más fino de control, Qt también proporciona un medio para cargar bibliotecas sobre la marcha con QLibrary: Qt 5 docs (o para los viejos Qt 4.8 docs ver aquí)

    Mejor aún, estas son portables entre plataformas.

  6. 1

    Dinámica lenguajes como Perl hacer esto todo el tiempo. El intérprete de Perl está escrito en C, y muchos módulos de Perl que son parcialmente escrito en C. Cuando los módulos son necesarios, el compilado C componentes se cargan dinámicamente sobre la marcha. Como se señaló en otra respuesta, el mecanismo para el almacenamiento de los módulos es de Dll en windows, y las bibliotecas compartidas (.por lo que los archivos) en UNIX. Creo que el llamado para la carga de una biblioteca compartida en UNIX es dlopen(). Usted probablemente puede encontrar consejos para cómo lograr esto en UNIX, comenzando con la documentación de la convocatoria. Para Windows, necesitaría de investigación en archivos Dll y aprenda cómo cargar dinámicamente en tiempo de ejecución. [O, posiblemente, ir a través de la Cygwin UNIX capa de emulación, que probablemente iba a permitir que usted utilice el mismo llama en Windows como en UNIX, pero yo no lo recomiendo a menos que usted ya está usando y compilar en contra de Cygwin.]

    Tenga en cuenta que esto es diferente de la vinculación en contra de una biblioteca compartida. Si usted sabe de antemano exactamente lo que el código que llama, usted puede construir en contra de una biblioteca compartida y la construcción será «dinámicamente vinculadas» a la biblioteca; sin ningún manejo especial de que las rutinas de la biblioteca será cargado en la memoria sólo cuando y si el programa se llama realmente a ellos. Pero usted no puede hacer esto si usted está planeando escribir algo capaz de cargar cualquier arbitraria código objeto, el código que no se puede identificar ahora, en tiempo de compilación, sino que están esperando para ser seleccionado de alguna manera, en tiempo de ejecución. Para que usted tendrá el uso de dlopen() y su Windows primos.

    Que usted puede buscar en la forma en Perl o en otros lenguajes dinámicos hacer esto para ver algunos ejemplos reales. El Perl de la biblioteca responsable de este tipo de carga dinámica DynaLoader; tiene un Perl y C componente, creo. Estoy seguro de que otros lenguajes dinámicos como Python tiene algo similar que usted podría mirar; y el Loro, la máquina virtual para el inéditas de Perl 6, sin duda tiene un mecanismo para hacerlo así (o lo hará en el futuro).

    Para que la materia, Java logra esto a través de su JNI (Java Native Interface) interfaz, por lo que, probablemente, podría mirar el código fuente para OpenJDK para ver cómo Java logra esto en UNIX y Windows.

  7. 0

    No es un DIY enfoque. Mientras que el método (y posibilidad) de hacerlo varía de sistema a sistema, la idea general es la de abrir un archivo, leer el contenido del archivo en la memoria, hacer que dijo de memoria ejecutable, inicializar un puntero a función a una posición válida dentro de esta memoria, y hay.

    Por supuesto, esto es, asumiendo que es sólo el código ejecutable – bastante improbable. El código requiere probablemente de datos a ser cargados en la memoria RAM también, y puede requerir espacio para variables globales o estáticos. Usted podría cargar todo esto a ti mismo, pero usted tendría que ir en el código ejecutable y ajustar todas las referencias de memoria en la misma.

    La mayoría de los sistemas operativos permiten la vinculación dinámica, que hace todo esto para usted.

    • La lectura de un archivo ejecutable en la memoria, recibiendo toda la configuración de la protección correcta, y la búsqueda de símbolos correctos es duro. Por qué reinventar la rueda cuando hay estándar de las funciones del sistema operativo que puede hacer un mejor trabajo para usted?
    • Las partes acerca de «leer el contenido del archivo en la memoria, hacer que dijo de memoria ejecutable» cubre mucho, porque por lo general hay un montón de reubicación y ajuste del código en tiempo de carga. De hecho, me la probé una vez. No es para los débiles.
  8. 0

    Bajo Windows, esta es la forma en que yo lo hago:

    • Generar código (en C, ya que es fácil encontrar compiladores, y la biblioteca de los requerimientos son mínimos)
    • generar un trabajo de compilación y enlace en un archivo DLL
    • cargar con LoadLibrary
    • obtener punteros de función con GetProcAddress

    El generar/compilar/link pasos que generalmente toma menos de un segundo.

Dejar respuesta

Please enter your comment!
Please enter your name here