autistic456
autistic456

Reputation: 183

Why am I getting segfault with loading objects from shared library?

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

Answers (1)

alk
alk

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

Related Questions