Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 4 Feb 2003 10:46:13 -0500
From:      Mikhail Teterin <mi+mx@aldan.algebra.com>
To:        arch@FreeBSD.org, Alfred Perlstein <bright@mu.org>, Wes Peters <wes@softweyr.com>
Cc:        Mario Sergio Fujikawa Ferreira <lioux@FreeBSD.org>
Subject:   dlclose() vs. atexit()
Message-ID:  <200302041046.13767.mi%2Bmx@aldan.algebra.com>
In-Reply-To: <20030204082625.GB85104@elvis.mu.org>
References:  <200302030506.h1356Nha011918@repoman.freebsd.org> <1044319099.358.57.camel@zaphod.softweyr.com> <20030204082625.GB85104@elvis.mu.org>

next in thread | previous in thread | raw e-mail | index | archive | help

--------------Boundary-00=_1TJS4DHZQUSZ02JIZ5RR
Content-Type: text/plain;
  charset="iso-8859-1"
Content-Transfer-Encoding: 8bit

	[Moved to -arch]

On Tuesday 04 February 2003 03:26 am, Alfred Perlstein wrote:
= * Wes Peters <wes@softweyr.com> [030203 23:41] wrote:
= > On Mon, 2003-02-03 at 18:58, Mikhail Teterin wrote:
= > > There remains an unresolved issue with mplayer on FreeBSD -- some
= > > of the libraries it dlopens and dlcloses are calling atexit() in
= > > between with their own functions.
= > > 
= > > This causes SEGFAULTs in exit(), which tries to call those
= > > functions. The application catches the signals and would not quit
= > > until SIGKILL-ed.
= > > 
= > > This does not affect Linux, where, reportedly, calls to atexit()
= > > are treated differently if made from dlopened code. I'm not sure
= > > how best to fix this (Call _exit()? Remove signal handlers before
= > > exit()?), but something needs to be done...

= > I think ideally we'd want dlclose to be able to deinstall any
= > atexit handlers that were installed by library functions. The
= > most straight- forward path to this I can see is an atexit-remove
= > call that can be passed a start and end address and will remove
= > any function references found between the two. dlclose could call
= > this function with the start and end addresses of the library text
= > segment to remove any exit handlers in the library code space.

= > I can probably take a look at this later in the week if this seems
= > like a reasonable approach.

= Please see if you can emulate the glibc behaviour just to ease
= porting. I think that means you must actually call the atexit handler,
= not just deregister it.

Last time I brought this up, it was not clear, what The Right Thing to
do is. Are these details mandatated by some standard out there, or is
everyone on their own, and we should, indeed, do what Linux does, to not
increase the enthropy?

Should, for example, exit handlers, which will still be valid after the
dlclose() still be called at dlclose() or at the actual exit()? How
about atexit() calls made between dlopen() and dlclose(), and not by the
library but by the application, or by another library? Have each library
get its own list of exit-handlers?

Or should it still be a global list, but examined by both dlclose()
and exit() -- if an item on the list is about to become invalid after
dlclose() (how can we know, BTW?), have dlclose() call it -- otherwise,
leave it alone?

That is what Solaris is doing, apparently. From their atexit(3c)

     The atexit() function registers the function pointed  to  by
     func to be called without arguments on normal termination of
     the program or when the  object  defining  the  function  is
     unloaded.
	[...]
     On process exit, functions are called in the  reverse  order
     of  their  registration.  On object unloading, any functions
     belonging to an unloadable object are called in the  reverse
     order of their registration.

The Solaris' approach seems the most robust. The Linux' one fails,
when the exit handler belongs to another library. Attached are two
files, which illustrate this -- even if somewhat artificially.

	. the main program dlopens two libraries (both built from
	  the same source);
	. it then passes the library number (integer) -- for reporting,
	  and the address of the handler _found in the other library_
	  to each one;
	. the libraries both register each other's functions as handlers;
	. the application unloads both libraris.

On FreeBSD both libraries are quietly unloaded, and the application
SEGFAULTs in exit(). On Linux, the crash happens at the second unload,
which tries to call the handler from the first library. On Solaris
everything runs to completion, which -- unless it violates some standard --
makes it the most appealing implementation (hostnames edited out):
 
