]>
Commit | Line | Data |
---|---|---|
9fc813e1 FW |
1 | /* Dynamic loading of the libgcc unwinder. |
2 | Copyright (C) 2021 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 * | |
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 | } | |
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 | |
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 */ |