|  | // Copyright (C) 2012-2025 Free Software Foundation, Inc. | 
|  | // | 
|  | // This file is part of GCC. | 
|  | // | 
|  | // GCC is free software; you can redistribute it and/or modify | 
|  | // it under the terms of the GNU General Public License as published by | 
|  | // the Free Software Foundation; either version 3, or (at your option) | 
|  | // any later version. | 
|  |  | 
|  | // GCC 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 General Public License for more details. | 
|  |  | 
|  | // Under Section 7 of GPL version 3, you are granted additional | 
|  | // permissions described in the GCC Runtime Library Exception, version | 
|  | // 3.1, as published by the Free Software Foundation. | 
|  |  | 
|  | // You should have received a copy of the GNU General Public License and | 
|  | // a copy of the GCC Runtime Library Exception along with this program; | 
|  | // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see | 
|  | // <http://www.gnu.org/licenses/>. | 
|  |  | 
|  | #include <cxxabi.h> | 
|  | #include <cstdlib> | 
|  | #include <new> | 
|  | #include "bits/gthr.h" | 
|  |  | 
|  | #ifdef __USING_MCFGTHREAD__ | 
|  |  | 
|  | #include <mcfgthread/cxa.h> | 
|  |  | 
|  | namespace __cxxabiv1 { | 
|  |  | 
|  | extern "C" int | 
|  | __cxa_thread_atexit (void (_GLIBCXX_CDTOR_CALLABI *dtor)(void *), | 
|  | void *obj, void *dso_handle) _GLIBCXX_NOTHROW | 
|  | { | 
|  | return __MCF_cxa_thread_atexit (dtor, obj, dso_handle); | 
|  | } | 
|  |  | 
|  | }  // namespace __cxxabiv1 | 
|  |  | 
|  | #else // __USING_MCFGTHREAD__ | 
|  |  | 
|  | #ifdef _GLIBCXX_THREAD_ATEXIT_WIN32 | 
|  | #define WIN32_LEAN_AND_MEAN | 
|  | #include <windows.h> | 
|  | #endif | 
|  |  | 
|  | // Simplify it a little for this file. | 
|  | #ifndef _GLIBCXX_CDTOR_CALLABI | 
|  | #  define _GLIBCXX_CDTOR_CALLABI | 
|  | #endif | 
|  |  | 
|  | #if _GLIBCXX_HAVE___CXA_THREAD_ATEXIT | 
|  |  | 
|  | // Libc provides __cxa_thread_atexit definition. | 
|  |  | 
|  | #elif _GLIBCXX_HAVE___CXA_THREAD_ATEXIT_IMPL | 
|  |  | 
|  | extern "C" int __cxa_thread_atexit_impl (void (_GLIBCXX_CDTOR_CALLABI *func) (void *), | 
|  | void *arg, void *d); | 
|  | extern "C" int | 
|  | __cxxabiv1::__cxa_thread_atexit (void (_GLIBCXX_CDTOR_CALLABI *dtor)(void *), | 
|  | void *obj, void *dso_handle) | 
|  | _GLIBCXX_NOTHROW | 
|  | { | 
|  | return __cxa_thread_atexit_impl (dtor, obj, dso_handle); | 
|  | } | 
|  |  | 
|  | #else /* _GLIBCXX_HAVE___CXA_THREAD_ATEXIT_IMPL */ | 
|  |  | 
|  | namespace { | 
|  | // One element in a singly-linked stack of cleanups. | 
|  | struct elt | 
|  | { | 
|  | void (_GLIBCXX_CDTOR_CALLABI *destructor)(void *); | 
|  | void *object; | 
|  | elt *next; | 
|  | #ifdef _GLIBCXX_THREAD_ATEXIT_WIN32 | 
|  | HMODULE dll; | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | // Keep a per-thread list of cleanups in gthread_key storage. | 
|  | __gthread_key_t key; | 
|  | // But also support non-threaded mode. | 
|  | elt *single_thread; | 
|  |  | 
|  | // Run the specified stack of cleanups. | 
|  | void run (void *p) | 
|  | { | 
|  | elt *e = static_cast<elt*>(p); | 
|  | while (e) | 
|  | { | 
|  | elt *old_e = e; | 
|  | e->destructor (e->object); | 
|  | #ifdef _GLIBCXX_THREAD_ATEXIT_WIN32 | 
|  | /* Decrement DLL count */ | 
|  | if (e->dll) | 
|  | FreeLibrary (e->dll); | 
|  | #endif | 
|  | e = e->next; | 
|  | delete (old_e); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Run the stack of cleanups for the current thread. | 
|  | void run () | 
|  | { | 
|  | void *e; | 
|  | if (__gthread_active_p ()) | 
|  | { | 
|  | e = __gthread_getspecific (key); | 
|  | __gthread_setspecific (key, NULL); | 
|  | } | 
|  | else | 
|  | { | 
|  | e = single_thread; | 
|  | single_thread = NULL; | 
|  | } | 
|  | run (e); | 
|  | } | 
|  |  | 
|  | // Initialize the key for the cleanup stack.  We use a static local for | 
|  | // key init/delete rather than atexit so that delete is run on dlclose. | 
|  | void key_init() { | 
|  | struct key_s { | 
|  | key_s() { __gthread_key_create (&key, run); } | 
|  | ~key_s() { __gthread_key_delete (key); } | 
|  | }; | 
|  | static key_s ks; | 
|  | // Also make sure the destructors are run by std::exit. | 
|  | // FIXME TLS cleanups should run before static cleanups and atexit | 
|  | // cleanups. | 
|  | std::atexit (run); | 
|  | } | 
|  | } | 
|  |  | 
|  | #if __GXX_WEAK__ && _GLIBCXX_MAY_HAVE___CXA_THREAD_ATEXIT_IMPL | 
|  | extern "C" | 
|  | int __attribute__ ((__weak__)) | 
|  | __cxa_thread_atexit_impl (void (_GLIBCXX_CDTOR_CALLABI *func) (void *), | 
|  | void *arg, void *d); | 
|  | #endif | 
|  |  | 
|  | // ??? We can't make it an ifunc, can we? | 
|  | extern "C" int | 
|  | __cxxabiv1::__cxa_thread_atexit (void (_GLIBCXX_CDTOR_CALLABI *dtor)(void *), | 
|  | void *obj, [[maybe_unused]] void *dso_handle) | 
|  | _GLIBCXX_NOTHROW | 
|  | { | 
|  | #if __GXX_WEAK__ && _GLIBCXX_MAY_HAVE___CXA_THREAD_ATEXIT_IMPL | 
|  | if (__cxa_thread_atexit_impl) | 
|  | // Rely on a (presumably libc-provided) __cxa_thread_atexit_impl, | 
|  | // if it happens to be defined, even if configure couldn't find it | 
|  | // during the build.  _GLIBCXX_MAY_HAVE___CXA_THREAD_ATEXIT_IMPL | 
|  | // may be defined e.g. in os_defines.h on platforms where some | 
|  | // versions of libc have a __cxa_thread_atexit_impl definition, | 
|  | // but whose earlier versions didn't.  This enables programs build | 
|  | // by toolchains compatible with earlier libc versions to still | 
|  | // benefit from a libc-provided __cxa_thread_atexit_impl. | 
|  | return __cxa_thread_atexit_impl (dtor, obj, dso_handle); | 
|  | #endif | 
|  |  | 
|  | // Do this initialization once. | 
|  | if (__gthread_active_p ()) | 
|  | { | 
|  | // When threads are active use __gthread_once. | 
|  | static __gthread_once_t once = __GTHREAD_ONCE_INIT; | 
|  | __gthread_once (&once, key_init); | 
|  | } | 
|  | else | 
|  | { | 
|  | // And when threads aren't active use a static local guard. | 
|  | static bool queued; | 
|  | if (!queued) | 
|  | { | 
|  | queued = true; | 
|  | std::atexit (run); | 
|  | } | 
|  | } | 
|  |  | 
|  | elt *first; | 
|  | if (__gthread_active_p ()) | 
|  | first = static_cast<elt*>(__gthread_getspecific (key)); | 
|  | else | 
|  | first = single_thread; | 
|  |  | 
|  | elt *new_elt = new (std::nothrow) elt; | 
|  | if (!new_elt) | 
|  | return -1; | 
|  | new_elt->destructor = dtor; | 
|  | new_elt->object = obj; | 
|  | new_elt->next = first; | 
|  | #ifdef _GLIBCXX_THREAD_ATEXIT_WIN32 | 
|  | /* Store the DLL address for a later call to FreeLibrary in new_elt and | 
|  | increment DLL load count.  This blocks the unloading of the DLL | 
|  | before the thread-local dtors have been called.  This does NOT help | 
|  | if FreeLibrary/dlclose is called in excess. */ | 
|  | GetModuleHandleExW (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | 
|  | (LPCWSTR) dtor, &new_elt->dll); | 
|  | #endif | 
|  |  | 
|  | if (__gthread_active_p ()) | 
|  | __gthread_setspecific (key, new_elt); | 
|  | else | 
|  | single_thread = new_elt; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #endif /* _GLIBCXX_HAVE___CXA_THREAD_ATEXIT_IMPL */ | 
|  |  | 
|  | #endif // __USING_MCFGTHREAD__ |