From bhughes@trolltech.com  Fri Nov 21 07:30:53 2003
Return-Path: <bhughes@trolltech.com>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 891A516A4CE
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 21 Nov 2003 07:30:53 -0800 (PST)
Received: from dhcp243.troll.no (dhcp243.troll.no [80.232.37.243])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 92D3043FDD
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 21 Nov 2003 07:30:51 -0800 (PST)
	(envelope-from bhughes@trolltech.com)
Received: from dhcp243.troll.no (dhcp243.troll.no [80.232.37.243])
	by dhcp243.troll.no (8.12.10/8.12.10) with ESMTP id hALFKSKW016127
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 21 Nov 2003 16:20:28 +0100 (CET)
	(envelope-from bhughes@dhcp243.troll.no)
Received: (from bhughes@localhost)
	by dhcp243.troll.no (8.12.10/8.12.10/Submit) id hALFKR3u016126;
	Fri, 21 Nov 2003 16:20:27 +0100 (CET)
	(envelope-from bhughes)
Message-Id: <200311211520.hALFKR3u016126@dhcp243.troll.no>
Date: Fri, 21 Nov 2003 16:20:27 +0100 (CET)
From: Bradley T Hughes <bhughes@trolltech.com>
Reply-To: Bradley T Hughes <bhughes@trolltech.com>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: Patch to support the C++ DSO Object Destruction API
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         59552
>Category:       bin
>Synopsis:       Patch to support the C++ DSO Object Destruction API
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kan
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Fri Nov 21 07:40:18 PST 2003
>Closed-Date:    Fri Dec 19 10:58:37 PST 2003
>Last-Modified:  Sun Aug  5 15:40:02 GMT 2007
>Originator:     Bradley T Hughes
>Release:        FreeBSD 5.1-CURRENT i386
>Organization:
Trolltech AS
>Environment:
System: FreeBSD rage.troll.no 5.1-CURRENT FreeBSD 5.1-CURRENT #17: Fri Nov 21 14:01:00 CET 2003 root@:/usr/obj/usr/src/sys/RAGE i386


>Description:

Below is a test-case and patch to support the cross-vendor C++ DSO
Object Destruction API used by GCC 3.x and the Intel C/C++ compilers,
defined at http://www.codesourcery.com/cxx-abi/abi.html#dso-dtor

Here is a quote from the web-page, describing the motivation/rationale
behind this API:

	The C++ Standard requires that destructors be called for
	global objects when a program exits in the opposite order of
	construction.  Most implementations have handled this by
	calling the C library atexit(3) routine to register the
	destructors.  This is problematic because the 1999 C Standard
	only requires that the implementation support 32 register
	function, although most implementations support many more.
	More important, it does not deal at all with the ability in
	most implementations to remove DSOs (dynamic shared objects)
	from a running program image by calling dlclose(3) prior to
	program termination.

	The API specified below is intended to provide
	standard-conforming treatment during normal program exit,
	which includes executing atexit(3)-registered functions in the
	correct sequence relative to constructor-registered
	destructors, and reasonable treatment during early DSO unload
	(e.g. dlclose(3)).

>How-To-Repeat:

This test case reproduces the problem.  Note: remove -fuse-cxa-atexit
if testing without the included patch (otherwise the test will fail to
link due to undefined symbols).

Makefile:
------------------------------------------------------------------------
.if $(CXX) == "icpc"
CXXFLAGS = -g
# LFLAGS = -Qoption,ld,,-rpath,/usr/obj/usr/src/lib/libc \
#	 -L/usr/obj/usr/src/lib/libc -lc
.else
CXXFLAGS = -g -fuse-cxa-atexit
# LFLAGS = -rpath /usr/obj/usr/src/lib/libc -L/usr/obj/usr/src/lib/libc -lc
.endif

all: app lib.so

app: app.cpp
	$(CXX) $(CXXFLAGS) -g -o app app.cpp $(LFLAGS)