[...] 5.0-CURRENT FreeBSD 5.0-CURRENT #0: Wed Jan  8 19:15:03 EST 2003     
[....]
	Loading the libraries
	./l0.so loaded as 0x28064200
	./l1.so loaded as 0x28064300
	Library 0 calling atexit(0x2813a6f0)
	Library 1 calling atexit(0x281386f0)
	Unloading the libraries
	0x28064200 unloaded
	0x28064300 unloaded
	Libraries unloaded. Returning
	Segmentation fault

Linux [...] 2.4.18-14 #1 Wed Sep 4 13:35:50 EDT 2002 i686 i686 i386 GNU/Linux
	Loading the libraries
	./l0.so loaded as 0x8049ad0
	./l1.so loaded as 0x8049e00
	Library 0 calling atexit(0x40016788)
	Library 1 calling atexit(0x40014788)
	Unloading the libraries
	Exit handler 0x40016788 of library 1 is invoked
	0x8049ad0 unloaded
	Segmentation fault (core dumped)

SunOS [...] 5.8 Generic_108528-13 sun4u sparc SUNW,Sun-Fire-280R
	Loading the libraries
	./l0.so loaded as ff3a1458
	./l1.so loaded as ff3a17b8
	Library 0 calling atexit(ff260380)
	Library 1 calling atexit(ff350380)
	Unloading the libraries
	Exit handler ff350380 of library 0 is invoked
	ff3a1458 unloaded
	Exit handler ff260380 of library 1 is invoked
	ff3a17b8 unloaded
	Libraries unloaded. Returning

Yet another plan would be to have the atexit() call simply increase the
ref-count of the library a handler is from, so it will not actually be
unloaded by dlclose(). But that will be yet another implementation...

	-mi


--------------Boundary-00=_1TJS4DHZQUSZ02JIZ5RR
Content-Type: text/x-csrc;
  charset="iso-8859-1";
  name="l.c"
Content-Transfer-Encoding: 7bit
Content-Description: Both l0.so and l1.so are built from this file
Content-Disposition: attachment; filename="l.c"

static int mynumber;

void
handler(void)
{
	printf("Exit handler %p of library %d is invoked\n",
	    handler, mynumber);
}

void
in(int i, void (*func)(void))
{
	mynumber = i;
	printf("Library %d calling atexit(%p)\n", mynumber, func);
	if (atexit(func)) {
		perror("atexit");
		_exit(2);
	}
}

--------------Boundary-00=_1TJS4DHZQUSZ02JIZ5RR
Content-Type: text/x-csrc;
  charset="iso-8859-1";
  name="main.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="main.c"

#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main()
{
	struct {
		void	*lib;
		void	(*handler)(void);
		void	(*in)(int, void (*)(void));
	} l[2];
	int	i;

	system("uname -a");
	
	puts("Loading the libraries");
	for (i = 0; i < 2; i++) {
		char lname[8];
		sprintf(lname, "./l%d.so", i);
		l[i].lib = dlopen(lname, RTLD_LAZY);
		if (l[i].lib == NULL) {
			fprintf(stderr, "%s: %s\n", lname, dlerror());
			_exit(1);
		}
		printf("%s loaded as %p\n", lname, l[i].lib);
		l[i].handler = dlsym(l[i].lib, "handler");
		if (l[i].handler == NULL) {
			fprintf(stderr, "%s: dlsym for ``handler'': %s\n",
			    lname, dlerror());
			_exit(1);
		}
		l[i].in = dlsym(l[i].lib, "in");
		if (l[i].in == NULL) {
			fprintf(stderr, "%s: dlsym for ``in'': %s\n",
			    lname, dlerror());
			_exit(1);
		}
	}
	l[0].in(0, l[1].handler);
	l[1].in(1, l[0].handler);
	puts("Unloading the libraries");
	for (i = 0; i < 2; i++) {
		dlclose(l[i].lib);
		printf("%p unloaded\n", l[i].lib);
	}
	puts("Libraries unloaded. Returning");
	return 0;
}

--------------Boundary-00=_1TJS4DHZQUSZ02JIZ5RR--


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-arch" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200302041046.13767.mi%2Bmx>