How to correctly assign a pointer returned by dlsym into a variable of function pointer type?

前端 未结 6 930
悲&欢浪女
悲&欢浪女 2020-12-31 01:00

I am trying to use dlopen() and dlsym() in my code and compile it with gcc.

Here is the first file.

/* main.c          


        
相关标签:
6条回答
  • 2020-12-31 01:15

    you can use union, like this:

    union {
        void *ptr;
        void (*init_google_logging) (char* argv0);
    } orig_func;
    
    orig_func.ptr = dlsym (RTLD_NEXT, "_ZN6google17InitGoogleLoggingEPKc");
    
    orig_func.init_google_logging (argv0);
    
    0 讨论(0)
  • 2020-12-31 01:16

    The compiler only "tries to help", so you have to use two typecasts:

    #include <inttypes.h>
    
    void (*func)() = (void (*)())(intptr_t)dlsym(handle, "func");
    
    0 讨论(0)
  • 2020-12-31 01:19

    The problem here is that a pointer to object is subtly separated from a function pointer. In ISO/IEC 9899:201x paper §6.3.2.3 Pointers it's stated:

    1. A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

    .

    1. A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.

    So a function pointer is different from object pointers, and consequently the assignment of a void * to a function pointer is always strictly non compliant.

    Anyway, as I said in comments, in 99.9999....9999% of cases it is permitted thanks to the ANNEX J - Portability issues, §J.5.7 Function pointer casts of the previously mentioned paper that states:

    1. A pointer to an object or to void may be cast to a pointer to a function, allowing data to be invoked as a function (6.5.4).
    2. A pointer to a function may be cast to a pointer to an object or to void, allowing a function to be inspected or modified (for example, by a debugger) (6.5.4).

    Now on the practical side a technique that avoid the splitting of code in more files is to use pragmas to suppress pedantic warnings for a small piece of code.
    The more brutal form can be:

    /* main.c */
    
    #include <dlfcn.h>
    
    #pragma GCC diagnostic push    //Save actual diagnostics state
    #pragma GCC diagnostic ignored "-pedantic"    //Disable pedantic
    int main()
    {
        void *handle = dlopen("./foo.so", RTLD_NOW);
        if (handle) {
            void (*func)() = dlsym(handle, "func");
            func();
        }
        return 0;
    }
    #pragma GCC diagnostic pop    //Restore diagnostics state
    

    A more sophisticated way could be actuated isolating the offending code in a small function, then forcing its inlining. It's more a makeup than effective solution, but will suppress the unwanted diagnostic:

    /* main.c */
    
    #include <dlfcn.h>
    
    #pragma GCC diagnostic push    //Save actual diagnostics state
    #pragma GCC diagnostic ignored "-pedantic"    //Disable pedantic
    void (*)() __attribute__((always_inline)) Assigndlsym(void *handle, char *func)
    {
        return dlsym(handle, func);  //The non compliant assignment is done here
    }
    #pragma GCC diagnostic pop    //Restore diagnostics state
    
    int main()
    {
        void *handle = dlopen("./foo.so", RTLD_NOW);
        if (handle) {
            void (*func)() = Assigndlsym(handle, "func"); //Now the assignment is compliant
            func();
        }
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-31 01:24

    This made my code sufficiently pedantic:

    *(void**)(&func_ptr) = dlsym(handle, "function_name");
    

    (I found it here http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html)

    0 讨论(0)
  • 2020-12-31 01:26

    To keep the -pedantic option for your code while having parts of code that are not strictly conforming, separate that code into a separate file with custom warning options.

    So, make a function that wraps the dlsym function and returns a function pointer. Put it in a separate file and compile that file without -pedantic.

    0 讨论(0)
  • 2020-12-31 01:30

    If you want to be pedantically correct, don't try to resolve the address of a function. Instead, export some kind of structure from the dynamic library:

    In the library

    struct export_vtable {
       void (*helloworld)(void);
    };
    struct export_vtable exports = { func };
    

    In the caller

    struct export_vtable {
       void (*helloworld)(void);
    };
    
    int main() {
       struct export_vtable* imports;
       void *handle = dlopen("./foo.so", RTLD_NOW);
    
       if (handle) {
            imports = dlsym(handle, "exports");
            if (imports) imports->helloworld();
        }
    
        return 0;
    }
    

    This technique is actually quite common, not for portability -- POSIX guarantees that function pointers can be converted to and from void* -- but because it allows more flexibility.

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