lib.so: lib.cpp
	$(CXX) $(CXXFLAGS) -g -shared -o lib.so lib.cpp

clean:
	rm -f app
	rm -f lib.so
	rm -f app.core

------------------------------------------------------------------------

app.cpp:
------------------------------------------------------------------------
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

struct APP {
    inline ~APP() { fprintf(stderr, "APP::~APP()\n"); }
};

typedef void (*fn_t)();

void f()
{
    fprintf(stderr, "app: f()\n");
    static APP app;
}

void x()
{
    printf("app: x()\n");
}

void y()
{
    printf("app: y()\n");
}

int main(int, char **)
{
    void *lib;
    fn_t gp;

    fprintf(stderr, "app: dlopen\n");
    lib = dlopen("./lib.so", RTLD_LAZY);
    if (!lib) return 1;
    fprintf(stderr, "app: dlfunc\n");
    gp = (fn_t) dlfunc(lib, "g");
    if (!gp) return 1;
    fprintf(stderr, "app: calling lib, expect g()\n");
    (*gp)();

    f();
    fprintf(stderr, "app: atexit(x)\n");
    atexit(x);
    fprintf(stderr, "app: atexit(y)\n");
    atexit(y);

    fprintf(stderr, "app: calling dlcose(), expect LIB::~LIB()\n");
    dlclose(lib);

    fprintf(stderr, "app: done, expect y(), x(), and APP::~APP()\n");
    return 0;
}
------------------------------------------------------------------------

lib.cpp:
------------------------------------------------------------------------
#include <stdio.h>

struct LIB {
    inline ~LIB() { fprintf(stderr, "LIB::~LIB()\n"); }
};

extern "C" void g()
{
    fprintf(stderr, "lib: g()\n");
    static LIB lib;;
}
------------------------------------------------------------------------

>Fix:

Currently, FreeBSD uses the crtstuff.c distributed with GCC (or the
crtx*.o objects distributed with ICC), so the only thing missing is
the implementation of __cxa_atexit() and __cxa_finalize().  This patch
against src/lib/libc implements these functions and adjusts atexit(3)
to use __cxa_atexit() as recommended at the web-page mentioned in the
description.

A few notes:

1. After applying this patch, the lang/icc port must be modified to
not include its own (incorrect) implementations of __cxa_atexit() and
__cxa_finalize().  In addition, devel/stlport-icc must be rebuilt with
the new icc port to remove a local definitions of these 2 functions
(since the icc port links with libcxa.a).  In fact, all C++
libraries/programs compiled with icc will need to be (at the very
least) relinked (for the same reason that devel/stlport-icc port must
be rebuilt).

2. The system compiler does not current use __cxa_atexit(), so
programs compiled with the system compiler will not benefit from this
fix until the compiler defaults to using __cxa_atexit() (or the user
explicitly uses -fuse-cxa-atexit).  Once this patch is included in the
base system, it should be possible to turn on use of __cxa_atexit() in
the system compiler.

Additionally, this change will preserve binary compatibility, since
_fini() uses a weak reference to __cxa_finalize().  In fact, programs
built with -fuse-cxa-atexit will continue to work on systems that do
not have __cxa_finalize(), since _fini() works on a weak definition.
The only difference is that global destructors will not be called *at
all* in such cases.

3. It would probably make sense to bump __FreeBSD_version after
applying this (since it is an ABI addition).  This would allow (for
example) the lang/icc port to detect whether or not its
__cxa_atexit()/__cxa_finalize() workarounds are necessary.

4. This is my first patch submission to FreeBSD, so I may not have
done somethings correctly.  This patch can be used as-is, or with
modifications to fit style(9) and other conventions.

