xref: /illumos-gate/usr/src/lib/libc/port/gen/atexit.c (revision dd4eeefdb8e4583c47e28a7f315db6087931ef06)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*	Copyright (c) 1988 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 #pragma weak atexit = _atexit
33 
34 #include "synonyms.h"
35 #include "thr_uberdata.h"
36 #include "libc_int.h"
37 #include "atexit.h"
38 #include "stdiom.h"
39 
40 /*
41  * Note that memory is managed by lmalloc()/lfree().
42  *
43  * Among other reasons, this is occasioned by the insistence of our
44  * brothers sh(1) and csh(1) that they can do malloc, etc., better than
45  * libc can.  Those programs define their own malloc routines, and
46  * initialize the underlying mechanism in main().  This means that calls
47  * to malloc occuring before main will crash.  The loader calls atexit(3C)
48  * before calling main, so we'd better avoid malloc() when it does.
49  *
50  * Another reason for using lmalloc()/lfree() is that the atexit()
51  * list must transcend all link maps.  See the Linker and Libraries
52  * Guide for information on alternate link maps.
53  *
54  * See "thr_uberdata.h" for the definitions of structures used here.
55  */
56 
57 static int in_range(_exithdlr_func_t, Lc_addr_range_t[], uint_t count);
58 
59 extern	caddr_t	_getfp(void);
60 
61 /*
62  * exitfns_lock is declared to be a recursive mutex so that we
63  * can hold it while calling out to the registered functions.
64  * If they call back to us, we are self-consistent and everything
65  * works, even the case of calling exit() from functions called
66  * by _exithandle() (recursive exit()).  All that is required is
67  * that the registered functions actually return (no longjmp()s).
68  *
69  * Because exitfns_lock is declared to be a recursive mutex, we
70  * cannot use it with lmutex_lock()/lmutex_unlock() and we must use
71  * rmutex_lock()/rmutex_unlock() (which are defined to be simply
72  * mutex_lock()/mutex_unlock()).  This means that atexit() and
73  * exit() are not async-signal-safe.  We make them fork1-safe
74  * via the atexit_locks()/atexit_unlocks() functions, called from
75  * libc_prepare_atfork()/libc_child_atfork()/libc_parent_atfork()
76  */
77 
78 /*
79  * atexit_locks() and atexit_unlocks() are called on every link map.
80  * Do not use curthread->ul_uberdata->atexit_root for these.
81  */
82 void
83 atexit_locks()
84 {
85 	(void) rmutex_lock(&__uberdata.atexit_root.exitfns_lock);
86 }
87 
88 void
89 atexit_unlocks()
90 {
91 	(void) rmutex_unlock(&__uberdata.atexit_root.exitfns_lock);
92 }
93 
94 /*
95  * atexit() is called before the primordial thread is fully set up.
96  * Be careful about dereferencing self->ul_uberdata->atexit_root.
97  */
98 int
99 _atexit(void (*func)(void))
100 {
101 	ulwp_t *self;
102 	atexit_root_t *arp;
103 	_exthdlr_t *p;
104 
105 	if ((p = lmalloc(sizeof (_exthdlr_t))) == NULL)
106 		return (-1);
107 
108 	if ((self = __curthread()) == NULL)
109 		arp = &__uberdata.atexit_root;
110 	else {
111 		arp = &self->ul_uberdata->atexit_root;
112 		(void) rmutex_lock(&arp->exitfns_lock);
113 	}
114 	p->hdlr = func;
115 	p->next = arp->head;
116 	arp->head = p;
117 	if (self != NULL)
118 		(void) rmutex_unlock(&arp->exitfns_lock);
119 	return (0);
120 }
121 
122 void
123 _exithandle(void)
124 {
125 	atexit_root_t *arp = &curthread->ul_uberdata->atexit_root;
126 	_exthdlr_t *p;
127 
128 	(void) rmutex_lock(&arp->exitfns_lock);
129 	arp->exit_frame_monitor = _getfp() + STACK_BIAS;
130 	p = arp->head;
131 	while (p != NULL) {
132 		arp->head = p->next;
133 		p->hdlr();
134 		lfree(p, sizeof (_exthdlr_t));
135 		p = arp->head;
136 	}
137 	(void) rmutex_unlock(&arp->exitfns_lock);
138 }
139 
140 /*
141  * _get_exit_frame_monitor is called by the C++ runtimes.
142  */
143 void *
144 _get_exit_frame_monitor(void)
145 {
146 	atexit_root_t *arp = &curthread->ul_uberdata->atexit_root;
147 	return (&arp->exit_frame_monitor);
148 }
149 
150 /*
151  * The following is a routine which the loader (ld.so.1) calls when it
152  * processes a dlclose call on an object.  It resets all signal handlers
153  * which fall within the union of the ranges specified by the elements
154  * of the array range to SIG_DFL.
155  */
156 static void
157 _preexec_sig_unload(Lc_addr_range_t range[], uint_t count)
158 {
159 	uberdata_t *udp = curthread->ul_uberdata;
160 	int sig;
161 	rwlock_t *rwlp;
162 	struct sigaction *sap;
163 	struct sigaction oact;
164 	void (*handler)();
165 
166 	for (sig = 1; sig < NSIG; sig++) {
167 		sap = (struct sigaction *)&udp->siguaction[sig].sig_uaction;
168 again:
169 		handler = sap->sa_handler;
170 		if (handler != SIG_DFL && handler != SIG_IGN &&
171 		    in_range(handler, range, count)) {
172 			rwlp = &udp->siguaction[sig].sig_lock;
173 			lrw_wrlock(rwlp);
174 			if (handler != sap->sa_handler) {
175 				lrw_unlock(rwlp);
176 				goto again;
177 			}
178 			sap->sa_handler = SIG_DFL;
179 			sap->sa_flags = SA_SIGINFO;
180 			(void) sigemptyset(&sap->sa_mask);
181 			if (__sigaction(sig, NULL, &oact) == 0 &&
182 			    oact.sa_handler != SIG_DFL &&
183 			    oact.sa_handler != SIG_IGN)
184 				(void) __sigaction(sig, sap, NULL);
185 			lrw_unlock(rwlp);
186 		}
187 	}
188 }
189 
190 /*
191  * The following is a routine which the loader (ld.so.1) calls when it
192  * processes a dlclose call on an object.  It cancels all atfork() entries
193  * whose prefork, parent postfork, or child postfork functions fall within
194  * the union of the ranges specified by the elements of the array range.
195  */
196 static void
197 _preexec_atfork_unload(Lc_addr_range_t range[], uint_t count)
198 {
199 	ulwp_t *self = curthread;
200 	uberdata_t *udp = self->ul_uberdata;
201 	atfork_t *atfork_q;
202 	atfork_t *atfp;
203 	atfork_t *next;
204 	void (*func)(void);
205 	int start_again;
206 
207 	(void) _private_mutex_lock(&udp->atfork_lock);
208 	if ((atfork_q = udp->atforklist) != NULL) {
209 		atfp = atfork_q;
210 		do {
211 			next = atfp->forw;
212 			start_again = 0;
213 
214 			if (((func = atfp->prepare) != NULL &&
215 			    in_range(func, range, count)) ||
216 			    ((func = atfp->parent) != NULL &&
217 			    in_range(func, range, count)) ||
218 			    ((func = atfp->child) != NULL &&
219 			    in_range(func, range, count))) {
220 				if (self->ul_fork) {
221 					/*
222 					 * dlclose() called from a fork handler.
223 					 * Deleting the entry would wreak havoc.
224 					 * Just null out the function pointers
225 					 * and leave the entry in place.
226 					 */
227 					atfp->prepare = NULL;
228 					atfp->parent = NULL;
229 					atfp->child = NULL;
230 					continue;
231 				}
232 				if (atfp == atfork_q) {
233 					/* deleting the list head member */
234 					udp->atforklist = atfork_q = next;
235 					start_again = 1;
236 				}
237 				atfp->forw->back = atfp->back;
238 				atfp->back->forw = atfp->forw;
239 				lfree(atfp, sizeof (atfork_t));
240 				if (atfp == atfork_q) {
241 					/* we deleted the whole list */
242 					udp->atforklist = NULL;
243 					break;
244 				}
245 			}
246 		} while ((atfp = next) != atfork_q || start_again);
247 	}
248 	(void) _private_mutex_unlock(&udp->atfork_lock);
249 }
250 
251 /*
252  * The following is a routine which the loader (ld.so.1) calls when it
253  * processes a dlclose call on an object.  It sets the destructor
254  * function pointer to NULL for all keys whose destructors fall within
255  * the union of the ranges specified by the elements of the array range.
256  * We don't assign TSD_UNALLOCATED (the equivalent of pthread_key_destroy())
257  * because the thread may use the key's TSD further on in fini processing.
258  */
259 static void
260 _preexec_tsd_unload(Lc_addr_range_t range[], uint_t count)
261 {
262 	tsd_metadata_t *tsdm = &curthread->ul_uberdata->tsd_metadata;
263 	void (*func)(void *);
264 	int key;
265 
266 	lmutex_lock(&tsdm->tsdm_lock);
267 	for (key = 1; key < tsdm->tsdm_nused; key++) {
268 		if ((func = tsdm->tsdm_destro[key]) != NULL &&
269 		    func != TSD_UNALLOCATED &&
270 		    in_range((_exithdlr_func_t)func, range, count))
271 			tsdm->tsdm_destro[key] = NULL;
272 	}
273 	lmutex_unlock(&tsdm->tsdm_lock);
274 }
275 
276 /*
277  * The following is a routine which the loader (ld.so.1) calls when it
278  * processes dlclose calls on objects with atexit registrations.  It
279  * executes the exit handlers that fall within the union of the ranges
280  * specified by the elements of the array range in the REVERSE ORDER of
281  * their registration.  Do not change this characteristic; it is REQUIRED
282  * BEHAVIOR.
283  */
284 int
285 _preexec_exit_handlers(Lc_addr_range_t range[], uint_t count)
286 {
287 	atexit_root_t *arp = &curthread->ul_uberdata->atexit_root;
288 	_exthdlr_t *o;		/* previous node */
289 	_exthdlr_t *p;		/* this node */
290 
291 	(void) rmutex_lock(&arp->exitfns_lock);
292 	o = NULL;
293 	p = arp->head;
294 	while (p != NULL) {
295 		if (in_range(p->hdlr, range, count)) {
296 			/* We need to execute this one */
297 			if (o != NULL)
298 				o->next = p->next;
299 			else
300 				arp->head = p->next;
301 			p->hdlr();
302 			lfree(p, sizeof (_exthdlr_t));
303 			o = NULL;
304 			p = arp->head;
305 		} else {
306 			o = p;
307 			p = p->next;
308 		}
309 	}
310 	(void) rmutex_unlock(&arp->exitfns_lock);
311 
312 	_preexec_tsd_unload(range, count);
313 	_preexec_atfork_unload(range, count);
314 	_preexec_sig_unload(range, count);
315 
316 	return (0);
317 }
318 
319 static int
320 in_range(_exithdlr_func_t addr, Lc_addr_range_t ranges[], uint_t count)
321 {
322 	uint_t idx;
323 
324 	for (idx = 0; idx < count; idx++) {
325 		if ((void *)addr >= ranges[idx].lb &&
326 		    (void *)addr < ranges[idx].ub) {
327 			return (1);
328 		}
329 	}
330 
331 	return (0);
332 }
333