Reputation: 183
Having this files:
plusone.c
int op(int i){ return i+1; }
main.c
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **argv){
if (argc<3){
printf("usage %s <library> <number>\n",argv[0]);
exit(1);
}
char *lname = argv[1];
int num = atoi(argv[2]);
void *handle = dlopen(lname, RTLD_LAZY);
if(!handle)
perror("dlopen");
int (*opp)(int);
opp=dlsym(handle, "op");
if(!opp)
perror("dlsym");
printf("number before:%i\nnumber after:%i\n",num,opp(num));
dlclose(handle);
}
Compiled as:
$cc -fPIC -shared -o plusone.so -ldl plusone.c
$cc -o main.exe -ldl -Wpedantic main.c
warning: ISO C forbids assignment between function pointer and ‘void *’ [-Wpedantic]
$ls
main.c main.exe plusone.so main.exe
$main.exe
usage main.exe <library> <number>
$main plusone.so 1
dlopen: Success
dlsym: Success
Segmentation fault
Why is segfault?
As could be seen from the bash output, both the dlopen
and dlsym
give success (but they should not even output, otherwise that mean, the condition was true, and the returned values from those functions was NULL? - as from condition). But even of the "success" the perror
returned, I cannot reproduce the segfault, since do not know where is the bug.
Upvotes: 1
Views: 1730
Reputation: 70971
Why is segfault?
Most likely because opp
equals NULL
the moment opp(num)
is trying to be called.
You do not handle errors correctly for the calls to dlopen()
and dlysym()
, although the code tests the results it does not take the correct actions on failure of those two functions.
This code
void *handle = dlopen(lname, RTLD_LAZY);
if(!handle)
perror("dlopen");
correctly branches on dlopen()
returning NULL
which indicated an error, but then the code takes the wrong actions.
dlopen()
does not set errno
, so using perror()
to log an error makes no sense, as perror()
relies on errno
indicating an error, which is does not. So on failure of dlopen()
you see perror()
printing
dlopen: Success
which is misleading and contractionary to the fact that perror()
was called at all, which in fact only happened if dlopen()
returned NULL
, indicating a failure. If dlopen()
would have succeeded, perror()
would not have been called at all and nothing would have been printed.
The same mistake appears with the call to dlsym()
.
To retrieve error info on failure of a member of the dl*()
family of functions use dlerror()
.
For an example on how to correctly and completely implement error handling see below:
void *handle = dlopen(...);
if (!handle)
{
fprintf(stderr, "dlopen: %s\n", dlerror());
exit(EXIT_FAILURE); /* Or do what ever to avoid using the value of handle. */
}
#ifdef DEBUG
else
{
fputs("dlopen: Success\n", stderr);
}
#endif
The same approach should be taken to handle the outcome of dlsym()
.
Aside of all this and unrelated to the observed behaviour the code misses to call dlclose()
when done with using a valid handle
.
Upvotes: 5