From 5c069a6c4b95a7360294695998bf1d3b12473abf Mon Sep 17 00:00:00 2001 From: Richard Lowe Date: Wed, 26 Jun 2013 16:36:05 -0400 Subject: [PATCH] 3849 implement __cxa_atexit/__cxa_finalize Reviewed by: Hans Rosenfeld Reviewed by: Robert Mustacchi Approved by: Albert Lee --- usr/src/lib/libc/inc/thr_uberdata.h | 11 ++++- usr/src/lib/libc/port/gen/atexit.c | 89 ++++++++++++++++++++++++++++--------- usr/src/lib/libc/port/mapfile-vers | 6 +++ 3 files changed, 84 insertions(+), 22 deletions(-) diff --git a/usr/src/lib/libc/inc/thr_uberdata.h b/usr/src/lib/libc/inc/thr_uberdata.h index af3083138d..42c08049b2 100644 --- a/usr/src/lib/libc/inc/thr_uberdata.h +++ b/usr/src/lib/libc/inc/thr_uberdata.h @@ -832,16 +832,25 @@ typedef struct { * 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 --git a/usr/src/lib/libc/port/gen/atexit.c b/usr/src/lib/libc/port/gen/atexit.c index fd016b5c37..32e54fae11 100644 --- a/usr/src/lib/libc/port/gen/atexit.c +++ b/usr/src/lib/libc/port/gen/atexit.c @@ -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 @@ atexit_unlocks() (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,36 +109,71 @@ atexit(void (*func)(void)) 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 -_exithandle(void) +__cxa_finalize(void *dso) { atexit_root_t *arp = &curthread->ul_uberdata->atexit_root; - _exthdlr_t *p; + _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); - arp->exit_frame_monitor = _getfp() + STACK_BIAS; + + o = NULL; p = arp->head; while (p != NULL) { - arp->head = p->next; - p->hdlr(); - lfree(p, sizeof (_exthdlr_t)); - p = arp->head; + 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; + + arp->exit_frame_monitor = _getfp() + STACK_BIAS; + __cxa_finalize(NULL); +} + /* * _get_exit_frame_monitor is called by the C++ runtimes. */ @@ -169,7 +205,7 @@ _preexec_sig_unload(Lc_addr_range_t range[], uint_t count) 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 @@ _preexec_atfork_unload(Lc_addr_range_t range[], uint_t count) 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 @@ _preexec_tsd_unload(Lc_addr_range_t range[], uint_t count) 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 @@ _preexec_exit_handlers(Lc_addr_range_t range[], uint_t count) 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 @@ _preexec_exit_handlers(Lc_addr_range_t range[], uint_t count) } 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 --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers index fccca8b44f..d6907e359d 100644 --- a/usr/src/lib/libc/port/mapfile-vers +++ b/usr/src/lib/libc/port/mapfile-vers @@ -90,6 +90,12 @@ $if _x86 && _ELF64 $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; -- 2.11.4.GIT