1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "pthread_impl.h"
17
18 __attribute__((__weak__)) extern void add_dso_handle_node(void *dso_handle) ;
19 __attribute__((__weak__)) extern void remove_dso_handle_node(void *dso_handle);
20
21 /*
22 * There are two ways to implement cxa_thread_atexit_impl:
23 * - CXA_THREAD_USE_TSD(default): use pthread_key_xxx to implement cxa_thread_atexit_impl.
24 * - CXA_THREAD_USE_TLS: put dtors in pthread to implement cxa_thread_atexit_impl.
25 */
26 #ifdef CXA_THREAD_USE_TSD
27 struct dtor_list {
28 void (*dtor) (void*);
29 void *arg;
30 void *dso_handle;
31 struct dtor_list* next;
32 };
33
34 #ifndef ENABLE_HWASAN
35 // A list for current thread local dtors.
36 __thread struct dtor_list* thread_local_dtors = NULL;
37 // Whether the current thread local dtors have not been executed or registered.
38 __thread bool thread_local_dtors_alive = false;
39 #endif
40
41 static pthread_key_t dtors_key;
42
43 #ifndef ENABLE_HWASAN
run_cur_thread_dtors(void * unused)44 void run_cur_thread_dtors(void *unused)
45 #else
46 void run_cur_thread_dtors(void *thread_local_dtors)
47 #endif
48 {
49 while (thread_local_dtors != NULL) {
50 struct dtor_list* cur = thread_local_dtors;
51 thread_local_dtors = cur->next;
52 cur->dtor(cur->arg);
53 if (remove_dso_handle_node) {
54 remove_dso_handle_node(cur->dso_handle);
55 }
56 __libc_free(cur);
57 }
58 #ifndef ENABLE_HWASAN
59 thread_local_dtors_alive = false;
60 #endif
61 return;
62 }
63
cxa_thread_init()64 __attribute__((constructor())) void cxa_thread_init()
65 {
66 if (pthread_key_create(&dtors_key, run_cur_thread_dtors) != 0) {
67 abort();
68 }
69 return;
70 }
71
72 /*
73 * Used for the thread calls exit(include main thread).
74 * We can't register a destructor of libc for run_cur_thread_dtors because of deadlock problem:
75 * exit -> __libc_exit_fini[acquire init_fini_lock] -> run_cur_thread_dtors ->
76 * remove_dso_handle_node-> do_dlclose ->dlclose_impl[try to get init_fini_lock] -> deadlock.
77 * So we call __cxa_thread_finalize actively at exit.
78 */
__cxa_thread_finalize()79 void __cxa_thread_finalize()
80 {
81 #ifndef ENABLE_HWASAN
82 run_cur_thread_dtors(NULL);
83 #else
84 run_cur_thread_dtors(pthread_getspecific(dtors_key));
85 #endif
86
87 return;
88 }
89
__cxa_thread_atexit_impl(void (* func)(void *),void * arg,void * dso_handle)90 int __cxa_thread_atexit_impl(void (*func)(void*), void *arg, void *dso_handle)
91 {
92 #ifndef ENABLE_HWASAN
93 if (!thread_local_dtors_alive) {
94 // Bind dtors_key to current thread, so that `run_cur_thread_dtors` can be executed when thread exits.
95 if (pthread_setspecific(dtors_key, &dtors_key) != 0) {
96 return -1;
97 }
98 thread_local_dtors_alive = true;
99 }
100 #else
101 struct dtor_list* prev_dtors = pthread_getspecific(dtors_key);
102 #endif
103 struct dtor_list* dtor = __libc_malloc(sizeof(*dtor));
104 if (!dtor) {
105 return -1;
106 }
107 dtor->dtor = func;
108 dtor->arg = arg;
109 dtor->dso_handle = dso_handle;
110 #ifndef ENABLE_HWASAN
111 dtor->next = thread_local_dtors;
112 thread_local_dtors = dtor;
113 #else
114 dtor->next = prev_dtors;
115 pthread_setspecific(dtors_key, dtor);
116 #endif
117 if (add_dso_handle_node != NULL) {
118 add_dso_handle_node(dso_handle);
119 }
120
121 return 0;
122 }
123 #endif
124
125 #ifdef CXA_THREAD_USE_TLS
126
__cxa_thread_atexit_impl(void (* func)(void *),void * arg,void * dso_handle)127 int __cxa_thread_atexit_impl(void (*func)(void*), void *arg, void *dso_handle)
128 {
129 struct thread_local_dtor* dtor = __libc_malloc(sizeof(*dtor));
130 if (!dtor) {
131 return -1;
132 }
133 dtor->func = func;
134 dtor->arg = arg;
135 dtor->dso_handle = dso_handle;
136 pthread_t thr = __pthread_self();
137 dtor->next = thr->thread_local_dtors;
138 thr->thread_local_dtors = dtor;
139 if (add_dso_handle_node != NULL) {
140 add_dso_handle_node(dso_handle);
141 }
142 return 0;
143 }
144
__cxa_thread_finalize()145 void __cxa_thread_finalize()
146 {
147 pthread_t thr = __pthread_self();
148 while (thr->thread_local_dtors != NULL) {
149 struct thread_local_dtor* cur = thr->thread_local_dtors;
150 thr->thread_local_dtors= cur->next;
151 cur->func(cur->arg);
152 if (remove_dso_handle_node) {
153 remove_dso_handle_node(cur->dso_handle);
154 }
155 }
156 return;
157 }
158
159 #endif
160
161