#include /* 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; }