1/* Dynamic loading of the libgcc unwinder.
2 Copyright (C) 2021-2024 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#include <pointer_guard.h>
27
28/* Statically allocate the object, so that we do not have to deal with
29 malloc failure. __libc_unwind_link_get must not fail if libgcc_s
30 has already been loaded by other means. */
31static struct unwind_link global;
32
33/* dlopen handle. Also used for the double-checked locking idiom. */
34static void *global_libgcc_handle;
35
36/* We cannot use __libc_once because the pthread_once implementation
37 may depend on unwinding. */
38__libc_lock_define (static, lock);
39
40struct unwind_link *
41__libc_unwind_link_get (void)
42{
43 /* Double-checked locking idiom. Synchronizes with the release MO
44 store at the end of this function. */
45 if (atomic_load_acquire (&global_libgcc_handle) != NULL)
46 return &global;
47
48 /* Initialize a copy of the data, so that we do not need about
49 unlocking in case the dynamic loader somehow triggers
50 unwinding. */
51 void *local_libgcc_handle = __libc_dlopen (LIBGCC_S_SO);
52 if (local_libgcc_handle == NULL)
53 {
54 __libc_lock_unlock (lock);
55 return NULL;
56 }
57
58 struct unwind_link local;
59 local.ptr__Unwind_Backtrace
60 = __libc_dlsym (local_libgcc_handle, "_Unwind_Backtrace");
61 local.ptr__Unwind_ForcedUnwind
62 = __libc_dlsym (local_libgcc_handle, "_Unwind_ForcedUnwind");
63 local.ptr__Unwind_GetCFA
64 = __libc_dlsym (local_libgcc_handle, "_Unwind_GetCFA");
65#if UNWIND_LINK_GETIP
66 local.ptr__Unwind_GetIP
67 = __libc_dlsym (local_libgcc_handle, "_Unwind_GetIP");
68#endif
69 local.ptr__Unwind_Resume
70 = __libc_dlsym (local_libgcc_handle, "_Unwind_Resume");
71#if UNWIND_LINK_FRAME_STATE_FOR
72 local.ptr___frame_state_for
73 = __libc_dlsym (local_libgcc_handle, "__frame_state_for");
74#endif
75 local.ptr_personality
76 = __libc_dlsym (local_libgcc_handle, "__gcc_personality_v0");
77 UNWIND_LINK_EXTRA_INIT
78
79 /* If a symbol is missing, libgcc_s has somehow been corrupted. */
80 assert (local.ptr__Unwind_Backtrace != NULL);
81 assert (local.ptr__Unwind_ForcedUnwind != NULL);
82 assert (local.ptr__Unwind_GetCFA != NULL);
83#if UNWIND_LINK_GETIP
84 assert (local.ptr__Unwind_GetIP != NULL);
85#endif
86 assert (local.ptr__Unwind_Resume != NULL);
87 assert (local.ptr_personality != NULL);
88
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
101 __libc_lock_lock (lock);
102 if (atomic_load_relaxed (&global_libgcc_handle) != NULL)
103 /* This thread lost the race. Clean up. */
104 __libc_dlclose (local_libgcc_handle);
105 else
106 {
107 global = local;
108
109 /* Completes the double-checked locking idiom. */
110 atomic_store_release (&global_libgcc_handle, local_libgcc_handle);
111 }
112
113 __libc_lock_unlock (lock);
114 return &global;
115}
116libc_hidden_def (__libc_unwind_link_get)
117
118void
119__libc_unwind_link_after_fork (void)
120{
121 if (__libc_lock_trylock (lock) == 0)
122 /* The lock was not acquired during the fork. This covers both
123 the initialized and uninitialized case. */
124 __libc_lock_unlock (lock);
125 else
126 {
127 /* Initialization was in progress in another thread.
128 Reinitialize the lock. */
129 __libc_lock_init (lock);
130 global_libgcc_handle = NULL;
131 }
132}
133
134void
135__libc_unwind_link_freeres (void)
136{
137 if (global_libgcc_handle != NULL)
138 {
139 __libc_dlclose (global_libgcc_handle );
140 global_libgcc_handle = NULL;
141 }
142}
143
144#endif /* SHARED */
145

source code of glibc/misc/unwind-link.c