Blame elf/dl-close.c

Packit 6c4009
/* Close a shared object opened by `_dl_open'.
Packit 6c4009
   Copyright (C) 1996-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <dlfcn.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <stddef.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <libc-lock.h>
Packit 6c4009
#include <ldsodefs.h>
Packit 6c4009
#include <sys/types.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <sysdep-cancel.h>
Packit 6c4009
#include <tls.h>
Packit 6c4009
#include <stap-probe.h>
Packit 6c4009
Packit 6c4009
#include <dl-unmap-segments.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Type of the constructor functions.  */
Packit 6c4009
typedef void (*fini_t) (void);
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Special l_idx value used to indicate which objects remain loaded.  */
Packit 6c4009
#define IDX_STILL_USED -1
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Returns true we an non-empty was found.  */
Packit 6c4009
static bool
Packit 6c4009
remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp,
Packit 6c4009
		 bool should_be_there)
Packit 6c4009
{
Packit 6c4009
  if (idx - disp >= listp->len)
Packit 6c4009
    {
Packit 6c4009
      if (listp->next == NULL)
Packit 6c4009
	{
Packit 6c4009
	  /* The index is not actually valid in the slotinfo list,
Packit 6c4009
	     because this object was closed before it was fully set
Packit 6c4009
	     up due to some error.  */
Packit 6c4009
	  assert (! should_be_there);
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  if (remove_slotinfo (idx, listp->next, disp + listp->len,
Packit 6c4009
			       should_be_there))
Packit 6c4009
	    return true;
Packit 6c4009
Packit 6c4009
	  /* No non-empty entry.  Search from the end of this element's
Packit 6c4009
	     slotinfo array.  */
Packit 6c4009
	  idx = disp + listp->len;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      struct link_map *old_map = listp->slotinfo[idx - disp].map;
Packit 6c4009
Packit 6c4009
      /* The entry might still be in its unused state if we are closing an
Packit 6c4009
	 object that wasn't fully set up.  */
Packit 6c4009
      if (__glibc_likely (old_map != NULL))
Packit 6c4009
	{
Packit 6c4009
	  assert (old_map->l_tls_modid == idx);
Packit 6c4009
Packit 6c4009
	  /* Mark the entry as unused. */
Packit 6c4009
	  listp->slotinfo[idx - disp].gen = GL(dl_tls_generation) + 1;
Packit 6c4009
	  listp->slotinfo[idx - disp].map = NULL;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* If this is not the last currently used entry no need to look
Packit 6c4009
	 further.  */
Packit 6c4009
      if (idx != GL(dl_tls_max_dtv_idx))
Packit 6c4009
	return true;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  while (idx - disp > (disp == 0 ? 1 + GL(dl_tls_static_nelem) : 0))
Packit 6c4009
    {
Packit 6c4009
      --idx;
Packit 6c4009
Packit 6c4009
      if (listp->slotinfo[idx - disp].map != NULL)
Packit 6c4009
	{
Packit 6c4009
	  /* Found a new last used index.  */
Packit 6c4009
	  GL(dl_tls_max_dtv_idx) = idx;
Packit 6c4009
	  return true;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* No non-entry in this list element.  */
Packit 6c4009
  return false;
Packit 6c4009
}
Packit 6c4009
Packit Service 5c587e
/* Invoke dstructors for CLOSURE (a struct link_map *).  Called with
Packit Service 5c587e
   exception handling temporarily disabled, to make errors fatal.  */
Packit Service 5c587e
static void
Packit Service 5c587e
call_destructors (void *closure)
Packit Service 5c587e
{
Packit Service 5c587e
  struct link_map *map = closure;
Packit Service 5c587e
Packit Service 5c587e
  if (map->l_info[DT_FINI_ARRAY] != NULL)
Packit Service 5c587e
    {
Packit Service 5c587e
      ElfW(Addr) *array =
Packit Service 5c587e
	(ElfW(Addr) *) (map->l_addr
Packit Service 5c587e
			+ map->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
Packit Service 5c587e
      unsigned int sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
Packit Service 5c587e
			 / sizeof (ElfW(Addr)));
Packit Service 5c587e
Packit Service 5c587e
      while (sz-- > 0)
Packit Service 5c587e
	((fini_t) array[sz]) ();
Packit Service 5c587e
    }
Packit Service 5c587e
Packit Service 5c587e
  /* Next try the old-style destructor.  */
Packit Service 5c587e
  if (map->l_info[DT_FINI] != NULL)
Packit Service 5c587e
    DL_CALL_DT_FINI (map, ((void *) map->l_addr
Packit Service 5c587e
			   + map->l_info[DT_FINI]->d_un.d_ptr));
Packit Service 5c587e
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_dl_close_worker (struct link_map *map, bool force)
Packit 6c4009
{
Packit 6c4009
  /* One less direct use.  */
Packit 6c4009
  --map->l_direct_opencount;
Packit 6c4009
Packit 6c4009
  /* If _dl_close is called recursively (some destructor call dlclose),
Packit 6c4009
     just record that the parent _dl_close will need to do garbage collection
Packit 6c4009
     again and return.  */
Packit 6c4009
  static enum { not_pending, pending, rerun } dl_close_state;
Packit 6c4009
Packit 6c4009
  if (map->l_direct_opencount > 0 || map->l_type != lt_loaded
Packit 6c4009
      || dl_close_state != not_pending)
Packit 6c4009
    {
Packit 6c4009
      if (map->l_direct_opencount == 0 && map->l_type == lt_loaded)
Packit 6c4009
	dl_close_state = rerun;
Packit 6c4009
Packit 6c4009
      /* There are still references to this object.  Do nothing more.  */
Packit 6c4009
      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
Packit 6c4009
	_dl_debug_printf ("\nclosing file=%s; direct_opencount=%u\n",
Packit 6c4009
			  map->l_name, map->l_direct_opencount);
Packit 6c4009
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  Lmid_t nsid = map->l_ns;
Packit 6c4009
  struct link_namespaces *ns = &GL(dl_ns)[nsid];
Packit 6c4009
Packit 6c4009
 retry:
Packit 6c4009
  dl_close_state = pending;
Packit 6c4009
Packit 6c4009
  bool any_tls = false;
Packit 6c4009
  const unsigned int nloaded = ns->_ns_nloaded;
Packit 6c4009
  char used[nloaded];
Packit 6c4009
  char done[nloaded];
Packit 6c4009
  struct link_map *maps[nloaded];
Packit 6c4009
Packit 6c4009
  /* Run over the list and assign indexes to the link maps and enter
Packit 6c4009
     them into the MAPS array.  */
Packit 6c4009
  int idx = 0;
Packit 6c4009
  for (struct link_map *l = ns->_ns_loaded; l != NULL; l = l->l_next)
Packit 6c4009
    {
Packit 6c4009
      l->l_idx = idx;
Packit 6c4009
      maps[idx] = l;
Packit 6c4009
      ++idx;
Packit 6c4009
Packit 6c4009
    }
Packit 6c4009
  assert (idx == nloaded);
Packit 6c4009
Packit 6c4009
  /* Prepare the bitmaps.  */
Packit 6c4009
  memset (used, '\0', sizeof (used));
Packit 6c4009
  memset (done, '\0', sizeof (done));
Packit 6c4009
Packit 6c4009
  /* Keep track of the lowest index link map we have covered already.  */
Packit 6c4009
  int done_index = -1;
Packit 6c4009
  while (++done_index < nloaded)
Packit 6c4009
    {
Packit 6c4009
      struct link_map *l = maps[done_index];
Packit 6c4009
Packit 6c4009
      if (done[done_index])
Packit 6c4009
	/* Already handled.  */
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      /* Check whether this object is still used.  */
Packit 6c4009
      if (l->l_type == lt_loaded
Packit 6c4009
	  && l->l_direct_opencount == 0
Packit Service 4d38ce
	  && !l->l_nodelete_active
Packit 6c4009
	  /* See CONCURRENCY NOTES in cxa_thread_atexit_impl.c to know why
Packit 6c4009
	     acquire is sufficient and correct.  */
Packit 6c4009
	  && atomic_load_acquire (&l->l_tls_dtor_count) == 0
Packit 6c4009
	  && !used[done_index])
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      /* We need this object and we handle it now.  */
Packit 6c4009
      done[done_index] = 1;
Packit 6c4009
      used[done_index] = 1;
Packit 6c4009
      /* Signal the object is still needed.  */
Packit 6c4009
      l->l_idx = IDX_STILL_USED;
Packit 6c4009
Packit 6c4009
      /* Mark all dependencies as used.  */
Packit 6c4009
      if (l->l_initfini != NULL)
Packit 6c4009
	{
Packit 6c4009
	  /* We are always the zeroth entry, and since we don't include
Packit 6c4009
	     ourselves in the dependency analysis start at 1.  */
Packit 6c4009
	  struct link_map **lp = &l->l_initfini[1];
Packit 6c4009
	  while (*lp != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      if ((*lp)->l_idx != IDX_STILL_USED)
Packit 6c4009
		{
Packit 6c4009
		  assert ((*lp)->l_idx >= 0 && (*lp)->l_idx < nloaded);
Packit 6c4009
Packit 6c4009
		  if (!used[(*lp)->l_idx])
Packit 6c4009
		    {
Packit 6c4009
		      used[(*lp)->l_idx] = 1;
Packit 6c4009
		      /* If we marked a new object as used, and we've
Packit 6c4009
			 already processed it, then we need to go back
Packit 6c4009
			 and process again from that point forward to
Packit 6c4009
			 ensure we keep all of its dependencies also.  */
Packit 6c4009
		      if ((*lp)->l_idx - 1 < done_index)
Packit 6c4009
			done_index = (*lp)->l_idx - 1;
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      ++lp;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
      /* And the same for relocation dependencies.  */
Packit 6c4009
      if (l->l_reldeps != NULL)
Packit 6c4009
	for (unsigned int j = 0; j < l->l_reldeps->act; ++j)
Packit 6c4009
	  {
Packit 6c4009
	    struct link_map *jmap = l->l_reldeps->list[j];
Packit 6c4009
Packit 6c4009
	    if (jmap->l_idx != IDX_STILL_USED)
Packit 6c4009
	      {
Packit 6c4009
		assert (jmap->l_idx >= 0 && jmap->l_idx < nloaded);
Packit 6c4009
Packit 6c4009
		if (!used[jmap->l_idx])
Packit 6c4009
		  {
Packit 6c4009
		    used[jmap->l_idx] = 1;
Packit 6c4009
		    if (jmap->l_idx - 1 < done_index)
Packit 6c4009
		      done_index = jmap->l_idx - 1;
Packit 6c4009
		  }
Packit 6c4009
	      }
Packit 6c4009
	  }
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Sort the entries.  We can skip looking for the binary itself which is
Packit 6c4009
     at the front of the search list for the main namespace.  */
Packit 6c4009
  _dl_sort_maps (maps + (nsid == LM_ID_BASE), nloaded - (nsid == LM_ID_BASE),
Packit 6c4009
		 used + (nsid == LM_ID_BASE), true);
Packit 6c4009
Packit 6c4009
  /* Call all termination functions at once.  */
Packit 6c4009
#ifdef SHARED
Packit 6c4009
  bool do_audit = GLRO(dl_naudit) > 0 && !ns->_ns_loaded->l_auditing;
Packit 6c4009
#endif
Packit 6c4009
  bool unload_any = false;
Packit 6c4009
  bool scope_mem_left = false;
Packit 6c4009
  unsigned int unload_global = 0;
Packit 6c4009
  unsigned int first_loaded = ~0;
Packit 6c4009
  for (unsigned int i = 0; i < nloaded; ++i)
Packit 6c4009
    {
Packit 6c4009
      struct link_map *imap = maps[i];
Packit 6c4009
Packit 6c4009
      /* All elements must be in the same namespace.  */
Packit 6c4009
      assert (imap->l_ns == nsid);
Packit 6c4009
Packit 6c4009
      if (!used[i])
Packit 6c4009
	{
Packit Service 4d38ce
	  assert (imap->l_type == lt_loaded && !imap->l_nodelete_active);
Packit 6c4009
Packit 6c4009
	  /* Call its termination function.  Do not do it for
Packit Service 5c587e
	     half-cooked objects.  Temporarily disable exception
Packit Service 5c587e
	     handling, so that errors are fatal.  */
Packit 6c4009
	  if (imap->l_init_called)
Packit 6c4009
	    {
Packit 6c4009
	      /* When debugging print a message first.  */
Packit 6c4009
	      if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS,
Packit 6c4009
				    0))
Packit 6c4009
		_dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
Packit 6c4009
				  imap->l_name, nsid);
Packit 6c4009
Packit Service 5c587e
	      if (imap->l_info[DT_FINI_ARRAY] != NULL
Packit Service 5c587e
		  || imap->l_info[DT_FINI] != NULL)
Packit Service 5c587e
		_dl_catch_exception (NULL, call_destructors, imap);
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
#ifdef SHARED
Packit 6c4009
	  /* Auditing checkpoint: we remove an object.  */
Packit 6c4009
	  if (__glibc_unlikely (do_audit))
Packit 6c4009
	    {
Packit 6c4009
	      struct audit_ifaces *afct = GLRO(dl_audit);
Packit 6c4009
	      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
Packit 6c4009
		{
Packit 6c4009
		  if (afct->objclose != NULL)
Packit Service 050912
		    {
Packit Service 050912
		      struct auditstate *state
Packit Service 050912
			= link_map_audit_state (imap, cnt);
Packit Service 050912
		      /* Return value is ignored.  */
Packit Service 050912
		      (void) afct->objclose (&state->cookie);
Packit Service 050912
		    }
Packit 6c4009
Packit 6c4009
		  afct = afct->next;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
	  /* This object must not be used anymore.  */
Packit 6c4009
	  imap->l_removed = 1;
Packit 6c4009
Packit 6c4009
	  /* We indeed have an object to remove.  */
Packit 6c4009
	  unload_any = true;
Packit 6c4009
Packit 6c4009
	  if (imap->l_global)
Packit 6c4009
	    ++unload_global;
Packit 6c4009
Packit 6c4009
	  /* Remember where the first dynamically loaded object is.  */
Packit 6c4009
	  if (i < first_loaded)
Packit 6c4009
	    first_loaded = i;
Packit 6c4009
	}
Packit 6c4009
      /* Else used[i].  */
Packit 6c4009
      else if (imap->l_type == lt_loaded)
Packit 6c4009
	{
Packit 6c4009
	  struct r_scope_elem *new_list = NULL;
Packit 6c4009
Packit 6c4009
	  if (imap->l_searchlist.r_list == NULL && imap->l_initfini != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      /* The object is still used.  But one of the objects we are
Packit 6c4009
		 unloading right now is responsible for loading it.  If
Packit 6c4009
		 the current object does not have it's own scope yet we
Packit 6c4009
		 have to create one.  This has to be done before running
Packit 6c4009
		 the finalizers.
Packit 6c4009
Packit 6c4009
		 To do this count the number of dependencies.  */
Packit 6c4009
	      unsigned int cnt;
Packit 6c4009
	      for (cnt = 1; imap->l_initfini[cnt] != NULL; ++cnt)
Packit 6c4009
		;
Packit 6c4009
Packit 6c4009
	      /* We simply reuse the l_initfini list.  */
Packit 6c4009
	      imap->l_searchlist.r_list = &imap->l_initfini[cnt + 1];
Packit 6c4009
	      imap->l_searchlist.r_nlist = cnt;
Packit 6c4009
Packit 6c4009
	      new_list = &imap->l_searchlist;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Count the number of scopes which remain after the unload.
Packit 6c4009
	     When we add the local search list count it.  Always add
Packit 6c4009
	     one for the terminating NULL pointer.  */
Packit 6c4009
	  size_t remain = (new_list != NULL) + 1;
Packit 6c4009
	  bool removed_any = false;
Packit 6c4009
	  for (size_t cnt = 0; imap->l_scope[cnt] != NULL; ++cnt)
Packit 6c4009
	    /* This relies on l_scope[] entries being always set either
Packit 6c4009
	       to its own l_symbolic_searchlist address, or some map's
Packit 6c4009
	       l_searchlist address.  */
Packit 6c4009
	    if (imap->l_scope[cnt] != &imap->l_symbolic_searchlist)
Packit 6c4009
	      {
Packit 6c4009
		struct link_map *tmap = (struct link_map *)
Packit 6c4009
		  ((char *) imap->l_scope[cnt]
Packit 6c4009
		   - offsetof (struct link_map, l_searchlist));
Packit 6c4009
		assert (tmap->l_ns == nsid);
Packit 6c4009
		if (tmap->l_idx == IDX_STILL_USED)
Packit 6c4009
		  ++remain;
Packit 6c4009
		else
Packit 6c4009
		  removed_any = true;
Packit 6c4009
	      }
Packit 6c4009
	    else
Packit 6c4009
	      ++remain;
Packit 6c4009
Packit 6c4009
	  if (removed_any)
Packit 6c4009
	    {
Packit 6c4009
	      /* Always allocate a new array for the scope.  This is
Packit 6c4009
		 necessary since we must be able to determine the last
Packit 6c4009
		 user of the current array.  If possible use the link map's
Packit 6c4009
		 memory.  */
Packit 6c4009
	      size_t new_size;
Packit 6c4009
	      struct r_scope_elem **newp;
Packit 6c4009
Packit 6c4009
#define SCOPE_ELEMS(imap) \
Packit 6c4009
  (sizeof (imap->l_scope_mem) / sizeof (imap->l_scope_mem[0]))
Packit 6c4009
Packit 6c4009
	      if (imap->l_scope != imap->l_scope_mem
Packit 6c4009
		  && remain < SCOPE_ELEMS (imap))
Packit 6c4009
		{
Packit 6c4009
		  new_size = SCOPE_ELEMS (imap);
Packit 6c4009
		  newp = imap->l_scope_mem;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  new_size = imap->l_scope_max;
Packit 6c4009
		  newp = (struct r_scope_elem **)
Packit 6c4009
		    malloc (new_size * sizeof (struct r_scope_elem *));
Packit 6c4009
		  if (newp == NULL)
Packit 6c4009
		    _dl_signal_error (ENOMEM, "dlclose", NULL,
Packit 6c4009
				      N_("cannot create scope list"));
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      /* Copy over the remaining scope elements.  */
Packit 6c4009
	      remain = 0;
Packit 6c4009
	      for (size_t cnt = 0; imap->l_scope[cnt] != NULL; ++cnt)
Packit 6c4009
		{
Packit 6c4009
		  if (imap->l_scope[cnt] != &imap->l_symbolic_searchlist)
Packit 6c4009
		    {
Packit 6c4009
		      struct link_map *tmap = (struct link_map *)
Packit 6c4009
			((char *) imap->l_scope[cnt]
Packit 6c4009
			 - offsetof (struct link_map, l_searchlist));
Packit 6c4009
		      if (tmap->l_idx != IDX_STILL_USED)
Packit 6c4009
			{
Packit 6c4009
			  /* Remove the scope.  Or replace with own map's
Packit 6c4009
			     scope.  */
Packit 6c4009
			  if (new_list != NULL)
Packit 6c4009
			    {
Packit 6c4009
			      newp[remain++] = new_list;
Packit 6c4009
			      new_list = NULL;
Packit 6c4009
			    }
Packit 6c4009
			  continue;
Packit 6c4009
			}
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  newp[remain++] = imap->l_scope[cnt];
Packit 6c4009
		}
Packit 6c4009
	      newp[remain] = NULL;
Packit 6c4009
Packit 6c4009
	      struct r_scope_elem **old = imap->l_scope;
Packit 6c4009
Packit 6c4009
	      imap->l_scope = newp;
Packit 6c4009
Packit 6c4009
	      /* No user anymore, we can free it now.  */
Packit 6c4009
	      if (old != imap->l_scope_mem)
Packit 6c4009
		{
Packit 6c4009
		  if (_dl_scope_free (old))
Packit 6c4009
		    /* If _dl_scope_free used THREAD_GSCOPE_WAIT (),
Packit 6c4009
		       no need to repeat it.  */
Packit 6c4009
		    scope_mem_left = false;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		scope_mem_left = true;
Packit 6c4009
Packit 6c4009
	      imap->l_scope_max = new_size;
Packit 6c4009
	    }
Packit 6c4009
	  else if (new_list != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      /* We didn't change the scope array, so reset the search
Packit 6c4009
		 list.  */
Packit 6c4009
	      imap->l_searchlist.r_list = NULL;
Packit 6c4009
	      imap->l_searchlist.r_nlist = 0;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* The loader is gone, so mark the object as not having one.
Packit 6c4009
	     Note: l_idx != IDX_STILL_USED -> object will be removed.  */
Packit 6c4009
	  if (imap->l_loader != NULL
Packit 6c4009
	      && imap->l_loader->l_idx != IDX_STILL_USED)
Packit 6c4009
	    imap->l_loader = NULL;
Packit 6c4009
Packit 6c4009
	  /* Remember where the first dynamically loaded object is.  */
Packit 6c4009
	  if (i < first_loaded)
Packit 6c4009
	    first_loaded = i;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If there are no objects to unload, do nothing further.  */
Packit 6c4009
  if (!unload_any)
Packit 6c4009
    goto out;
Packit 6c4009
Packit 6c4009
#ifdef SHARED
Packit 6c4009
  /* Auditing checkpoint: we will start deleting objects.  */
Packit 6c4009
  if (__glibc_unlikely (do_audit))
Packit 6c4009
    {
Packit 6c4009
      struct link_map *head = ns->_ns_loaded;
Packit 6c4009
      struct audit_ifaces *afct = GLRO(dl_audit);
Packit 6c4009
      /* Do not call the functions for any auditing object.  */
Packit 6c4009
      if (head->l_auditing == 0)
Packit 6c4009
	{
Packit 6c4009
	  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
Packit 6c4009
	    {
Packit 6c4009
	      if (afct->activity != NULL)
Packit Service 050912
		{
Packit Service 050912
		  struct auditstate *state = link_map_audit_state (head, cnt);
Packit Service 050912
		  afct->activity (&state->cookie, LA_ACT_DELETE);
Packit Service 050912
		}
Packit 6c4009
Packit 6c4009
	      afct = afct->next;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* Notify the debugger we are about to remove some loaded objects.  */
Packit 6c4009
  struct r_debug *r = _dl_debug_initialize (0, nsid);
Packit 6c4009
  r->r_state = RT_DELETE;
Packit 6c4009
  _dl_debug_state ();
Packit 6c4009
  LIBC_PROBE (unmap_start, 2, nsid, r);
Packit 6c4009
Packit 6c4009
  if (unload_global)
Packit 6c4009
    {
Packit 6c4009
      /* Some objects are in the global scope list.  Remove them.  */
Packit 6c4009
      struct r_scope_elem *ns_msl = ns->_ns_main_searchlist;
Packit 6c4009
      unsigned int i;
Packit 6c4009
      unsigned int j = 0;
Packit 6c4009
      unsigned int cnt = ns_msl->r_nlist;
Packit 6c4009
Packit 6c4009
      while (cnt > 0 && ns_msl->r_list[cnt - 1]->l_removed)
Packit 6c4009
	--cnt;
Packit 6c4009
Packit 6c4009
      if (cnt + unload_global == ns_msl->r_nlist)
Packit 6c4009
	/* Speed up removing most recently added objects.  */
Packit 6c4009
	j = cnt;
Packit 6c4009
      else
Packit 6c4009
	for (i = 0; i < cnt; i++)
Packit 6c4009
	  if (ns_msl->r_list[i]->l_removed == 0)
Packit 6c4009
	    {
Packit 6c4009
	      if (i != j)
Packit 6c4009
		ns_msl->r_list[j] = ns_msl->r_list[i];
Packit 6c4009
	      j++;
Packit 6c4009
	    }
Packit 6c4009
      ns_msl->r_nlist = j;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (!RTLD_SINGLE_THREAD_P
Packit 6c4009
      && (unload_global
Packit 6c4009
	  || scope_mem_left
Packit 6c4009
	  || (GL(dl_scope_free_list) != NULL
Packit 6c4009
	      && GL(dl_scope_free_list)->count)))
Packit 6c4009
    {
Packit 6c4009
      THREAD_GSCOPE_WAIT ();
Packit 6c4009
Packit 6c4009
      /* Now we can free any queued old scopes.  */
Packit 6c4009
      struct dl_scope_free_list *fsl = GL(dl_scope_free_list);
Packit 6c4009
      if (fsl != NULL)
Packit 6c4009
	while (fsl->count > 0)
Packit 6c4009
	  free (fsl->list[--fsl->count]);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  size_t tls_free_start;
Packit 6c4009
  size_t tls_free_end;
Packit 6c4009
  tls_free_start = tls_free_end = NO_TLS_OFFSET;
Packit 6c4009
Packit 6c4009
  /* We modify the list of loaded objects.  */
Packit 6c4009
  __rtld_lock_lock_recursive (GL(dl_load_write_lock));
Packit 6c4009
Packit 6c4009
  /* Check each element of the search list to see if all references to
Packit 6c4009
     it are gone.  */
Packit 6c4009
  for (unsigned int i = first_loaded; i < nloaded; ++i)
Packit 6c4009
    {
Packit 6c4009
      struct link_map *imap = maps[i];
Packit 6c4009
      if (!used[i])
Packit 6c4009
	{
Packit 6c4009
	  assert (imap->l_type == lt_loaded);
Packit 6c4009
Packit 6c4009
	  /* That was the last reference, and this was a dlopen-loaded
Packit 6c4009
	     object.  We can unmap it.  */
Packit 6c4009
Packit 6c4009
	  /* Remove the object from the dtv slotinfo array if it uses TLS.  */
Packit 6c4009
	  if (__glibc_unlikely (imap->l_tls_blocksize > 0))
Packit 6c4009
	    {
Packit 6c4009
	      any_tls = true;
Packit 6c4009
Packit 6c4009
	      if (GL(dl_tls_dtv_slotinfo_list) != NULL
Packit 6c4009
		  && ! remove_slotinfo (imap->l_tls_modid,
Packit 6c4009
					GL(dl_tls_dtv_slotinfo_list), 0,
Packit 6c4009
					imap->l_init_called))
Packit 6c4009
		/* All dynamically loaded modules with TLS are unloaded.  */
Packit 6c4009
		GL(dl_tls_max_dtv_idx) = GL(dl_tls_static_nelem);
Packit 6c4009
Packit 6c4009
	      if (imap->l_tls_offset != NO_TLS_OFFSET
Packit 6c4009
		  && imap->l_tls_offset != FORCED_DYNAMIC_TLS_OFFSET)
Packit 6c4009
		{
Packit 6c4009
		  /* Collect a contiguous chunk built from the objects in
Packit 6c4009
		     this search list, going in either direction.  When the
Packit 6c4009
		     whole chunk is at the end of the used area then we can
Packit 6c4009
		     reclaim it.  */
Packit 6c4009
#if TLS_TCB_AT_TP
Packit 6c4009
		  if (tls_free_start == NO_TLS_OFFSET
Packit 6c4009
		      || (size_t) imap->l_tls_offset == tls_free_start)
Packit 6c4009
		    {
Packit 6c4009
		      /* Extend the contiguous chunk being reclaimed.  */
Packit 6c4009
		      tls_free_start
Packit 6c4009
			= imap->l_tls_offset - imap->l_tls_blocksize;
Packit 6c4009
Packit 6c4009
		      if (tls_free_end == NO_TLS_OFFSET)
Packit 6c4009
			tls_free_end = imap->l_tls_offset;
Packit 6c4009
		    }
Packit 6c4009
		  else if (imap->l_tls_offset - imap->l_tls_blocksize
Packit 6c4009
			   == tls_free_end)
Packit 6c4009
		    /* Extend the chunk backwards.  */
Packit 6c4009
		    tls_free_end = imap->l_tls_offset;
Packit 6c4009
		  else
Packit 6c4009
		    {
Packit 6c4009
		      /* This isn't contiguous with the last chunk freed.
Packit 6c4009
			 One of them will be leaked unless we can free
Packit 6c4009
			 one block right away.  */
Packit 6c4009
		      if (tls_free_end == GL(dl_tls_static_used))
Packit 6c4009
			{
Packit 6c4009
			  GL(dl_tls_static_used) = tls_free_start;
Packit 6c4009
			  tls_free_end = imap->l_tls_offset;
Packit 6c4009
			  tls_free_start
Packit 6c4009
			    = tls_free_end - imap->l_tls_blocksize;
Packit 6c4009
			}
Packit 6c4009
		      else if ((size_t) imap->l_tls_offset
Packit 6c4009
			       == GL(dl_tls_static_used))
Packit 6c4009
			GL(dl_tls_static_used)
Packit 6c4009
			  = imap->l_tls_offset - imap->l_tls_blocksize;
Packit 6c4009
		      else if (tls_free_end < (size_t) imap->l_tls_offset)
Packit 6c4009
			{
Packit 6c4009
			  /* We pick the later block.  It has a chance to
Packit 6c4009
			     be freed.  */
Packit 6c4009
			  tls_free_end = imap->l_tls_offset;
Packit 6c4009
			  tls_free_start
Packit 6c4009
			    = tls_free_end - imap->l_tls_blocksize;
Packit 6c4009
			}
Packit 6c4009
		    }
Packit 6c4009
#elif TLS_DTV_AT_TP
Packit 6c4009
		  if (tls_free_start == NO_TLS_OFFSET)
Packit 6c4009
		    {
Packit 6c4009
		      tls_free_start = imap->l_tls_firstbyte_offset;
Packit 6c4009
		      tls_free_end = (imap->l_tls_offset
Packit 6c4009
				      + imap->l_tls_blocksize);
Packit 6c4009
		    }
Packit 6c4009
		  else if (imap->l_tls_firstbyte_offset == tls_free_end)
Packit 6c4009
		    /* Extend the contiguous chunk being reclaimed.  */
Packit 6c4009
		    tls_free_end = imap->l_tls_offset + imap->l_tls_blocksize;
Packit 6c4009
		  else if (imap->l_tls_offset + imap->l_tls_blocksize
Packit 6c4009
			   == tls_free_start)
Packit 6c4009
		    /* Extend the chunk backwards.  */
Packit 6c4009
		    tls_free_start = imap->l_tls_firstbyte_offset;
Packit 6c4009
		  /* This isn't contiguous with the last chunk freed.
Packit 6c4009
		     One of them will be leaked unless we can free
Packit 6c4009
		     one block right away.  */
Packit 6c4009
		  else if (imap->l_tls_offset + imap->l_tls_blocksize
Packit 6c4009
			   == GL(dl_tls_static_used))
Packit 6c4009
		    GL(dl_tls_static_used) = imap->l_tls_firstbyte_offset;
Packit 6c4009
		  else if (tls_free_end == GL(dl_tls_static_used))
Packit 6c4009
		    {
Packit 6c4009
		      GL(dl_tls_static_used) = tls_free_start;
Packit 6c4009
		      tls_free_start = imap->l_tls_firstbyte_offset;
Packit 6c4009
		      tls_free_end = imap->l_tls_offset + imap->l_tls_blocksize;
Packit 6c4009
		    }
Packit 6c4009
		  else if (tls_free_end < imap->l_tls_firstbyte_offset)
Packit 6c4009
		    {
Packit 6c4009
		      /* We pick the later block.  It has a chance to
Packit 6c4009
			 be freed.  */
Packit 6c4009
		      tls_free_start = imap->l_tls_firstbyte_offset;
Packit 6c4009
		      tls_free_end = imap->l_tls_offset + imap->l_tls_blocksize;
Packit 6c4009
		    }
Packit 6c4009
#else
Packit 6c4009
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
Packit 6c4009
#endif
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Reset unique symbols if forced.  */
Packit 6c4009
	  if (force)
Packit 6c4009
	    {
Packit 6c4009
	      struct unique_sym_table *tab = &ns->_ns_unique_sym_table;
Packit 6c4009
	      __rtld_lock_lock_recursive (tab->lock);
Packit 6c4009
	      struct unique_sym *entries = tab->entries;
Packit 6c4009
	      if (entries != NULL)
Packit 6c4009
		{
Packit 6c4009
		  size_t idx, size = tab->size;
Packit 6c4009
		  for (idx = 0; idx < size; ++idx)
Packit 6c4009
		    {
Packit 6c4009
		      /* Clear unique symbol entries that belong to this
Packit 6c4009
			 object.  */
Packit 6c4009
		      if (entries[idx].name != NULL
Packit 6c4009
			  && entries[idx].map == imap)
Packit 6c4009
			{
Packit 6c4009
			  entries[idx].name = NULL;
Packit 6c4009
			  entries[idx].hashval = 0;
Packit 6c4009
			  tab->n_elements--;
Packit 6c4009
			}
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	      __rtld_lock_unlock_recursive (tab->lock);
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* We can unmap all the maps at once.  We determined the
Packit 6c4009
	     start address and length when we loaded the object and
Packit 6c4009
	     the `munmap' call does the rest.  */
Packit 6c4009
	  DL_UNMAP (imap);
Packit 6c4009
Packit 6c4009
	  /* Finally, unlink the data structure and free it.  */
Packit 6c4009
#if DL_NNS == 1
Packit 6c4009
	  /* The assert in the (imap->l_prev == NULL) case gives
Packit 6c4009
	     the compiler license to warn that NS points outside
Packit 6c4009
	     the dl_ns array bounds in that case (as nsid != LM_ID_BASE
Packit 6c4009
	     is tantamount to nsid >= DL_NNS).  That should be impossible
Packit 6c4009
	     in this configuration, so just assert about it instead.  */
Packit 6c4009
	  assert (nsid == LM_ID_BASE);
Packit 6c4009
	  assert (imap->l_prev != NULL);
Packit 6c4009
#else
Packit 6c4009
	  if (imap->l_prev == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      assert (nsid != LM_ID_BASE);
Packit 6c4009
	      ns->_ns_loaded = imap->l_next;
Packit 6c4009
Packit 6c4009
	      /* Update the pointer to the head of the list
Packit 6c4009
		 we leave for debuggers to examine.  */
Packit 6c4009
	      r->r_map = (void *) ns->_ns_loaded;
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
#endif
Packit 6c4009
	    imap->l_prev->l_next = imap->l_next;
Packit 6c4009
Packit 6c4009
	  --ns->_ns_nloaded;
Packit 6c4009
	  if (imap->l_next != NULL)
Packit 6c4009
	    imap->l_next->l_prev = imap->l_prev;
Packit 6c4009
Packit 6c4009
	  free (imap->l_versions);
Packit 6c4009
	  if (imap->l_origin != (char *) -1)
Packit 6c4009
	    free ((char *) imap->l_origin);
Packit 6c4009
Packit 6c4009
	  free (imap->l_reldeps);
Packit 6c4009
Packit 6c4009
	  /* Print debugging message.  */
Packit 6c4009
	  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
Packit 6c4009
	    _dl_debug_printf ("\nfile=%s [%lu];  destroying link map\n",
Packit 6c4009
			      imap->l_name, imap->l_ns);
Packit 6c4009
Packit 6c4009
	  /* This name always is allocated.  */
Packit 6c4009
	  free (imap->l_name);
Packit 6c4009
	  /* Remove the list with all the names of the shared object.  */
Packit 6c4009
Packit 6c4009
	  struct libname_list *lnp = imap->l_libname;
Packit 6c4009
	  do
Packit 6c4009
	    {
Packit 6c4009
	      struct libname_list *this = lnp;
Packit 6c4009
	      lnp = lnp->next;
Packit 6c4009
	      if (!this->dont_free)
Packit 6c4009
		free (this);
Packit 6c4009
	    }
Packit 6c4009
	  while (lnp != NULL);
Packit 6c4009
Packit 6c4009
	  /* Remove the searchlists.  */
Packit 6c4009
	  free (imap->l_initfini);
Packit 6c4009
Packit 6c4009
	  /* Remove the scope array if we allocated it.  */
Packit 6c4009
	  if (imap->l_scope != imap->l_scope_mem)
Packit 6c4009
	    free (imap->l_scope);
Packit 6c4009
Packit 6c4009
	  if (imap->l_phdr_allocated)
Packit 6c4009
	    free ((void *) imap->l_phdr);
Packit 6c4009
Packit 6c4009
	  if (imap->l_rpath_dirs.dirs != (void *) -1)
Packit 6c4009
	    free (imap->l_rpath_dirs.dirs);
Packit 6c4009
	  if (imap->l_runpath_dirs.dirs != (void *) -1)
Packit 6c4009
	    free (imap->l_runpath_dirs.dirs);
Packit 6c4009
Packit Service bfd320
	  /* Clear GL(dl_initfirst) when freeing its link_map memory.  */
Packit Service bfd320
	  if (imap == GL(dl_initfirst))
Packit Service bfd320
	    GL(dl_initfirst) = NULL;
Packit Service bfd320
Packit 6c4009
	  free (imap);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  __rtld_lock_unlock_recursive (GL(dl_load_write_lock));
Packit 6c4009
Packit 6c4009
  /* If we removed any object which uses TLS bump the generation counter.  */
Packit 6c4009
  if (any_tls)
Packit 6c4009
    {
Packit 6c4009
      if (__glibc_unlikely (++GL(dl_tls_generation) == 0))
Packit 6c4009
	_dl_fatal_printf ("TLS generation counter wrapped!  Please report as described in "REPORT_BUGS_TO".\n");
Packit 6c4009
Packit 6c4009
      if (tls_free_end == GL(dl_tls_static_used))
Packit 6c4009
	GL(dl_tls_static_used) = tls_free_start;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
#ifdef SHARED
Packit 6c4009
  /* Auditing checkpoint: we have deleted all objects.  */
Packit 6c4009
  if (__glibc_unlikely (do_audit))
Packit 6c4009
    {
Packit 6c4009
      struct link_map *head = ns->_ns_loaded;
Packit 6c4009
      /* Do not call the functions for any auditing object.  */
Packit 6c4009
      if (head->l_auditing == 0)
Packit 6c4009
	{
Packit 6c4009
	  struct audit_ifaces *afct = GLRO(dl_audit);
Packit 6c4009
	  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
Packit 6c4009
	    {
Packit 6c4009
	      if (afct->activity != NULL)
Packit Service 050912
		{
Packit Service 050912
		  struct auditstate *state = link_map_audit_state (head, cnt);
Packit Service 050912
		  afct->activity (&state->cookie, LA_ACT_CONSISTENT);
Packit Service 050912
		}
Packit 6c4009
Packit 6c4009
	      afct = afct->next;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  if (__builtin_expect (ns->_ns_loaded == NULL, 0)
Packit 6c4009
      && nsid == GL(dl_nns) - 1)
Packit 6c4009
    do
Packit 6c4009
      --GL(dl_nns);
Packit 6c4009
    while (GL(dl_ns)[GL(dl_nns) - 1]._ns_loaded == NULL);
Packit 6c4009
Packit 6c4009
  /* Notify the debugger those objects are finalized and gone.  */
Packit 6c4009
  r->r_state = RT_CONSISTENT;
Packit 6c4009
  _dl_debug_state ();
Packit 6c4009
  LIBC_PROBE (unmap_complete, 2, nsid, r);
Packit 6c4009
Packit 6c4009
  /* Recheck if we need to retry, release the lock.  */
Packit 6c4009
 out:
Packit 6c4009
  if (dl_close_state == rerun)
Packit 6c4009
    goto retry;
Packit 6c4009
Packit 6c4009
  dl_close_state = not_pending;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_dl_close (void *_map)
Packit 6c4009
{
Packit 6c4009
  struct link_map *map = _map;
Packit 6c4009
Packit 6c4009
  /* We must take the lock to examine the contents of map and avoid
Packit 6c4009
     concurrent dlopens.  */
Packit 6c4009
  __rtld_lock_lock_recursive (GL(dl_load_lock));
Packit 6c4009
Packit 6c4009
  /* At this point we are guaranteed nobody else is touching the list of
Packit 6c4009
     loaded maps, but a concurrent dlclose might have freed our map
Packit 6c4009
     before we took the lock. There is no way to detect this (see below)
Packit 6c4009
     so we proceed assuming this isn't the case.  First see whether we
Packit 6c4009
     can remove the object at all.  */
Packit Service 4d38ce
  if (__glibc_unlikely (map->l_nodelete_active))
Packit 6c4009
    {
Packit 6c4009
      /* Nope.  Do nothing.  */
Packit 6c4009
      __rtld_lock_unlock_recursive (GL(dl_load_lock));
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* At present this is an unreliable check except in the case where the
Packit 6c4009
     caller has recursively called dlclose and we are sure the link map
Packit 6c4009
     has not been freed.  In a non-recursive dlclose the map itself
Packit 6c4009
     might have been freed and this access is potentially a data race
Packit 6c4009
     with whatever other use this memory might have now, or worse we
Packit 6c4009
     might silently corrupt memory if it looks enough like a link map.
Packit 6c4009
     POSIX has language in dlclose that appears to guarantee that this
Packit 6c4009
     should be a detectable case and given that dlclose should be threadsafe
Packit 6c4009
     we need this to be a reliable detection.
Packit 6c4009
     This is bug 20990. */
Packit 6c4009
  if (__builtin_expect (map->l_direct_opencount, 1) == 0)
Packit 6c4009
    {
Packit 6c4009
      __rtld_lock_unlock_recursive (GL(dl_load_lock));
Packit 6c4009
      _dl_signal_error (0, map->l_name, NULL, N_("shared object not open"));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  _dl_close_worker (map, false);
Packit 6c4009
Packit 6c4009
  __rtld_lock_unlock_recursive (GL(dl_load_lock));
Packit 6c4009
}