335 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			335 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C
		
	
	
	
| /* Close a shared object opened by `_dl_open'.
 | |
|    Copyright (C) 1996,1997,1998,1999,2000,2001 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, write to the Free
 | |
|    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 | |
|    02111-1307 USA.  */
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <dlfcn.h>
 | |
| #include <libintl.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <bits/libc-lock.h>
 | |
| #include <ldsodefs.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/mman.h>
 | |
| 
 | |
| 
 | |
| /* Type of the constructor functions.  */
 | |
| typedef void (*fini_t) (void);
 | |
| 
 | |
| 
 | |
| void
 | |
| internal_function
 | |
| _dl_close (void *_map)
 | |
| {
 | |
|   struct reldep_list
 | |
|   {
 | |
|     struct link_map **rellist;
 | |
|     unsigned int nrellist;
 | |
|     struct reldep_list *next;
 | |
|   } *reldeps = NULL;
 | |
|   struct link_map **list;
 | |
|   struct link_map *map = _map;
 | |
|   unsigned int i;
 | |
|   unsigned int *new_opencount;
 | |
| 
 | |
|   /* First see whether we can remove the object at all.  */
 | |
|   if (__builtin_expect (map->l_flags_1 & DF_1_NODELETE, 0)
 | |
|       && map->l_init_called)
 | |
|     /* Nope.  Do nothing.  */
 | |
|     return;
 | |
| 
 | |
|   if (__builtin_expect (map->l_opencount, 1) == 0)
 | |
|     _dl_signal_error (0, map->l_name, NULL, N_("shared object not open"));
 | |
| 
 | |
|   /* Acquire the lock.  */
 | |
| #ifdef HAVE_DD_LOCK
 | |
|     __lock_acquire(_dl_load_lock);
 | |
| #endif
 | |
| 
 | |
| 
 | |
|   /* Decrement the reference count.  */
 | |
|   if (map->l_opencount > 1 || map->l_type != lt_loaded)
 | |
|     {
 | |
|       /* There are still references to this object.  Do nothing more.  */
 | |
|       if (__builtin_expect (_dl_debug_mask & DL_DEBUG_FILES, 0))
 | |
| 	_dl_debug_printf ("\nclosing file=%s; opencount == %u\n",
 | |
| 			  map->l_name, map->l_opencount);
 | |
| 
 | |
|       /* One decrement the object itself, not the dependencies.  */
 | |
|       --map->l_opencount;
 | |
| 
 | |
| #ifdef HAVE_DD_LOCK
 | |
|         __lock_release(_dl_load_lock);
 | |
| #endif
 | |
| 
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   list = map->l_initfini;
 | |
| 
 | |
|   /* Compute the new l_opencount values.  */
 | |
|   i = map->l_searchlist.r_nlist;
 | |
|   if (__builtin_expect (i == 0, 0))
 | |
|     /* This can happen if we handle relocation dependencies for an
 | |
|        object which wasn't loaded directly.  */
 | |
|     for (i = 1; list[i] != NULL; ++i)
 | |
|       ;
 | |
| 
 | |
|   new_opencount = (unsigned int *) alloca (i * sizeof (unsigned int));
 | |
| 
 | |
|   for (i = 0; list[i] != NULL; ++i)
 | |
|     {
 | |
|       list[i]->l_idx = i;
 | |
|       new_opencount[i] = list[i]->l_opencount;
 | |
|     }
 | |
|   --new_opencount[0];
 | |
|   for (i = 1; list[i] != NULL; ++i)
 | |
|     if ((! (list[i]->l_flags_1 & DF_1_NODELETE) || ! list[i]->l_init_called)
 | |
| 	/* 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;
 | |
| 
 | |
| 	for (j = 1; j < list[i]->l_searchlist.r_nlist; ++j)
 | |
| 	  if (! (dep_list[j]->l_flags_1 & DF_1_NODELETE)
 | |
| 	      || ! dep_list[j]->l_init_called)
 | |
| 	    {
 | |
| 	      assert (dep_list[j]->l_idx < map->l_searchlist.r_nlist);
 | |
| 	      --new_opencount[dep_list[j]->l_idx];
 | |
| 	    }
 | |
|       }
 | |
|   assert (new_opencount[0] == 0);
 | |
| 
 | |
|   /* Call all termination functions at once.  */
 | |
|   for (i = 0; list[i] != NULL; ++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) || ! imap->l_init_called)
 | |
| 	  /* Skip any half-cooked objects that were never initialized.  */
 | |
| 	  && imap->l_init_called)
 | |
| 	{
 | |
| 	  /* When debugging print a message first.  */
 | |
| 	  if (__builtin_expect (_dl_debug_mask & DL_DEBUG_IMPCALLS, 0))
 | |
| 	    _dl_debug_printf ("\ncalling fini: %s\n\n", imap->l_name);
 | |
| 
 | |
| 	  /* Call its termination function.  */
 | |
| 	  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)));
 | |
| 	      unsigned int cnt;
 | |
| 
 | |
| 	      for (cnt = 0; cnt < sz; ++cnt)
 | |
| 		((fini_t) (imap->l_addr + array[cnt])) ();
 | |
| 	    }
 | |
| 
 | |
