Creating a module system (dynamic loading) in C

前端 未结 9 1975
故里飘歌
故里飘歌 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: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 
    #include 
    #include  
    

    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.

提交回复
热议问题