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 */ |
146 | |