src-lib-libc.diff:
------------------------------------------------------------------------
Index: stdlib/atexit.c
===================================================================
RCS file: /home/ncvs/src/lib/libc/stdlib/atexit.c,v
retrieving revision 1.6
diff -b -u -r1.6 atexit.c
--- stdlib/atexit.c	22 Mar 2002 21:53:09 -0000	1.6
+++ stdlib/atexit.c	21 Nov 2003 14:44:05 -0000
@@ -41,6 +41,7 @@
 __FBSDID("$FreeBSD: src/lib/libc/stdlib/atexit.c,v 1.6 2002/03/22 21:53:09 obrien Exp $");
 
 #include "namespace.h"
+#include <assert.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -55,22 +56,34 @@
 #define _MUTEX_LOCK(x)		if (__isthreaded) _pthread_mutex_lock(x)
 #define _MUTEX_UNLOCK(x)	if (__isthreaded) _pthread_mutex_unlock(x)
 
-struct atexit *__atexit;	/* points to head of LIFO stack */
+struct atexit {
+	struct atexit *next;		/* next in list */
+	int ind;			/* next index in this table */
+	struct {
+		void (*func)(void *);
+		void *arg;
+		void *dso;
+	} fns[ATEXIT_SIZE];		/* the table itself */
+};
+
+static
+struct atexit __atexit0;		/* one guaranteed table */
+static
+struct atexit *__atexit = &__atexit0;  /* points to head of LIFO stack */
 
 /*
- * Register a function to be performed at exit.
+ * Register the function 'func' to be called with argument 'arg' when
+ * the shared object owning 'dso' is unloaded.  Note: if 'dso' is
+ * NULL, the function 'func' will be called at application exit
  */
 int
