diff options
Diffstat (limited to 'lang/ifc7/files/cxa_atexit.c')
-rw-r--r-- | lang/ifc7/files/cxa_atexit.c | 206 |
1 files changed, 184 insertions, 22 deletions
diff --git a/lang/ifc7/files/cxa_atexit.c b/lang/ifc7/files/cxa_atexit.c index d9dd1d0a1700..d6f05131bbaf 100644 --- a/lang/ifc7/files/cxa_atexit.c +++ b/lang/ifc7/files/cxa_atexit.c @@ -1,6 +1,11 @@ -/* - * Copyright (c) 2002 Marius Strobl - * All rights reserved. +/* Based on code marked: */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,11 +15,18 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -23,30 +35,180 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * from: FreeBSD: src/lib/libc/stdlib/atexit.c,v 1.7 2003/12/19 17:11:20 kan Exp + * */ +/* + * Copyright (c) 2004 Marius Strobl <marius@alchemy.franken.de>. + * All rights reserved. + * + * The same license as above applies. If clause 3 of the license in the + * above mentioned file gets removed this file should follow and this + * sentence should get removed. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stddef.h> #include <stdlib.h> +#include <unistd.h> +#include <osreldate.h> + +#if __FreeBSD_version >= 502101 + #error "Not required on this version of FreeBSD." +#endif + +#define ATEXIT_FN_EMPTY 0 +#define ATEXIT_FN_STD 1 +#define ATEXIT_FN_CXA 2 + +/* must be at least 32 to guarantee ANSI conformance */ +#define ATEXIT_SIZE 32 + +extern int __isthreaded; + +#if __FreeBSD_version < 500016 +typedef struct { + volatile long access_lock; + volatile long lock_owner; + volatile char *fname; + volatile int lineno; +} spinlock_t; + +#define _SPINLOCK_INITIALIZER { 0, 0, 0, 0 } + +#define _SPINUNLOCK(_lck) (_lck)->access_lock = 0 +#ifdef _LOCK_DEBUG +#define _SPINLOCK(_lck) _spinlock_debug(_lck, __FILE__, __LINE__) +#else +#define _SPINLOCK(_lck) _spinlock(_lck) +#endif + +static spinlock_t thread_lock = _SPINLOCK_INITIALIZER; + +#define _ATEXIT_LOCK() if (__isthreaded) _SPINLOCK(&thread_lock); +#define _ATEXIT_UNLOCK() if (__isthreaded) _SPINUNLOCK(&thread_lock); +#else +#include <pthread.h> + +static pthread_mutex_t atexit_mutex = PTHREAD_MUTEX_INITIALIZER; + +#define _ATEXIT_LOCK() if (__isthreaded) \ + _pthread_mutex_lock(&atexit_mutex) +#define _ATEXIT_UNLOCK() if (__isthreaded) \ + _pthread_mutex_unlock(&atexit_mutex) +#endif + +struct atexit { + struct atexit *next; /* next in list */ + int ind; /* next index in this table */ + struct atexit_fn { + int fn_type; /* ATEXIT_? from above */ + union { + void (*std_func)(void); + void (*cxa_func)(void *); + } fn_ptr; /* function pointer */ + void *fn_arg; /* argument for CXA callback */ + void *fn_dso; /* shared module handle */ + } fns[ATEXIT_SIZE]; /* the table itself */ +}; + +static struct atexit *__atexit; /* points to head of LIFO stack */ /* - * The __cxa_atexit() function and friends are needed for full (IA64) C++ ABI - * compatibility but FreeBSD doesn't have implemented them, yet. In addition - * to the classic atexit() it is not only used to register functions to be - * called at program exit but also to call them (C++ destructors in that case) - * when a shared object is unloaded. For the later to work the dynamic linker - * assigns a unique dynamic shared object handle to every shared object while - * a handle of NULL represents a main program. When __cxa_finalize() is called - * with a specific (non-NULL) handle as an argument all functions registered - * via __cxa_atexit() and having the same handle are called. - * The best we can do here to emulate that behaviour until FreeBSD supports - * this is to register the functions via atexit(). While this certainly is a - * bad hack it seems to work, even the current dynamic linker is assigning - * the handles. I didn't see a function getting registered with an argument - * so far. + * Register the function described by 'fptr' to be called at application + * exit or owning shared object unload time. This is a helper function + * for atexit and __cxa_atexit. + */ +static int +atexit_register(struct atexit_fn *fptr) +{ + static struct atexit __atexit0; /* one guaranteed table */ + struct atexit *p; + + _ATEXIT_LOCK(); + if ((p = __atexit) == NULL) + __atexit = p = &__atexit0; + else while (p->ind >= ATEXIT_SIZE) { + struct atexit *old__atexit; + old__atexit = __atexit; + _ATEXIT_UNLOCK(); + if ((p = (struct atexit *)malloc(sizeof(*p))) == NULL) + return (-1); + _ATEXIT_LOCK(); + if (old__atexit != __atexit) { + /* Lost race, retry operation */ + _ATEXIT_UNLOCK(); + free(p); + _ATEXIT_LOCK(); + p = __atexit; + continue; + } + p->ind = 0; + p->next = __atexit; + __atexit = p; + } + p->fns[p->ind++] = *fptr; + _ATEXIT_UNLOCK(); + return 0; +} + +/* + * Register a function to be performed at exit or when an shared object + * with given dso handle is unloaded dynamically. */ int -__cxa_atexit(void (*fn)(), void *arg, void *handle) +__cxa_atexit(void (*func)(void *), void *arg, void *dso) +{ + struct atexit_fn fn; + int error; + + fn.fn_type = ATEXIT_FN_CXA; + fn.fn_ptr.cxa_func = func;; + fn.fn_arg = arg; + fn.fn_dso = dso; + + error = atexit_register(&fn); + return (error); +} + +/* + * 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; + struct atexit_fn fn; + int n; - return (handle ? atexit(fn) : 0); + _ATEXIT_LOCK(); + for (p = __atexit; p; p = p->next) { + for (n = p->ind; --n >= 0;) { + if (p->fns[n].fn_type == ATEXIT_FN_EMPTY) + continue; /* already been called */ + if (dso != NULL && dso != p->fns[n].fn_dso) + continue; /* wrong DSO */ + fn = p->fns[n]; + /* + Mark entry to indicate that this particular handler + has already been called. + */ + p->fns[n].fn_type = ATEXIT_FN_EMPTY; + _ATEXIT_UNLOCK(); + + /* Call the function of correct type. */ + if (fn.fn_type == ATEXIT_FN_CXA) + fn.fn_ptr.cxa_func(fn.fn_arg); + else if (fn.fn_type == ATEXIT_FN_STD) + fn.fn_ptr.std_func(); + _ATEXIT_LOCK(); + } + } + _ATEXIT_UNLOCK(); } |