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 len; // the number of nodes currently in use
28 static size_t 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
grownull36 static 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
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 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
remove_node(struct node *node)83 static 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
__funcs_on_exitnull115 void __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
__cxa_finalize(void *dso)133 void __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
159 static void call(void *p);
160 __attribute__ ((__weak__)) extern void *addr2dso(size_t a);
161
__cxa_atexit(void (*func)(void *), void *arg, void *dso)162 int __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
call(void *p)193 static void call(void *p)
194 {
195 if (p != NULL)
196 ((void (*)(void))(uintptr_t)p)();
197 }
198
atexit(void (*func)(void))199 int atexit(void (*func)(void))
200 {
201 return __cxa_atexit(call, (void *)(uintptr_t)func, 0);
202 }
203
invalidate_exit_funcs(struct dso *p)204 int 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 }