about summary refs log tree commit diff
path: root/elf/dlclose.c
diff options
context:
space:
mode:
Diffstat (limited to 'elf/dlclose.c')
-rw-r--r--elf/dlclose.c100
1 files changed, 52 insertions, 48 deletions
diff --git a/elf/dlclose.c b/elf/dlclose.c
index 06b2d1bdcd..fbb3ca6fe2 100644
--- a/elf/dlclose.c
+++ b/elf/dlclose.c
@@ -1,5 +1,5 @@
 /* dlclose -- Close a handle opened by `dlopen'.
-Copyright (C) 1995 Free Software Foundation, Inc.
+Copyright (C) 1995, 1996 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
@@ -32,64 +32,68 @@ dlclose (void *handle)
   void doit (void)
     {
       struct link_map *map = handle;
+      struct link_map **list;
+      unsigned int i;
 
       if (map->l_opencount == 0)
 	LOSE ("shared object not open");
 
       /* Decrement the reference count.  */
-      --map->l_opencount;
-
-      if (map->l_opencount == 0 && map->l_type == lt_loaded)
+      if (--map->l_opencount > 0 || map->l_type != lt_loaded)
+	/* There are still references to this object.  Do nothing more.  */
+	return;
+
+      list = map->l_searchlist;
+
+      /* The search list contains a counted reference to each object it
+	 points to, the 0th elt being MAP itself.  Decrement the reference
+	 counts on all the objects MAP depends on.  */
+      for (i = 1; i < map->l_nsearchlist; ++i)
+	--list[i]->l_opencount;
+
+      /* Clear the search list so it doesn't get freed while we are still
+         using it.  We have cached it in LIST and will free it when
+         finished.  */
+      map->l_searchlist = NULL;
+
+      /* Check each element of the search list to see if all references to
+         it are gone.  */
+      for (i = 0; i < map->l_nsearchlist; ++i)
 	{
-	  /* That was the last reference, and this was a dlopen-loaded
-	     object.  We can unmap it.  */
-	  const Elf32_Phdr *ph;
-
-	  if (map->l_info[DT_FINI])
-	    /* Call its termination function.  */
-	    (*(void (*) (void)) ((void *) map->l_addr +
-				 map->l_info[DT_FINI]->d_un.d_ptr)) ();
-
-	  if (map->l_info[DT_NEEDED])
+	  struct link_map *map = list[i];
+	  if (map->l_opencount == 0 && map->l_type == lt_loaded)
 	    {
-	      /* Also close all the dependencies.  */
-	      const char *strtab
-		= (void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr;
-	      const Elf32_Dyn *d;
-	      for (d = map->l_ld; d->d_tag != DT_NULL; ++d)
-		if (d->d_tag == DT_NEEDED)
+	      /* That was the last reference, and this was a dlopen-loaded
+		 object.  We can unmap it.  */
+	      const Elf32_Phdr *ph;
+
+	      if (map->l_info[DT_FINI])
+		/* Call its termination function.  */
+		(*(void (*) (void)) ((void *) map->l_addr +
+				     map->l_info[DT_FINI]->d_un.d_ptr)) ();
+
+	      /* Unmap the segments.  */
+	      for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph)
+		if (ph->p_type == PT_LOAD)
 		  {
-		    /* It must already be open, since this one needed it;
-		       so dlopen will just find us its `struct link_map'
-		       and bump its reference count.  */
-		    struct link_map *o, *dep
-		      = dlopen (strtab + d->d_un.d_val, RTLD_LAZY);
-		    --dep->l_opencount; /* Lose the ref from that dlopen.  */
-		    /* Now we have the handle; we can close it for real.  */
-		    o = map;
-		    map = dep;
-		    doit ();
-		    map = o;
+		    Elf32_Addr mapstart = ph->p_vaddr & ~(ph->p_align - 1);
+		    Elf32_Addr mapend = ((ph->p_vaddr + ph->p_memsz
+					  + ph->p_align - 1)
+					 & ~(ph->p_align - 1));
+		    munmap ((caddr_t) mapstart, mapend - mapstart);
 		  }
-	    }
 
-	  /* Unmap the segments.  */
-	  for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph)
-	    if (ph->p_type == PT_LOAD)
-	      {
-		Elf32_Addr mapstart = ph->p_vaddr & ~(ph->p_align - 1);
-		Elf32_Addr mapend = ((ph->p_vaddr + ph->p_memsz
-				      + ph->p_align - 1)
-				     & ~(ph->p_align - 1));
-		munmap ((caddr_t) mapstart, mapend - mapstart);
-	      }
-
-	  /* Finally, unlink the data structure and free it.  */
-	  map->l_prev->l_next = map->l_next;
-	  if (map->l_next)
-	    map->l_next->l_prev = map->l_prev;
-	  free (map);
+	      /* Finally, unlink the data structure and free it.  */
+	      map->l_prev->l_next = map->l_next;
+	      if (map->l_next)
+		map->l_next->l_prev = map->l_prev;
+	      if (map->l_searchlist)
+		free (map->l_searchlist);
+	      free (map);
+	    }
 	}
+
+      free (list);
     }
 
   return _dlerror_run (doit) ? -1 : 0;