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