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
grow()36 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_exit()115 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 if (func != NULL) {
123 arg = tail->arg;
124 UNLOCK(lock);
125 func(arg);
126 LOCK(lock);
127 }
128 }
129 UNLOCK(lock);
130 }
131
__cxa_finalize(void * dso)132 void __cxa_finalize(void *dso)
133 {
134 void (*func)(void *), *arg;
135 struct node *node;
136
137 LOCK(lock);
138 for (node = tail; node;) {
139 if (dso == node->dso) {
140 func = node->func;
141 if (func != NULL) {
142 arg = node->arg;
143 UNLOCK(lock);
144 func(arg);
145 LOCK(lock);
146 }
147
148 node = remove_node(node);
149 continue;
150 }
151
152 node = node->prev;
153 }
154
155 UNLOCK(lock);
156 }
157
158 static void call(void *p);
159
__cxa_atexit(void (* func)(void *),void * arg,void * dso)160 int __cxa_atexit(void (*func)(void *), void *arg, void *dso)
161 {
162 struct dso *p = NULL;
163 LOCK(lock);
164
165 #if (defined(FEATURE_ATEXIT_CB_PROTECT))
166 if ((func == (void *)call) && (dso == NULL)) {
167 p = addr2dso((size_t)arg);
168 if (p == NULL) {
169 UNLOCK(lock);
170 MUSL_LOGE("call atexit with invalid callback ptr=%{public}p", arg);
171 return -1;
172 }
173 }
174 #endif
175
176 if (len >= capacity) {
177 if (grow()) {
178 UNLOCK(lock);
179 return -1;
180 }
181 }
182
183 append_node(func, arg, dso, p);
184
185 UNLOCK(lock);
186 return 0;
187 }
188
call(void * p)189 static void call(void *p)
190 {
191 if (p != NULL)
192 ((void (*)(void))(uintptr_t)p)();
193 }
194
atexit(void (* func)(void))195 int atexit(void (*func)(void))
196 {
197 return __cxa_atexit(call, (void *)(uintptr_t)func, 0);
198 }
199
invalidate_exit_funcs(struct dso * p)200 int invalidate_exit_funcs(struct dso *p)
201 {
202 struct node *node;
203
204 LOCK(lock);
205 for (node = tail; node; node = node->prev) {
206 // if found exit callback relative to this dso, and
207 if (p == node->internal_dso) {
208 if ((node->dso == NULL) && node->func == (void *)call) {
209 MUSL_LOGD("invalidate callback ptr=%{public}p when uninstall %{public}s", node->arg, p->name);
210 node->arg = NULL;
211 }
212 }
213 }
214 UNLOCK(lock);
215
216 return 0;
217 }