1570af302Sopenharmony_ci#include <stdlib.h>
2570af302Sopenharmony_ci#include <stdint.h>
3570af302Sopenharmony_ci#include <stddef.h>
4570af302Sopenharmony_ci#include "libc.h"
5570af302Sopenharmony_ci#include "lock.h"
6570af302Sopenharmony_ci#include "fork_impl.h"
7570af302Sopenharmony_ci#include "dynlink.h"
8570af302Sopenharmony_ci#include "musl_log.h"
9570af302Sopenharmony_ci
10570af302Sopenharmony_ci#define malloc __libc_malloc
11570af302Sopenharmony_ci#define calloc __libc_calloc
12570af302Sopenharmony_ci#define realloc undef
13570af302Sopenharmony_ci#define free __libc_free
14570af302Sopenharmony_ci
15570af302Sopenharmony_ci/* Ensure that at least 32 atexit handlers can be registered without malloc */
16570af302Sopenharmony_ci#define COUNT 32
17570af302Sopenharmony_ci
18570af302Sopenharmony_cistruct node {
19570af302Sopenharmony_ci	void (*func)(void *);
20570af302Sopenharmony_ci	void *arg;
21570af302Sopenharmony_ci	void *dso;
22570af302Sopenharmony_ci	struct dso *internal_dso; // the internal dso weekptr, used for dlclose
23570af302Sopenharmony_ci	struct node *prev;
24570af302Sopenharmony_ci	struct node *next;
25570af302Sopenharmony_ci};
26570af302Sopenharmony_ci
27570af302Sopenharmony_cistatic size_t len;                  // the number of nodes currently in use
28570af302Sopenharmony_cistatic size_t capacity;             // the number of available nodes
29570af302Sopenharmony_cistatic struct node builtin[COUNT];  // 32 builtin nodes without malloc
30570af302Sopenharmony_cistatic struct node *tail;           // point to the last node, or NULL
31570af302Sopenharmony_cistatic struct node *head;           // point to the first node
32570af302Sopenharmony_ci
33570af302Sopenharmony_cistatic volatile int lock[1];
34570af302Sopenharmony_civolatile int *const __atexit_lockptr = lock;
35570af302Sopenharmony_ci
36570af302Sopenharmony_cistatic int grow()
37570af302Sopenharmony_ci{
38570af302Sopenharmony_ci	struct node *nodes;
39570af302Sopenharmony_ci
40570af302Sopenharmony_ci	if (capacity == 0) {
41570af302Sopenharmony_ci		nodes = builtin;
42570af302Sopenharmony_ci		head = nodes;
43570af302Sopenharmony_ci	} else {
44570af302Sopenharmony_ci		nodes = malloc(sizeof(struct node) * COUNT);
45570af302Sopenharmony_ci		if (nodes == NULL) {
46570af302Sopenharmony_ci			return -1;
47570af302Sopenharmony_ci		}
48570af302Sopenharmony_ci	}
49570af302Sopenharmony_ci
50570af302Sopenharmony_ci	for (size_t i = 0; i < COUNT - 1; i++) {
51570af302Sopenharmony_ci		nodes[i].next = nodes + (i + 1);
52570af302Sopenharmony_ci	}
53570af302Sopenharmony_ci	nodes[COUNT - 1].next = NULL;
54570af302Sopenharmony_ci
55570af302Sopenharmony_ci	// link new nodes after tail
56570af302Sopenharmony_ci	if (tail) {
57570af302Sopenharmony_ci		tail->next = nodes;
58570af302Sopenharmony_ci	}
59570af302Sopenharmony_ci
60570af302Sopenharmony_ci	capacity += COUNT;
61570af302Sopenharmony_ci	return 0;
62570af302Sopenharmony_ci}
63570af302Sopenharmony_ci
64570af302Sopenharmony_cistatic void append_node(void (*func)(void *), void *arg, void *dso, struct dso *internal_dso) {
65570af302Sopenharmony_ci	struct node *new_tail;
66570af302Sopenharmony_ci	if (tail == NULL) {
67570af302Sopenharmony_ci		new_tail = head;
68570af302Sopenharmony_ci	} else {
69570af302Sopenharmony_ci		new_tail = tail->next;
70570af302Sopenharmony_ci	}
71570af302Sopenharmony_ci
72570af302Sopenharmony_ci	new_tail->func = func;
73570af302Sopenharmony_ci	new_tail->arg = arg;
74570af302Sopenharmony_ci	new_tail->dso = dso;
75570af302Sopenharmony_ci	new_tail->internal_dso = internal_dso;
76570af302Sopenharmony_ci
77570af302Sopenharmony_ci	new_tail->prev = tail;
78570af302Sopenharmony_ci	tail = new_tail;
79570af302Sopenharmony_ci
80570af302Sopenharmony_ci	len++;
81570af302Sopenharmony_ci}
82570af302Sopenharmony_ci
83570af302Sopenharmony_cistatic struct node* remove_node(struct node *node) {
84570af302Sopenharmony_ci	struct node *prev = node->prev;
85570af302Sopenharmony_ci	if (tail == node) {
86570af302Sopenharmony_ci		// move back
87570af302Sopenharmony_ci		tail = prev;
88570af302Sopenharmony_ci		if (tail == NULL) {
89570af302Sopenharmony_ci			head = node;
90570af302Sopenharmony_ci		}
91570af302Sopenharmony_ci	} else {
92570af302Sopenharmony_ci		// remove node
93570af302Sopenharmony_ci		struct node *next = node->next;
94570af302Sopenharmony_ci		if (next) {
95570af302Sopenharmony_ci			next->prev = prev;
96570af302Sopenharmony_ci		}
97570af302Sopenharmony_ci		if (prev) {
98570af302Sopenharmony_ci			prev->next = next;
99570af302Sopenharmony_ci		}
100570af302Sopenharmony_ci
101570af302Sopenharmony_ci		// insert node after tail
102570af302Sopenharmony_ci		struct node *tail_next = tail->next;
103570af302Sopenharmony_ci		node->prev = tail;
104570af302Sopenharmony_ci		node->next = tail_next;
105570af302Sopenharmony_ci		tail->next = node;
106570af302Sopenharmony_ci		if (tail_next) {
107570af302Sopenharmony_ci			tail_next->prev = node;
108570af302Sopenharmony_ci		}
109570af302Sopenharmony_ci	}
110570af302Sopenharmony_ci
111570af302Sopenharmony_ci	len--;
112570af302Sopenharmony_ci	return prev;
113570af302Sopenharmony_ci}
114570af302Sopenharmony_ci
115570af302Sopenharmony_civoid __funcs_on_exit()
116570af302Sopenharmony_ci{
117570af302Sopenharmony_ci	void (*func)(void *), *arg;
118570af302Sopenharmony_ci
119570af302Sopenharmony_ci	LOCK(lock);
120570af302Sopenharmony_ci	for (; tail; tail = tail->prev) {
121570af302Sopenharmony_ci		func = tail->func;
122570af302Sopenharmony_ci		tail->func = NULL; // Avoid repeated invocation.
123570af302Sopenharmony_ci		if (func != NULL) {
124570af302Sopenharmony_ci			arg = tail->arg;
125570af302Sopenharmony_ci			UNLOCK(lock);
126570af302Sopenharmony_ci			func(arg);
127570af302Sopenharmony_ci			LOCK(lock);
128570af302Sopenharmony_ci		}
129570af302Sopenharmony_ci	}
130570af302Sopenharmony_ci	UNLOCK(lock);
131570af302Sopenharmony_ci}
132570af302Sopenharmony_ci
133570af302Sopenharmony_civoid __cxa_finalize(void *dso)
134570af302Sopenharmony_ci{
135570af302Sopenharmony_ci	void (*func)(void *), *arg;
136570af302Sopenharmony_ci	struct node *node;
137570af302Sopenharmony_ci
138570af302Sopenharmony_ci	LOCK(lock);
139570af302Sopenharmony_ci	for (node = tail; node;) {
140570af302Sopenharmony_ci		if (dso == node->dso) {
141570af302Sopenharmony_ci			func = node->func;
142570af302Sopenharmony_ci			if (func != NULL) {
143570af302Sopenharmony_ci				arg = node->arg;
144570af302Sopenharmony_ci				UNLOCK(lock);
145570af302Sopenharmony_ci				func(arg);
146570af302Sopenharmony_ci				LOCK(lock);
147570af302Sopenharmony_ci			}
148570af302Sopenharmony_ci
149570af302Sopenharmony_ci			node = remove_node(node);
150570af302Sopenharmony_ci			continue;
151570af302Sopenharmony_ci		}
152570af302Sopenharmony_ci
153570af302Sopenharmony_ci		node = node->prev;
154570af302Sopenharmony_ci	}
155570af302Sopenharmony_ci
156570af302Sopenharmony_ci	UNLOCK(lock);
157570af302Sopenharmony_ci}
158570af302Sopenharmony_ci
159570af302Sopenharmony_cistatic void call(void *p);
160570af302Sopenharmony_ci__attribute__ ((__weak__)) extern void *addr2dso(size_t a);
161570af302Sopenharmony_ci
162570af302Sopenharmony_ciint __cxa_atexit(void (*func)(void *), void *arg, void *dso)
163570af302Sopenharmony_ci{
164570af302Sopenharmony_ci	struct dso *p = NULL;
165570af302Sopenharmony_ci	LOCK(lock);
166570af302Sopenharmony_ci
167570af302Sopenharmony_ci#if (defined(FEATURE_ATEXIT_CB_PROTECT))
168570af302Sopenharmony_ci	if ((func == (void *)call) && (dso == NULL)) {
169570af302Sopenharmony_ci		if (addr2dso != NULL) {
170570af302Sopenharmony_ci			p = addr2dso((size_t)arg);
171570af302Sopenharmony_ci			if (p == NULL) {
172570af302Sopenharmony_ci				UNLOCK(lock);
173570af302Sopenharmony_ci				MUSL_LOGE("call atexit with invalid callback ptr=%{public}p", arg);
174570af302Sopenharmony_ci				return -1;
175570af302Sopenharmony_ci			}
176570af302Sopenharmony_ci		}
177570af302Sopenharmony_ci	}
178570af302Sopenharmony_ci#endif
179570af302Sopenharmony_ci
180570af302Sopenharmony_ci	if (len >= capacity) {
181570af302Sopenharmony_ci		if (grow()) {
182570af302Sopenharmony_ci			UNLOCK(lock);
183570af302Sopenharmony_ci			return -1;
184570af302Sopenharmony_ci		}
185570af302Sopenharmony_ci	}
186570af302Sopenharmony_ci
187570af302Sopenharmony_ci	append_node(func, arg, dso, p);
188570af302Sopenharmony_ci
189570af302Sopenharmony_ci	UNLOCK(lock);
190570af302Sopenharmony_ci	return 0;
191570af302Sopenharmony_ci}
192570af302Sopenharmony_ci
193570af302Sopenharmony_cistatic void call(void *p)
194570af302Sopenharmony_ci{
195570af302Sopenharmony_ci	if (p != NULL)
196570af302Sopenharmony_ci		((void (*)(void))(uintptr_t)p)();
197570af302Sopenharmony_ci}
198570af302Sopenharmony_ci
199570af302Sopenharmony_ciint atexit(void (*func)(void))
200570af302Sopenharmony_ci{
201570af302Sopenharmony_ci	return __cxa_atexit(call, (void *)(uintptr_t)func, 0);
202570af302Sopenharmony_ci}
203570af302Sopenharmony_ci
204570af302Sopenharmony_ciint invalidate_exit_funcs(struct dso *p)
205570af302Sopenharmony_ci{
206570af302Sopenharmony_ci	struct node *node;
207570af302Sopenharmony_ci
208570af302Sopenharmony_ci	LOCK(lock);
209570af302Sopenharmony_ci	for (node = tail; node; node = node->prev) {
210570af302Sopenharmony_ci		// if found exit callback relative to this dso, and
211570af302Sopenharmony_ci		if (p == node->internal_dso) {
212570af302Sopenharmony_ci			if ((node->dso == NULL) && node->func == (void *)call) {
213570af302Sopenharmony_ci				MUSL_LOGD("invalidate callback ptr=%{public}p when uninstall %{public}s", node->arg, p->name);
214570af302Sopenharmony_ci				node->arg = NULL;
215570af302Sopenharmony_ci			}
216570af302Sopenharmony_ci		}
217570af302Sopenharmony_ci	}
218570af302Sopenharmony_ci	UNLOCK(lock);
219570af302Sopenharmony_ci
220570af302Sopenharmony_ci	return 0;
221570af302Sopenharmony_ci}