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 g_len;                  // the number of nodes currently in use
28570af302Sopenharmony_cistatic size_t g_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 (g_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    g_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{
66570af302Sopenharmony_ci    struct node *new_tail;
67570af302Sopenharmony_ci    if (tail == NULL) {
68570af302Sopenharmony_ci        new_tail = head;
69570af302Sopenharmony_ci    } else {
70570af302Sopenharmony_ci        new_tail = tail->next;
71570af302Sopenharmony_ci    }
72570af302Sopenharmony_ci
73570af302Sopenharmony_ci    new_tail->func = func;
74570af302Sopenharmony_ci    new_tail->arg = arg;
75570af302Sopenharmony_ci    new_tail->dso = dso;
76570af302Sopenharmony_ci    new_tail->internal_dso = internal_dso;
77570af302Sopenharmony_ci
78570af302Sopenharmony_ci    new_tail->prev = tail;
79570af302Sopenharmony_ci    tail = new_tail;
80570af302Sopenharmony_ci
81570af302Sopenharmony_ci    g_len++;
82570af302Sopenharmony_ci}
83570af302Sopenharmony_ci
84570af302Sopenharmony_cistatic struct node* RemoveNode(struct node *node)
85570af302Sopenharmony_ci{
86570af302Sopenharmony_ci    struct node *prev = node->prev;
87570af302Sopenharmony_ci    if (tail == node) {
88570af302Sopenharmony_ci        // move back
89570af302Sopenharmony_ci        tail = prev;
90570af302Sopenharmony_ci        if (tail == NULL) {
91570af302Sopenharmony_ci            head = node;
92570af302Sopenharmony_ci        }
93570af302Sopenharmony_ci    } else {
94570af302Sopenharmony_ci        // remove node
95570af302Sopenharmony_ci        struct node *next = node->next;
96570af302Sopenharmony_ci        if (next) {
97570af302Sopenharmony_ci            next->prev = prev;
98570af302Sopenharmony_ci        }
99570af302Sopenharmony_ci        if (prev) {
100570af302Sopenharmony_ci            prev->next = next;
101570af302Sopenharmony_ci        }
102570af302Sopenharmony_ci
103570af302Sopenharmony_ci        // insert node after tail
104570af302Sopenharmony_ci        struct node *tail_next = tail->next;
105570af302Sopenharmony_ci        node->prev = tail;
106570af302Sopenharmony_ci        node->next = tail_next;
107570af302Sopenharmony_ci        tail->next = node;
108570af302Sopenharmony_ci        if (tail_next) {
109570af302Sopenharmony_ci            tail_next->prev = node;
110570af302Sopenharmony_ci        }
111570af302Sopenharmony_ci    }
112570af302Sopenharmony_ci
113570af302Sopenharmony_ci    g_len--;
114570af302Sopenharmony_ci    return prev;
115570af302Sopenharmony_ci}
116570af302Sopenharmony_ci
117570af302Sopenharmony_civoid __funcs_on_exit()
118570af302Sopenharmony_ci{
119570af302Sopenharmony_ci    void (*func)(void *), *arg;
120570af302Sopenharmony_ci
121570af302Sopenharmony_ci    LOCK(lock);
122570af302Sopenharmony_ci    for (; tail; tail = tail->prev) {
123570af302Sopenharmony_ci        func = tail->func;
124570af302Sopenharmony_ci        if (func != NULL) {
125570af302Sopenharmony_ci            arg = tail->arg;
126570af302Sopenharmony_ci            UNLOCK(lock);
127570af302Sopenharmony_ci            func(arg);
128570af302Sopenharmony_ci            LOCK(lock);
129570af302Sopenharmony_ci        }
130570af302Sopenharmony_ci    }
131570af302Sopenharmony_ci    UNLOCK(lock);
132570af302Sopenharmony_ci}
133570af302Sopenharmony_ci
134570af302Sopenharmony_civoid __cxa_finalize(void *dso)
135570af302Sopenharmony_ci{
136570af302Sopenharmony_ci    void (*func)(void *), *arg;
137570af302Sopenharmony_ci    struct node *node;
138570af302Sopenharmony_ci
139570af302Sopenharmony_ci    LOCK(lock);
140570af302Sopenharmony_ci    for (node = tail; node; ) {
141570af302Sopenharmony_ci        if (dso == node->dso) {
142570af302Sopenharmony_ci            func = node->func;
143570af302Sopenharmony_ci            if (func != NULL) {
144570af302Sopenharmony_ci                arg = node->arg;
145570af302Sopenharmony_ci                UNLOCK(lock);
146570af302Sopenharmony_ci                func(arg);
147570af302Sopenharmony_ci                LOCK(lock);
148570af302Sopenharmony_ci            }
149570af302Sopenharmony_ci
150570af302Sopenharmony_ci            node = RemoveNode(node);
151570af302Sopenharmony_ci            continue;
152570af302Sopenharmony_ci        }
153570af302Sopenharmony_ci
154570af302Sopenharmony_ci        node = node->prev;
155570af302Sopenharmony_ci    }
156570af302Sopenharmony_ci
157570af302Sopenharmony_ci    UNLOCK(lock);
158570af302Sopenharmony_ci}
159570af302Sopenharmony_ci
160570af302Sopenharmony_cistatic void call(void *p);
161570af302Sopenharmony_ci__attribute__ ((__weak__)) extern void *addr2dso(size_t a);
162570af302Sopenharmony_ci
163570af302Sopenharmony_ciint __cxa_atexit(void (*func)(void *), void *arg, void *dso)
164570af302Sopenharmony_ci{
165570af302Sopenharmony_ci    struct dso *p = NULL;
166570af302Sopenharmony_ci    LOCK(lock);
167570af302Sopenharmony_ci
168570af302Sopenharmony_ci#if (defined(FEATURE_ATEXIT_CB_PROTECT))
169570af302Sopenharmony_ci	if ((func == (void *)call) && (dso == NULL)) {
170570af302Sopenharmony_ci		if (addr2dso != NULL) {
171570af302Sopenharmony_ci			p = addr2dso((size_t)arg);
172570af302Sopenharmony_ci			if (p == NULL) {
173570af302Sopenharmony_ci				UNLOCK(lock);
174570af302Sopenharmony_ci				MUSL_LOGE("call atexit with invalid callback ptr=%{public}p", arg);
175570af302Sopenharmony_ci				return -1;
176570af302Sopenharmony_ci			}
177570af302Sopenharmony_ci		}
178570af302Sopenharmony_ci	}
179570af302Sopenharmony_ci#endif
180570af302Sopenharmony_ci
181570af302Sopenharmony_ci    if (g_len >= g_capacity) {
182570af302Sopenharmony_ci        if (grow()) {
183570af302Sopenharmony_ci            UNLOCK(lock);
184570af302Sopenharmony_ci            return -1;
185570af302Sopenharmony_ci        }
186570af302Sopenharmony_ci    }
187570af302Sopenharmony_ci
188570af302Sopenharmony_ci    append_node(func, arg, dso, p);
189570af302Sopenharmony_ci
190570af302Sopenharmony_ci    UNLOCK(lock);
191570af302Sopenharmony_ci    return 0;
192570af302Sopenharmony_ci}
193570af302Sopenharmony_ci
194570af302Sopenharmony_cistatic void call(void *p)
195570af302Sopenharmony_ci{
196570af302Sopenharmony_ci    if (p != NULL)
197570af302Sopenharmony_ci        ((void (*)(void))(uintptr_t)p)();
198570af302Sopenharmony_ci}
199570af302Sopenharmony_ci
200570af302Sopenharmony_ciint atexit(void (*func)(void))
201570af302Sopenharmony_ci{
202570af302Sopenharmony_ci    return __cxa_atexit(call, (void *)(uintptr_t)func, 0);
203570af302Sopenharmony_ci}
204570af302Sopenharmony_ci
205570af302Sopenharmony_ciint invalidate_exit_funcs(struct dso *p)
206570af302Sopenharmony_ci{
207570af302Sopenharmony_ci    struct node *node;
208570af302Sopenharmony_ci
209570af302Sopenharmony_ci    LOCK(lock);
210570af302Sopenharmony_ci    for (node = tail; node; node = node->prev) {
211570af302Sopenharmony_ci        // if found exit callback relative to this dso, and
212570af302Sopenharmony_ci        if (p == node->internal_dso) {
213570af302Sopenharmony_ci            if ((node->dso == NULL) && node->func == (void *)call) {
214570af302Sopenharmony_ci                MUSL_LOGD("invalidate callback ptr=%{public}p when uninstall %{public}s", node->arg, p->name);
215570af302Sopenharmony_ci                node->arg = NULL;
216570af302Sopenharmony_ci            }
217570af302Sopenharmony_ci        }
218570af302Sopenharmony_ci    }
219570af302Sopenharmony_ci    UNLOCK(lock);
220570af302Sopenharmony_ci
221570af302Sopenharmony_ci    return 0;
222570af302Sopenharmony_ci}