Bug 59552 - Patch to support the C++ DSO Object Destruction API
Summary: Patch to support the C++ DSO Object Destruction API
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: 5.1-CURRENT
Hardware: Any Any
: Normal Affects Only Me
Assignee: Alexander Kabaev
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2003-11-21 15:40 UTC by Bradley T Hughes
Modified: 2007-08-05 16:40 UTC (History)
0 users

See Also:


Attachments
file.diff (4.80 KB, patch)
2003-11-21 15:40 UTC, Bradley T Hughes
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Bradley T Hughes 2003-11-21 15:40:18 UTC
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)).

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:
------------------------------------------------------------------------
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;;
}
------------------------------------------------------------------------
Comment 1 Alexander Kabaev freebsd_committer freebsd_triage 2003-12-16 21:34:12 UTC
Responsible Changed
From-To: freebsd-bugs->kan

I will take care of this.
Comment 2 Alexander Kabaev freebsd_committer freebsd_triage 2003-12-19 18:56:57 UTC
State Changed
From-To: open->closed

Commited modifies version of the patch, thanks!
Comment 3 Mikhail Teterin 2007-08-05 16:35:58 UTC
	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