1 #define _ALL_SOURCE
2 #include "libc.h"
3 #include <stdint.h>
4 #include <stdlib.h>
5 #include <threads.h>
6 
7 /* Ensure that at least 32 atexit handlers can be registered without malloc */
8 #define COUNT 32
9 
10 static struct fl {
11     struct fl* next;
12     void (*f[COUNT])(void*);
13     void* a[COUNT];
14 } builtin, *head;
15 
16 static int slot;
17 static mtx_t lock = MTX_INIT;
18 
19 // Phantom unlock to satisfy analysis when actually we leave it locked forever.
20 __TA_RELEASE(&lock) __TA_NO_THREAD_SAFETY_ANALYSIS
synchronize_exit(void)21 static void synchronize_exit(void) {}
22 
__funcs_on_exit(void)23 void __funcs_on_exit(void) {
24     void (*func)(void*), *arg;
25     mtx_lock(&lock);
26     for (; head; head = head->next, slot = COUNT) {
27         while (slot-- > 0) {
28             func = head->f[slot];
29             arg = head->a[slot];
30             mtx_unlock(&lock);
31             func(arg);
32             mtx_lock(&lock);
33         }
34     }
35 
36     // Leaving this lock held effectively synchronizes the rest of exit after
37     // we return to it.  It's technically undefined behavior for the program
38     // to enter exit twice no matter what, so worrying about it at all is just
39     // trying to give the most useful possible result for a buggy program.  Up
40     // to this point, we gracefully handle multiple threads calling exit by
41     // giving them a random interleaving of which thread runs the next atexit
42     // hook.  The rest of the teardown that exit does after this is presumed
43     // to happen once in a single thread.  So the most graceful way to
44     // maintain orderly shutdown in a buggy program is to err on the side of
45     // deadlock (if DSO destructors or stdio teardown try to synchronize with
46     // another thread that's illegally trying to enter exit again).
47     synchronize_exit();
48 }
49 
__cxa_finalize(void * dso)50 void __cxa_finalize(void* dso) {}
51 
__cxa_atexit(void (* func)(void *),void * arg,void * dso)52 int __cxa_atexit(void (*func)(void*), void* arg, void* dso) {
53     mtx_lock(&lock);
54 
55     /* Defer initialization of head so it can be in BSS */
56     if (!head)
57         head = &builtin;
58 
59     /* If the current function list is full, add a new one */
60     if (slot == COUNT) {
61         struct fl* new_fl = calloc(sizeof(struct fl), 1);
62         if (!new_fl) {
63             mtx_unlock(&lock);
64             return -1;
65         }
66         new_fl->next = head;
67         head = new_fl;
68         slot = 0;
69     }
70 
71     /* Append function to the list. */
72     head->f[slot] = func;
73     head->a[slot] = arg;
74     slot++;
75 
76     mtx_unlock(&lock);
77     return 0;
78 }
79 
call(void * p)80 static void call(void* p) {
81     ((void (*)(void))(uintptr_t)p)();
82 }
83 
84 // In an implementation where dlclose actually unloads a module and runs
85 // its destructors, the third argument to __cxa_atexit must differ between
86 // modules (that is, between the main executable and between each DSO) so
87 // that dlclose can run the subset of destructors registered by that one
88 // DSO's code.  For C++ static destructors, the compiler generates the call:
89 //     __cxa_atexit(&destructor, &instance, &__dso_handle);
90 // __dso_handle is defined with __attribute__((visibility("hidden"))) in
91 // a special object crtbegin.o that is included implicitly in every link.
92 // For the C atexit API to do the equivalent, atexit must be defined in
93 // a small static library that is linked into things that dynamically link
94 // in -lc; that's the only way for &__dso_handle to refer to the different
95 // instance of that symbol in each module.
96 //
97 // Our dlclose doesn't actually do anything, so we never need to run a
98 // subset of destructors before we run them all at actual process exit.
99 // Hence, the third argument to __cxa_atexit is ignored and it doesn't
100 // matter what we pass it; thus, we can include atexit in the -lc DSO
101 // as we do here.
atexit(void (* func)(void))102 int atexit(void (*func)(void)) {
103     return __cxa_atexit(call, (void*)(uintptr_t)func, NULL);
104 }
105