63 lines
1.6 KiB
C
63 lines
1.6 KiB
C
|
#include <stddef.h>
|
||
|
|
||
|
/* We don't support shared object loading, so provide a single handle */
|
||
|
__attribute__((visibility("hidden")))
|
||
|
void *__dso_handle = (void *)&__dso_handle;
|
||
|
|
||
|
/* Number of atexit() calls supported, must be at least 32 (7.20.4.2§3).*/
|
||
|
#define ATEXIT_MAX 32
|
||
|
|
||
|
struct dtor {
|
||
|
void (*f)(void *);
|
||
|
void *p;
|
||
|
void *d;
|
||
|
};
|
||
|
|
||
|
static struct dtor _dtors[ATEXIT_MAX];
|
||
|
static int _dtor_count = 0;
|
||
|
|
||
|
int __cxa_atexit(void (*f)(void *), void *p, void *d)
|
||
|
{
|
||
|
if(_dtor_count >= ATEXIT_MAX)
|
||
|
return 1;
|
||
|
_dtors[_dtor_count++] = (struct dtor){ f, p, d };
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* We walk the destructor list in reverse order. Destructors may themselves
|
||
|
call __cxa_atexit(), causing new destructors to be added. When that
|
||
|
happens, we must call the new ones first before resuming (7.20.4.3§3). We
|
||
|
track changes in _dtor_count to detect this situation.
|
||
|
|
||
|
This function calls destructs in the interval [low..high) that match DSO
|
||
|
handle d, plus any other destructors registered as a consequence.
|
||
|
_dtor_count may increase. */
|
||
|
static void call_dtors_in_interval(void *d, int low, int high)
|
||
|
{
|
||
|
int end = _dtor_count;
|
||
|
|
||
|
for(int i = high - 1; i >= low; i--) {
|
||
|
if(d == NULL || _dtors[i].d == d)
|
||
|
_dtors[i].f(_dtors[i].p);
|
||
|
|
||
|
if(_dtor_count > end) {
|
||
|
call_dtors_in_interval(d, end, _dtor_count);
|
||
|
end = _dtor_count;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void __cxa_finalize(void *d)
|
||
|
{
|
||
|
call_dtors_in_interval(d, 0, _dtor_count);
|
||
|
|
||
|
/* Re-compact the array to keep only destructors we didn't call. */
|
||
|
int j = 0;
|
||
|
for(int i = 0; i < _dtor_count; i++) {
|
||
|
if(d == NULL || _dtors[i].d == d)
|
||
|
continue;
|
||
|
_dtors[j++] = _dtors[i];
|
||
|
}
|
||
|
_dtor_count = j;
|
||
|
}
|