X-Git-Url: http://git.csclub.uwaterloo.ca/?p=kopensolaris-gnu%2Fglibc.git;a=blobdiff_plain;f=elf%2Fdl-close.c;h=a738b11d50547981919c0c98c5758b7e270cd5ae;hp=9467385080f1f9b77b17f69152915482549c067b;hb=1d42347e27bfa3b94529af46f3eb11f258faf01c;hpb=4d0f9da7b76a8aee347d5f08ad39a110f14ee203 diff --git a/elf/dl-close.c b/elf/dl-close.c index 9467385080..a738b11d50 100644 --- a/elf/dl-close.c +++ b/elf/dl-close.c @@ -1,5 +1,5 @@ /* Close a shared object opened by `_dl_open'. - Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999 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 @@ -17,13 +17,14 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include +#include #include #include #include +#include +#include #include #include -#include /* During the program run we must not modify the global data of @@ -34,9 +35,11 @@ __libc_lock_define (extern, _dl_load_lock) #define LOSE(s) _dl_signal_error (0, map->l_name, s) void +internal_function _dl_close (struct link_map *map) { struct link_map **list; + unsigned nsearchlist; unsigned int i; if (map->l_opencount == 0) @@ -46,83 +49,147 @@ _dl_close (struct link_map *map) __libc_lock_lock (_dl_load_lock); /* Decrement the reference count. */ - if (--map->l_opencount > 0 || map->l_type != lt_loaded) + if (map->l_opencount > 1 || map->l_type != lt_loaded) { /* There are still references to this object. Do nothing more. */ + --map->l_opencount; __libc_lock_unlock (_dl_load_lock); return; } + list = map->l_searchlist.r_list; + nsearchlist = map->l_searchlist.r_nlist; + + /* Call all termination functions at once. */ + for (i = 0; i < nsearchlist; ++i) + { + struct link_map *imap = list[i]; + if (imap->l_opencount == 1 && imap->l_type == lt_loaded + && imap->l_info[DT_FINI] + /* Skip any half-cooked objects that were never initialized. */ + && imap->l_init_called) + { + /* When debugging print a message first. */ + if (_dl_debug_impcalls) + _dl_debug_message (1, "\ncalling fini: ", imap->l_name, + "\n\n", NULL); + /* Call its termination function. */ + (*(void (*) (void)) ((void *) imap->l_addr + + imap->l_info[DT_FINI]->d_un.d_ptr)) (); + } + } + /* Notify the debugger we are about to remove some loaded objects. */ _r_debug.r_state = RT_DELETE; _dl_debug_state (); - 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) + for (i = 0; i < 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) + for (i = 0; i < nsearchlist; ++i) { - struct link_map *map = list[i]; - if (map->l_opencount == 0 && map->l_type == lt_loaded) + 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. */ - const ElfW(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_global) + if (imap->l_global) { /* This object is in the global scope list. Remove it. */ - struct link_map **tail = _dl_global_scope_end; + int cnt = _dl_main_searchlist->r_nlist; + do - --tail; - while (*tail != map); - --_dl_global_scope_end; - memcpy (tail, tail + 1, _dl_global_scope_end - tail); - _dl_global_scope_end[0] = NULL; - _dl_global_scope_end[1] = NULL; + --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; } - /* Unmap the segments. */ - for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph) - if (ph->p_type == PT_LOAD) - { - ElfW(Addr) mapstart = ph->p_vaddr & ~(ph->p_align - 1); - ElfW(Addr) mapend = ((ph->p_vaddr + ph->p_memsz - + ph->p_align - 1) - & ~(ph->p_align - 1)); - __munmap ((caddr_t) mapstart, mapend - mapstart); - } + /* 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. */ + __munmap ((void *) imap->l_map_start, + imap->l_map_end - imap->l_map_start); /* Finally, unlink the data structure and free it. */ - if (map->l_prev) - 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); +#ifdef PIC + /* 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 + 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); + + /* 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; + free (this); + } + while (lnp != NULL); + + /* Remove the searchlists. */ + if (imap->l_searchlist.r_duplist != imap->l_searchlist.r_list) + { + /* If a r_list exists there always also is a r_duplist. */ + assert (imap->l_searchlist.r_list != NULL); + free (imap->l_searchlist.r_duplist); + } + if (imap != map && imap->l_searchlist.r_list != NULL) + free (imap->l_searchlist.r_list); + + if (imap->l_phdr_allocated) + free ((void *) imap->l_phdr); + + free (imap); } } free (list); + if (_dl_global_scope_alloc != 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); + } + /* Notify the debugger those objects are finalized and gone. */ _r_debug.r_state = RT_CONSISTENT; _dl_debug_state ();