| /* Register destructors for C++ TLS variables declared with thread_local. |
| Copyright (C) 2013-2014 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 |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <stdlib.h> |
| #include <ldsodefs.h> |
| |
| typedef void (*dtor_func) (void *); |
| |
| struct dtor_list |
| { |
| dtor_func func; |
| void *obj; |
| struct link_map *map; |
| struct dtor_list *next; |
| }; |
| |
| static __thread struct dtor_list *tls_dtor_list; |
| static __thread void *dso_symbol_cache; |
| static __thread struct link_map *lm_cache; |
| |
| /* Register a destructor for TLS variables declared with the 'thread_local' |
| keyword. This function is only called from code generated by the C++ |
| compiler. FUNC is the destructor function and OBJ is the object to be |
| passed to the destructor. DSO_SYMBOL is the __dso_handle symbol that each |
| DSO has at a unique address in its map, added from crtbegin.o during the |
| linking phase. */ |
| int |
| __cxa_thread_atexit_impl (dtor_func func, void *obj, void *dso_symbol) |
| { |
| /* Prepend. */ |
| struct dtor_list *new = calloc (1, sizeof (struct dtor_list)); |
| new->func = func; |
| new->obj = obj; |
| new->next = tls_dtor_list; |
| tls_dtor_list = new; |
| |
| /* See if we already encountered the DSO. */ |
| __rtld_lock_lock_recursive (GL(dl_load_lock)); |
| |
| if (__builtin_expect (dso_symbol_cache != dso_symbol, 0)) |
| { |
| ElfW(Addr) caller = (ElfW(Addr)) dso_symbol; |
| |
| struct link_map *l = _dl_find_dso_for_object (caller); |
| |
| /* If the address is not recognized the call comes from the main |
| program (we hope). */ |
| lm_cache = l ? l : GL(dl_ns)[LM_ID_BASE]._ns_loaded; |
| } |
| /* A destructor could result in a thread_local construction and the former |
| could have cleared the flag. */ |
| if (lm_cache->l_type == lt_loaded && lm_cache->l_tls_dtor_count == 0) |
| lm_cache->l_flags_1 |= DF_1_NODELETE; |
| |
| new->map = lm_cache; |
| new->map->l_tls_dtor_count++; |
| |
| __rtld_lock_unlock_recursive (GL(dl_load_lock)); |
| |
| return 0; |
| } |
| |
| /* Call the destructors. This is called either when a thread returns from the |
| initial function or when the process exits via the exit function. */ |
| void |
| __call_tls_dtors (void) |
| { |
| while (tls_dtor_list) |
| { |
| struct dtor_list *cur = tls_dtor_list; |
| tls_dtor_list = tls_dtor_list->next; |
| |
| cur->func (cur->obj); |
| |
| __rtld_lock_lock_recursive (GL(dl_load_lock)); |
| |
| /* Allow DSO unload if count drops to zero. */ |
| cur->map->l_tls_dtor_count--; |
| if (cur->map->l_tls_dtor_count == 0 && cur->map->l_type == lt_loaded) |
| cur->map->l_flags_1 &= ~DF_1_NODELETE; |
| |
| __rtld_lock_unlock_recursive (GL(dl_load_lock)); |
| |
| free (cur); |
| } |
| } |
| libc_hidden_def (__call_tls_dtors) |