-atexit(fn)
-	void (*fn)();
+__cxa_atexit(void (*func)(void *), void *arg, void *dso)
 {
-	static struct atexit __atexit0;	/* one guaranteed table */
 	struct atexit *p;
 
 	_MUTEX_LOCK(&atexit_mutex);
-	if ((p = __atexit) == NULL)
-		__atexit = p = &__atexit0;
-	else while (p->ind >= ATEXIT_SIZE) {
+	assert((p = __atexit) != NULL);
+	while (p->ind >= ATEXIT_SIZE) {
 		struct atexit *old__atexit;
 		old__atexit = __atexit;
 	        _MUTEX_UNLOCK(&atexit_mutex);
@@ -89,7 +102,64 @@
 		p->next = __atexit;
 		__atexit = p;
 	}
-	p->fns[p->ind++] = fn;
+	p->fns[p->ind].func = func;
+	p->fns[p->ind].arg  = arg;
+	p->fns[p->ind].dso  = dso;
+	++p->ind;
+	_MUTEX_UNLOCK(&atexit_mutex);
+
+	return 0;
+}
+
+/*
+ * Call all handlers registered with __cxa_atexit for the shared
+ * object owning \dso'.  Note: if 'dso' is NULL, then all remaining
+ * handlers are called.
+ */
+void
+__cxa_finalize(void *dso)
+{
+	struct atexit *p;
+	int n;
+
+	_MUTEX_LOCK(&atexit_mutex);
+	for (p = __atexit; p; p = p->next) {
+		for (n = p->ind; --n >= 0;) {
+			if (p->fns[n].func == NULL) 
+				continue; /* already been called */
+			if (dso != NULL && dso != p->fns[n].dso)
+				continue; /* wrong DSO */
+		        _MUTEX_UNLOCK(&atexit_mutex);
+			/* 
+			   pass argument to handler... Note: this
+			   means that we endup passing a NULL
+			   parameter to functions registered with
+			   atexit()
+			*/
+			(*p->fns[n].func)(p->fns[n].arg);
+			_MUTEX_LOCK(&atexit_mutex);
+			/*
+			  use NULL as a sentinel to indicate that this
+			  particular handler has already been called
+			*/
+			p->fns[n].func = NULL;
+			p->fns[n].arg  = NULL;
+			p->fns[n].dso  = NULL;
+		}
+	}
 	_MUTEX_UNLOCK(&atexit_mutex);
-	return (0);
+}
+
+/*
+ * Register a function to be performed at exit.
+ */
+int
+atexit(void (*func)(void))
+{
+	union {
+		void (*func)(void);
+		void (*cxa_func)(void *);
+	} fn;
+	fn.func = func;
+	return __cxa_atexit(fn.cxa_func, NULL, NULL);
 }
Index: stdlib/atexit.h
===================================================================
RCS file: /home/ncvs/src/lib/libc/stdlib/atexit.h,v
retrieving revision 1.2
diff -b -u -r1.2 atexit.h
--- stdlib/atexit.h	22 Mar 2002 23:42:03 -0000	1.2
+++ stdlib/atexit.h	21 Nov 2003 14:44:05 -0000
@@ -33,14 +33,8 @@
  *	@(#)atexit.h	8.2 (Berkeley) 7/3/94
  * $FreeBSD: src/lib/libc/stdlib/atexit.h,v 1.2 2002/03/22 23:42:03 obrien Exp $
  */
-
 /* must be at least 32 to guarantee ANSI conformance */
 #define	ATEXIT_SIZE	32
 
-struct atexit {
-	struct atexit *next;		/* next in list */
-	int ind;			/* next index in this table */
-	void (*fns[ATEXIT_SIZE])();	/* the table itself */
-};
-
-extern struct atexit *__atexit;	/* points to head of LIFO stack */
+int __cxa_atexit(void (*func)(void *), void *arg, void *dso);
+void __cxa_finalize(void *dso);
Index: stdlib/exit.c
===================================================================
RCS file: /home/ncvs/src/lib/libc/stdlib/exit.c,v
retrieving revision 1.6
diff -b -u -r1.6 exit.c
--- stdlib/exit.c	22 Mar 2002 21:53:10 -0000	1.6
+++ stdlib/exit.c	21 Nov 2003 14:44:05 -0000
@@ -61,17 +61,12 @@
 exit(status)
 	int status;
 {
-	struct atexit *p;
-	int n;
-
 	/* Ensure that the auto-initialization routine is linked in: */
 	extern int _thread_autoinit_dummy_decl;
 
 	_thread_autoinit_dummy_decl = 1;
 
-	for (p = __atexit; p; p = p->next)
-		for (n = p->ind; --n >= 0;)
-			(*p->fns[n])();
+	__cxa_finalize(NULL);
 	if (__cleanup)
 		(*__cleanup)();
 	_exit(status);
------------------------------------------------------------------------
>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->kan 
Responsible-Changed-By: kan 
Responsible-Changed-When: Tue Dec 16 13:34:12 PST 2003 
Responsible-Changed-Why:  
I will take care of this. 


http://www.freebsd.org/cgi/query-pr.cgi?pr=59552 
State-Changed-From-To: open->closed 
State-Changed-By: kan 
State-Changed-When: Fri Dec 19 10:56:57 PST 2003 
State-Changed-Why:  
Commited modifies version of the patch, thanks! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=59552 

From: Mikhail Teterin <mi+kde@aldan.algebra.com>
To: bug-followup@freebsd.org, bhughes@trolltech.com
Cc: standards@freebsd.org
Subject: Re: bin/59552: __cxa_atexit
Date: Sun, 5 Aug 2007 11:35:58 -0400

 	http://www.freebsd.org/cgi/query-pr.cgi?pr=59552
 
 Should not the new function(s) be declared in some header file?
 
 On FreeBSD-6.2 at the moment the only header, which declares __cxa_atexit(), 
 is cxxabi.h (in /usr/include/c++/3.4/cxxabi.h), where the function is 
 declared inside the __cxxabiv1 namespace...
 
 For a C-program finding the declaration is quite difficult too.
 
 A number of ports install shared libraries, which call atexit(). This leads to 
 crashes, when the shared library is dlclose-ed. To correct them cleanly, 
 port-maintainers need documentation (perhaps inside atexit(3)?). Currently 
 `make -k cxa' shows nothing.
 
 	-mi
>Unformatted:
