* elf/dl-fini.c (_dl_fini): Split sorting of the maps in separate

function _dl_sort_fini.
	(_dl_sort_fini): New function.
	* sysdeps/generic/ldsodefs.h: Declare _dl_sort_fini.
	* elf/dl-close.c (_dl_close): Call _dl_sort_fini before running
	destructors to call them in the right order.
This commit is contained in:
Ulrich Drepper 2005-03-19 07:22:23 +00:00
parent bb4bb82b13
commit c3381f3eb2
4 changed files with 152 additions and 103 deletions

View File

@ -1,3 +1,12 @@
2005-03-18 Ulrich Drepper <drepper@redhat.com>
* elf/dl-fini.c (_dl_fini): Split sorting of the maps in separate
function _dl_sort_fini.
(_dl_sort_fini): New function.
* sysdeps/generic/ldsodefs.h: Declare _dl_sort_fini.
* elf/dl-close.c (_dl_close): Call _dl_sort_fini before running
destructors to call them in the right order.
2005-02-07 Steven Munroe <sjmunroe@us.ibm.com>
* sysdeps/powerpc/bits/link.h (La_ppc64_regs): Add lr_vrsave.

View File

@ -136,15 +136,9 @@ _dl_close (void *_map)
return;
}
#define NWORDS(n) (((n) + 8 * sizeof (unsigned long int) - 1) \
/ (sizeof (unsigned long int)))
#define SETBIT(a, n) a[(n) / sizeof (unsigned long int)] \
|= 1 << ((n) % (sizeof (unsigned long int)))
#define ISSET(a, n) (a[(n) / sizeof (unsigned long int)] \
& 1 << ((n) % (sizeof (unsigned long int))))
const unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded;
unsigned long int used[NWORDS (nloaded)];
unsigned long int done[NWORDS (nloaded)];
char used[nloaded];
char done[nloaded];
struct link_map *maps[nloaded];
/* Run over the list and assign indeces to the link maps and enter
@ -168,7 +162,7 @@ _dl_close (void *_map)
{
struct link_map *l = maps[done_index];
if (ISSET (done, done_index))
if (done[done_index])
/* Already handled. */
continue;
@ -176,27 +170,32 @@ _dl_close (void *_map)
if (l->l_type == lt_loaded
&& l->l_direct_opencount == 0
&& (l->l_flags_1 & DF_1_NODELETE) == 0
&& !ISSET (used, done_index))
&& !used[done_index])
continue;
/* We need this object and we handle it now. */
SETBIT (done, done_index);
SETBIT (used, done_index);
done[done_index] = 1;
used[done_index] = 1;
/* Signal the object is still needed. */
l->l_idx = -1;
/* Mark all dependencies as used. */
if (l->l_initfini != NULL)
{
struct link_map **lp = &l->l_initfini[1];
while (*lp != NULL)
{
if ((*lp)->l_idx != -1)
{
assert ((*lp)->l_idx >= 0 && (*lp)->l_idx < nloaded);
if (!ISSET (used, (*lp)->l_idx))
if (!used[(*lp)->l_idx])
{
SETBIT (used, (*lp)->l_idx);
used[(*lp)->l_idx] = 1;
if ((*lp)->l_idx - 1 < done_index)
done_index = (*lp)->l_idx - 1;
}
}
++lp;
}
@ -207,16 +206,22 @@ _dl_close (void *_map)
{
struct link_map *jmap = l->l_reldeps[j];
if (jmap->l_idx != -1)
{
assert (jmap->l_idx >= 0 && jmap->l_idx < nloaded);
if (!ISSET (used, jmap->l_idx))
if (!used[jmap->l_idx])
{
SETBIT (used, jmap->l_idx);
used[jmap->l_idx] = 1;
if (jmap->l_idx - 1 < done_index)
done_index = jmap->l_idx - 1;
}
}
}
}
/* Sort the entries. */
_dl_sort_fini (GL(dl_ns)[ns]._ns_loaded, maps, nloaded, used, ns);
/* Call all termination functions at once. */
#ifdef SHARED
@ -231,7 +236,7 @@ _dl_close (void *_map)
/* All elements must be in the same namespace. */
assert (imap->l_ns == ns);
if (!ISSET (used, i))
if (!used[i])
{
assert (imap->l_type == lt_loaded
&& (imap->l_flags_1 & DF_1_NODELETE) == 0);
@ -291,7 +296,7 @@ _dl_close (void *_map)
if (i < first_loaded)
first_loaded = i;
}
/* Else ISSET (used, i). */
/* Else used[i]. */
else if (imap->l_type == lt_loaded)
{
if (imap->l_searchlist.r_list == NULL
@ -320,8 +325,9 @@ _dl_close (void *_map)
}
}
/* The loader is gone, so mark the object as not having one. */
if (imap->l_loader != NULL && !ISSET (used, imap->l_loader->l_idx))
/* The loader is gone, so mark the object as not having one.
Note: l_idx == -1 -> object will be removed. */
if (imap->l_loader != NULL && imap->l_loader->l_idx != -1)
imap->l_loader = NULL;
/* Remember where the first dynamically loaded object is. */
@ -370,7 +376,7 @@ _dl_close (void *_map)
for (i = first_loaded; i < nloaded; ++i)
{
struct link_map *imap = maps[i];
if (!ISSET (used, i))
if (!used[i])
{
assert (imap->l_type == lt_loaded);

View File

@ -29,97 +29,21 @@ typedef void (*fini_t) (void);
void
internal_function
_dl_fini (void)
_dl_sort_fini (struct link_map *l, struct link_map **maps, size_t nmaps,
char *used, Lmid_t ns)
{
/* Lots of fun ahead. We have to call the destructors for all still
loaded objects, in all namespaces. The problem is that the ELF
specification now demands that dependencies between the modules
are taken into account. I.e., the destructor for a module is
called before the ones for any of its dependencies.
To make things more complicated, we cannot simply use the reverse
order of the constructors. Since the user might have loaded objects
using `dlopen' there are possibly several other modules with its
dependencies to be taken into account. Therefore we have to start
determining the order of the modules once again from the beginning. */
struct link_map **maps = NULL;
size_t maps_size = 0;
/* We run the destructors of the main namespaces last. As for the
other namespaces, we pick run the destructors in them in reverse
order of the namespace ID. */
#ifdef SHARED
int do_audit = 0;
again:
#endif
for (Lmid_t cnt = DL_NNS - 1; cnt >= 0; --cnt)
{
/* Protect against concurrent loads and unloads. */
__rtld_lock_lock_recursive (GL(dl_load_lock));
unsigned int nmaps = 0;
unsigned int nloaded = GL(dl_ns)[cnt]._ns_nloaded;
/* No need to do anything for empty namespaces or those used for
auditing DSOs. */
if (nloaded == 0
#ifdef SHARED
|| GL(dl_ns)[cnt]._ns_loaded->l_auditing != do_audit
#endif
)
goto out;
/* XXX Could it be (in static binaries) that there is no object
loaded? */
assert (cnt != LM_ID_BASE || nloaded > 0);
/* Now we can allocate an array to hold all the pointers and copy
the pointers in. */
if (maps_size < nloaded * sizeof (struct link_map *))
{
if (maps_size == 0)
{
maps_size = nloaded * sizeof (struct link_map *);
maps = (struct link_map **) alloca (maps_size);
}
else
maps = (struct link_map **)
extend_alloca (maps, maps_size,
nloaded * sizeof (struct link_map *));
}
unsigned int i;
struct link_map *l;
assert (nloaded != 0 || GL(dl_ns)[cnt]._ns_loaded == NULL);
for (l = GL(dl_ns)[cnt]._ns_loaded, i = 0; l != NULL; l = l->l_next)
/* Do not handle ld.so in secondary namespaces. */
if (l == l->l_real)
{
assert (i < nloaded);
maps[i++] = l;
/* Bump l_direct_opencount of all objects so that they are
not dlclose()ed from underneath us. */
++l->l_direct_opencount;
}
assert (cnt != LM_ID_BASE || i == nloaded);
assert (cnt == LM_ID_BASE || i == nloaded || i == nloaded - 1);
nmaps = i;
if (nmaps != 0)
{
/* Now we have to do the sorting. */
l = GL(dl_ns)[cnt]._ns_loaded;
if (cnt == LM_ID_BASE)
if (ns == LM_ID_BASE)
/* The main executable always comes first. */
l = l->l_next;
for (; l != NULL; l = l->l_next)
/* Do not handle ld.so in secondary namespaces. */
if (l == l->l_real)
/* Do not handle ld.so in secondary namespaces and object which
are not removed. */
if (l == l->l_real && l->l_idx != -1)
{
/* Find the place in the 'maps' array. */
unsigned int j;
for (j = cnt == LM_ID_BASE ? 1 : 0; maps[j] != l; ++j)
for (j = ns == LM_ID_BASE ? 1 : 0; maps[j] != l; ++j)
assert (j < nmaps);
/* Find all object for which the current one is a dependency
@ -136,9 +60,19 @@ _dl_fini (void)
/* Move it now. */
memmove (&maps[j] + 1,
&maps[j],
(k - j) * sizeof (struct link_map *));
maps[j++] = here;
&maps[j], (k - j) * sizeof (struct link_map *));
maps[j] = here;
if (used != NULL)
{
char here_used = used[k];
memmove (&used[j] + 1,
&used[j], (k - j) * sizeof (char));
used[j] = here_used;
}
++j;
break;
}
@ -163,13 +97,108 @@ _dl_fini (void)
(k - j) * sizeof (struct link_map *));
maps[j] = here;
if (used != NULL)
{
char here_used = used[k];
memmove (&used[j] + 1,
&used[j], (k - j) * sizeof (char));
used[j] = here_used;
}
break;
}
}
}
}
}
}
void
internal_function
_dl_fini (void)
{
/* Lots of fun ahead. We have to call the destructors for all still
loaded objects, in all namespaces. The problem is that the ELF
specification now demands that dependencies between the modules
are taken into account. I.e., the destructor for a module is
called before the ones for any of its dependencies.
To make things more complicated, we cannot simply use the reverse
order of the constructors. Since the user might have loaded objects
using `dlopen' there are possibly several other modules with its
dependencies to be taken into account. Therefore we have to start
determining the order of the modules once again from the beginning. */
struct link_map **maps = NULL;
size_t maps_size = 0;
/* We run the destructors of the main namespaces last. As for the
other namespaces, we pick run the destructors in them in reverse
order of the namespace ID. */
#ifdef SHARED
int do_audit = 0;
again:
#endif
for (Lmid_t ns = DL_NNS - 1; ns >= 0; --ns)
{
/* Protect against concurrent loads and unloads. */
__rtld_lock_lock_recursive (GL(dl_load_lock));
unsigned int nmaps = 0;
unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded;
/* No need to do anything for empty namespaces or those used for
auditing DSOs. */
if (nloaded == 0
#ifdef SHARED
|| GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit
#endif
)
goto out;
/* XXX Could it be (in static binaries) that there is no object
loaded? */
assert (ns != LM_ID_BASE || nloaded > 0);
/* Now we can allocate an array to hold all the pointers and copy
the pointers in. */
if (maps_size < nloaded * sizeof (struct link_map *))
{
if (maps_size == 0)
{
maps_size = nloaded * sizeof (struct link_map *);
maps = (struct link_map **) alloca (maps_size);
}
else
maps = (struct link_map **)
extend_alloca (maps, maps_size,
nloaded * sizeof (struct link_map *));
}
unsigned int i;
struct link_map *l;
assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
/* Do not handle ld.so in secondary namespaces. */
if (l == l->l_real)
{
assert (i < nloaded);
maps[i] = l;
l->l_idx = i;
++i;
/* Bump l_direct_opencount of all objects so that they are
not dlclose()ed from underneath us. */
++l->l_direct_opencount;
}
assert (ns != LM_ID_BASE || i == nloaded);
assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1);
nmaps = i;
if (nmaps != 0)
/* Now we have to do the sorting. */
_dl_sort_fini (GL(dl_ns)[ns]._ns_loaded, maps, nmaps, NULL, ns);
/* We do not rely on the linked list of loaded object anymore from
this point on. We have our own list here (maps). The various
@ -200,7 +229,7 @@ _dl_fini (void)
& DL_DEBUG_IMPCALLS, 0))
_dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
l->l_name[0] ? l->l_name : rtld_progname,
cnt);
ns);
/* First see whether an array is given. */
if (l->l_info[DT_FINI_ARRAY] != NULL)
@ -247,7 +276,6 @@ _dl_fini (void)
do_audit = 1;
goto again;
}
#endif
if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS, 0))
_dl_debug_printf ("\nruntime linker statistics:\n"
@ -255,4 +283,5 @@ _dl_fini (void)
"final number of relocations from cache: %lu\n",
GL(dl_num_relocations),
GL(dl_num_cache_relocations));
#endif
}

View File

@ -892,6 +892,11 @@ extern void _dl_init (struct link_map *main_map, int argc, char **argv,
initializer functions have completed. */
extern void _dl_fini (void) internal_function;
/* Sort array MAPS according to dependencies of the contained objects. */
extern void _dl_sort_fini (struct link_map *l, struct link_map **maps,
size_t nmaps, char *used, Lmid_t ns)
internal_function;
/* The dynamic linker calls this function before and having changing
any shared object mappings. The `r_state' member of `struct r_debug'
says what change is taking place. This function's address is