I am trying to use dlopen()
and dlsym()
in my code and compile it with gcc
.
Here is the first file.
/* main.c */
#include <dlfcn.h>
int main()
{
void *handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
void (*func)() = dlsym(handle, "func");
func();
}
return 0;
}
Here is the second file.
/* foo.c */
#include <stdio.h>
void func()
{
printf("hello, world\n");
}
Here is how I compile and run the code.
$ gcc -std=c99 -pedantic -Wall -Wextra -shared -fPIC -o foo.so foo.c
$ gcc -std=c99 -pedantic -Wall -Wextra -ldl -o main main.c
main.c: In function ‘main’:
main.c:10:26: warning: ISO C forbids initialization between function pointer and ‘void *’ [-Wpedantic]
void (*func)() = dlsym(handle, "func");
^
$ ./main
hello, world
How can I get rid of the warning?
Type casting doesn't help. If I try to type cast the return value of dlsym()
into a function pointer, I get this warning instead.
main.c:10:26: warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]
void (*func)() = (void (*)()) dlsym(handle, "func");
^
What would convince the compiler that this code is fine?
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:
struct export_vtable {
void (*helloworld)(void);
};
struct export_vtable exports = { func };
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.