1 /* Dynamic loading of the libgcc unwinder.
2 Copyright (C) 2021-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19 #ifdef SHARED
20
21 #include <assert.h>
22 #include <dlfcn.h>
23 #include <gnu/lib-names.h>
24 #include <unwind-link.h>
25 #include <libc-lock.h>
26
27 /* Statically allocate the object, so that we do not have to deal with
28 malloc failure. __libc_unwind_link_get must not fail if libgcc_s
29 has already been loaded by other means. */
30 static struct unwind_link global;
31
32 /* dlopen handle. Also used for the double-checked locking idiom. */
33 static void *global_libgcc_handle;
34
35 /* We cannot use __libc_once because the pthread_once implementation
36 may depend on unwinding. */
37 __libc_lock_define (static, lock);
38
39 struct unwind_link *
__libc_unwind_link_get(void)40 __libc_unwind_link_get (void)
41 {
42 /* Double-checked locking idiom. Synchronizes with the release MO
43 store at the end of this function. */
44 if (atomic_load_acquire (&global_libgcc_handle) != NULL)
45 return &global;
46
47 /* Initialize a copy of the data, so that we do not need about
48 unlocking in case the dynamic loader somehow triggers
49 unwinding. */
50 void *local_libgcc_handle = __libc_dlopen (LIBGCC_S_SO);
51 if (local_libgcc_handle == NULL)
52 {
53 __libc_lock_unlock (lock);
54 return NULL;
55 }
56
57 struct unwind_link local;
58 local.ptr__Unwind_Backtrace
59 = __libc_dlsym (local_libgcc_handle, "_Unwind_Backtrace");
60 local.ptr__Unwind_ForcedUnwind
61 = __libc_dlsym (local_libgcc_handle, "_Unwind_ForcedUnwind");
62 local.ptr__Unwind_GetCFA
63 = __libc_dlsym (local_libgcc_handle, "_Unwind_GetCFA");
64 #if UNWIND_LINK_GETIP
65 local.ptr__Unwind_GetIP
66 = __libc_dlsym (local_libgcc_handle, "_Unwind_GetIP");
67 #endif
68 local.ptr__Unwind_Resume
69 = __libc_dlsym (local_libgcc_handle, "_Unwind_Resume");
70 #if UNWIND_LINK_FRAME_STATE_FOR
71 local.ptr___frame_state_for
72 = __libc_dlsym (local_libgcc_handle, "__frame_state_for");
73 #endif
74 local.ptr_personality
75 = __libc_dlsym (local_libgcc_handle, "__gcc_personality_v0");
76 UNWIND_LINK_EXTRA_INIT
77
78 /* If a symbol is missing, libgcc_s has somehow been corrupted. */
79 assert (local.ptr__Unwind_Backtrace != NULL);
80 assert (local.ptr__Unwind_ForcedUnwind != NULL);
81 assert (local.ptr__Unwind_GetCFA != NULL);
82 #if UNWIND_LINK_GETIP
83 assert (local.ptr__Unwind_GetIP != NULL);
84 #endif
85 assert (local.ptr__Unwind_Resume != NULL);
86 assert (local.ptr_personality != NULL);
87
88 #ifdef PTR_MANGLE
89 PTR_MANGLE (local.ptr__Unwind_Backtrace);
90 PTR_MANGLE (local.ptr__Unwind_ForcedUnwind);
91 PTR_MANGLE (local.ptr__Unwind_GetCFA);
92 # if UNWIND_LINK_GETIP
93 PTR_MANGLE (local.ptr__Unwind_GetIP);
94 # endif
95 PTR_MANGLE (local.ptr__Unwind_Resume);
96 # if UNWIND_LINK_FRAME_STATE_FOR
97 PTR_MANGLE (local.ptr___frame_state_for);
98 # endif
99 PTR_MANGLE (local.ptr_personality);
100 #endif
101
102 __libc_lock_lock (lock);
103 if (atomic_load_relaxed (&global_libgcc_handle) != NULL)
104 /* This thread lost the race. Clean up. */
105 __libc_dlclose (local_libgcc_handle);
106 else
107 {
108 global = local;
109
110 /* Completes the double-checked locking idiom. */
111 atomic_store_release (&global_libgcc_handle, local_libgcc_handle);
112 }
113
114 __libc_lock_unlock (lock);
115 return &global;
116 }
libc_hidden_def(__libc_unwind_link_get)117 libc_hidden_def (__libc_unwind_link_get)
118
119 void
120 __libc_unwind_link_after_fork (void)
121 {
122 if (__libc_lock_trylock (lock) == 0)
123 /* The lock was not acquired during the fork. This covers both
124 the initialized and uninitialized case. */
125 __libc_lock_unlock (lock);
126 else
127 {
128 /* Initialization was in progress in another thread.
129 Reinitialize the lock. */
130 __libc_lock_init (lock);
131 global_libgcc_handle = NULL;
132 }
133 }
134
135 void __libc_freeres_fn_section
__libc_unwind_link_freeres(void)136 __libc_unwind_link_freeres (void)
137 {
138 if (global_libgcc_handle != NULL)
139 {
140 __libc_dlclose (global_libgcc_handle );
141 global_libgcc_handle = NULL;
142 }
143 }
144
145 #endif /* SHARED */
146