From 4358a16b9209b848ae3f22f78b0be0efd3b99579 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Wed, 7 Sep 2022 21:03:53 +0200 Subject: [PATCH] dso, stdlib: __cxa_atexit(), __dso_handle, atexit() (TEST) --- CMakeLists.txt | 3 +++ STATUS | 4 ++- include/stdlib.h | 1 + src/dso.c | 62 +++++++++++++++++++++++++++++++++++++++++++++ src/stdlib/atexit.c | 8 ++++++ src/stdlib/exit.c | 5 +++- 6 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/dso.c create mode 100644 src/stdlib/atexit.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 81a9665..1405997 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,8 @@ set(SOURCES 3rdparty/grisu2b_59_56/grisu2b_59_56.c 3rdparty/tinymt32/rand.c 3rdparty/tinymt32/tinymt32.c + # C++ API details + src/dso.c # assert src/assert/assert.c # ctype @@ -157,6 +159,7 @@ set(SOURCES # stdlib src/stdlib/abort.c src/stdlib/abs.c + src/stdlib/atexit.c src/stdlib/atof.c src/stdlib/atoi.c src/stdlib/atol.c diff --git a/STATUS b/STATUS index 15d2be2..754cb1f 100644 --- a/STATUS +++ b/STATUS @@ -162,7 +162,7 @@ TEST: Function/symbol/macro needs to be tested 7.20.3.3 malloc - (gint) 7.20.3.4 realloc - (gint) 7.20.4.1 abort - (stream flushing/closing/etc?) - 7.20.4.2 atexit TODO + 7.20.4.2 atexit TEST 7.20.4.3 exit - (stream flushing/closing/etc?) 7.20.4.4 _Exit - (gint) 7.20.4.5 getenv TODO @@ -173,6 +173,8 @@ TEST: Function/symbol/macro needs to be tested 7.20.6.2 div, ldiv, lldiv - 7.20.7 Multibyte/wide char conv TODO 7.20.8 Multibyte/wide string conv TODO + (EXT) __cxa_atexit TEST + (EXT) __cxa_finalize TEST 7.21 7.21.2.1 memcpy - diff --git a/include/stdlib.h b/include/stdlib.h index 8267c6a..5e796ad 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -38,6 +38,7 @@ extern void free(void *__ptr); __attribute__((noreturn)) extern void abort(void); +/* Register a function to be called at program exit. */ extern int atexit(void (*__func)(void)); /* Exit; calls handlers, flushes and closes streams and temporary files. */ diff --git a/src/dso.c b/src/dso.c new file mode 100644 index 0000000..0dc7215 --- /dev/null +++ b/src/dso.c @@ -0,0 +1,62 @@ +#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; +} diff --git a/src/stdlib/atexit.c b/src/stdlib/atexit.c new file mode 100644 index 0000000..fb3cc88 --- /dev/null +++ b/src/stdlib/atexit.c @@ -0,0 +1,8 @@ +#include + +extern int __cxa_atexit(void (*f)(void *), void *p, void *d); + +int atexit(void (*f)(void)) +{ + return __cxa_atexit((void (*)(void *))f, NULL, NULL); +} diff --git a/src/stdlib/exit.c b/src/stdlib/exit.c index 596a48a..27025f4 100644 --- a/src/stdlib/exit.c +++ b/src/stdlib/exit.c @@ -1,8 +1,11 @@ #include +extern void __cxa_finalize(void *d); + void exit(int rc) { - /* TODO: invoke atexit callbacks */ + __cxa_finalize(NULL); + /* TODO: exit: Flush all streams */ /* TODO: exit: Close all streams */ /* TODO: exit: Remove temporary files */