xref: /illumos-gate/usr/src/lib/libc/port/gen/atexit.c (revision fc2512cf)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
541efec22Sraf  * Common Development and Distribution License (the "License").
641efec22Sraf  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21e8031f0aSraf 
227c478bd9Sstevel@tonic-gate /*
23a574db85Sraf  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*fc2512cfSRobert Mustacchi  *
26*fc2512cfSRobert Mustacchi  * Copyright 2016 Joyent, Inc.
277c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*	Copyright (c) 1988 AT&T	*/
307c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
317c478bd9Sstevel@tonic-gate 
327257d1b4Sraf #pragma weak _atexit = atexit
337c478bd9Sstevel@tonic-gate 
347257d1b4Sraf #include "lint.h"
357c478bd9Sstevel@tonic-gate #include "thr_uberdata.h"
367c478bd9Sstevel@tonic-gate #include "libc_int.h"
377c478bd9Sstevel@tonic-gate #include "atexit.h"
387c478bd9Sstevel@tonic-gate #include "stdiom.h"
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate /*
417c478bd9Sstevel@tonic-gate  * Note that memory is managed by lmalloc()/lfree().
427c478bd9Sstevel@tonic-gate  *
437c478bd9Sstevel@tonic-gate  * Among other reasons, this is occasioned by the insistence of our
447c478bd9Sstevel@tonic-gate  * brothers sh(1) and csh(1) that they can do malloc, etc., better than
457c478bd9Sstevel@tonic-gate  * libc can.  Those programs define their own malloc routines, and
467c478bd9Sstevel@tonic-gate  * initialize the underlying mechanism in main().  This means that calls
477c478bd9Sstevel@tonic-gate  * to malloc occuring before main will crash.  The loader calls atexit(3C)
487c478bd9Sstevel@tonic-gate  * before calling main, so we'd better avoid malloc() when it does.
497c478bd9Sstevel@tonic-gate  *
507c478bd9Sstevel@tonic-gate  * Another reason for using lmalloc()/lfree() is that the atexit()
517c478bd9Sstevel@tonic-gate  * list must transcend all link maps.  See the Linker and Libraries
527c478bd9Sstevel@tonic-gate  * Guide for information on alternate link maps.
537c478bd9Sstevel@tonic-gate  *
547c478bd9Sstevel@tonic-gate  * See "thr_uberdata.h" for the definitions of structures used here.
557c478bd9Sstevel@tonic-gate  */
567c478bd9Sstevel@tonic-gate 
575c069a6cSRichard Lowe static int in_range(void *, Lc_addr_range_t[], uint_t count);
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate extern	caddr_t	_getfp(void);
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate /*
627c478bd9Sstevel@tonic-gate  * exitfns_lock is declared to be a recursive mutex so that we
637c478bd9Sstevel@tonic-gate  * can hold it while calling out to the registered functions.
647c478bd9Sstevel@tonic-gate  * If they call back to us, we are self-consistent and everything
657c478bd9Sstevel@tonic-gate  * works, even the case of calling exit() from functions called
667c478bd9Sstevel@tonic-gate  * by _exithandle() (recursive exit()).  All that is required is
677c478bd9Sstevel@tonic-gate  * that the registered functions actually return (no longjmp()s).
687c478bd9Sstevel@tonic-gate  *
697c478bd9Sstevel@tonic-gate  * Because exitfns_lock is declared to be a recursive mutex, we
70a574db85Sraf  * cannot use it with lmutex_lock()/lmutex_unlock() and we must
71a574db85Sraf  * use mutex_lock()/mutex_unlock().  This means that atexit()
72a574db85Sraf  * and exit() are not async-signal-safe.  We make them fork1-safe
737c478bd9Sstevel@tonic-gate  * via the atexit_locks()/atexit_unlocks() functions, called from
747c478bd9Sstevel@tonic-gate  * libc_prepare_atfork()/libc_child_atfork()/libc_parent_atfork()
757c478bd9Sstevel@tonic-gate  */
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate /*
787c478bd9Sstevel@tonic-gate  * atexit_locks() and atexit_unlocks() are called on every link map.
797c478bd9Sstevel@tonic-gate  * Do not use curthread->ul_uberdata->atexit_root for these.
807c478bd9Sstevel@tonic-gate  */
817c478bd9Sstevel@tonic-gate void
atexit_locks()827c478bd9Sstevel@tonic-gate atexit_locks()
837c478bd9Sstevel@tonic-gate {
848cd45542Sraf 	(void) mutex_lock(&__uberdata.atexit_root.exitfns_lock);
85*fc2512cfSRobert Mustacchi 	(void) mutex_lock(&__uberdata.quickexit_root.exitfns_lock);
867c478bd9Sstevel@tonic-gate }
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate void
atexit_unlocks()897c478bd9Sstevel@tonic-gate atexit_unlocks()
907c478bd9Sstevel@tonic-gate {
91*fc2512cfSRobert Mustacchi 	(void) mutex_unlock(&__uberdata.quickexit_root.exitfns_lock);
928cd45542Sraf 	(void) mutex_unlock(&__uberdata.atexit_root.exitfns_lock);
937c478bd9Sstevel@tonic-gate }
947c478bd9Sstevel@tonic-gate 
955c069a6cSRichard Lowe 
967c478bd9Sstevel@tonic-gate /*
975c069a6cSRichard Lowe  * This is called via atexit() before the primordial thread is fully set up.
987c478bd9Sstevel@tonic-gate  * Be careful about dereferencing self->ul_uberdata->atexit_root.
997c478bd9Sstevel@tonic-gate  */
1007c478bd9Sstevel@tonic-gate int
__cxa_atexit(void (* hdlr)(void *),void * arg,void * dso)1015c069a6cSRichard Lowe __cxa_atexit(void (*hdlr)(void *), void *arg, void *dso)
1027c478bd9Sstevel@tonic-gate {
1037c478bd9Sstevel@tonic-gate 	ulwp_t *self;
1047c478bd9Sstevel@tonic-gate 	atexit_root_t *arp;
1057c478bd9Sstevel@tonic-gate 	_exthdlr_t *p;
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	if ((p = lmalloc(sizeof (_exthdlr_t))) == NULL)
1087c478bd9Sstevel@tonic-gate 		return (-1);
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	if ((self = __curthread()) == NULL)
1117c478bd9Sstevel@tonic-gate 		arp = &__uberdata.atexit_root;
1127c478bd9Sstevel@tonic-gate 	else {
1137c478bd9Sstevel@tonic-gate 		arp = &self->ul_uberdata->atexit_root;
1148cd45542Sraf 		(void) mutex_lock(&arp->exitfns_lock);
1157c478bd9Sstevel@tonic-gate 	}
1165c069a6cSRichard Lowe 	p->hdlr = hdlr;
1175c069a6cSRichard Lowe 	p->arg = arg;
1185c069a6cSRichard Lowe 	p->dso = dso;
1197c478bd9Sstevel@tonic-gate 	p->next = arp->head;
1207c478bd9Sstevel@tonic-gate 	arp->head = p;
1215c069a6cSRichard Lowe 
1227c478bd9Sstevel@tonic-gate 	if (self != NULL)
1238cd45542Sraf 		(void) mutex_unlock(&arp->exitfns_lock);
1247c478bd9Sstevel@tonic-gate 	return (0);
1257c478bd9Sstevel@tonic-gate }
1267c478bd9Sstevel@tonic-gate 
1275c069a6cSRichard Lowe int
atexit(void (* func)(void))1285c069a6cSRichard Lowe atexit(void (*func)(void))
1295c069a6cSRichard Lowe {
1305c069a6cSRichard Lowe 	return (__cxa_atexit((_exithdlr_func_t)func, NULL, NULL));
1315c069a6cSRichard Lowe }
1325c069a6cSRichard Lowe 
1335c069a6cSRichard Lowe /*
1345c069a6cSRichard Lowe  * Note that we may be entered recursively, as we'll call __cxa_finalize(0) at
1355c069a6cSRichard Lowe  * exit, one of our handlers is ld.so.1`atexit_fini, and libraries may call
1365c069a6cSRichard Lowe  * __cxa_finalize(__dso_handle) from their _fini.
1375c069a6cSRichard Lowe  */
1387c478bd9Sstevel@tonic-gate void
__cxa_finalize(void * dso)1395c069a6cSRichard Lowe __cxa_finalize(void *dso)
1407c478bd9Sstevel@tonic-gate {
1417c478bd9Sstevel@tonic-gate 	atexit_root_t *arp = &curthread->ul_uberdata->atexit_root;
1425c069a6cSRichard Lowe 	_exthdlr_t *p, *o;
14358a67dd2SRoger A. Faulkner 	int cancel_state;
1447c478bd9Sstevel@tonic-gate 
14558a67dd2SRoger A. Faulkner 	/* disable cancellation while running atexit handlers */
14658a67dd2SRoger A. Faulkner 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
1478cd45542Sraf 	(void) mutex_lock(&arp->exitfns_lock);
1485c069a6cSRichard Lowe 
1495c069a6cSRichard Lowe 	o = NULL;
1507c478bd9Sstevel@tonic-gate 	p = arp->head;
1517c478bd9Sstevel@tonic-gate 	while (p != NULL) {
1525c069a6cSRichard Lowe 		if ((dso == NULL) || (p->dso == dso)) {
1535c069a6cSRichard Lowe 			if (o != NULL)
1545c069a6cSRichard Lowe 				o->next = p->next;
1555c069a6cSRichard Lowe 			else
1565c069a6cSRichard Lowe 				arp->head = p->next;
1575c069a6cSRichard Lowe 
1585c069a6cSRichard Lowe 			p->hdlr(p->arg);
1595c069a6cSRichard Lowe 			lfree(p, sizeof (_exthdlr_t));
1605c069a6cSRichard Lowe 			o = NULL;
1615c069a6cSRichard Lowe 			p = arp->head;
1625c069a6cSRichard Lowe 		} else {
1635c069a6cSRichard Lowe 			o = p;
1645c069a6cSRichard Lowe 			p = p->next;
1655c069a6cSRichard Lowe 		}
1667c478bd9Sstevel@tonic-gate 	}
1675c069a6cSRichard Lowe 
1688cd45542Sraf 	(void) mutex_unlock(&arp->exitfns_lock);
16958a67dd2SRoger A. Faulkner 	(void) pthread_setcancelstate(cancel_state, NULL);
1707c478bd9Sstevel@tonic-gate }
1717c478bd9Sstevel@tonic-gate 
1725c069a6cSRichard Lowe void
_exithandle(void)1735c069a6cSRichard Lowe _exithandle(void)
1745c069a6cSRichard Lowe {
1755c069a6cSRichard Lowe 	atexit_root_t *arp = &curthread->ul_uberdata->atexit_root;
1765c069a6cSRichard Lowe 
1775c069a6cSRichard Lowe 	arp->exit_frame_monitor = _getfp() + STACK_BIAS;
1785c069a6cSRichard Lowe 	__cxa_finalize(NULL);
1795c069a6cSRichard Lowe }
1805c069a6cSRichard Lowe 
1817c478bd9Sstevel@tonic-gate /*
1827c478bd9Sstevel@tonic-gate  * _get_exit_frame_monitor is called by the C++ runtimes.
1837c478bd9Sstevel@tonic-gate  */
1847c478bd9Sstevel@tonic-gate void *
_get_exit_frame_monitor(void)1857c478bd9Sstevel@tonic-gate _get_exit_frame_monitor(void)
1867c478bd9Sstevel@tonic-gate {
1877c478bd9Sstevel@tonic-gate 	atexit_root_t *arp = &curthread->ul_uberdata->atexit_root;
1887c478bd9Sstevel@tonic-gate 	return (&arp->exit_frame_monitor);
1897c478bd9Sstevel@tonic-gate }
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate /*
1927c478bd9Sstevel@tonic-gate  * The following is a routine which the loader (ld.so.1) calls when it
1937c478bd9Sstevel@tonic-gate  * processes a dlclose call on an object.  It resets all signal handlers
1947c478bd9Sstevel@tonic-gate  * which fall within the union of the ranges specified by the elements
1957c478bd9Sstevel@tonic-gate  * of the array range to SIG_DFL.
1967c478bd9Sstevel@tonic-gate  */
1977c478bd9Sstevel@tonic-gate static void
_preexec_sig_unload(Lc_addr_range_t range[],uint_t count)1987c478bd9Sstevel@tonic-gate _preexec_sig_unload(Lc_addr_range_t range[], uint_t count)
1997c478bd9Sstevel@tonic-gate {
2007c478bd9Sstevel@tonic-gate 	uberdata_t *udp = curthread->ul_uberdata;
2017c478bd9Sstevel@tonic-gate 	int sig;
20241efec22Sraf 	rwlock_t *rwlp;
2037c478bd9Sstevel@tonic-gate 	struct sigaction *sap;
2047c478bd9Sstevel@tonic-gate 	struct sigaction oact;
2057c478bd9Sstevel@tonic-gate 	void (*handler)();
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	for (sig = 1; sig < NSIG; sig++) {
2087c478bd9Sstevel@tonic-gate 		sap = (struct sigaction *)&udp->siguaction[sig].sig_uaction;
2097c478bd9Sstevel@tonic-gate again:
2107c478bd9Sstevel@tonic-gate 		handler = sap->sa_handler;
2117c478bd9Sstevel@tonic-gate 		if (handler != SIG_DFL && handler != SIG_IGN &&
2125c069a6cSRichard Lowe 		    in_range((void *)handler, range, count)) {
21341efec22Sraf 			rwlp = &udp->siguaction[sig].sig_lock;
21441efec22Sraf 			lrw_wrlock(rwlp);
2157c478bd9Sstevel@tonic-gate 			if (handler != sap->sa_handler) {
21641efec22Sraf 				lrw_unlock(rwlp);
2177c478bd9Sstevel@tonic-gate 				goto again;
2187c478bd9Sstevel@tonic-gate 			}
2197c478bd9Sstevel@tonic-gate 			sap->sa_handler = SIG_DFL;
2207c478bd9Sstevel@tonic-gate 			sap->sa_flags = SA_SIGINFO;
2217c478bd9Sstevel@tonic-gate 			(void) sigemptyset(&sap->sa_mask);
2227c478bd9Sstevel@tonic-gate 			if (__sigaction(sig, NULL, &oact) == 0 &&
2237c478bd9Sstevel@tonic-gate 			    oact.sa_handler != SIG_DFL &&
2247c478bd9Sstevel@tonic-gate 			    oact.sa_handler != SIG_IGN)
2257c478bd9Sstevel@tonic-gate 				(void) __sigaction(sig, sap, NULL);
22641efec22Sraf 			lrw_unlock(rwlp);
2277c478bd9Sstevel@tonic-gate 		}
2287c478bd9Sstevel@tonic-gate 	}
2297c478bd9Sstevel@tonic-gate }
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate /*
2327c478bd9Sstevel@tonic-gate  * The following is a routine which the loader (ld.so.1) calls when it
2337c478bd9Sstevel@tonic-gate  * processes a dlclose call on an object.  It cancels all atfork() entries
2347c478bd9Sstevel@tonic-gate  * whose prefork, parent postfork, or child postfork functions fall within
2357c478bd9Sstevel@tonic-gate  * the union of the ranges specified by the elements of the array range.
2367c478bd9Sstevel@tonic-gate  */
2377c478bd9Sstevel@tonic-gate static void
_preexec_atfork_unload(Lc_addr_range_t range[],uint_t count)2387c478bd9Sstevel@tonic-gate _preexec_atfork_unload(Lc_addr_range_t range[], uint_t count)
2397c478bd9Sstevel@tonic-gate {
2402e145884Sraf 	ulwp_t *self = curthread;
2412e145884Sraf 	uberdata_t *udp = self->ul_uberdata;
2427c478bd9Sstevel@tonic-gate 	atfork_t *atfork_q;
2437c478bd9Sstevel@tonic-gate 	atfork_t *atfp;
2447c478bd9Sstevel@tonic-gate 	atfork_t *next;
2457c478bd9Sstevel@tonic-gate 	void (*func)(void);
2467c478bd9Sstevel@tonic-gate 	int start_again;
2477c478bd9Sstevel@tonic-gate 
2488cd45542Sraf 	(void) mutex_lock(&udp->atfork_lock);
2497c478bd9Sstevel@tonic-gate 	if ((atfork_q = udp->atforklist) != NULL) {
2507c478bd9Sstevel@tonic-gate 		atfp = atfork_q;
2517c478bd9Sstevel@tonic-gate 		do {
2527c478bd9Sstevel@tonic-gate 			next = atfp->forw;
2537c478bd9Sstevel@tonic-gate 			start_again = 0;
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 			if (((func = atfp->prepare) != NULL &&
2565c069a6cSRichard Lowe 			    in_range((void *)func, range, count)) ||
2577c478bd9Sstevel@tonic-gate 			    ((func = atfp->parent) != NULL &&
2585c069a6cSRichard Lowe 			    in_range((void *)func, range, count)) ||
2597c478bd9Sstevel@tonic-gate 			    ((func = atfp->child) != NULL &&
2605c069a6cSRichard Lowe 			    in_range((void *)func, range, count))) {
2612e145884Sraf 				if (self->ul_fork) {
2627c478bd9Sstevel@tonic-gate 					/*
2637c478bd9Sstevel@tonic-gate 					 * dlclose() called from a fork handler.
2647c478bd9Sstevel@tonic-gate 					 * Deleting the entry would wreak havoc.
2657c478bd9Sstevel@tonic-gate 					 * Just null out the function pointers
2667c478bd9Sstevel@tonic-gate 					 * and leave the entry in place.
2677c478bd9Sstevel@tonic-gate 					 */
2687c478bd9Sstevel@tonic-gate 					atfp->prepare = NULL;
2697c478bd9Sstevel@tonic-gate 					atfp->parent = NULL;
2707c478bd9Sstevel@tonic-gate 					atfp->child = NULL;
2717c478bd9Sstevel@tonic-gate 					continue;
2727c478bd9Sstevel@tonic-gate 				}
2737c478bd9Sstevel@tonic-gate 				if (atfp == atfork_q) {
2747c478bd9Sstevel@tonic-gate 					/* deleting the list head member */
2757c478bd9Sstevel@tonic-gate 					udp->atforklist = atfork_q = next;
2767c478bd9Sstevel@tonic-gate 					start_again = 1;
2777c478bd9Sstevel@tonic-gate 				}
2787c478bd9Sstevel@tonic-gate 				atfp->forw->back = atfp->back;
2797c478bd9Sstevel@tonic-gate 				atfp->back->forw = atfp->forw;
2807c478bd9Sstevel@tonic-gate 				lfree(atfp, sizeof (atfork_t));
2817c478bd9Sstevel@tonic-gate 				if (atfp == atfork_q) {
2827c478bd9Sstevel@tonic-gate 					/* we deleted the whole list */
2837c478bd9Sstevel@tonic-gate 					udp->atforklist = NULL;
2847c478bd9Sstevel@tonic-gate 					break;
2857c478bd9Sstevel@tonic-gate 				}
2867c478bd9Sstevel@tonic-gate 			}
2877c478bd9Sstevel@tonic-gate 		} while ((atfp = next) != atfork_q || start_again);
2887c478bd9Sstevel@tonic-gate 	}
2898cd45542Sraf 	(void) mutex_unlock(&udp->atfork_lock);
2907c478bd9Sstevel@tonic-gate }
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate /*
2937c478bd9Sstevel@tonic-gate  * The following is a routine which the loader (ld.so.1) calls when it
2947c478bd9Sstevel@tonic-gate  * processes a dlclose call on an object.  It sets the destructor
2957c478bd9Sstevel@tonic-gate  * function pointer to NULL for all keys whose destructors fall within
2967c478bd9Sstevel@tonic-gate  * the union of the ranges specified by the elements of the array range.
2977c478bd9Sstevel@tonic-gate  * We don't assign TSD_UNALLOCATED (the equivalent of pthread_key_destroy())
2987c478bd9Sstevel@tonic-gate  * because the thread may use the key's TSD further on in fini processing.
2997c478bd9Sstevel@tonic-gate  */
3007c478bd9Sstevel@tonic-gate static void
_preexec_tsd_unload(Lc_addr_range_t range[],uint_t count)3017c478bd9Sstevel@tonic-gate _preexec_tsd_unload(Lc_addr_range_t range[], uint_t count)
3027c478bd9Sstevel@tonic-gate {
3037c478bd9Sstevel@tonic-gate 	tsd_metadata_t *tsdm = &curthread->ul_uberdata->tsd_metadata;
3047c478bd9Sstevel@tonic-gate 	void (*func)(void *);
3057c478bd9Sstevel@tonic-gate 	int key;
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 	lmutex_lock(&tsdm->tsdm_lock);
3087c478bd9Sstevel@tonic-gate 	for (key = 1; key < tsdm->tsdm_nused; key++) {
3097c478bd9Sstevel@tonic-gate 		if ((func = tsdm->tsdm_destro[key]) != NULL &&
3107c478bd9Sstevel@tonic-gate 		    func != TSD_UNALLOCATED &&
3115c069a6cSRichard Lowe 		    in_range((void *)func, range, count))
3127c478bd9Sstevel@tonic-gate 			tsdm->tsdm_destro[key] = NULL;
3137c478bd9Sstevel@tonic-gate 	}
3147c478bd9Sstevel@tonic-gate 	lmutex_unlock(&tsdm->tsdm_lock);
3157c478bd9Sstevel@tonic-gate }
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate /*
3187c478bd9Sstevel@tonic-gate  * The following is a routine which the loader (ld.so.1) calls when it
3197c478bd9Sstevel@tonic-gate  * processes dlclose calls on objects with atexit registrations.  It
3207c478bd9Sstevel@tonic-gate  * executes the exit handlers that fall within the union of the ranges
3217c478bd9Sstevel@tonic-gate  * specified by the elements of the array range in the REVERSE ORDER of
3227c478bd9Sstevel@tonic-gate  * their registration.  Do not change this characteristic; it is REQUIRED
3237c478bd9Sstevel@tonic-gate  * BEHAVIOR.
3247c478bd9Sstevel@tonic-gate  */
3257c478bd9Sstevel@tonic-gate int
_preexec_exit_handlers(Lc_addr_range_t range[],uint_t count)3267c478bd9Sstevel@tonic-gate _preexec_exit_handlers(Lc_addr_range_t range[], uint_t count)
3277c478bd9Sstevel@tonic-gate {
3287c478bd9Sstevel@tonic-gate 	atexit_root_t *arp = &curthread->ul_uberdata->atexit_root;
3297c478bd9Sstevel@tonic-gate 	_exthdlr_t *o;		/* previous node */
3307c478bd9Sstevel@tonic-gate 	_exthdlr_t *p;		/* this node */
33158a67dd2SRoger A. Faulkner 	int cancel_state;
3327c478bd9Sstevel@tonic-gate 
33358a67dd2SRoger A. Faulkner 	/* disable cancellation while running atexit handlers */
33458a67dd2SRoger A. Faulkner 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
3358cd45542Sraf 	(void) mutex_lock(&arp->exitfns_lock);
3367c478bd9Sstevel@tonic-gate 	o = NULL;
3377c478bd9Sstevel@tonic-gate 	p = arp->head;
3387c478bd9Sstevel@tonic-gate 	while (p != NULL) {
3395c069a6cSRichard Lowe 		/*
3405c069a6cSRichard Lowe 		 * We call even CXA handlers of functions present in the
3415c069a6cSRichard Lowe 		 * library being unloaded.  The specification isn't
3425c069a6cSRichard Lowe 		 * particularly clear on this, and this seems the most sane.
3435c069a6cSRichard Lowe 		 * This is the behaviour of FreeBSD 9.1 (GNU libc leaves the
3445c069a6cSRichard Lowe 		 * handler on the exit list, and crashes at exit time).
3455c069a6cSRichard Lowe 		 *
3465c069a6cSRichard Lowe 		 * This won't cause handlers to be called twice, because
3475c069a6cSRichard Lowe 		 * anything called from a __cxa_finalize call from the
3485c069a6cSRichard Lowe 		 * language runtime will have been removed from the list.
3495c069a6cSRichard Lowe 		 */
3505c069a6cSRichard Lowe 		if (in_range((void *)p->hdlr, range, count)) {
3517c478bd9Sstevel@tonic-gate 			/* We need to execute this one */
3527c478bd9Sstevel@tonic-gate 			if (o != NULL)
3537c478bd9Sstevel@tonic-gate 				o->next = p->next;
3547c478bd9Sstevel@tonic-gate 			else
3557c478bd9Sstevel@tonic-gate 				arp->head = p->next;
3565c069a6cSRichard Lowe 			p->hdlr(p->arg);
3577c478bd9Sstevel@tonic-gate 			lfree(p, sizeof (_exthdlr_t));
3587c478bd9Sstevel@tonic-gate 			o = NULL;
3597c478bd9Sstevel@tonic-gate 			p = arp->head;
3607c478bd9Sstevel@tonic-gate 		} else {
3617c478bd9Sstevel@tonic-gate 			o = p;
3627c478bd9Sstevel@tonic-gate 			p = p->next;
3637c478bd9Sstevel@tonic-gate 		}
3647c478bd9Sstevel@tonic-gate 	}
3658cd45542Sraf 	(void) mutex_unlock(&arp->exitfns_lock);
36658a67dd2SRoger A. Faulkner 	(void) pthread_setcancelstate(cancel_state, NULL);
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	_preexec_tsd_unload(range, count);
3697c478bd9Sstevel@tonic-gate 	_preexec_atfork_unload(range, count);
3707c478bd9Sstevel@tonic-gate 	_preexec_sig_unload(range, count);
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate 	return (0);
3737c478bd9Sstevel@tonic-gate }
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate static int
in_range(void * addr,Lc_addr_range_t ranges[],uint_t count)3765c069a6cSRichard Lowe in_range(void *addr, Lc_addr_range_t ranges[], uint_t count)
3777c478bd9Sstevel@tonic-gate {
3787c478bd9Sstevel@tonic-gate 	uint_t idx;
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	for (idx = 0; idx < count; idx++) {
3815c069a6cSRichard Lowe 		if (addr >= ranges[idx].lb &&
3825c069a6cSRichard Lowe 		    addr < ranges[idx].ub) {
3837c478bd9Sstevel@tonic-gate 			return (1);
3847c478bd9Sstevel@tonic-gate 		}
3857c478bd9Sstevel@tonic-gate 	}
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	return (0);
3887c478bd9Sstevel@tonic-gate }
389*fc2512cfSRobert Mustacchi 
390*fc2512cfSRobert Mustacchi int
at_quick_exit(void (* func)(void))391*fc2512cfSRobert Mustacchi at_quick_exit(void (*func)(void))
392*fc2512cfSRobert Mustacchi {
393*fc2512cfSRobert Mustacchi 	ulwp_t *self;
394*fc2512cfSRobert Mustacchi 	quickexit_root_t *arp;
395*fc2512cfSRobert Mustacchi 	_qexthdlr_t *p;
396*fc2512cfSRobert Mustacchi 
397*fc2512cfSRobert Mustacchi 	if ((p = lmalloc(sizeof (_qexthdlr_t))) == NULL)
398*fc2512cfSRobert Mustacchi 		return (-1);
399*fc2512cfSRobert Mustacchi 
400*fc2512cfSRobert Mustacchi 	if ((self = __curthread()) == NULL) {
401*fc2512cfSRobert Mustacchi 		arp = &__uberdata.quickexit_root;
402*fc2512cfSRobert Mustacchi 	} else {
403*fc2512cfSRobert Mustacchi 		arp = &self->ul_uberdata->quickexit_root;
404*fc2512cfSRobert Mustacchi 		(void) mutex_lock(&arp->exitfns_lock);
405*fc2512cfSRobert Mustacchi 	}
406*fc2512cfSRobert Mustacchi 	p->hdlr = func;
407*fc2512cfSRobert Mustacchi 	p->next = arp->head;
408*fc2512cfSRobert Mustacchi 	arp->head = p;
409*fc2512cfSRobert Mustacchi 
410*fc2512cfSRobert Mustacchi 	if (self != NULL)
411*fc2512cfSRobert Mustacchi 		(void) mutex_unlock(&arp->exitfns_lock);
412*fc2512cfSRobert Mustacchi 	return (0);
413*fc2512cfSRobert Mustacchi 
414*fc2512cfSRobert Mustacchi }
415*fc2512cfSRobert Mustacchi 
416*fc2512cfSRobert Mustacchi void
quick_exit(int status)417*fc2512cfSRobert Mustacchi quick_exit(int status)
418*fc2512cfSRobert Mustacchi {
419*fc2512cfSRobert Mustacchi 	quickexit_root_t *qrp = &curthread->ul_uberdata->quickexit_root;
420*fc2512cfSRobert Mustacchi 	_qexthdlr_t *p;
421*fc2512cfSRobert Mustacchi 	int cancel_state;
422*fc2512cfSRobert Mustacchi 
423*fc2512cfSRobert Mustacchi 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
424*fc2512cfSRobert Mustacchi 	(void) mutex_lock(&qrp->exitfns_lock);
425*fc2512cfSRobert Mustacchi 
426*fc2512cfSRobert Mustacchi 	p = qrp->head;
427*fc2512cfSRobert Mustacchi 	while (p != NULL) {
428*fc2512cfSRobert Mustacchi 		qrp->head = p->next;
429*fc2512cfSRobert Mustacchi 		p->hdlr();
430*fc2512cfSRobert Mustacchi 		lfree(p, sizeof (_qexthdlr_t));
431*fc2512cfSRobert Mustacchi 		p = qrp->head;
432*fc2512cfSRobert Mustacchi 	}
433*fc2512cfSRobert Mustacchi 
434*fc2512cfSRobert Mustacchi 	(void) mutex_unlock(&qrp->exitfns_lock);
435*fc2512cfSRobert Mustacchi 	(void) pthread_setcancelstate(cancel_state, NULL);
436*fc2512cfSRobert Mustacchi 	_Exit(status);
437*fc2512cfSRobert Mustacchi }
438