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>