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 // A list for current thread local dtors.
35 __thread struct dtor_list* thread_local_dtors = NULL;
36 // Whether the current thread local dtors have not been executed or registered.
37 __thread bool thread_local_dtors_alive = false;
38 static pthread_key_t dtors_key;
39 
run_cur_thread_dtors(void *)40 void run_cur_thread_dtors(void *)
41 {
42     while (thread_local_dtors != NULL) {
43         struct dtor_list* cur = thread_local_dtors;
44         thread_local_dtors = cur->next;
45         cur->dtor(cur->arg);
46         if (remove_dso_handle_node) {
47             remove_dso_handle_node(cur->dso_handle);
48         }
49         __libc_free(cur);
50     }
51     thread_local_dtors_alive = false;
52     return;
53 }
54 
cxa_thread_initnull55 __attribute__((constructor())) void cxa_thread_init()
56 {
57     if (pthread_key_create(&dtors_key, run_cur_thread_dtors) != 0) {
58         abort();
59     }
60     return;
61 }
62 
63 /*
64  * Used for the thread calls exit(include main thread).
65  * We can't register a destructor of libc for run_cur_thread_dtors because of deadlock problem:
66  *   exit -> __libc_exit_fini[acquire init_fini_lock] -> run_cur_thread_dtors ->
67  *   remove_dso_handle_node-> do_dlclose ->dlclose_impl[try to get init_fini_lock] -> deadlock.
68  * So we call __cxa_thread_finalize actively at exit.
69  */
__cxa_thread_finalizenull70 void __cxa_thread_finalize()
71 {
72     run_cur_thread_dtors(NULL);
73     return;
74 }
75 
__cxa_thread_atexit_impl(void (*func)(void*), void *arg, void *dso_handle)76 int __cxa_thread_atexit_impl(void (*func)(void*), void *arg, void *dso_handle)
77 {
78     if (!thread_local_dtors_alive) {
79         // Bind dtors_key to current thread, so that `run_cur_thread_dtors` can be executed when thread exits.
80         if (pthread_setspecific(dtors_key, &dtors_key) != 0) {
81             return -1;
82         }
83         thread_local_dtors_alive = true;
84     }
85     struct dtor_list* dtor = __libc_malloc(sizeof(*dtor));
86     if (!dtor) {
87         return -1;
88     }
89     dtor->dtor = func;
90     dtor->arg = arg;
91     dtor->dso_handle = dso_handle;
92     dtor->next = thread_local_dtors;
93     thread_local_dtors = dtor;
94     if (add_dso_handle_node != NULL) {
95         add_dso_handle_node(dso_handle);
96     }
97 
98     return 0;
99 }
100 #endif
101 
102 #ifdef CXA_THREAD_USE_TLS
103 
__cxa_thread_atexit_impl(void (*func)(void*), void *arg, void *dso_handle)104 int __cxa_thread_atexit_impl(void (*func)(void*), void *arg, void *dso_handle)
105 {
106     struct thread_local_dtor* dtor = __libc_malloc(sizeof(*dtor));
107     if (!dtor) {
108         return -1;
109     }
110     dtor->func = func;
111     dtor->arg = arg;
112     dtor->dso_handle = dso_handle;
113     pthread_t thr = __pthread_self();
114     dtor->next = thr->thread_local_dtors;
115     thr->thread_local_dtors = dtor;
116     if (add_dso_handle_node != NULL) {
117         add_dso_handle_node(dso_handle);
118     }
119     return 0;
120 }
121 
__cxa_thread_finalizenull122 void __cxa_thread_finalize()
123 {
124     pthread_t thr = __pthread_self();
125     while (thr->thread_local_dtors != NULL) {
126         struct thread_local_dtor* cur = thr->thread_local_dtors;
127         thr->thread_local_dtors= cur->next;
128         cur->func(cur->arg);
129         if (remove_dso_handle_node) {
130             remove_dso_handle_node(cur->dso_handle);
131         }
132     }
133     return;
134 }
135 
136 #endif
137 
138