1#include <stdlib.h> 2#include <stdint.h> 3#include <stddef.h> 4#include "libc.h" 5#include "lock.h" 6#include "fork_impl.h" 7#include "dynlink.h" 8#include "musl_log.h" 9 10#define malloc __libc_malloc 11#define calloc __libc_calloc 12#define realloc undef 13#define free __libc_free 14 15/* Ensure that at least 32 atexit handlers can be registered without malloc */ 16#define COUNT 32 17 18struct node { 19 void (*func)(void *); 20 void *arg; 21 void *dso; 22 struct dso *internal_dso; // the internal dso weekptr, used for dlclose 23 struct node *prev; 24 struct node *next; 25}; 26 27static size_t len; // the number of nodes currently in use 28static size_t capacity; // the number of available nodes 29static struct node builtin[COUNT]; // 32 builtin nodes without malloc 30static struct node *tail; // point to the last node, or NULL 31static struct node *head; // point to the first node 32 33static volatile int lock[1]; 34volatile int *const __atexit_lockptr = lock; 35 36static int grow() 37{ 38 struct node *nodes; 39 40 if (capacity == 0) { 41 nodes = builtin; 42 head = nodes; 43 } else { 44 nodes = malloc(sizeof(struct node) * COUNT); 45 if (nodes == NULL) { 46 return -1; 47 } 48 } 49 50 for (size_t i = 0; i < COUNT - 1; i++) { 51 nodes[i].next = nodes + (i + 1); 52 } 53 nodes[COUNT - 1].next = NULL; 54 55 // link new nodes after tail 56 if (tail) { 57 tail->next = nodes; 58 } 59 60 capacity += COUNT; 61 return 0; 62} 63 64static void append_node(void (*func)(void *), void *arg, void *dso, struct dso *internal_dso) { 65 struct node *new_tail; 66 if (tail == NULL) { 67 new_tail = head; 68 } else { 69 new_tail = tail->next; 70 } 71 72 new_tail->func = func; 73 new_tail->arg = arg; 74 new_tail->dso = dso; 75 new_tail->internal_dso = internal_dso; 76 77 new_tail->prev = tail; 78 tail = new_tail; 79 80 len++; 81} 82 83static struct node* remove_node(struct node *node) { 84 struct node *prev = node->prev; 85 if (tail == node) { 86 // move back 87 tail = prev; 88 if (tail == NULL) { 89 head = node; 90 } 91 } else { 92 // remove node 93 struct node *next = node->next; 94 if (next) { 95 next->prev = prev; 96 } 97 if (prev) { 98 prev->next = next; 99 } 100 101 // insert node after tail 102 struct node *tail_next = tail->next; 103 node->prev = tail; 104 node->next = tail_next; 105 tail->next = node; 106 if (tail_next) { 107 tail_next->prev = node; 108 } 109 } 110 111 len--; 112 return prev; 113} 114 115void __funcs_on_exit() 116{ 117 void (*func)(void *), *arg; 118 119 LOCK(lock); 120 for (; tail; tail = tail->prev) { 121 func = tail->func; 122 tail->func = NULL; // Avoid repeated invocation. 123 if (func != NULL) { 124 arg = tail->arg; 125 UNLOCK(lock); 126 func(arg); 127 LOCK(lock); 128 } 129 } 130 UNLOCK(lock); 131} 132 133void __cxa_finalize(void *dso) 134{ 135 void (*func)(void *), *arg; 136 struct node *node; 137 138 LOCK(lock); 139 for (node = tail; node;) { 140 if (dso == node->dso) { 141 func = node->func; 142 if (func != NULL) { 143 arg = node->arg; 144 UNLOCK(lock); 145 func(arg); 146 LOCK(lock); 147 } 148 149 node = remove_node(node); 150 continue; 151 } 152 153 node = node->prev; 154 } 155 156 UNLOCK(lock); 157} 158 159static void call(void *p); 160__attribute__ ((__weak__)) extern void *addr2dso(size_t a); 161 162int __cxa_atexit(void (*func)(void *), void *arg, void *dso) 163{ 164 struct dso *p = NULL; 165 LOCK(lock); 166 167#if (defined(FEATURE_ATEXIT_CB_PROTECT)) 168 if ((func == (void *)call) && (dso == NULL)) { 169 if (addr2dso != NULL) { 170 p = addr2dso((size_t)arg); 171 if (p == NULL) { 172 UNLOCK(lock); 173 MUSL_LOGE("call atexit with invalid callback ptr=%{public}p", arg); 174 return -1; 175 } 176 } 177 } 178#endif 179 180 if (len >= capacity) { 181 if (grow()) { 182 UNLOCK(lock); 183 return -1; 184 } 185 } 186 187 append_node(func, arg, dso, p); 188 189 UNLOCK(lock); 190 return 0; 191} 192 193static void call(void *p) 194{ 195 if (p != NULL) 196 ((void (*)(void))(uintptr_t)p)(); 197} 198 199int atexit(void (*func)(void)) 200{ 201 return __cxa_atexit(call, (void *)(uintptr_t)func, 0); 202} 203 204int invalidate_exit_funcs(struct dso *p) 205{ 206 struct node *node; 207 208 LOCK(lock); 209 for (node = tail; node; node = node->prev) { 210 // if found exit callback relative to this dso, and 211 if (p == node->internal_dso) { 212 if ((node->dso == NULL) && node->func == (void *)call) { 213 MUSL_LOGD("invalidate callback ptr=%{public}p when uninstall %{public}s", node->arg, p->name); 214 node->arg = NULL; 215 } 216 } 217 } 218 UNLOCK(lock); 219 220 return 0; 221}