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 }