2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A/* Copyright (c) 1988 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A#pragma weak _atexit = atexit
2N/A
2N/A#include "lint.h"
2N/A#include "thr_uberdata.h"
2N/A#include "libc_int.h"
2N/A#include "atexit.h"
2N/A#include "stdiom.h"
2N/A
2N/A/*
2N/A * Note that memory is managed by lmalloc()/lfree().
2N/A *
2N/A * Among other reasons, this is occasioned by the insistence of our
2N/A * brothers sh(1) and csh(1) that they can do malloc, etc., better than
2N/A * libc can. Those programs define their own malloc routines, and
2N/A * initialize the underlying mechanism in main(). This means that calls
2N/A * to malloc occuring before main will crash. The loader calls atexit(3C)
2N/A * before calling main, so we'd better avoid malloc() when it does.
2N/A *
2N/A * Another reason for using lmalloc()/lfree() is that the atexit()
2N/A * list must transcend all link maps. See the Linker and Libraries
2N/A * Guide for information on alternate link maps.
2N/A *
2N/A * See "thr_uberdata.h" for the definitions of structures used here.
2N/A */
2N/A
2N/Astatic int in_range(_exithdlr_func_t, Lc_addr_range_t[], uint_t count);
2N/A
2N/Aextern caddr_t _getfp(void);
2N/A
2N/A/*
2N/A * exitfns_lock is declared to be a recursive mutex so that we
2N/A * can hold it while calling out to the registered functions.
2N/A * If they call back to us, we are self-consistent and everything
2N/A * works, even the case of calling exit() from functions called
2N/A * by _exithandle() (recursive exit()). All that is required is
2N/A * that the registered functions actually return (no longjmp()s).
2N/A *
2N/A * Because exitfns_lock is declared to be a recursive mutex, we
2N/A * cannot use it with lmutex_lock()/lmutex_unlock() and we must
2N/A * use mutex_lock()/mutex_unlock(). This means that atexit()
2N/A * and exit() are not async-signal-safe. We make them fork1-safe
2N/A * via the atexit_locks()/atexit_unlocks() functions, called from
2N/A * libc_prepare_atfork()/libc_child_atfork()/libc_parent_atfork()
2N/A */
2N/A
2N/A/*
2N/A * atexit_locks() and atexit_unlocks() are called on every link map.
2N/A * Do not use curthread->ul_uberdata->atexit_root for these.
2N/A */
2N/Avoid
2N/Aatexit_locks()
2N/A{
2N/A (void) mutex_lock(&__uberdata.atexit_root.exitfns_lock);
2N/A}
2N/A
2N/Avoid
2N/Aatexit_unlocks()
2N/A{
2N/A (void) mutex_unlock(&__uberdata.atexit_root.exitfns_lock);
2N/A}
2N/A
2N/A/*
2N/A * atexit() is called before the primordial thread is fully set up.
2N/A * Be careful about dereferencing self->ul_uberdata->atexit_root.
2N/A */
2N/Aint
2N/Aatexit(void (*func)(void))
2N/A{
2N/A ulwp_t *self;
2N/A atexit_root_t *arp;
2N/A _exthdlr_t *p;
2N/A
2N/A if ((p = lmalloc(sizeof (_exthdlr_t))) == NULL)
2N/A return (-1);
2N/A
2N/A if ((self = __curthread()) == NULL)
2N/A arp = &__uberdata.atexit_root;
2N/A else {
2N/A arp = &self->ul_uberdata->atexit_root;
2N/A (void) mutex_lock(&arp->exitfns_lock);
2N/A }
2N/A p->hdlr = func;
2N/A p->next = arp->head;
2N/A arp->head = p;
2N/A if (self != NULL)
2N/A (void) mutex_unlock(&arp->exitfns_lock);
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/A_exithandle(void)
2N/A{
2N/A atexit_root_t *arp = &curthread->ul_uberdata->atexit_root;
2N/A _exthdlr_t *p;
2N/A int cancel_state;
2N/A
2N/A /* disable cancellation while running atexit handlers */
2N/A (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
2N/A (void) mutex_lock(&arp->exitfns_lock);
2N/A arp->exit_frame_monitor = _getfp() + STACK_BIAS;
2N/A p = arp->head;
2N/A while (p != NULL) {
2N/A arp->head = p->next;
2N/A p->hdlr();
2N/A lfree(p, sizeof (_exthdlr_t));
2N/A p = arp->head;
2N/A }
2N/A (void) mutex_unlock(&arp->exitfns_lock);
2N/A (void) pthread_setcancelstate(cancel_state, NULL);
2N/A}
2N/A
2N/A/*
2N/A * _get_exit_frame_monitor is called by the C++ runtimes.
2N/A */
2N/Avoid *
2N/A_get_exit_frame_monitor(void)
2N/A{
2N/A atexit_root_t *arp = &curthread->ul_uberdata->atexit_root;
2N/A return (&arp->exit_frame_monitor);
2N/A}
2N/A
2N/A/*
2N/A * The following is a routine which the loader (ld.so.1) calls when it
2N/A * processes a dlclose call on an object. It resets all signal handlers
2N/A * which fall within the union of the ranges specified by the elements
2N/A * of the array range to SIG_DFL.
2N/A */
2N/Astatic void
2N/A_preexec_sig_unload(Lc_addr_range_t range[], uint_t count)
2N/A{
2N/A uberdata_t *udp = curthread->ul_uberdata;
2N/A int sig;
2N/A rwlock_t *rwlp;
2N/A struct sigaction *sap;
2N/A struct sigaction oact;
2N/A void (*handler)();
2N/A
2N/A for (sig = 1; sig < NSIG; sig++) {
2N/A sap = (struct sigaction *)&udp->siguaction[sig].sig_uaction;
2N/Aagain:
2N/A handler = sap->sa_handler;
2N/A if (handler != SIG_DFL && handler != SIG_IGN &&
2N/A in_range(handler, range, count)) {
2N/A rwlp = &udp->siguaction[sig].sig_lock;
2N/A lrw_wrlock(rwlp);
2N/A if (handler != sap->sa_handler) {
2N/A lrw_unlock(rwlp);
2N/A goto again;
2N/A }
2N/A sap->sa_handler = SIG_DFL;
2N/A sap->sa_flags = SA_SIGINFO;
2N/A (void) sigemptyset(&sap->sa_mask);
2N/A if (__sigaction(sig, NULL, &oact) == 0 &&
2N/A oact.sa_handler != SIG_DFL &&
2N/A oact.sa_handler != SIG_IGN)
2N/A (void) __sigaction(sig, sap, NULL);
2N/A lrw_unlock(rwlp);
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * The following is a routine which the loader (ld.so.1) calls when it
2N/A * processes a dlclose call on an object. It cancels all atfork() entries
2N/A * whose prefork, parent postfork, or child postfork functions fall within
2N/A * the union of the ranges specified by the elements of the array range.
2N/A */
2N/Astatic void
2N/A_preexec_atfork_unload(Lc_addr_range_t range[], uint_t count)
2N/A{
2N/A ulwp_t *self = curthread;
2N/A uberdata_t *udp = self->ul_uberdata;
2N/A atfork_t *atfork_q;
2N/A atfork_t *atfp;
2N/A atfork_t *next;
2N/A void (*func)(void);
2N/A int start_again;
2N/A
2N/A (void) mutex_lock(&udp->atfork_lock);
2N/A if ((atfork_q = udp->atforklist) != NULL) {
2N/A atfp = atfork_q;
2N/A do {
2N/A next = atfp->forw;
2N/A start_again = 0;
2N/A
2N/A if (((func = atfp->prepare) != NULL &&
2N/A in_range(func, range, count)) ||
2N/A ((func = atfp->parent) != NULL &&
2N/A in_range(func, range, count)) ||
2N/A ((func = atfp->child) != NULL &&
2N/A in_range(func, range, count))) {
2N/A if (self->ul_fork) {
2N/A /*
2N/A * dlclose() called from a fork handler.
2N/A * Deleting the entry would wreak havoc.
2N/A * Just null out the function pointers
2N/A * and leave the entry in place.
2N/A */
2N/A atfp->prepare = NULL;
2N/A atfp->parent = NULL;
2N/A atfp->child = NULL;
2N/A continue;
2N/A }
2N/A if (atfp == atfork_q) {
2N/A /* deleting the list head member */
2N/A udp->atforklist = atfork_q = next;
2N/A start_again = 1;
2N/A }
2N/A atfp->forw->back = atfp->back;
2N/A atfp->back->forw = atfp->forw;
2N/A lfree(atfp, sizeof (atfork_t));
2N/A if (atfp == atfork_q) {
2N/A /* we deleted the whole list */
2N/A udp->atforklist = NULL;
2N/A break;
2N/A }
2N/A }
2N/A } while ((atfp = next) != atfork_q || start_again);
2N/A }
2N/A (void) mutex_unlock(&udp->atfork_lock);
2N/A}
2N/A
2N/A/*
2N/A * The following is a routine which the loader (ld.so.1) calls when it
2N/A * processes a dlclose call on an object. It sets the destructor
2N/A * function pointer to NULL for all keys whose destructors fall within
2N/A * the union of the ranges specified by the elements of the array range.
2N/A * We don't assign TSD_UNALLOCATED (the equivalent of pthread_key_destroy())
2N/A * because the thread may use the key's TSD further on in fini processing.
2N/A */
2N/Astatic void
2N/A_preexec_tsd_unload(Lc_addr_range_t range[], uint_t count)
2N/A{
2N/A tsd_metadata_t *tsdm = &curthread->ul_uberdata->tsd_metadata;
2N/A void (*func)(void *);
2N/A int key;
2N/A
2N/A lmutex_lock(&tsdm->tsdm_lock);
2N/A for (key = 1; key < tsdm->tsdm_nused; key++) {
2N/A if ((func = tsdm->tsdm_destro[key]) != NULL &&
2N/A func != TSD_UNALLOCATED &&
2N/A in_range((_exithdlr_func_t)func, range, count))
2N/A tsdm->tsdm_destro[key] = NULL;
2N/A }
2N/A lmutex_unlock(&tsdm->tsdm_lock);
2N/A}
2N/A
2N/A/*
2N/A * The following is a routine which the loader (ld.so.1) calls when it
2N/A * processes dlclose calls on objects with atexit registrations. It
2N/A * executes the exit handlers that fall within the union of the ranges
2N/A * specified by the elements of the array range in the REVERSE ORDER of
2N/A * their registration. Do not change this characteristic; it is REQUIRED
2N/A * BEHAVIOR.
2N/A */
2N/Aint
2N/A_preexec_exit_handlers(Lc_addr_range_t range[], uint_t count)
2N/A{
2N/A atexit_root_t *arp = &curthread->ul_uberdata->atexit_root;
2N/A _exthdlr_t *o; /* previous node */
2N/A _exthdlr_t *p; /* this node */
2N/A int cancel_state;
2N/A
2N/A /* disable cancellation while running atexit handlers */
2N/A (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
2N/A (void) mutex_lock(&arp->exitfns_lock);
2N/A o = NULL;
2N/A p = arp->head;
2N/A while (p != NULL) {
2N/A if (in_range(p->hdlr, range, count)) {
2N/A /* We need to execute this one */
2N/A if (o != NULL)
2N/A o->next = p->next;
2N/A else
2N/A arp->head = p->next;
2N/A p->hdlr();
2N/A lfree(p, sizeof (_exthdlr_t));
2N/A o = NULL;
2N/A p = arp->head;
2N/A } else {
2N/A o = p;
2N/A p = p->next;
2N/A }
2N/A }
2N/A (void) mutex_unlock(&arp->exitfns_lock);
2N/A (void) pthread_setcancelstate(cancel_state, NULL);
2N/A
2N/A _preexec_tsd_unload(range, count);
2N/A _preexec_atfork_unload(range, count);
2N/A _preexec_sig_unload(range, count);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Ain_range(_exithdlr_func_t addr, Lc_addr_range_t ranges[], uint_t count)
2N/A{
2N/A uint_t idx;
2N/A
2N/A for (idx = 0; idx < count; idx++) {
2N/A if ((void *)addr >= ranges[idx].lb &&
2N/A (void *)addr < ranges[idx].ub) {
2N/A return (1);
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}