dlopen(), atexit() crash on FreeBSD (testcase included)

Markus Hoenicka markus.hoenicka at mhoenicka.de
Mon Dec 31 09:22:02 PST 2007


Hi,

I've been redirected by Giorgos Keramidas to this list after reporting
a problem on the freebsd-questions list. I'd greatly appreciate if you
could have a look at the following problem. Apparently programs are
doomed to segfault on FreeBSD if dlopen()ed modules install exit
handlers via atexit(). Similar problem reports have cropped up before,
see e.g.

http://www.imagemagick.org/pipermail/magick-developers/2006-March/002523.html

My system runs:

FreeBSD yeti.mininet 6.1-RELEASE FreeBSD 6.1-RELEASE #1: Mon Aug 28
22:24:48 CEST 2006
markus at yeti.mininet:/usr/src/sys/i386/compile/YETI  i386

I'm one of the developers of libdbi, a database abstraction layer for
C, see http://libdbi.sourceforge.net.

libdbi is a library for programs which are supposed to be able to
access different database engines with a unified API. libdbi
essentially maps generic API calls to the specific database client
library calls of a particular database engine. To do this, libdbi
loads available database drivers at runtime via dlopen() calls. Each
of these drivers is linked against one database client
library. E.g. the Firebird driver is linked against
libfbclient.so. When libdbi is properly shut down, it unloads all
loaded drivers by calling dlclose() on each of them.

This design works well on all supported platforms and with all
supported database engines, with one exception: the Firebird driver on
FreeBSD invariably causes a segfault when the application linked
against libdbi exits:

#0  0x28514fe4 in ?? ()
#1  0x281507c3 in __cxa_finalize () from /lib/libc.so.6
#2  0x281503fe in exit () from /lib/libc.so.6
#3  0x0804a40f in main (argc=1, argv=0xbfbfe754) at test_dbi.c:419

The reason appears to be that the Firebird client libraries install
exit handlers via atexit(). Remember that due to libdbi's design to
load all available drivers whether or not they are used later, libdbi
will cause a crash even if no Firebird database is accessed - it is
sufficient that the driver has been loaded. As per Giorgos' suggestion
it is simple to circumvent this segfault by avoiding the call to
dlclose() before exiting, but I wonder whether there is a more robust
solution for this problem.

The attached minimal testcase is sufficient to illustrate the
problem. atexitmod.c defines a module which is loaded by datest.c Make
sure to fix the hardcoded path in datest.c before building the app. To
build the test program and watch it crash, do the following:

gcc -shared -o atexitmod.so atexitmod.c
gcc -o datest datest.c
./datest

Commenting out either the atexit() call in atexitmod.c or the
dlclose() call in datest.c prevent the segfault.

If you find some solution, please cc me as I'm not subscribed to
freebsd-hackers.

regards,
Markus

--8< datest.c ---
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>

int main (void) {
  void *dlhandle;
  void (*hellodriver) (void);
  dlhandle = dlopen("/home/markus/prog/datest/atexitmod.so", RTLD_LAZY);
  if (!dlhandle) {
    printf("cannot open module\n");
    exit(1);
  }
  hellodriver = dlsym(dlhandle, "hellodriver");
  if (!hellodriver) {
    printf("cannot locate function\n");
    exit(1);
  }
  hellodriver();
  dlclose(dlhandle);
  exit(0);
}

--8<-------------

--8< atexitmod.c ---
#include <stdlib.h>
#include <stdio.h>

void exithandler(void);

void hellodriver(void) {
  printf("hello driver\n");
  fflush(stdout);
  atexit(exithandler);
}

void exithandler(void) {
  printf("now exiting\n");
  fflush(stdout);
}

--8<-------------

-- 
Markus Hoenicka
markus.hoenicka at cats.de
(Spam-protected email: replace the quadrupeds with "mhoenicka")
http://www.mhoenicka.de



More information about the freebsd-hackers mailing list