atexit.c revision 1219:f89f56c2d9ac
1234287Sdim/*
2234287Sdim * CDDL HEADER START
3353358Sdim *
4353358Sdim * The contents of this file are subject to the terms of the
5353358Sdim * Common Development and Distribution License, Version 1.0 only
6234287Sdim * (the "License").  You may not use this file except in compliance
7234287Sdim * with the License.
8234287Sdim *
9234287Sdim * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10234287Sdim * or http://www.opensolaris.org/os/licensing.
11234287Sdim * See the License for the specific language governing permissions
12234287Sdim * and limitations under the License.
13234287Sdim *
14234287Sdim * When distributing Covered Code, include this CDDL HEADER in each
15234287Sdim * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16234287Sdim * If applicable, add the following below this CDDL HEADER, with the
17234287Sdim * fields enclosed by brackets "[]" replaced with your own identifying
18234287Sdim * information: Portions Copyright [yyyy] [name of copyright owner]
19234287Sdim *
20234287Sdim * CDDL HEADER END
21234287Sdim */
22234287Sdim
23234287Sdim/*
24234287Sdim * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25234287Sdim * Use is subject to license terms.
26234287Sdim */
27234287Sdim
28234287Sdim#pragma ident	"%Z%%M%	%I%	%E% SMI"
29234287Sdim
30234287Sdim/*	Copyright (c) 1988 AT&T	*/
31234287Sdim/*	  All Rights Reserved  	*/
32234287Sdim
33276479Sdim#pragma weak atexit = _atexit
34234287Sdim
35249423Sdim#include "synonyms.h"
36234287Sdim#include "thr_uberdata.h"
37249423Sdim#include "libc_int.h"
38249423Sdim#include "atexit.h"
39239462Sdim#include "stdiom.h"
40234287Sdim
41234287Sdim/*
42234287Sdim * Note that memory is managed by lmalloc()/lfree().
43234287Sdim *
44234287Sdim * Among other reasons, this is occasioned by the insistence of our
45234287Sdim * brothers sh(1) and csh(1) that they can do malloc, etc., better than
46296417Sdim * libc can.  Those programs define their own malloc routines, and
47234287Sdim * initialize the underlying mechanism in main().  This means that calls
48296417Sdim * to malloc occuring before main will crash.  The loader calls atexit(3C)
49296417Sdim * before calling main, so we'd better avoid malloc() when it does.
50296417Sdim *
51296417Sdim * Another reason for using lmalloc()/lfree() is that the atexit()
52296417Sdim * list must transcend all link maps.  See the Linker and Libraries
53296417Sdim * Guide for information on alternate link maps.
54234287Sdim *
55296417Sdim * See "thr_uberdata.h" for the definitions of structures used here.
56296417Sdim */
57296417Sdim
58296417Sdimstatic int in_range(_exithdlr_func_t, Lc_addr_range_t[], uint_t count);
59296417Sdim
60234287Sdimextern	caddr_t	_getfp(void);
61296417Sdim
62296417Sdim/*
63296417Sdim * exitfns_lock is declared to be a recursive mutex so that we
64296417Sdim * can hold it while calling out to the registered functions.
65296417Sdim * If they call back to us, we are self-consistent and everything
66296417Sdim * works, even the case of calling exit() from functions called
67296417Sdim * by _exithandle() (recursive exit()).  All that is required is
68296417Sdim * that the registered functions actually return (no longjmp()s).
69296417Sdim *
70296417Sdim * Because exitfns_lock is declared to be a recursive mutex, we
71296417Sdim * cannot use it with lmutex_lock()/lmutex_unlock() and we must use
72296417Sdim * rmutex_lock()/rmutex_unlock() (which are defined to be simply
73296417Sdim * mutex_lock()/mutex_unlock()).  This means that atexit() and
74296417Sdim * exit() are not async-signal-safe.  We make them fork1-safe
75296417Sdim * via the atexit_locks()/atexit_unlocks() functions, called from
76296417Sdim * libc_prepare_atfork()/libc_child_atfork()/libc_parent_atfork()
77296417Sdim */
78296417Sdim
79296417Sdim/*
80296417Sdim * atexit_locks() and atexit_unlocks() are called on every link map.
81296417Sdim * Do not use curthread->ul_uberdata->atexit_root for these.
82296417Sdim */
83296417Sdimvoid
84296417Sdimatexit_locks()
85296417Sdim{
86296417Sdim	(void) rmutex_lock(&__uberdata.atexit_root.exitfns_lock);
87296417Sdim}
88296417Sdim
89296417Sdimvoid
90296417Sdimatexit_unlocks()
91296417Sdim{
92296417Sdim	(void) rmutex_unlock(&__uberdata.atexit_root.exitfns_lock);
93296417Sdim}
94296417Sdim
95296417Sdim/*
96296417Sdim * atexit() is called before the primordial thread is fully set up.
97296417Sdim * Be careful about dereferencing self->ul_uberdata->atexit_root.
98296417Sdim */
99296417Sdimint
100296417Sdim_atexit(void (*func)(void))
101296417Sdim{
102296417Sdim	ulwp_t *self;
103296417Sdim	atexit_root_t *arp;
104296417Sdim	_exthdlr_t *p;
105296417Sdim
106234287Sdim	if ((p = lmalloc(sizeof (_exthdlr_t))) == NULL)
107234287Sdim		return (-1);
108296417Sdim
109296417Sdim	if ((self = __curthread()) == NULL)
110296417Sdim		arp = &__uberdata.atexit_root;
111296417Sdim	else {
112296417Sdim		arp = &self->ul_uberdata->atexit_root;
113296417Sdim		(void) rmutex_lock(&arp->exitfns_lock);
114296417Sdim	}
115296417Sdim	p->hdlr = func;
116234287Sdim	p->next = arp->head;
117234287Sdim	arp->head = p;
118234287Sdim	if (self != NULL)
119234287Sdim		(void) rmutex_unlock(&arp->exitfns_lock);
120234287Sdim	return (0);
121234287Sdim}
122234287Sdim
123234287Sdimvoid
124234287Sdim_exithandle(void)
125234287Sdim{
126234287Sdim	atexit_root_t *arp = &curthread->ul_uberdata->atexit_root;
127234287Sdim	_exthdlr_t *p;
128234287Sdim
129234287Sdim	(void) rmutex_lock(&arp->exitfns_lock);
130234287Sdim	arp->exit_frame_monitor = _getfp() + STACK_BIAS;
131234287Sdim	p = arp->head;
132234287Sdim	while (p != NULL) {
133234287Sdim		arp->head = p->next;
134341825Sdim		p->hdlr();
135341825Sdim		lfree(p, sizeof (_exthdlr_t));
136234287Sdim		p = arp->head;
137234287Sdim	}
138234287Sdim	(void) rmutex_unlock(&arp->exitfns_lock);
139234287Sdim}
140234287Sdim
141234287Sdim/*
142234287Sdim * _get_exit_frame_monitor is called by the C++ runtimes.
143353358Sdim */
144353358Sdimvoid *
145353358Sdim_get_exit_frame_monitor(void)
146353358Sdim{
147234287Sdim	atexit_root_t *arp = &curthread->ul_uberdata->atexit_root;
148353358Sdim	return (&arp->exit_frame_monitor);
149353358Sdim}
150353358Sdim
151353358Sdim/*
152353358Sdim * The following is a routine which the loader (ld.so.1) calls when it
153353358Sdim * processes a dlclose call on an object.  It resets all signal handlers
154353358Sdim * which fall within the union of the ranges specified by the elements
155234287Sdim * of the array range to SIG_DFL.
156234287Sdim */
157353358Sdimstatic void
158353358Sdim_preexec_sig_unload(Lc_addr_range_t range[], uint_t count)
159353358Sdim{
160353358Sdim	uberdata_t *udp = curthread->ul_uberdata;
161234287Sdim	int sig;
162234287Sdim	mutex_t *mp;
163261991Sdim	struct sigaction *sap;
164261991Sdim	struct sigaction oact;
165261991Sdim	void (*handler)();
166261991Sdim
167261991Sdim	for (sig = 1; sig < NSIG; sig++) {
168261991Sdim		sap = (struct sigaction *)&udp->siguaction[sig].sig_uaction;
169261991Sdimagain:
170261991Sdim		handler = sap->sa_handler;
171261991Sdim		if (handler != SIG_DFL && handler != SIG_IGN &&
172261991Sdim		    in_range(handler, range, count)) {
173261991Sdim			mp = &udp->siguaction[sig].sig_lock;
174261991Sdim			lmutex_lock(mp);
175261991Sdim			if (handler != sap->sa_handler) {
176261991Sdim				lmutex_unlock(mp);
177261991Sdim				goto again;
178261991Sdim			}
179261991Sdim			sap->sa_handler = SIG_DFL;
180261991Sdim			sap->sa_flags = SA_SIGINFO;
181261991Sdim			(void) sigemptyset(&sap->sa_mask);
182234287Sdim			if (__sigaction(sig, NULL, &oact) == 0 &&
183234287Sdim			    oact.sa_handler != SIG_DFL &&
184234287Sdim			    oact.sa_handler != SIG_IGN)
185234287Sdim				(void) __sigaction(sig, sap, NULL);
186234287Sdim			lmutex_unlock(mp);
187234287Sdim		}
188234287Sdim	}
189234287Sdim}
190234287Sdim
191341825Sdim/*
192234287Sdim * The following is a routine which the loader (ld.so.1) calls when it
193234287Sdim * processes a dlclose call on an object.  It cancels all atfork() entries
194341825Sdim * whose prefork, parent postfork, or child postfork functions fall within
195234287Sdim * the union of the ranges specified by the elements of the array range.
196341825Sdim */
197234287Sdimstatic void
198234287Sdim_preexec_atfork_unload(Lc_addr_range_t range[], uint_t count)
199234287Sdim{
200234287Sdim	uberdata_t *udp = curthread->ul_uberdata;
201234287Sdim	atfork_t *atfork_q;
202234287Sdim	atfork_t *atfp;
203234287Sdim	atfork_t *next;
204234287Sdim	void (*func)(void);
205234287Sdim	int start_again;
206234287Sdim	int error;
207234287Sdim
208234287Sdim	error = fork_lock_enter(NULL);
209234287Sdim	if ((atfork_q = udp->atforklist) != NULL) {
210341825Sdim		atfp = atfork_q;
211341825Sdim		do {
212341825Sdim			next = atfp->forw;
213234287Sdim			start_again = 0;
214234287Sdim
215234287Sdim			if (((func = atfp->prepare) != NULL &&
216234287Sdim			    in_range(func, range, count)) ||
217234287Sdim			    ((func = atfp->parent) != NULL &&
218234287Sdim			    in_range(func, range, count)) ||
219234287Sdim			    ((func = atfp->child) != NULL &&
220234287Sdim			    in_range(func, range, count))) {
221234287Sdim				if (error) {
222234287Sdim					/*
223234287Sdim					 * dlclose() called from a fork handler.
224243830Sdim					 * Deleting the entry would wreak havoc.
225234287Sdim					 * Just null out the function pointers
226234287Sdim					 * and leave the entry in place.
227234287Sdim					 */
228234287Sdim					atfp->prepare = NULL;
229234287Sdim					atfp->parent = NULL;
230234287Sdim					atfp->child = NULL;
231234287Sdim					continue;
232341825Sdim				}
233341825Sdim				if (atfp == atfork_q) {
234341825Sdim					/* deleting the list head member */
235234287Sdim					udp->atforklist = atfork_q = next;
236234287Sdim					start_again = 1;
237234287Sdim				}
238296417Sdim				atfp->forw->back = atfp->back;
239276479Sdim				atfp->back->forw = atfp->forw;
240276479Sdim				lfree(atfp, sizeof (atfork_t));
241276479Sdim				if (atfp == atfork_q) {
242243830Sdim					/* we deleted the whole list */
243243830Sdim					udp->atforklist = NULL;
244234287Sdim					break;
245243830Sdim				}
246243830Sdim			}
247243830Sdim		} while ((atfp = next) != atfork_q || start_again);
248243830Sdim	}
249243830Sdim	fork_lock_exit();
250234287Sdim}
251234287Sdim
252234287Sdim/*
253234287Sdim * The following is a routine which the loader (ld.so.1) calls when it
254341825Sdim * processes a dlclose call on an object.  It sets the destructor
255296417Sdim * function pointer to NULL for all keys whose destructors fall within
256296417Sdim * the union of the ranges specified by the elements of the array range.
257296417Sdim * We don't assign TSD_UNALLOCATED (the equivalent of pthread_key_destroy())
258296417Sdim * because the thread may use the key's TSD further on in fini processing.
259296417Sdim */
260296417Sdimstatic void
261296417Sdim_preexec_tsd_unload(Lc_addr_range_t range[], uint_t count)
262296417Sdim{
263296417Sdim	tsd_metadata_t *tsdm = &curthread->ul_uberdata->tsd_metadata;
264296417Sdim	void (*func)(void *);
265296417Sdim	int key;
266296417Sdim
267296417Sdim	lmutex_lock(&tsdm->tsdm_lock);
268234287Sdim	for (key = 1; key < tsdm->tsdm_nused; key++) {
269234287Sdim		if ((func = tsdm->tsdm_destro[key]) != NULL &&
270243830Sdim		    func != TSD_UNALLOCATED &&
271234287Sdim		    in_range((_exithdlr_func_t)func, range, count))
272234287Sdim			tsdm->tsdm_destro[key] = NULL;
273234287Sdim	}
274234287Sdim	lmutex_unlock(&tsdm->tsdm_lock);
275234287Sdim}
276234287Sdim
277234287Sdim/*
278234287Sdim * The following is a routine which the loader (ld.so.1) calls when it
279234982Sdim * processes dlclose calls on objects with atexit registrations.  It
280234287Sdim * executes the exit handlers that fall within the union of the ranges
281234287Sdim * specified by the elements of the array range in the REVERSE ORDER of
282341825Sdim * their registration.  Do not change this characteristic; it is REQUIRED
283341825Sdim * BEHAVIOR.
284341825Sdim */
285341825Sdimint
286234287Sdim_preexec_exit_handlers(Lc_addr_range_t range[], uint_t count)
287234287Sdim{
288234287Sdim	atexit_root_t *arp = &curthread->ul_uberdata->atexit_root;
289234287Sdim	_exthdlr_t *o;		/* previous node */
290234287Sdim	_exthdlr_t *p;		/* this node */
291234287Sdim
292234287Sdim	(void) rmutex_lock(&arp->exitfns_lock);
293234287Sdim	o = NULL;
294234287Sdim	p = arp->head;
295234287Sdim	while (p != NULL) {
296234287Sdim		if (in_range(p->hdlr, range, count)) {
297234287Sdim			/* We need to execute this one */
298239462Sdim			if (o != NULL)
299234287Sdim				o->next = p->next;
300276479Sdim			else
301234287Sdim				arp->head = p->next;
302276479Sdim			p->hdlr();
303276479Sdim			lfree(p, sizeof (_exthdlr_t));
304276479Sdim			o = NULL;
305276479Sdim			p = arp->head;
306243830Sdim		} else {
307243830Sdim			o = p;
308234287Sdim			p = p->next;
309234287Sdim		}
310234287Sdim	}
311234287Sdim	(void) rmutex_unlock(&arp->exitfns_lock);
312234287Sdim
313234287Sdim	_preexec_tsd_unload(range, count);
314234287Sdim	_preexec_atfork_unload(range, count);
315234287Sdim	_preexec_sig_unload(range, count);
316234287Sdim
317341825Sdim	return (0);
318234287Sdim}
319234287Sdim
320341825Sdimstatic int
321234287Sdimin_range(_exithdlr_func_t addr, Lc_addr_range_t ranges[], uint_t count)
322341825Sdim{
323341825Sdim	uint_t idx;
324341825Sdim
325341825Sdim	for (idx = 0; idx < count; idx++) {
326276479Sdim		if ((void *)addr >= ranges[idx].lb &&
327234287Sdim		    (void *)addr < ranges[idx].ub) {
328234287Sdim			return (1);
329234287Sdim		}
330234287Sdim	}
331234287Sdim
332276479Sdim	return (0);
333276479Sdim}
334234287Sdim