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
18 struct 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
27 static size_t g_len; // the number of nodes currently in use
28 static size_t g_capacity; // the number of available nodes
29 static struct node builtin[COUNT]; // 32 builtin nodes without malloc
30 static struct node *tail; // point to the last node, or NULL
31 static struct node *head; // point to the first node
32
33 static volatile int lock[1];
34 volatile int *const __atexit_lockptr = lock;
35
grow()36 static int grow()
37 {
38 struct node *nodes;
39
40 if (g_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 g_capacity += COUNT;
61 return 0;
62 }
63
append_node(void (* func)(void *),void * arg,void * dso,struct dso * internal_dso)64 static void append_node(void (*func)(void *), void *arg, void *dso, struct dso *internal_dso)
65 {
66 struct node *new_tail;
67 if (tail == NULL) {
68 new_tail = head;
69 } else {
70 new_tail = tail->next;
71 }
72
73 new_tail->func = func;
74 new_tail->arg = arg;
75 new_tail->dso = dso;
76 new_tail->internal_dso = internal_dso;
77
78 new_tail->prev = tail;
79 tail = new_tail;
80
81 g_len++;
82 }
83
RemoveNode(struct node * node)84 static struct node* RemoveNode(struct node *node)
85 {
86 struct node *prev = node->prev;
87 if (tail == node) {
88 // move back
89 tail = prev;
90 if (tail == NULL) {
91 head = node;
92 }
93 } else {
94 // remove node
95 struct node *next = node->next;
96 if (next) {
97 next->prev = prev;
98 }
99 if (prev) {
100 prev->next = next;
101 }
102
103 // insert node after tail
104 struct node *tail_next = tail->next;
105 node->prev = tail;
106 node->next = tail_next;
107 tail->next = node;
108 if (tail_next) {
109 tail_next->prev = node;
110 }
111 }
112
113 g_len--;
114 return prev;
115 }
116
__funcs_on_exit()117 void __funcs_on_exit()
118 {
119 void (*func)(void *), *arg;
120
121 LOCK(lock);
122 for (; tail; tail = tail->prev) {
123 func = tail->func;
124 if (func != NULL) {
125 arg = tail->arg;
126 UNLOCK(lock);
127 func(arg);
128 LOCK(lock);
129 }
130 }
131 UNLOCK(lock);
132 }
133
__cxa_finalize(void * dso)134 void __cxa_finalize(void *dso)
135 {
136 void (*func)(void *), *arg;
137 struct node *node;
138
139 LOCK(lock);
140 for (node = tail; node; ) {
141 if (dso == node->dso) {
142 func = node->func;
143 if (func != NULL) {
144 arg = node->arg;
145 UNLOCK(lock);
146 func(arg);
147 LOCK(lock);
148 }
149
150 node = RemoveNode(node);
151 continue;
152 }
153
154 node = node->prev;
155 }
156
157 UNLOCK(lock);
158 }
159
160 static void call(void *p);
161 __attribute__ ((__weak__)) extern void *addr2dso(size_t a);
162
__cxa_atexit(void (* func)(void *),void * arg,void * dso)163 int __cxa_atexit(void (*func)(void *), void *arg, void *dso)
164 {
165 struct dso *p = NULL;
166 LOCK(lock);
167
168 #if (defined(FEATURE_ATEXIT_CB_PROTECT))
169 if ((func == (void *)call) && (dso == NULL)) {
170 if (addr2dso != NULL) {
171 p = addr2dso((size_t)arg);
172 if (p == NULL) {
173 UNLOCK(lock);
174 MUSL_LOGE("call atexit with invalid callback ptr=%{public}p", arg);
175 return -1;
176 }
177 }
178 }
179 #endif
180
181 if (g_len >= g_capacity) {
182 if (grow()) {
183 UNLOCK(lock);
184 return -1;
185 }
186 }
187
188 append_node(func, arg, dso, p);
189
190 UNLOCK(lock);
191 return 0;
192 }
193
call(void * p)194 static void call(void *p)
195 {
196 if (p != NULL)
197 ((void (*)(void))(uintptr_t)p)();
198 }
199
atexit(void (* func)(void))200 int atexit(void (*func)(void))
201 {
202 return __cxa_atexit(call, (void *)(uintptr_t)func, 0);
203 }
204
invalidate_exit_funcs(struct dso * p)205 int invalidate_exit_funcs(struct dso *p)
206 {
207 struct node *node;
208
209 LOCK(lock);
210 for (node = tail; node; node = node->prev) {
211 // if found exit callback relative to this dso, and
212 if (p == node->internal_dso) {
213 if ((node->dso == NULL) && node->func == (void *)call) {
214 MUSL_LOGD("invalidate callback ptr=%{public}p when uninstall %{public}s", node->arg, p->name);
215 node->arg = NULL;
216 }
217 }
218 }
219 UNLOCK(lock);
220
221 return 0;
222 }