Creating a module system (dynamic loading) in C

前端 未结 9 1947
故里飘歌
故里飘歌 2020-12-12 12:53

How would one go about loading compiled C code at run time, and then calling functions within it? Not like simply calling exec().

EDIT: The the program loading the

相关标签:
9条回答
  • 2020-12-12 13:31

    If you are willing to consider the framework, Qt provides QPluginLoader: Qt 5 docs (or for old Qt 4.8 docs see here)

    If you need/want more fine grained control, Qt also provides a means to load libraries on the fly with QLibrary: Qt 5 docs (or for old Qt 4.8 docs see here)

    Even better, these are portable across platforms.

    0 讨论(0)
  • 2020-12-12 13:33

    There is a DIY approach. While the method (and possibility) of doing this varies from system to system, the general idea is to open a file, read the contents of the file into memory, make said memory executable, initialise a function pointer to a valid position within this memory, and there you are.

    Of course this is assuming that it's just executable code - quite unlikely. The code probably requires data to be loaded into RAM too, and may require space for global/static variables. You could load this all yourself, but you'd need to go into the executable code and adjust all the memory references in it.

    Most operating systems allow dynamic linking, which does all this for you.

    0 讨论(0)
  • 2020-12-12 13:39

    Also you can look at cpluff. It is a plugin management library on pure c.

    0 讨论(0)
  • 2020-12-12 13:48

    In Linux/UNIX you can use the POSIX dlopen / dlsym / dlerror / dlclose functions to dynamically open shared libraries and access the symbols (including functions) they provide, see the man page for details.

    0 讨论(0)
  • 2020-12-12 13:48

    Under Windows, this is how I do it:

    • Generate code (in C because it's easy to find compilers, and library requirements are minimal)
    • spawn a job to compile/link it into a DLL
    • load it with LoadLibrary
    • get function pointers with GetProcAddress

    The generate/compile/link steps generally take less than a second.

    0 讨论(0)
  • 2020-12-12 13:50

    for GNU/Linux users

    dynamic loading library is a mechanism that with the help of that, we can run our program, and at run time, decide what function, we want to use. I think in some cases static variable is possible as well.

    First start with seeing man 3 dlopen or see it online

    The header file that is required is: dlfcn and since this is not part of the standard you should like it to your object file with this library: libdl.(so/a) and therefore you need something like:

    gcc yours.c -ldl
    

    then you have a file name a.out and you can run it BUT it does not work properly and I will explain it why.


    A complete example:

    first crate 2 files func1.c and func2.c respectively. We want to call these functions at runtime.

    func.c

    int func1(){
        return 1;
    }
    

    func2.c

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

    Now we have 2 functions, let's make our modules:

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

    for inquiring mind about -fPIC => PIC

    Now you have a dynamic library names: libfunc.so

    Let's create the main program (= temp.c) that wants to use those functions.

    header files

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

    and the main program

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

    Now we just need to compile the this code (= temp.c), thus try:

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

    It does not work! WHY easy; because our a.out program does not know where to find the related library: libfunc.so and therefore it tells us cannot not open ...

    how to tell the program (= a.out) to find its library?

    1. using ld linker
    2. using environment variable LD_LIBRARY_PATH
    3. using the standard path

    first way, with help of ld

    use -Wl,-rpath, and pwd and put the path as a argument for it

    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
    

    second way

    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 
    

    and third way

    you have libfunc.so in you current path, thus you can copy it into a standard path for libraries.

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

    you can remove it from /usr/lib and use it. It is up to you.

    NOTE

    how to find out that our a.out knows about its path?
    easy:

    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
    

    how we can use it in c++?
    As long as I know you cannot because g++ mangles the function names whereas gcc does not thus you should use: extern "C" int func1(); for example.

    For any more details see man pages and Linux programing books.

    0 讨论(0)
提交回复
热议问题