(root)/
glibc-2.38/
misc/
unwind-link.c
       1  /* Dynamic loading of the libgcc unwinder.
       2     Copyright (C) 2021-2023 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.  */
      31  static struct unwind_link global;
      32  
      33  /* dlopen handle.  Also used for the double-checked locking idiom.  */
      34  static 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  
      40  struct 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  }
     116  libc_hidden_def (__libc_unwind_link_get)
     117  
     118  void
     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  
     134  void
     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 */