1 #include <stdlib.h>
2 #include <stdint.h>
3 #include "libc.h"
4 #include "lock.h"
5 #include "fork_impl.h"
6 #include "dynlink.h"
7 #include "musl_log.h"
8
9 #define malloc __libc_malloc
10 #define calloc __libc_calloc
11 #define realloc undef
12 #define free undef
13
14 /* Ensure that at least 32 atexit handlers can be registered without malloc */
15 #define COUNT 32
16
17 static struct fl
18 {
19 struct fl *next;
20 void (*f[COUNT])(void *);
21 void *a[COUNT];
22 void *dso[COUNT];
23 struct dso *internal_dso[COUNT]; // the internal dso weekptr, used for dlclose
24 } builtin, *head;
25
26 static int slot;
27 static volatile int lock[1];
28 volatile int *const __atexit_lockptr = lock;
29
__funcs_on_exit()30 void __funcs_on_exit()
31 {
32 void (*func)(void *), *arg;
33 LOCK(lock);
34 for (; head; head=head->next, slot=COUNT) while(slot-->0) {
35 if (head->f[slot] != NULL) {
36 func = head->f[slot];
37 arg = head->a[slot];
38 UNLOCK(lock);
39 func(arg);
40 LOCK(lock);
41 }
42 }
43 UNLOCK(lock);
44 }
45
__cxa_finalize(void * dso)46 void __cxa_finalize(void *dso)
47 {
48 void (*func)(void *), *arg;
49 struct fl *head_tmp = head;
50 int slot_tmp = slot;
51
52 LOCK(lock);
53 for (; head_tmp; head_tmp=head_tmp->next, slot_tmp=COUNT) while(slot_tmp-->0) {
54 if (dso == head_tmp->dso[slot_tmp]) {
55 func = head_tmp->f[slot_tmp];
56 arg = head_tmp->a[slot_tmp];
57 UNLOCK(lock);
58 func(arg);
59 LOCK(lock);
60
61 head_tmp->dso[slot_tmp] = NULL;
62 head_tmp->f[slot_tmp] = NULL;
63 }
64 }
65 UNLOCK(lock);
66 }
67
68 static void call(void *p);
69
__cxa_atexit(void (* func)(void *),void * arg,void * dso)70 int __cxa_atexit(void (*func)(void *), void *arg, void *dso)
71 {
72 struct dso *p = NULL;
73 LOCK(lock);
74
75 /* Defer initialization of head so it can be in BSS */
76 if (!head) head = &builtin;
77
78 // if called from atexit, check callback ptr mem range.
79 #if (defined(FEATURE_ATEXIT_CB_PROTECT))
80 if ((func == (void *)call) && (dso == NULL)) {
81 p = addr2dso((size_t)arg);
82 if (p == NULL) {
83 UNLOCK(lock);
84 MUSL_LOGE("call atexit with invalid callback ptr=%{public}p", arg);
85 return -1;
86 }
87 }
88 #endif
89
90 /* If the current function list is full, add a new one */
91 if (slot==COUNT) {
92 struct fl *new_fl = calloc(sizeof(struct fl), 1);
93 if (!new_fl) {
94 UNLOCK(lock);
95 return -1;
96 }
97 new_fl->next = head;
98 head = new_fl;
99 slot = 0;
100 }
101
102 /* Append function to the list. */
103 head->f[slot] = func;
104 head->a[slot] = arg;
105 head->dso[slot] = dso;
106 head->internal_dso[slot] = p;
107
108 slot++;
109
110 UNLOCK(lock);
111 return 0;
112 }
113
call(void * p)114 static void call(void *p)
115 {
116 if (p != NULL)
117 ((void (*)(void))(uintptr_t)p)();
118 }
119
atexit(void (* func)(void))120 int atexit(void (*func)(void))
121 {
122 return __cxa_atexit(call, (void *)(uintptr_t)func, 0);
123 }
124
invalidate_exit_funcs(struct dso * p)125 int invalidate_exit_funcs(struct dso *p)
126 {
127 struct fl *head_tmp = head;
128 int slot_tmp = slot;
129
130 LOCK(lock);
131 for (; head_tmp; head_tmp=head_tmp->next, slot_tmp=COUNT) {
132 while(slot_tmp-->0) {
133 // if found exit callback relative to this dso, and
134 if (p == head_tmp->internal_dso[slot_tmp]) {
135 if ((head_tmp->dso[slot_tmp] == NULL) && head_tmp->f[slot_tmp] == (void *)call) {
136 MUSL_LOGD("invalidate callback ptr=%{public}p when uninstall %{public}%s", head_tmp->a[slot_tmp], p->name);
137 head_tmp->a[slot_tmp] = NULL;
138 }
139 }
140 }
141 }
142 UNLOCK(lock);
143
144 return 0;
145 }