Print this page
3849 implement __cxa_atexit/__cxa_finalize

@@ -50,11 +50,11 @@
  * Guide for information on alternate link maps.
  *
  * 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);
 
 /*
  * exitfns_lock is declared to be a recursive mutex so that we

@@ -86,16 +86,17 @@
 atexit_unlocks()
 {
         (void) mutex_unlock(&__uberdata.atexit_root.exitfns_lock);
 }
 
+
 /*
  * atexit() is called 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;
         _exthdlr_t *p;
 

@@ -106,40 +107,75 @@
                 arp = &__uberdata.atexit_root;
         else {
                 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) {
+                if ((dso == NULL) || (p->dso == dso)) {
+                        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;
+                } 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.
  */
 void *
 _get_exit_frame_monitor(void)

@@ -167,11 +203,11 @@
         for (sig = 1; sig < NSIG; sig++) {
                 sap = (struct sigaction *)&udp->siguaction[sig].sig_uaction;
 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) {
                                 lrw_unlock(rwlp);
                                 goto again;

@@ -211,15 +247,15 @@
                 do {
                         next = atfp->forw;
                         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.
                                          * Deleting the entry would wreak havoc.
                                          * Just null out the function pointers

@@ -266,11 +302,11 @@
 
         lmutex_lock(&tsdm->tsdm_lock);
         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);
 }
 

@@ -294,17 +330,28 @@
         (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
         (void) mutex_lock(&arp->exitfns_lock);
         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;
                 } else {
                         o = p;

@@ -320,17 +367,17 @@
 
         return (0);
 }
 
 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);
                 }
         }
 
         return (0);