Blame elf/dl-fini.c

Packit 6c4009
/* Call the termination functions of loaded shared objects.
Packit 6c4009
   Copyright (C) 1995-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 <string.h>
Packit 6c4009
#include <ldsodefs.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
void
Packit 6c4009
_dl_fini (void)
Packit 6c4009
{
Packit 6c4009
  /* Lots of fun ahead.  We have to call the destructors for all still
Packit 6c4009
     loaded objects, in all namespaces.  The problem is that the ELF
Packit 6c4009
     specification now demands that dependencies between the modules
Packit 6c4009
     are taken into account.  I.e., the destructor for a module is
Packit 6c4009
     called before the ones for any of its dependencies.
Packit 6c4009
Packit 6c4009
     To make things more complicated, we cannot simply use the reverse
Packit 6c4009
     order of the constructors.  Since the user might have loaded objects
Packit 6c4009
     using `dlopen' there are possibly several other modules with its
Packit 6c4009
     dependencies to be taken into account.  Therefore we have to start
Packit 6c4009
     determining the order of the modules once again from the beginning.  */
Packit 6c4009
Packit 6c4009
  /* We run the destructors of the main namespaces last.  As for the
Packit 6c4009
     other namespaces, we pick run the destructors in them in reverse
Packit 6c4009
     order of the namespace ID.  */
Packit 6c4009
#ifdef SHARED
Packit 6c4009
  int do_audit = 0;
Packit 6c4009
 again:
Packit 6c4009
#endif
Packit 6c4009
  for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns)
Packit 6c4009
    {
Packit 6c4009
      /* Protect against concurrent loads and unloads.  */
Packit 6c4009
      __rtld_lock_lock_recursive (GL(dl_load_lock));
Packit 6c4009
Packit 6c4009
      unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded;
Packit 6c4009
      /* No need to do anything for empty namespaces or those used for
Packit 6c4009
	 auditing DSOs.  */
Packit 6c4009
      if (nloaded == 0
Packit 6c4009
#ifdef SHARED
Packit 6c4009
	  || GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit
Packit 6c4009
#endif
Packit 6c4009
	  )
Packit 6c4009
	__rtld_lock_unlock_recursive (GL(dl_load_lock));
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  /* Now we can allocate an array to hold all the pointers and
Packit 6c4009
	     copy the pointers in.  */
Packit 6c4009
	  struct link_map *maps[nloaded];
Packit 6c4009
Packit 6c4009
	  unsigned int i;
Packit 6c4009
	  struct link_map *l;
Packit 6c4009
	  assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
Packit 6c4009
	  for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
Packit 6c4009
	    /* Do not handle ld.so in secondary namespaces.  */
Packit 6c4009
	    if (l == l->l_real)
Packit 6c4009
	      {
Packit 6c4009
		assert (i < nloaded);
Packit 6c4009
Packit 6c4009
		maps[i] = l;
Packit 6c4009
		l->l_idx = i;
Packit 6c4009
		++i;
Packit 6c4009
Packit 6c4009
		/* Bump l_direct_opencount of all objects so that they
Packit 6c4009
		   are not dlclose()ed from underneath us.  */
Packit 6c4009
		++l->l_direct_opencount;
Packit 6c4009
	      }
Packit 6c4009
	  assert (ns != LM_ID_BASE || i == nloaded);
Packit 6c4009
	  assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1);
Packit 6c4009
	  unsigned int nmaps = i;
Packit 6c4009
Packit 6c4009
	  /* Now we have to do the sorting.  We can skip looking for the
Packit 6c4009
	     binary itself which is at the front of the search list for
Packit 6c4009
	     the main namespace.  */
Packit 6c4009
	  _dl_sort_maps (maps + (ns == LM_ID_BASE), nmaps - (ns == LM_ID_BASE),
Packit 6c4009
			 NULL, true);
