glibc/elf/dl-fini.c
Ulrich Drepper dacc8ffa42 Update.
2000-03-30  Ulrich Drepper  <drepper@redhat.com>

	Implement dynamic determination of constructor/destructor order in
	the dynamic linker.
	* elf/Versions [ld.so] (GLIBC_2.0): Remove _dl_init_next.
	(GLIBC_2.2): Add _dl_init.
	* elf/dl-close.c: Also call all destructors in FINI_ARRAY.
	r_duplist is not anymore allocated separately.  l_initfini is and
	therefore free it if necessary.
	* elf/dl-deps.c: If a searchlist has to be allocated, put all in one
	malloc block.  Otherwise allocate l_initfini list only.
	Put dependencies for the object in l_initfini list.
	Sort dependencies for the object to be loaded topologically.
	* elf/dl-fini.c: Before running the destructors sort the topologically.
	* elf/dl-init.c (_dl_init): Renamed from _dl_init_next.  Rewrite to
	call constructors instead of iterating over the pointers.  Get list of
	objects for which to run constructors from l_initfini element. Accept
	argc, argv, and env as parameters and pass them to the constructors.
	* elf/ld-load.c (_dl_map_object_from_fd): Initialize l_ldnum member
	with size of dynamic section.
	* elf/dl-open.c (dl_open_worker): Only call _dl_init instead of
	_dl_init_next and calling constructors ourself.
	* elf/dl-preinit.c (_dl_preinit): Renamed from _dl_preinit_next.
	Take argc, argv, and env as parameters and pass them to the
	constructors.  Rewrite to call all constructors and not iterate over
	the pointers.
	* elf/dynamic-link.h: Don't relocate DT_FINI_ARRAY entry.  Don't
	precompute l_initcount and l_preinitcount.
	* elf/link.h (struct link_map): Add l_ldnum member.
	Make l_phdr_allocated part of the bitfield.  Remove l_runcount,
	l_initcount, and l_preinitcount.  Add l_initfini.
	* sysdeps/generic/ldsodefs.h: Replace _dl_init_next prototype with
	one for _dl_init.
	* sysdeps/i386/dl-machine (RTLD_START): Rewrite to match new init
	function interface.
	* sysdeps/unix/sysv/linux/init-first.h: Removed.
	* sysdeps/unix/sysv/linux/Dist: Delete file here as well.
	* sysdeps/unix/sysv/linux/init-first.c [PIC]: Don't use
	SYSDEP_CALL_INIT.  Make _init a strong alias of init.  The calling
	conventions now match.

	* sysdeps/generic/libc-start.c: Calling __libc_init_first has no
	effect for shared objects.  Don't emit message and call only for
	static library.
2000-03-30 16:30:49 +00:00

145 lines
4.4 KiB
C

/* Call the termination functions of loaded shared objects.
Copyright (C) 1995,96,98,99,2000 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 Library General Public License as
published by the Free Software Foundation; either version 2 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <assert.h>
#include <string.h>
#include <ldsodefs.h>
/* Type of the constructor functions. */
typedef void (*fini_t) (void);
void
internal_function
_dl_fini (void)
{
/* Lots of fun ahead. We have to call the destructors for all still
loaded objects. 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. */
unsigned int nloaded = 0;
unsigned int i;
struct link_map *l;
struct link_map **maps;
/* First count how many objects are there. */
for (l = _dl_loaded; l != NULL; l = l->l_next)
++nloaded;
/* XXX Could it be (in static binaries) that there is no object loaded? */
assert (nloaded > 0);
/* Now we can allocate an array to hold all the pointers and copy
the pointers in. */
maps = (struct link_map **) alloca (nloaded * sizeof (struct link_map *));
for (l = _dl_loaded, nloaded = 0; l != NULL; l = l->l_next)
maps[nloaded++] = l;
/* Now we have to do the sorting. */
for (l = _dl_loaded->l_next; l != NULL; l = l->l_next)
{
unsigned int j;
unsigned int k;
/* Find the place in the `maps' array. */
for (j = 1; maps[j] != l; ++j)
;
/* Find all object for which the current one is a dependency and
move the found object (if necessary) in front. */
for (k = j + 1; k < nloaded; ++k)
{
struct link_map **runp;
runp = maps[k]->l_initfini;
if (runp != NULL)
{
while (*runp != NULL)
if (*runp == l)
{
struct link_map *here = maps[k];
/* Move it now. */
memmove (&maps[j] + 1,
&maps[j],
(k - j) * sizeof (struct link_map *));
maps[j] = here;
break;
}
else
++runp;
}
}
}
/* `maps' now contains the objects in the right order. Now call the
destructors. We have the process this array from the front. */
for (i = 0; i < nloaded; ++i)
{
l = maps[i];
if (l->l_init_called)
{
/* Make sure nothing happens if we are called twice. */
l->l_init_called = 0;
/* Don't call the destructors for objects we are not supposed to. */
if (l->l_name[0] == '\0' && l->l_type == lt_executable)
continue;
/* Is there a destructor function? */
if (l->l_info[DT_FINI_ARRAY] == NULL && l->l_info[DT_FINI] == NULL)
continue;
/* When debugging print a message first. */
if (_dl_debug_impcalls)
_dl_debug_message (1, "\ncalling fini: ",
l->l_name[0] ? l->l_name : _dl_argv[0],
"\n\n", NULL);
/* First see whether an array is given. */
if (l->l_info[DT_FINI_ARRAY] != NULL)
{
ElfW(Addr) *array =
(ElfW(Addr) *) (l->l_addr
+ l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
unsigned int sz = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
/ sizeof (ElfW(Addr)));
unsigned int cnt;
for (cnt = 0; cnt < sz; ++cnt)
((fini_t) (l->l_addr + array[cnt])) ();
}
/* Next try the old-style destructor. */
if (l->l_info[DT_FINI] != NULL)
((fini_t) (l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr)) ();
}
}
}