changeset 14080:d1349977b975

3849 implement __cxa_atexit/__cxa_finalize Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com> Reviewed by: Robert Mustacchi <rm@joyent.com> Approved by: Albert Lee <trisk@nexenta.com>
author Richard Lowe <richlowe@richlowe.net>
date Wed, 26 Jun 2013 16:36:05 -0400
parents eecf57cad0d1
children dfcd374b3af2
files usr/src/lib/libc/inc/thr_uberdata.h usr/src/lib/libc/port/gen/atexit.c usr/src/lib/libc/port/mapfile-vers
diffstat 3 files changed, 91 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- 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 *))];
--- 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);
 		}
 	}
--- 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;