Packit 6c4009
Packit 6c4009
	  /* We do not rely on the linked list of loaded object anymore
Packit 6c4009
	     from this point on.  We have our own list here (maps).  The
Packit 6c4009
	     various members of this list cannot vanish since the open
Packit 6c4009
	     count is too high and will be decremented in this loop.  So
Packit 6c4009
	     we release the lock so that some code which might be called
Packit 6c4009
	     from a destructor can directly or indirectly access the
Packit 6c4009
	     lock.  */
Packit 6c4009
	  __rtld_lock_unlock_recursive (GL(dl_load_lock));
Packit 6c4009
Packit 6c4009
	  /* 'maps' now contains the objects in the right order.  Now
Packit 6c4009
	     call the destructors.  We have to process this array from
Packit 6c4009
	     the front.  */
Packit 6c4009
	  for (i = 0; i < nmaps; ++i)
Packit 6c4009
	    {
Packit 6c4009
	      struct link_map *l = maps[i];
Packit 6c4009
Packit 6c4009
	      if (l->l_init_called)
Packit 6c4009
		{
Packit 6c4009
		  /* Make sure nothing happens if we are called twice.  */
Packit 6c4009
		  l->l_init_called = 0;
Packit 6c4009
Packit 6c4009
		  /* Is there a destructor function?  */
Packit 6c4009
		  if (l->l_info[DT_FINI_ARRAY] != NULL
Packit 6c4009
		      || l->l_info[DT_FINI] != NULL)
Packit 6c4009
		    {
Packit 6c4009
		      /* When debugging print a message first.  */
Packit 6c4009
		      if (__builtin_expect (GLRO(dl_debug_mask)
Packit 6c4009
					    & DL_DEBUG_IMPCALLS, 0))
Packit 6c4009
			_dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
Packit 6c4009
					  DSO_FILENAME (l->l_name),
Packit 6c4009
					  ns);
Packit 6c4009
Packit 6c4009
		      /* First see whether an array is given.  */
Packit 6c4009
		      if (l->l_info[DT_FINI_ARRAY] != NULL)
Packit 6c4009
			{
Packit 6c4009
			  ElfW(Addr) *array =
Packit 6c4009
			    (ElfW(Addr) *) (l->l_addr
Packit 6c4009
					    + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
Packit 6c4009
			  unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
Packit 6c4009
					    / sizeof (ElfW(Addr)));
Packit 6c4009
			  while (i-- > 0)
Packit 6c4009
			    ((fini_t) array[i]) ();
Packit 6c4009
			}
Packit 6c4009
Packit 6c4009
		      /* Next try the old-style destructor.  */
Packit 6c4009
		      if (l->l_info[DT_FINI] != NULL)
Packit 6c4009
			DL_CALL_DT_FINI
Packit 6c4009
			  (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr);
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
#ifdef SHARED
Packit 6c4009
		  /* Auditing checkpoint: another object closed.  */
Packit 6c4009
		  if (!do_audit && __builtin_expect (GLRO(dl_naudit) > 0, 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->objclose != NULL)
Packit Service dd92fb
			    {
Packit Service dd92fb
			      struct auditstate *state
Packit Service dd92fb
				= link_map_audit_state (l, cnt);
Packit Service dd92fb
			      /* Return value is ignored.  */
Packit Service dd92fb
			      (void) afct->objclose (&state->cookie);
Packit Service dd92fb
			    }
Packit 6c4009
			  afct = afct->next;
Packit 6c4009
			}
Packit 6c4009
		    }
Packit 6c4009
#endif
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      /* Correct the previous increment.  */
Packit 6c4009
	      --l->l_direct_opencount;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
#ifdef SHARED
Packit 6c4009
  if (! do_audit && GLRO(dl_naudit) > 0)
Packit 6c4009
    {
Packit 6c4009
      do_audit = 1;
Packit 6c4009
      goto again;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS))
Packit 6c4009
    _dl_debug_printf ("\nruntime linker statistics:\n"
Packit 6c4009
		      "           final number of relocations: %lu\n"
Packit 6c4009
		      "final number of relocations from cache: %lu\n",
Packit 6c4009
		      GL(dl_num_relocations),
Packit 6c4009
		      GL(dl_num_cache_relocations));
Packit 6c4009
#endif
Packit 6c4009
}