Fix lib/20558:

A dynamically linked program invokes the rtld cleanup routine via
an atexit handler.  This rtld cleanup routine invokes _fini() for
shared libraries, which in-turn invoke __cxa_finalize() with their
DSO handle.  By luck, this happens to work okay for non-threaded
programs, but for a threaded program, this leads to deadlock (sometimes
manifested as an assertion failure, if the program didn't actually
create any threads).

Fixed by teaching __cxa_finalize() that it can be recursively invoked,
adjusting the handler list manipulation accordingly.
This commit is contained in:
thorpej 2003-03-04 18:09:48 +00:00
parent 50c739e7b8
commit 65ec6ba6e7
1 changed files with 54 additions and 14 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: atexit.c,v 1.14 2003/03/01 04:19:37 thorpej Exp $ */
/* $NetBSD: atexit.c,v 1.15 2003/03/04 18:09:48 thorpej Exp $ */
/*-
* Copyright (c) 2003 The NetBSD Foundation, Inc.
@ -149,32 +149,72 @@ __cxa_atexit(void (*func)(void *), void *arg, void *dso)
/*
* Run the list of atexit handlers. If dso is NULL, run all of them,
* otherwise run only those matching the specified dso.
*
* Note that we can be recursively invoked; rtld cleanup is via an
* atexit handler, and rtld cleanup invokes _fini() for DSOs, which
* in turn invokes __cxa_finalize() for the DSO.
*/
void
__cxa_finalize(void *dso)
{
static thr_t owner;
static u_int call_depth;
struct atexit_handler *ah, *dead_handlers = NULL, **prevp;
void (*cxa_func)(void *);
void (*atexit_func)(void);
mutex_lock(&atexit_mutex);
/*
* We implement our own recursive mutex here because we need
* to keep track of the call depth anyway, and it saves us
* having to dynamically initialize the mutex.
*/
if (mutex_trylock(&atexit_mutex) == 0)
owner = thr_self();
else if (owner != thr_self()) {
mutex_lock(&atexit_mutex);
owner = thr_self();
}
call_depth++;
/*
* If we are at call depth 1 (which is usually the "do everything"
* call from exit(3)), we go ahead and remove elements from the
* list as we call them. This will prevent any nested calls from
* having to traverse elements we've already processed. If we are
* at call depth > 1, we simply mark elements we process as unused.
* When the depth 1 caller sees those, it will simply unlink them
* for us.
*/
for (prevp = &atexit_handler_stack; (ah = (*prevp)) != NULL;) {
if (dso == NULL || dso == ah->ah_dso) {
if (ah->ah_dso != NULL)
(*ah->ah_cxa_atexit)(ah->ah_arg);
else
(*ah->ah_atexit)();
*prevp = ah->ah_next;
if (STATIC_HANDLER_P(ah))
ah->ah_atexit = NULL; /* mark it free */
else {
ah->ah_next = dead_handlers;
dead_handlers = ah;
if (dso == NULL || dso == ah->ah_dso || ah->ah_atexit == NULL) {
if (ah->ah_atexit != NULL) {
if (ah->ah_dso != NULL) {
cxa_func = ah->ah_cxa_atexit;
ah->ah_cxa_atexit = NULL;
(*cxa_func)(ah->ah_arg);
} else {
atexit_func = ah->ah_atexit;
ah->ah_atexit = NULL;
(*atexit_func)();
}
}
if (call_depth == 1) {
*prevp = ah->ah_next;
if (! STATIC_HANDLER_P(ah)) {
ah->ah_next = dead_handlers;
dead_handlers = ah;
}
} else
prevp = &ah->ah_next;
} else
prevp = &ah->ah_next;
}
if (call_depth > 1)
return;
mutex_unlock(&atexit_mutex);
/*