| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- /* Dynamic loading of the libgcc unwinder.
- Copyright (C) 2021-2026 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, see
- <https://www.gnu.org/licenses/>. */
- #ifdef SHARED
- #include <assert.h>
- #include <dlfcn.h>
- #include <gnu/lib-names.h>
- #include <unwind-link.h>
- #include <libc-lock.h>
- #include <pointer_guard.h>
- /* Statically allocate the object, so that we do not have to deal with
- malloc failure. __libc_unwind_link_get must not fail if libgcc_s
- has already been loaded by other means. */
- static struct unwind_link global;
- /* dlopen handle. Also used for the double-checked locking idiom. */
- static void *global_libgcc_handle;
- /* We cannot use __libc_once because the pthread_once implementation
- may depend on unwinding. */
- __libc_lock_define (static, lock);
- struct unwind_link *
- __libc_unwind_link_get (void)
- {
- /* Double-checked locking idiom. Synchronizes with the release MO
- store at the end of this function. */
- if (atomic_load_acquire (&global_libgcc_handle) != NULL)
- return &global;
- /* Initialize a copy of the data, so that we do not need about
- unlocking in case the dynamic loader somehow triggers
- unwinding. */
- void *local_libgcc_handle = __libc_dlopen (LIBGCC_S_SO);
- if (local_libgcc_handle == NULL)
- {
- __libc_lock_unlock (lock);
- return NULL;
- }
- struct unwind_link local;
- local.ptr__Unwind_Backtrace
- = __libc_dlsym (local_libgcc_handle, "_Unwind_Backtrace");
- local.ptr__Unwind_ForcedUnwind
- = __libc_dlsym (local_libgcc_handle, "_Unwind_ForcedUnwind");
- local.ptr__Unwind_GetCFA
- = __libc_dlsym (local_libgcc_handle, "_Unwind_GetCFA");
- #if UNWIND_LINK_GETIP
- local.ptr__Unwind_GetIP
- = __libc_dlsym (local_libgcc_handle, "_Unwind_GetIP");
- #endif
- local.ptr__Unwind_Resume
- = __libc_dlsym (local_libgcc_handle, "_Unwind_Resume");
- #if UNWIND_LINK_FRAME_STATE_FOR
- local.ptr___frame_state_for
- = __libc_dlsym (local_libgcc_handle, "__frame_state_for");
- #endif
- local.ptr_personality
- = __libc_dlsym (local_libgcc_handle, "__gcc_personality_v0");
- UNWIND_LINK_EXTRA_INIT
- /* If a symbol is missing, libgcc_s has somehow been corrupted. */
- assert (local.ptr__Unwind_Backtrace != NULL);
- assert (local.ptr__Unwind_ForcedUnwind != NULL);
- assert (local.ptr__Unwind_GetCFA != NULL);
- #if UNWIND_LINK_GETIP
- assert (local.ptr__Unwind_GetIP != NULL);
- #endif
- assert (local.ptr__Unwind_Resume != NULL);
- assert (local.ptr_personality != NULL);
- PTR_MANGLE (local.ptr__Unwind_Backtrace);
- PTR_MANGLE (local.ptr__Unwind_ForcedUnwind);
- PTR_MANGLE (local.ptr__Unwind_GetCFA);
- #if UNWIND_LINK_GETIP
- PTR_MANGLE (local.ptr__Unwind_GetIP);
- #endif
- PTR_MANGLE (local.ptr__Unwind_Resume);
- #if UNWIND_LINK_FRAME_STATE_FOR
- PTR_MANGLE (local.ptr___frame_state_for);
- #endif
- PTR_MANGLE (local.ptr_personality);
- __libc_lock_lock (lock);
- if (atomic_load_relaxed (&global_libgcc_handle) != NULL)
- /* This thread lost the race. Clean up. */
- __libc_dlclose (local_libgcc_handle);
- else
- {
- global = local;
- /* Completes the double-checked locking idiom. */
- atomic_store_release (&global_libgcc_handle, local_libgcc_handle);
- }
- __libc_lock_unlock (lock);
- return &global;
- }
- libc_hidden_def (__libc_unwind_link_get)
- void
- __libc_unwind_link_after_fork (void)
- {
- if (__libc_lock_trylock (lock) == 0)
- /* The lock was not acquired during the fork. This covers both
- the initialized and uninitialized case. */
- __libc_lock_unlock (lock);
- else
- {
- /* Initialization was in progress in another thread.
- Reinitialize the lock. */
- __libc_lock_init (lock);
- global_libgcc_handle = NULL;
- }
- }
- void
- __libc_unwind_link_freeres (void)
- {
- if (global_libgcc_handle != NULL)
- {
- __libc_dlclose (global_libgcc_handle );
- global_libgcc_handle = NULL;
- }
- }
- #endif /* SHARED */
|