X-Git-Url: https://sourceware.org/git/?a=blobdiff_plain;f=elf%2Fdl-close.c;h=efb2b584f2a13556428155189ce17d40c5acafba;hb=4bff6e0175ed195871f4e01cc4c4c33274b8f6e3;hp=db11d3b50a4173baeb9a8ad8617f89fe7fb8c1aa;hpb=2e93b4a4cd190fc8d21682f69a5455dc6dcd04dd;p=glibc.git diff --git a/elf/dl-close.c b/elf/dl-close.c index db11d3b50a..efb2b584f2 100644 --- a/elf/dl-close.c +++ b/elf/dl-close.c @@ -1,198 +1,616 @@ /* Close a shared object opened by `_dl_open'. - Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. + Copyright (C) 1996-2007, 2009, 2010, 2011 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 Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. + 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 - Library General Public License for more details. + Lesser General Public License for more details. - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ #include #include +#include #include +#include +#include #include #include +#include #include #include #include #include - -#include +#include +#include /* Type of the constructor functions. */ typedef void (*fini_t) (void); -/* During the program run we must not modify the global data of - loaded shared object simultanously in two threads. Therefore we - protect `dlopen' and `dlclose' in dlclose.c. */ -__libc_lock_define (extern, _dl_load_lock) +/* Special l_idx value used to indicate which objects remain loaded. */ +#define IDX_STILL_USED -1 -void -internal_function -_dl_close (void *_map) + +/* Returns true we an non-empty was found. */ +static bool +remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp, + bool should_be_there) { - struct link_map **list; - struct link_map **rellist; - struct link_map *map = _map; - unsigned int nsearchlist; - unsigned int nrellist; - unsigned int i; - unsigned int *new_opencount; + if (idx - disp >= listp->len) + { + if (listp->next == NULL) + { + /* The index is not actually valid in the slotinfo list, + because this object was closed before it was fully set + up due to some error. */ + assert (! should_be_there); + } + else + { + if (remove_slotinfo (idx, listp->next, disp + listp->len, + should_be_there)) + return true; - /* First see whether we can remove the object at all. */ - if (map->l_flags_1 & DF_1_NODELETE) - /* Nope. Do nothing. */ - return; + /* No non-empty entry. Search from the end of this element's + slotinfo array. */ + idx = disp + listp->len; + } + } + else + { + struct link_map *old_map = listp->slotinfo[idx - disp].map; - if (__builtin_expect (map->l_opencount, 1) == 0) - _dl_signal_error (0, map->l_name, N_("shared object not open")); + /* The entry might still be in its unused state if we are closing an + object that wasn't fully set up. */ + if (__builtin_expect (old_map != NULL, 1)) + { + assert (old_map->l_tls_modid == idx); - /* Acquire the lock. */ - __libc_lock_lock (_dl_load_lock); + /* Mark the entry as unused. */ + listp->slotinfo[idx - disp].gen = GL(dl_tls_generation) + 1; + listp->slotinfo[idx - disp].map = NULL; + } + + /* If this is not the last currently used entry no need to look + further. */ + if (idx != GL(dl_tls_max_dtv_idx)) + return true; + } - /* Decrement the reference count. */ - if (map->l_opencount > 1 || map->l_type != lt_loaded) + while (idx - disp > (disp == 0 ? 1 + GL(dl_tls_static_nelem) : 0)) { - /* There are still references to this object. Do nothing more. */ - if (__builtin_expect (_dl_debug_files, 0)) + --idx; + + if (listp->slotinfo[idx - disp].map != NULL) { - char buf[20]; + /* Found a new last used index. */ + GL(dl_tls_max_dtv_idx) = idx; + return true; + } + } + + /* No non-entry in this list element. */ + return false; +} - buf[sizeof buf - 1] = '\0'; - _dl_debug_message (1, "\nclosing file=", map->l_name, - "; opencount == ", - _itoa_word (map->l_opencount, - buf + sizeof buf - 1, 10, 0), - "\n", NULL); +void +_dl_close_worker (struct link_map *map) +{ + /* One less direct use. */ + --map->l_direct_opencount; + + /* If _dl_close is called recursively (some destructor call dlclose), + just record that the parent _dl_close will need to do garbage collection + again and return. */ + static enum { not_pending, pending, rerun } dl_close_state; + + if (map->l_direct_opencount > 0 || map->l_type != lt_loaded + || dl_close_state != not_pending) + { + if (map->l_direct_opencount == 0) + { + if (map->l_type == lt_loaded) + dl_close_state = rerun; + else if (map->l_type == lt_library) + { + struct link_map **oldp = map->l_initfini; + map->l_initfini = map->l_orig_initfini; + _dl_scope_free (oldp); + } } - /* One decrement the object itself, not the dependencies. */ - --map->l_opencount; + /* There are still references to this object. Do nothing more. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) + _dl_debug_printf ("\nclosing file=%s; direct_opencount=%u\n", + map->l_name, map->l_direct_opencount); - __libc_lock_unlock (_dl_load_lock); return; } - list = map->l_initfini; - nsearchlist = map->l_searchlist.r_nlist; + Lmid_t nsid = map->l_ns; + struct link_namespaces *ns = &GL(dl_ns)[nsid]; + + retry: + dl_close_state = pending; - /* Compute the new l_opencount values. */ - new_opencount = (unsigned int *) alloca (nsearchlist - * sizeof (unsigned int)); - for (i = 0; i < nsearchlist; ++i) + bool any_tls = false; + const unsigned int nloaded = ns->_ns_nloaded; + char used[nloaded]; + char done[nloaded]; + struct link_map *maps[nloaded]; + + /* Run over the list and assign indexes to the link maps and enter + them into the MAPS array. */ + int idx = 0; + for (struct link_map *l = ns->_ns_loaded; l != NULL; l = l->l_next) { - list[i]->l_idx = i; - new_opencount[i] = list[i]->l_opencount; + l->l_idx = idx; + maps[idx] = l; + ++idx; } - --new_opencount[0]; - for (i = 1; i < nsearchlist; ++i) - if (! (list[i]->l_flags_1 & DF_1_NODELETE) - /* Decrement counter. */ - && --new_opencount[i] == 0 - /* Test whether this object was also loaded directly. */ - && list[i]->l_searchlist.r_list != NULL) - { - /* In this case we have the decrement all the dependencies of - this object. They are all in MAP's dependency list. */ - unsigned int j; - struct link_map **dep_list = list[i]->l_searchlist.r_list; + assert (idx == nloaded); + + /* Prepare the bitmaps. */ + memset (used, '\0', sizeof (used)); + memset (done, '\0', sizeof (done)); - for (j = 1; j < list[i]->l_searchlist.r_nlist; ++j) - if (! (dep_list[j]->l_flags_1 & DF_1_NODELETE)) + /* Keep track of the lowest index link map we have covered already. */ + int done_index = -1; + while (++done_index < nloaded) + { + struct link_map *l = maps[done_index]; + + if (done[done_index]) + /* Already handled. */ + continue; + + /* Check whether this object is still used. */ + if (l->l_type == lt_loaded + && l->l_direct_opencount == 0 + && (l->l_flags_1 & DF_1_NODELETE) == 0 + && !used[done_index]) + continue; + + /* We need this object and we handle it now. */ + done[done_index] = 1; + used[done_index] = 1; + /* Signal the object is still needed. */ + l->l_idx = IDX_STILL_USED; + + /* Mark all dependencies as used. */ + if (l->l_initfini != NULL) + { + struct link_map **lp = &l->l_initfini[1]; + while (*lp != NULL) { - assert (dep_list[j]->l_idx < nsearchlist); - --new_opencount[dep_list[j]->l_idx]; + if ((*lp)->l_idx != IDX_STILL_USED) + { + assert ((*lp)->l_idx >= 0 && (*lp)->l_idx < nloaded); + + if (!used[(*lp)->l_idx]) + { + used[(*lp)->l_idx] = 1; + if ((*lp)->l_idx - 1 < done_index) + done_index = (*lp)->l_idx - 1; + } + } + + ++lp; } - } - assert (new_opencount[0] == 0); + } + /* And the same for relocation dependencies. */ + if (l->l_reldeps != NULL) + for (unsigned int j = 0; j < l->l_reldeps->act; ++j) + { + struct link_map *jmap = l->l_reldeps->list[j]; + + if (jmap->l_idx != IDX_STILL_USED) + { + assert (jmap->l_idx >= 0 && jmap->l_idx < nloaded); + + if (!used[jmap->l_idx]) + { + used[jmap->l_idx] = 1; + if (jmap->l_idx - 1 < done_index) + done_index = jmap->l_idx - 1; + } + } + } + } - rellist = map->l_reldeps; - nrellist = map->l_reldepsact; + /* Sort the entries. */ + _dl_sort_fini (ns->_ns_loaded, maps, nloaded, used, nsid); /* Call all termination functions at once. */ - for (i = 0; i < nsearchlist; ++i) +#ifdef SHARED + bool do_audit = GLRO(dl_naudit) > 0 && !ns->_ns_loaded->l_auditing; +#endif + bool unload_any = false; + bool scope_mem_left = false; + unsigned int unload_global = 0; + unsigned int first_loaded = ~0; + for (unsigned int i = 0; i < nloaded; ++i) { - struct link_map *imap = list[i]; - if (new_opencount[i] == 0 && imap->l_type == lt_loaded - && (imap->l_info[DT_FINI] || imap->l_info[DT_FINI_ARRAY]) - && ! (imap->l_flags_1 & DF_1_NODELETE) - /* Skip any half-cooked objects that were never initialized. */ - && imap->l_init_called) + struct link_map *imap = maps[i]; + + /* All elements must be in the same namespace. */ + assert (imap->l_ns == nsid); + + if (!used[i]) { - /* When debugging print a message first. */ - if (__builtin_expect (_dl_debug_impcalls, 0)) - _dl_debug_message (1, "\ncalling fini: ", imap->l_name, - "\n\n", NULL); + assert (imap->l_type == lt_loaded + && (imap->l_flags_1 & DF_1_NODELETE) == 0); + + /* Call its termination function. Do not do it for + half-cooked objects. */ + if (imap->l_init_called) + { + /* When debugging print a message first. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, + 0)) + _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", + imap->l_name, nsid); + + if (imap->l_info[DT_FINI_ARRAY] != NULL) + { + ElfW(Addr) *array = + (ElfW(Addr) *) (imap->l_addr + + imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr); + unsigned int sz = (imap->l_info[DT_FINI_ARRAYSZ]->d_un.d_val + / sizeof (ElfW(Addr))); + + while (sz-- > 0) + ((fini_t) array[sz]) (); + } + + /* Next try the old-style destructor. */ + if (imap->l_info[DT_FINI] != NULL) + (*(void (*) (void)) DL_DT_FINI_ADDRESS + (imap, ((void *) imap->l_addr + + imap->l_info[DT_FINI]->d_un.d_ptr))) (); + } - /* Call its termination function. */ - if (imap->l_info[DT_FINI_ARRAY] != NULL) +#ifdef SHARED + /* Auditing checkpoint: we remove an object. */ + if (__builtin_expect (do_audit, 0)) { - ElfW(Addr) *array = - (ElfW(Addr) *) (imap->l_addr - + imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr); - unsigned int sz = (imap->l_info[DT_FINI_ARRAYSZ]->d_un.d_val - / sizeof (ElfW(Addr))); + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objclose != NULL) + /* Return value is ignored. */ + (void) afct->objclose (&imap->l_audit[cnt].cookie); + + afct = afct->next; + } + } +#endif + + /* This object must not be used anymore. */ + imap->l_removed = 1; + + /* We indeed have an object to remove. */ + unload_any = true; + + if (imap->l_global) + ++unload_global; + + /* Remember where the first dynamically loaded object is. */ + if (i < first_loaded) + first_loaded = i; + } + /* Else used[i]. */ + else if (imap->l_type == lt_loaded) + { + struct r_scope_elem *new_list = NULL; + + if (imap->l_searchlist.r_list == NULL && imap->l_initfini != NULL) + { + /* The object is still used. But one of the objects we are + unloading right now is responsible for loading it. If + the current object does not have it's own scope yet we + have to create one. This has to be done before running + the finalizers. + + To do this count the number of dependencies. */ unsigned int cnt; + for (cnt = 1; imap->l_initfini[cnt] != NULL; ++cnt) + ; + + /* We simply reuse the l_initfini list. */ + imap->l_searchlist.r_list = &imap->l_initfini[cnt + 1]; + imap->l_searchlist.r_nlist = cnt; + + new_list = &imap->l_searchlist; + } - for (cnt = 0; cnt < sz; ++cnt) - ((fini_t) (imap->l_addr + array[cnt])) (); + /* Count the number of scopes which remain after the unload. + When we add the local search list count it. Always add + one for the terminating NULL pointer. */ + size_t remain = (new_list != NULL) + 1; + bool removed_any = false; + for (size_t cnt = 0; imap->l_scope[cnt] != NULL; ++cnt) + /* This relies on l_scope[] entries being always set either + to its own l_symbolic_searchlist address, or some map's + l_searchlist address. */ + if (imap->l_scope[cnt] != &imap->l_symbolic_searchlist) + { + struct link_map *tmap = (struct link_map *) + ((char *) imap->l_scope[cnt] + - offsetof (struct link_map, l_searchlist)); + assert (tmap->l_ns == nsid); + if (tmap->l_idx == IDX_STILL_USED) + ++remain; + else + removed_any = true; + } + else + ++remain; + + if (removed_any) + { + /* Always allocate a new array for the scope. This is + necessary since we must be able to determine the last + user of the current array. If possible use the link map's + memory. */ + size_t new_size; + struct r_scope_elem **newp; + +#define SCOPE_ELEMS(imap) \ + (sizeof (imap->l_scope_mem) / sizeof (imap->l_scope_mem[0])) + + if (imap->l_scope != imap->l_scope_mem + && remain < SCOPE_ELEMS (imap)) + { + new_size = SCOPE_ELEMS (imap); + newp = imap->l_scope_mem; + } + else + { + new_size = imap->l_scope_max; + newp = (struct r_scope_elem **) + malloc (new_size * sizeof (struct r_scope_elem *)); + if (newp == NULL) + _dl_signal_error (ENOMEM, "dlclose", NULL, + N_("cannot create scope list")); + } + + /* Copy over the remaining scope elements. */ + remain = 0; + for (size_t cnt = 0; imap->l_scope[cnt] != NULL; ++cnt) + { + if (imap->l_scope[cnt] != &imap->l_symbolic_searchlist) + { + struct link_map *tmap = (struct link_map *) + ((char *) imap->l_scope[cnt] + - offsetof (struct link_map, l_searchlist)); + if (tmap->l_idx != IDX_STILL_USED) + { + /* Remove the scope. Or replace with own map's + scope. */ + if (new_list != NULL) + { + newp[remain++] = new_list; + new_list = NULL; + } + continue; + } + } + + newp[remain++] = imap->l_scope[cnt]; + } + newp[remain] = NULL; + + struct r_scope_elem **old = imap->l_scope; + + imap->l_scope = newp; + + /* No user anymore, we can free it now. */ + if (old != imap->l_scope_mem) + { + if (_dl_scope_free (old)) + /* If _dl_scope_free used THREAD_GSCOPE_WAIT (), + no need to repeat it. */ + scope_mem_left = false; + } + else + scope_mem_left = true; + + imap->l_scope_max = new_size; } - /* Next try the old-style destructor. */ - if (imap->l_info[DT_FINI] != NULL) - (*(void (*) (void)) ((void *) imap->l_addr - + imap->l_info[DT_FINI]->d_un.d_ptr)) (); + /* The loader is gone, so mark the object as not having one. + Note: l_idx != IDX_STILL_USED -> object will be removed. */ + if (imap->l_loader != NULL + && imap->l_loader->l_idx != IDX_STILL_USED) + imap->l_loader = NULL; + + /* Remember where the first dynamically loaded object is. */ + if (i < first_loaded) + first_loaded = i; } + } - /* Store the new l_opencount value. */ - imap->l_opencount = new_opencount[i]; - /* Just a sanity check. */ - assert (imap->l_type == lt_loaded || imap->l_opencount > 0); + /* If there are no objects to unload, do nothing further. */ + if (!unload_any) + goto out; + +#ifdef SHARED + /* Auditing checkpoint: we will start deleting objects. */ + if (__builtin_expect (do_audit, 0)) + { + struct link_map *head = ns->_ns_loaded; + struct audit_ifaces *afct = GLRO(dl_audit); + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_DELETE); + + afct = afct->next; + } + } } +#endif /* Notify the debugger we are about to remove some loaded objects. */ - _r_debug.r_state = RT_DELETE; + struct r_debug *r = _dl_debug_initialize (0, nsid); + r->r_state = RT_DELETE; _dl_debug_state (); + if (unload_global) + { + /* Some objects are in the global scope list. Remove them. */ + struct r_scope_elem *ns_msl = ns->_ns_main_searchlist; + unsigned int i; + unsigned int j = 0; + unsigned int cnt = ns_msl->r_nlist; + + while (cnt > 0 && ns_msl->r_list[cnt - 1]->l_removed) + --cnt; + + if (cnt + unload_global == ns_msl->r_nlist) + /* Speed up removing most recently added objects. */ + j = cnt; + else + for (i = 0; i < cnt; i++) + if (ns_msl->r_list[i]->l_removed == 0) + { + if (i != j) + ns_msl->r_list[j] = ns_msl->r_list[i]; + j++; + } + ns_msl->r_nlist = j; + } + + if (!RTLD_SINGLE_THREAD_P + && (unload_global + || scope_mem_left + || (GL(dl_scope_free_list) != NULL + && GL(dl_scope_free_list)->count))) + { + THREAD_GSCOPE_WAIT (); + + /* Now we can free any queued old scopes. */ + struct dl_scope_free_list *fsl = GL(dl_scope_free_list); + if (fsl != NULL) + while (fsl->count > 0) + free (fsl->list[--fsl->count]); + } + + size_t tls_free_start; + size_t tls_free_end; + tls_free_start = tls_free_end = NO_TLS_OFFSET; + + /* We modify the list of loaded objects. */ + __rtld_lock_lock_recursive (GL(dl_load_write_lock)); + /* Check each element of the search list to see if all references to it are gone. */ - for (i = 0; i < nsearchlist; ++i) + for (unsigned int i = first_loaded; i < nloaded; ++i) { - struct link_map *imap = list[i]; - if (imap->l_opencount == 0 && imap->l_type == lt_loaded) + struct link_map *imap = maps[i]; + if (!used[i]) { - struct libname_list *lnp; + assert (imap->l_type == lt_loaded); /* That was the last reference, and this was a dlopen-loaded object. We can unmap it. */ - if (__builtin_expect (imap->l_global, 0)) - { - /* This object is in the global scope list. Remove it. */ - int cnt = _dl_main_searchlist->r_nlist; - - do - --cnt; - while (_dl_main_searchlist->r_list[cnt] != imap); - /* The object was already correctly registered. */ - while (++cnt < _dl_main_searchlist->r_nlist) - _dl_main_searchlist->r_list[cnt - 1] - = _dl_main_searchlist->r_list[cnt]; - - --_dl_main_searchlist->r_nlist; + /* Remove the object from the dtv slotinfo array if it uses TLS. */ + if (__builtin_expect (imap->l_tls_blocksize > 0, 0)) + { + any_tls = true; + + if (GL(dl_tls_dtv_slotinfo_list) != NULL + && ! remove_slotinfo (imap->l_tls_modid, + GL(dl_tls_dtv_slotinfo_list), 0, + imap->l_init_called)) + /* All dynamically loaded modules with TLS are unloaded. */ + GL(dl_tls_max_dtv_idx) = GL(dl_tls_static_nelem); + + if (imap->l_tls_offset != NO_TLS_OFFSET + && imap->l_tls_offset != FORCED_DYNAMIC_TLS_OFFSET) + { + /* Collect a contiguous chunk built from the objects in + this search list, going in either direction. When the + whole chunk is at the end of the used area then we can + reclaim it. */ +#if TLS_TCB_AT_TP + if (tls_free_start == NO_TLS_OFFSET + || (size_t) imap->l_tls_offset == tls_free_start) + { + /* Extend the contiguous chunk being reclaimed. */ + tls_free_start + = imap->l_tls_offset - imap->l_tls_blocksize; + + if (tls_free_end == NO_TLS_OFFSET) + tls_free_end = imap->l_tls_offset; + } + else if (imap->l_tls_offset - imap->l_tls_blocksize + == tls_free_end) + /* Extend the chunk backwards. */ + tls_free_end = imap->l_tls_offset; + else + { + /* This isn't contiguous with the last chunk freed. + One of them will be leaked unless we can free + one block right away. */ + if (tls_free_end == GL(dl_tls_static_used)) + { + GL(dl_tls_static_used) = tls_free_start; + tls_free_end = imap->l_tls_offset; + tls_free_start + = tls_free_end - imap->l_tls_blocksize; + } + else if ((size_t) imap->l_tls_offset + == GL(dl_tls_static_used)) + GL(dl_tls_static_used) + = imap->l_tls_offset - imap->l_tls_blocksize; + else if (tls_free_end < (size_t) imap->l_tls_offset) + { + /* We pick the later block. It has a chance to + be freed. */ + tls_free_end = imap->l_tls_offset; + tls_free_start + = tls_free_end - imap->l_tls_blocksize; + } + } +#elif TLS_DTV_AT_TP + if ((size_t) imap->l_tls_offset == tls_free_end) + /* Extend the contiguous chunk being reclaimed. */ + tls_free_end -= imap->l_tls_blocksize; + else if (imap->l_tls_offset + imap->l_tls_blocksize + == tls_free_start) + /* Extend the chunk backwards. */ + tls_free_start = imap->l_tls_offset; + else + { + /* This isn't contiguous with the last chunk freed. + One of them will be leaked. */ + if (tls_free_end == GL(dl_tls_static_used)) + GL(dl_tls_static_used) = tls_free_start; + tls_free_start = imap->l_tls_offset; + tls_free_end = tls_free_start + imap->l_tls_blocksize; + } +#else +# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" +#endif + } } /* We can unmap all the maps at once. We determined the @@ -201,41 +619,51 @@ _dl_close (void *_map) DL_UNMAP (imap); /* Finally, unlink the data structure and free it. */ -#ifdef SHARED - /* We will unlink the first object only if this is a statically - linked program. */ - assert (imap->l_prev != NULL); - imap->l_prev->l_next = imap->l_next; -#else if (imap->l_prev != NULL) imap->l_prev->l_next = imap->l_next; else - _dl_loaded = imap->l_next; + { +#ifdef SHARED + assert (nsid != LM_ID_BASE); #endif - --_dl_nloaded; - if (imap->l_next) + ns->_ns_loaded = imap->l_next; + } + + --ns->_ns_nloaded; + if (imap->l_next != NULL) imap->l_next->l_prev = imap->l_prev; - if (imap->l_versions != NULL) - free (imap->l_versions); - if (imap->l_origin != NULL && imap->l_origin != (char *) -1) + free (imap->l_versions); + if (imap->l_origin != (char *) -1) free ((char *) imap->l_origin); + free (imap->l_reldeps); + + /* Print debugging message. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) + _dl_debug_printf ("\nfile=%s [%lu]; destroying link map\n", + imap->l_name, imap->l_ns); + /* This name always is allocated. */ free (imap->l_name); /* Remove the list with all the names of the shared object. */ - lnp = imap->l_libname; + + struct libname_list *lnp = imap->l_libname; do { struct libname_list *this = lnp; lnp = lnp->next; - free (this); + if (!this->dont_free) + free (this); } while (lnp != NULL); /* Remove the searchlists. */ - if (imap != map) - free (imap->l_initfini); + free (imap->l_initfini); + + /* Remove the scope array if we allocated it. */ + if (imap->l_scope != imap->l_scope_mem) + free (imap->l_scope); if (imap->l_phdr_allocated) free ((void *) imap->l_phdr); @@ -249,38 +677,83 @@ _dl_close (void *_map) } } - /* Now we can perhaps also remove the modules for which we had - dependencies because of symbol lookup. */ - if (__builtin_expect (rellist != NULL, 0)) + __rtld_lock_unlock_recursive (GL(dl_load_write_lock)); + + /* If we removed any object which uses TLS bump the generation counter. */ + if (any_tls) { - while (nrellist-- > 0) - _dl_close (rellist[nrellist]); + if (__builtin_expect (++GL(dl_tls_generation) == 0, 0)) + _dl_fatal_printf ("TLS generation counter wrapped! Please report as described in .\n"); - free (rellist); + if (tls_free_end == GL(dl_tls_static_used)) + GL(dl_tls_static_used) = tls_free_start; } - free (list); - - if (__builtin_expect (_dl_global_scope_alloc, 0) != 0 - && _dl_main_searchlist->r_nlist == _dl_initial_searchlist.r_nlist) +#ifdef SHARED + /* Auditing checkpoint: we have deleted all objects. */ + if (__builtin_expect (do_audit, 0)) { - /* All object dynamically loaded by the program are unloaded. Free - the memory allocated for the global scope variable. */ - struct link_map **old = _dl_main_searchlist->r_list; - - /* Put the old map in. */ - _dl_main_searchlist->r_list = _dl_initial_searchlist.r_list; - /* Signal that the original map is used. */ - _dl_global_scope_alloc = 0; + struct link_map *head = ns->_ns_loaded; + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT); - /* Now free the old map. */ - free (old); + afct = afct->next; + } + } } +#endif + + if (__builtin_expect (ns->_ns_loaded == NULL, 0) + && nsid == GL(dl_nns) - 1) + do + { + --GL(dl_nns); +#ifndef SHARED + if (GL(dl_nns) == 0) + break; +#endif + } + while (GL(dl_ns)[GL(dl_nns) - 1]._ns_loaded == NULL); /* Notify the debugger those objects are finalized and gone. */ - _r_debug.r_state = RT_CONSISTENT; + r->r_state = RT_CONSISTENT; _dl_debug_state (); - /* Release the lock. */ - __libc_lock_unlock (_dl_load_lock); + /* Recheck if we need to retry, release the lock. */ + out: + if (dl_close_state == rerun) + goto retry; + + dl_close_state = not_pending; +} + + +void +_dl_close (void *_map) +{ + struct link_map *map = _map; + + /* First see whether we can remove the object at all. */ + if (__builtin_expect (map->l_flags_1 & DF_1_NODELETE, 0)) + { + assert (map->l_init_called); + /* Nope. Do nothing. */ + return; + } + + if (__builtin_expect (map->l_direct_opencount, 1) == 0) + GLRO(dl_signal_error) (0, map->l_name, NULL, N_("shared object not open")); + + /* Acquire the lock. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + _dl_close_worker (map); + + __rtld_lock_unlock_recursive (GL(dl_load_lock)); }