# HG changeset patch # User Richard Lowe # Date 1372278965 14400 # Node ID d1349977b97514c8031cc668aab843d50eb83d44 # Parent eecf57cad0d19927819dc927ee23286b2e6e6d2e 3849 implement __cxa_atexit/__cxa_finalize Reviewed by: Hans Rosenfeld Reviewed by: Robert Mustacchi Approved by: Albert Lee diff -r eecf57cad0d1 -r d1349977b975 usr/src/lib/libc/inc/thr_uberdata.h --- a/usr/src/lib/libc/inc/thr_uberdata.h Sun Jul 14 17:30:29 2013 +0000 +++ b/usr/src/lib/libc/inc/thr_uberdata.h Wed Jun 26 16:36:05 2013 -0400 @@ -832,16 +832,25 @@ * atexit() data structures. * See port/gen/atexit.c for details. */ -typedef void (*_exithdlr_func_t) (void); +typedef void (*_exithdlr_func_t) (void*); typedef struct _exthdlr { struct _exthdlr *next; /* next in handler list */ _exithdlr_func_t hdlr; /* handler itself */ + void *arg; /* argument to handler */ + void *dso; /* DSO associated with handler */ } _exthdlr_t; typedef struct { mutex_t exitfns_lock; _exthdlr_t *head; + /* + * exit_frame_monitor is part of a private contract between libc and + * the Sun C++ runtime. + * + * It should be NULL until exit() is called, and thereafter hold the + * frame pointer of the function implementing our exit processing. + */ void *exit_frame_monitor; char exit_pad[64 - /* pad out to 64 bytes */ (sizeof (mutex_t) + sizeof (_exthdlr_t *) + sizeof (void *))]; diff -r eecf57cad0d1 -r d1349977b975 usr/src/lib/libc/port/gen/atexit.c --- a/usr/src/lib/libc/port/gen/atexit.c Sun Jul 14 17:30:29 2013 +0000 +++ b/usr/src/lib/libc/port/gen/atexit.c Wed Jun 26 16:36:05 2013 -0400 @@ -52,7 +52,7 @@ * See "thr_uberdata.h" for the definitions of structures used here. */ -static int in_range(_exithdlr_func_t, Lc_addr_range_t[], uint_t count); +static int in_range(void *, Lc_addr_range_t[], uint_t count); extern caddr_t _getfp(void); @@ -88,12 +88,13 @@ (void) mutex_unlock(&__uberdata.atexit_root.exitfns_lock); } + /* - * atexit() is called before the primordial thread is fully set up. + * This is called via atexit() before the primordial thread is fully set up. * Be careful about dereferencing self->ul_uberdata->atexit_root. */ int -atexit(void (*func)(void)) +__cxa_atexit(void (*hdlr)(void *), void *arg, void *dso) { ulwp_t *self; atexit_root_t *arp; @@ -108,34 +109,69 @@ arp = &self->ul_uberdata->atexit_root; (void) mutex_lock(&arp->exitfns_lock); } - p->hdlr = func; + p->hdlr = hdlr; + p->arg = arg; + p->dso = dso; p->next = arp->head; arp->head = p; + if (self != NULL) (void) mutex_unlock(&arp->exitfns_lock); return (0); } +int +atexit(void (*func)(void)) +{ + return (__cxa_atexit((_exithdlr_func_t)func, NULL, NULL)); +} + +/* + * Note that we may be entered recursively, as we'll call __cxa_finalize(0) at + * exit, one of our handlers is ld.so.1`atexit_fini, and libraries may call + * __cxa_finalize(__dso_handle) from their _fini. + */ +void +__cxa_finalize(void *dso) +{ + atexit_root_t *arp = &curthread->ul_uberdata->atexit_root; + _exthdlr_t *p, *o; + int cancel_state; + + /* disable cancellation while running atexit handlers */ + (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state); + (void) mutex_lock(&arp->exitfns_lock); + + o = NULL; + p = arp->head; + while (p != NULL) { + if ((dso == NULL) || (p->dso == dso)) { + if (o != NULL) + o->next = p->next; + else + arp->head = p->next; + + p->hdlr(p->arg); + lfree(p, sizeof (_exthdlr_t)); + o = NULL; + p = arp->head; + } else { + o = p; + p = p->next; + } + } + + (void) mutex_unlock(&arp->exitfns_lock); + (void) pthread_setcancelstate(cancel_state, NULL); +} + void _exithandle(void) { atexit_root_t *arp = &curthread->ul_uberdata->atexit_root; - _exthdlr_t *p; - int cancel_state; - /* disable cancellation while running atexit handlers */ - (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state); - (void) mutex_lock(&arp->exitfns_lock); arp->exit_frame_monitor = _getfp() + STACK_BIAS; - p = arp->head; - while (p != NULL) { - arp->head = p->next; - p->hdlr(); - lfree(p, sizeof (_exthdlr_t)); - p = arp->head; - } - (void) mutex_unlock(&arp->exitfns_lock); - (void) pthread_setcancelstate(cancel_state, NULL); + __cxa_finalize(NULL); } /* @@ -169,7 +205,7 @@ again: handler = sap->sa_handler; if (handler != SIG_DFL && handler != SIG_IGN && - in_range(handler, range, count)) { + in_range((void *)handler, range, count)) { rwlp = &udp->siguaction[sig].sig_lock; lrw_wrlock(rwlp); if (handler != sap->sa_handler) { @@ -213,11 +249,11 @@ start_again = 0; if (((func = atfp->prepare) != NULL && - in_range(func, range, count)) || + in_range((void *)func, range, count)) || ((func = atfp->parent) != NULL && - in_range(func, range, count)) || + in_range((void *)func, range, count)) || ((func = atfp->child) != NULL && - in_range(func, range, count))) { + in_range((void *)func, range, count))) { if (self->ul_fork) { /* * dlclose() called from a fork handler. @@ -268,7 +304,7 @@ for (key = 1; key < tsdm->tsdm_nused; key++) { if ((func = tsdm->tsdm_destro[key]) != NULL && func != TSD_UNALLOCATED && - in_range((_exithdlr_func_t)func, range, count)) + in_range((void *)func, range, count)) tsdm->tsdm_destro[key] = NULL; } lmutex_unlock(&tsdm->tsdm_lock); @@ -296,13 +332,24 @@ o = NULL; p = arp->head; while (p != NULL) { - if (in_range(p->hdlr, range, count)) { + /* + * We call even CXA handlers of functions present in the + * library being unloaded. The specification isn't + * particularly clear on this, and this seems the most sane. + * This is the behaviour of FreeBSD 9.1 (GNU libc leaves the + * handler on the exit list, and crashes at exit time). + * + * This won't cause handlers to be called twice, because + * anything called from a __cxa_finalize call from the + * language runtime will have been removed from the list. + */ + if (in_range((void *)p->hdlr, range, count)) { /* We need to execute this one */ if (o != NULL) o->next = p->next; else arp->head = p->next; - p->hdlr(); + p->hdlr(p->arg); lfree(p, sizeof (_exthdlr_t)); o = NULL; p = arp->head; @@ -322,13 +369,13 @@ } static int -in_range(_exithdlr_func_t addr, Lc_addr_range_t ranges[], uint_t count) +in_range(void *addr, Lc_addr_range_t ranges[], uint_t count) { uint_t idx; for (idx = 0; idx < count; idx++) { - if ((void *)addr >= ranges[idx].lb && - (void *)addr < ranges[idx].ub) { + if (addr >= ranges[idx].lb && + addr < ranges[idx].ub) { return (1); } } diff -r eecf57cad0d1 -r d1349977b975 usr/src/lib/libc/port/mapfile-vers --- a/usr/src/lib/libc/port/mapfile-vers Sun Jul 14 17:30:29 2013 +0000 +++ b/usr/src/lib/libc/port/mapfile-vers Wed Jun 26 16:36:05 2013 -0400 @@ -90,6 +90,12 @@ $add amd64 $endif +SYMBOL_VERSION ILLUMOS_0.5 { # common C++ ABI exit handlers + protected: + __cxa_atexit; + __cxa_finalize; +} ILLUMOS_0.4; + SYMBOL_VERSION ILLUMOS_0.4 { # Illumos additions protected: pipe2;