Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'newlib/libc/sys/linux/dl/dl-close.c')
-rw-r--r--newlib/libc/sys/linux/dl/dl-close.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/newlib/libc/sys/linux/dl/dl-close.c b/newlib/libc/sys/linux/dl/dl-close.c
new file mode 100644
index 000000000..ef53868d9
--- /dev/null
+++ b/newlib/libc/sys/linux/dl/dl-close.c
@@ -0,0 +1,334 @@
+/* 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);