summaryrefslogtreecommitdiff
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;