/* Handle loading and unloading shared objects for internal libc purposes.
- Copyright (C) 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc.
+ Copyright (C) 1999-2015 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Zack Weinberg <zack@rabi.columbia.edu>, 1999.
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. */
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
#include <dlfcn.h>
#include <stdlib.h>
{
const char *objname;
const char *last_errstring = NULL;
- int result;
+ bool malloced;
- (void) GLRO(dl_catch_error) (&objname, &last_errstring, operate, args);
+ int result = (GLRO(dl_catch_error) (&objname, &last_errstring, &malloced,
+ operate, args)
+ ?: last_errstring != NULL);
- result = last_errstring != NULL;
- if (result && last_errstring != _dl_out_of_memory)
+ if (result && malloced)
free ((char *) last_errstring);
return result;
const char *name;
/* Opening mode. */
int mode;
+ /* This is the caller of the dlopen() function. */
+ const void *caller_dlopen;
/* Return from do_dlopen. */
struct link_map *map;
{
struct do_dlopen_args *args = (struct do_dlopen_args *) ptr;
/* Open and relocate the shared object. */
- args->map = GLRO(dl_open) (args->name, args->mode, NULL, __LM_ID_CALLER,
- __libc_argc, __libc_argv, __environ);
+ args->map = GLRO(dl_open) (args->name, args->mode, args->caller_dlopen,
+ __LM_ID_CALLER, __libc_argc, __libc_argv,
+ __environ);
}
static void
struct do_dlopen_args args;
args.name = name;
args.mode = mode;
+ args.caller_dlopen = RETURN_ADDRESS (0);
#ifdef SHARED
- if (__builtin_expect (_dl_open_hook != NULL, 0))
+ if (__glibc_unlikely (_dl_open_hook != NULL))
return _dl_open_hook->dlopen_mode (name, mode);
return (dlerror_run (do_dlopen, &args) ? NULL : (void *) args.map);
#else
args.name = name;
#ifdef SHARED
- if (__builtin_expect (_dl_open_hook != NULL, 0))
+ if (__glibc_unlikely (_dl_open_hook != NULL))
return _dl_open_hook->dlsym (map, name);
#endif
return (dlerror_run (do_dlsym, &args) ? NULL
__libc_dlclose (void *map)
{
#ifdef SHARED
- if (__builtin_expect (_dl_open_hook != NULL, 0))
+ if (__glibc_unlikely (_dl_open_hook != NULL))
return _dl_open_hook->dlclose (map);
#endif
return dlerror_run (do_dlclose, map);
libc_hidden_def (__libc_dlclose)
+static bool __libc_freeres_fn_section
+free_slotinfo (struct dtv_slotinfo_list **elemp)
+{
+ size_t cnt;
+
+ if (*elemp == NULL)
+ /* Nothing here, all is removed (or there never was anything). */
+ return true;
+
+ if (!free_slotinfo (&(*elemp)->next))
+ /* We cannot free the entry. */
+ return false;
+
+ /* That cleared our next pointer for us. */
+
+ for (cnt = 0; cnt < (*elemp)->len; ++cnt)
+ if ((*elemp)->slotinfo[cnt].map != NULL)
+ /* Still used. */
+ return false;
+
+ /* We can remove the list element. */
+ free (*elemp);
+ *elemp = NULL;
+
+ return true;
+}
+
+
libc_freeres_fn (free_mem)
{
struct link_map *l;
free (old);
}
- /* Remove all additional names added to the objects. */
- for (Lmid_t ns = 0; ns < DL_NNS; ++ns)
- for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next)
- {
- struct libname_list *lnp = l->l_libname->next;
-
- l->l_libname->next = NULL;
-
- while (lnp != NULL)
- {
- struct libname_list *old = lnp;
- lnp = lnp->next;
- if (! old->dont_free)
- free (old);
- }
- }
+ for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns)
+ {
+ for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next)
+ {
+ struct libname_list *lnp = l->l_libname->next;
+
+ l->l_libname->next = NULL;
+
+ /* Remove all additional names added to the objects. */
+ while (lnp != NULL)
+ {
+ struct libname_list *old = lnp;
+ lnp = lnp->next;
+ if (! old->dont_free)
+ free (old);
+ }
+
+ /* Free the initfini dependency list. */
+ if (l->l_free_initfini)
+ free (l->l_initfini);
+ l->l_initfini = NULL;
+ }
+
+ if (__builtin_expect (GL(dl_ns)[ns]._ns_global_scope_alloc, 0) != 0
+ && (GL(dl_ns)[ns]._ns_main_searchlist->r_nlist
+ // XXX Check whether we need NS-specific initial_searchlist
+ == GLRO(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 = GL(dl_ns)[ns]._ns_main_searchlist->r_list;
+
+ /* Put the old map in. */
+ GL(dl_ns)[ns]._ns_main_searchlist->r_list
+ // XXX Check whether we need NS-specific initial_searchlist
+ = GLRO(dl_initial_searchlist).r_list;
+ /* Signal that the original map is used. */
+ GL(dl_ns)[ns]._ns_global_scope_alloc = 0;
+
+ /* Now free the old map. */
+ free (old);
+ }
+ }
+
+ /* Free the memory allocated for the dtv slotinfo array. We can do
+ this only if all modules which used this memory are unloaded. */
+#ifdef SHARED
+ if (GL(dl_initial_dtv) == NULL)
+ /* There was no initial TLS setup, it was set up later when
+ it used the normal malloc. */
+ free_slotinfo (&GL(dl_tls_dtv_slotinfo_list));
+ else
+#endif
+ /* The first element of the list does not have to be deallocated.
+ It was allocated in the dynamic linker (i.e., with a different
+ malloc), and in the static library it's in .bss space. */
+ free_slotinfo (&GL(dl_tls_dtv_slotinfo_list)->next);
+
+ void *scope_free_list = GL(dl_scope_free_list);
+ GL(dl_scope_free_list) = NULL;
+ free (scope_free_list);
}