| 	  /* 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)) ();
 | |
| 	}
 | |
|       else if (new_opencount[i] != 0 && imap->l_type == lt_loaded)
 | |
| 	{
 | |
| 	  /* The object is still used.  But the object we are unloading
 | |
| 	     right now is responsible for loading it and therefore we
 | |
| 	     have the search list of the current object in its scope.
 | |
| 	     Remove it.  */
 | |
| 	  struct r_scope_elem **runp = imap->l_scope;
 | |
| 
 | |
| 	  while (*runp != NULL)
 | |
| 	    if (*runp == &map->l_searchlist)
 | |
| 	      {
 | |
| 		/* Copy all later elements.  */
 | |
| 		while ((runp[0] = runp[1]) != NULL)
 | |
| 		  ++runp;
 | |
| 		break;
 | |
| 	      }
 | |
| 	  else
 | |
| 	    ++runp;
 | |
| 	}
 | |
| 
 | |
|       /* 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);
 | |
|     }
 | |
| 
 | |
|   /* Notify the debugger we are about to remove some loaded objects.  */
 | |
|   _r_debug.r_state = RT_DELETE;
 | |
|   _dl_debug_state ();
 | |
| 
 | |
|   /* Check each element of the search list to see if all references to
 | |
|      it are gone.  */
 | |
|   for (i = 0; list[i] != NULL; ++i)
 | |
|     {
 | |
|       struct link_map *imap = list[i];
 | |
|       if (imap->l_opencount == 0 && imap->l_type == lt_loaded)
 | |
| 	{
 | |
| 	  struct libname_list *lnp;
 | |
| 
 | |
| 	  /* 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.  */
 | |
| 	      unsigned 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;
 | |
| 	    }
 | |
| 
 | |
| 	  /* We can unmap all the maps at once.  We determined the
 | |
| 	     start address and length when we loaded the object and
 | |
| 	     the `munmap' call does the rest.  */
 | |
| 	  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;
 | |
| #endif
 | |
| 	  --_dl_nloaded;
 | |
| 	  if (imap->l_next)
 | |
| 	    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 ((char *) imap->l_origin);
 | |
| 
 | |
| 	  /* If the object has relocation dependencies save this
 | |
|              information for latter.  */
 | |
| 	  if (__builtin_expect (imap->l_reldeps != NULL, 0))
 | |
| 	    {
 | |
| 	      struct reldep_list *newrel;
 | |
| 
 | |
| 	      newrel = (struct reldep_list *) alloca (sizeof (*reldeps));
 | |
| 	      newrel->rellist = imap->l_reldeps;
 | |
| 	      newrel->nrellist = imap->l_reldepsact;
 | |
| 	      newrel->next = reldeps;
 | |
| 
 | |
| 	      reldeps = newrel;
 | |
| 	    }
 | |
| 
 | |
| 	  /* This name always is allocated.  */
 | |
| 	  free (imap->l_name);
 | |
| 	  /* Remove the list with all the names of the shared object.  */
 | |
| 	  lnp = imap->l_libname;
 | |
| 	  do
 | |
| 	    {
 | |
| 	      struct libname_list *this = lnp;
 | |
| 	      lnp = lnp->next;
 | |
| 	      if (!this->dont_free)
 | |
| 		free (this);
 | |
| 	    }
 | |
| 	  while (lnp != NULL);
 | |
| 
 | |
| 	  /* Remove the searchlists.  */
 | |
| 	  if (imap != map)
 | |
| 	      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);
 | |
| 
 | |
| 	  if (imap->l_rpath_dirs.dirs != (void *) -1)
 | |
| 	    free (imap->l_rpath_dirs.dirs);
 | |
| 	  if (imap->l_runpath_dirs.dirs != (void *) -1)
 | |
| 	    free (imap->l_runpath_dirs.dirs);
 | |
| 
 | |
| 	  free (imap);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* Notify the debugger those objects are finalized and gone.  */
 | |
|   _r_debug.r_state = RT_CONSISTENT;
 | |
|   _dl_debug_state ();
 | |
| 
 | |
|   /* Now we can perhaps also remove the modules for which we had
 | |
|      dependencies because of symbol lookup.  */
 | |
|   while (__builtin_expect (reldeps != NULL, 0))
 | |
|     {
 | |
|       while (reldeps->nrellist-- > 0)
 | |
| 	_dl_close (reldeps->rellist[reldeps->nrellist]);
 | |
| 
 | |
|       free (reldeps->rellist);
 | |
| 
 | |
|       reldeps = reldeps->next;
 | |
|     }
 | |
| 
 | |
|   free (list);
 | |
| 
 | |
|   /* Release the lock.  */
 | |
| #ifdef HAVE_DD_LOCK
 | |
|     __lock_release(_dl_load_lock);
 | |
| #endif
 | |
| 
 | |
|   
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| free_mem (void)
 | |
| {
 | |
|   if (__builtin_expect (_dl_global_scope_alloc, 0) != 0
 | |
|       && _dl_main_searchlist->r_nlist == _dl_initial_searchlist.r_nlist)
 | |
|     {
 | |
|       /* 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;
 | |
| 
 | |
|       /* Now free the old map.  */
 | |
|       free (old);
 | |
|     }
 | |
| }
 | |
| text_set_element (__libc_subfreeres, free_mem);
 |