From dacc8ffa420575038f04c543c31065fea81d5732 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Thu, 30 Mar 2000 16:30:49 +0000 Subject: Update. 2000-03-30 Ulrich Drepper 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. --- ChangeLog | 45 ++++++++++ elf/Versions | 4 +- elf/dl-close.c | 41 ++++++--- elf/dl-deps.c | 95 +++++++++++++++++--- elf/dl-fini.c | 163 +++++++++++++++++++++++++---------- elf/dl-init.c | 128 ++++++++++++++++----------- elf/dl-load.c | 1 + elf/dl-open.c | 13 +-- elf/dl-preinit.c | 40 +++++---- elf/dynamic-link.h | 13 --- elf/link.h | 20 ++--- sysdeps/generic/ldsodefs.h | 9 +- sysdeps/generic/libc-start.c | 10 +-- sysdeps/i386/dl-machine.h | 64 ++++++-------- sysdeps/unix/sysv/linux/Dist | 1 - sysdeps/unix/sysv/linux/init-first.c | 5 +- sysdeps/unix/sysv/linux/init-first.h | 50 ----------- 17 files changed, 422 insertions(+), 280 deletions(-) delete mode 100644 sysdeps/unix/sysv/linux/init-first.h diff --git a/ChangeLog b/ChangeLog index 78d37a776b..585d086893 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,48 @@ +2000-03-30 Ulrich Drepper + + 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-29 Ulrich Drepper * iconvdata/testdata/CSA_Z243.4-1985-1..UCS-2BE: Renamed from diff --git a/elf/Versions b/elf/Versions index b3d9d43a91..ca9b6974a6 100644 --- a/elf/Versions +++ b/elf/Versions @@ -21,7 +21,7 @@ ld.so { # Those are in the dynamic linker, but used by libc.so. __libc_enable_secure; _dl_catch_error; _dl_check_all_versions; _dl_debug_initialize; _dl_debug_state; _dl_default_scope; - _dl_global_scope_end; _dl_init_next; _dl_lookup_symbol; + _dl_global_scope_end; _dl_lookup_symbol; _dl_map_object; _dl_map_object_deps; _dl_object_relocation_scope; _dl_relocate_object; _dl_signal_error; _dl_starting_up; _dl_sysdep_start; _r_debug; @@ -49,6 +49,6 @@ ld.so { _dl_dst_count; _dl_dst_substitute; } GLIBC_2.2 { - _dl_preinit_next; + _dl_preinit_next; _dl_init; } } diff --git a/elf/dl-close.c b/elf/dl-close.c index 0636d2e16b..7740787a40 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, 1998, 1999 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999, 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 @@ -27,6 +27,10 @@ #include +/* Type of the constructor functions. */ +typedef void (*fini_t) (void); + + /* During the program run we must not modify the global data of loaded shared object simultanously in two threads. Therefore we protect `dlopen' and `dlclose' in dlclose.c. */ @@ -64,9 +68,9 @@ _dl_close (void *_map) /* Call all termination functions at once. */ for (i = 0; i < nsearchlist; ++i) { - struct link_map *imap = list[i]; + struct link_map *imap = map->l_initfini[i]; if (imap->l_opencount == 1 && imap->l_type == lt_loaded - && imap->l_info[DT_FINI] + && (imap->l_info[DT_FINI] || imap->l_info[DT_FINI_ARRAY]) /* Skip any half-cooked objects that were never initialized. */ && imap->l_init_called) { @@ -74,9 +78,25 @@ _dl_close (void *_map) 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)) (); + 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)) ((void *) imap->l_addr + + imap->l_info[DT_FINI]->d_un.d_ptr)) (); } } @@ -157,14 +177,13 @@ _dl_close (void *_map) while (lnp != NULL); /* Remove the searchlists. */ - if (imap->l_searchlist.r_duplist != imap->l_searchlist.r_list) + if (imap != map) { - /* 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->l_searchlist.r_list != NULL) + free (imap->l_searchlist.r_list); + else if (imap->l_initfini != NULL) + free (imap->l_initfini); } - 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); diff --git a/elf/dl-deps.c b/elf/dl-deps.c index 26dd0f80a9..dc372e6cd9 100644 --- a/elf/dl-deps.c +++ b/elf/dl-deps.c @@ -192,6 +192,17 @@ _dl_map_object_deps (struct link_map *map, for (runp = known; runp; ) { struct link_map *l = runp->map; + struct link_map **needed = NULL; + unsigned int nneeded = 0; + + /* Unless otherwise stated, this object is handled. */ + runp->done = 1; + + /* Allocate a temporary record to contain the references to the + dependencies of this object. */ + if (l->l_searchlist.r_list == NULL && l != map && l->l_ldnum > 0) + needed = (struct link_map **) alloca (l->l_ldnum + * sizeof (struct link_map *)); if (l->l_info[DT_NEEDED] || l->l_info[AUXTAG] || l->l_info[FILTERTAG]) { @@ -200,9 +211,6 @@ _dl_map_object_deps (struct link_map *map, struct list *orig; const ElfW(Dyn) *d; - /* Mark map as processed. */ - runp->done = 1; - args.strtab = strtab; args.map = l; args.trace_mode = trace_mode; @@ -250,6 +258,10 @@ _dl_map_object_deps (struct link_map *map, /* Set the mark bit that says it's already in the list. */ dep->l_reserved = 1; } + + /* Remember this dependency. */ + if (needed != NULL) + needed[nneeded++] = dep; } else if (d->d_tag == DT_AUXILIARY || d->d_tag == DT_FILTER) { @@ -320,6 +332,10 @@ _dl_map_object_deps (struct link_map *map, orig->done = 0; orig->map = args.aux; + /* Remember this dependency. */ + if (needed != NULL) + needed[nneeded++] = args.aux; + /* We must handle two situations here: the map is new, so we must add it in all three lists. If the map is already known, we have two further possibilities: @@ -427,9 +443,19 @@ _dl_map_object_deps (struct link_map *map, ++nduplist; } } - else - /* Mark as processed. */ - runp->done = 1; + + /* Terminate the list of dependencies and store the array address. */ + if (needed != NULL) + { + needed[nneeded++] = NULL; + + l->l_initfini = + (struct link_map **) malloc (nneeded * sizeof (struct link_map)); + if (l->l_initfini == NULL) + _dl_signal_error (ENOMEM, map->l_name, + "cannot allocate dependency list"); + memcpy (l->l_initfini, needed, nneeded * sizeof (struct link_map)); + } /* If we have no auxiliary objects just go on to the next map. */ if (runp->done) @@ -440,7 +466,9 @@ _dl_map_object_deps (struct link_map *map, /* Store the search list we built in the object. It will be used for searches in the scope of this object. */ - map->l_searchlist.r_list = malloc (nlist * sizeof (struct link_map *)); + map->l_searchlist.r_list = malloc ((2 * nlist + + (nlist == nduplist ? 0 : nduplist)) + * sizeof (struct link_map *)); if (map->l_searchlist.r_list == NULL) _dl_signal_error (ENOMEM, map->l_name, "cannot allocate symbol search list"); @@ -466,11 +494,7 @@ _dl_map_object_deps (struct link_map *map, { unsigned int cnt; - map->l_searchlist.r_duplist = malloc (nduplist - * sizeof (struct link_map *)); - if (map->l_searchlist.r_duplist == NULL) - _dl_signal_error (ENOMEM, map->l_name, - "cannot allocate symbol search list"); + map->l_searchlist.r_duplist = map->l_searchlist.r_list + nlist; for (cnt = 0, runp = known; runp; runp = runp->dup) if (trace_mode && runp->map->l_opencount == 0) @@ -479,4 +503,51 @@ _dl_map_object_deps (struct link_map *map, else map->l_searchlist.r_duplist[cnt++] = runp->map; } + + /* Now determine the order in which the initialization has to happen. */ + map->l_initfini = + (struct link_map **) memcpy (map->l_searchlist.r_duplist + nduplist, + map->l_searchlist.r_list, + nlist * sizeof (struct link_map *)); + /* We can skip looking for the binary itself which is at the front + of the search list. Look through the list backward so that circular + dependencies are not changing the order. */ + for (i = 1; i < nlist; ++i) + { + struct link_map *l = map->l_searchlist.r_list[i]; + unsigned int j; + unsigned int k; + + /* Find the place in the initfini list where the map is currently + located. */ + for (j = 1; map->l_initfini[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 < nlist; ++k) + { + struct link_map **runp; + + runp = map->l_initfini[k]->l_initfini; + if (runp != NULL) + { + while (*runp != NULL) + if (*runp == l) + { + struct link_map *here = map->l_initfini[k]; + + /* Move it now. */ + memmove (&map->l_initfini[j] + 1, + &map->l_initfini[j], + (k - j) * sizeof (struct link_map *)); + map->l_initfini[j] = here; + + break; + } + else + ++runp; + } + } + } } diff --git a/elf/dl-fini.c b/elf/dl-fini.c index 41637c5977..61dedd5168 100644 --- a/elf/dl-fini.c +++ b/elf/dl-fini.c @@ -17,59 +17,128 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include +#include #include + +/* 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])) (); + } - for (l = _dl_loaded; l; l = l->l_next) - if (l->l_init_called) - { - int first = 1; - - /* 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; - - /* 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) - { - /* When debugging print a message first. */ - if (_dl_debug_impcalls && first) - _dl_debug_message (1, "\ncalling fini: ", - l->l_name[0] ? l->l_name : _dl_argv[0], - "\n\n", NULL); - first = 0; - - (*(void (*) (void)) (l->l_addr + array[cnt])) (); - } - } - - /* Next try the old-style destructor. */ - if (l->l_info[DT_FINI]) - { - /* When debugging print a message first. */ - if (_dl_debug_impcalls && first) - _dl_debug_message (1, "\ncalling fini: ", - l->l_name[0] ? l->l_name : _dl_argv[0], - "\n\n", NULL); - - (*(void (*) (void)) (l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr)) (); - } - } + /* 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)) (); + } + } } diff --git a/elf/dl-init.c b/elf/dl-init.c index c1e927180c..c50c16a8d4 100644 --- a/elf/dl-init.c +++ b/elf/dl-init.c @@ -21,82 +21,112 @@ #include -/* Run initializers for MAP and its dependencies, in inverse dependency - order (that is, leaf nodes first). */ +/* Type of the initializer. */ +typedef void (*init_t) (int, char **, char **); -ElfW(Addr) + +static void internal_function -_dl_init_next (struct r_scope_elem *searchlist) +_dl_init_rec (struct link_map *map, int argc, char **argv, char **env) { unsigned int i; - /* The search list for symbol lookup is a flat list in top-down - dependency order, so processing that list from back to front gets us - breadth-first leaf-to-root order. */ + /* Stupid users forces the ELF specification to be changed. It now + says that the dynamic loader is responsible for determining the + order in which the constructors have to run. The constructors + for all dependencies of an object must run before the constructor + for the object itself. Circular dependencies are left unspecified. + + This is highly questionable since it puts the burden on the dynamic + loader which has to find the dependencies at runtime instead of + letting the user do it right. Stupidity rules! */ - i = searchlist->r_nlist; + i = map->l_searchlist.r_nlist; while (i-- > 0) { - struct link_map *l = searchlist->r_list[i]; - ElfW(Addr) *array; + struct link_map *l = map->l_initfini[i]; + int message_written; + init_t init; if (l->l_init_called) /* This object is all done. */ continue; - /* Check for object which constructors we do not run here. - XXX Maybe this should be pre-computed, but where? */ - if (l->l_name[0] == '\0' && l->l_type == lt_executable) - { - l->l_init_called = 1; - continue; - } + /* Avoid handling this constructor again in case we have a circular + dependency. */ + l->l_init_called = 1; - /* Account for running next constructor. */ - ++l->l_runcount; + /* Check for object which constructors we do not run here. */ + if (l->l_name[0] == '\0' && l->l_type == lt_executable) + continue; - if (l->l_runcount == 1) + /* See whether any dependent objects are not yet initialized. + XXX Is this necessary? I'm not sure anymore... */ + if (l->l_searchlist.r_nlist > 1) + _dl_init_rec (l, argc, argv, env); + + /* Now run the local constructors. There are several of them: + - the one named by DT_INIT + - the others in the DT_INIT_ARRAY. + */ + message_written = 0; + if (l->l_info[DT_INIT]) { - /* Try running the DT_INIT constructor. */ - if (l->l_info[DT_INIT]) + /* Print a debug message if wanted. */ + if (_dl_debug_impcalls) { - /* Print a debug message if wanted. */ - if (_dl_debug_impcalls) - _dl_debug_message (1, "\ncalling init: ", - l->l_name[0] ? l->l_name : _dl_argv[0], - "\n\n", NULL); - - return l->l_addr + l->l_info[DT_INIT]->d_un.d_ptr; + _dl_debug_message (1, "\ncalling init: ", + l->l_name[0] ? l->l_name : _dl_argv[0], + "\n\n", NULL); + message_written = 1; } - /* No DT_INIT, so go on with the array. */ - ++l->l_runcount; + init = (init_t) (l->l_addr + l->l_info[DT_INIT]->d_un.d_ptr); + + /* Call the function. */ + init (argc, argv, env); } - if (l->l_runcount > l->l_initcount) + /* Next see whether there is an array with initialiazation functions. */ + if (l->l_info[DT_INIT_ARRAY]) { - /* That were all of the constructors. */ - l->l_runcount = 0; - l->l_init_called = 1; - continue; - } + unsigned int j; + unsigned int jm; + ElfW(Addr) *addrs; - /* Print a debug message if wanted. */ - if (_dl_debug_impcalls && l->l_info[DT_INIT] == NULL - && l->l_runcount == 2) - _dl_debug_message (1, "\ncalling init: ", - l->l_name[0] ? l->l_name : _dl_argv[0], - "\n\n", NULL); + jm = l->l_info[DT_INIT_ARRAYSZ]->d_un.d_val / sizeof (ElfW(Addr)); - array = (ElfW(Addr) *) D_PTR (l, l_info[DT_INIT_ARRAY]); - return l->l_addr + array[l->l_runcount - 2]; - /* NOTREACHED */ + if (jm > 0 && _dl_debug_impcalls && ! message_written) + _dl_debug_message (1, "\ncalling init: ", + l->l_name[0] ? l->l_name : _dl_argv[0], + "\n\n", NULL); + + addrs = (ElfW(Addr) *) (l->l_info[DT_INIT_ARRAY]->d_un.d_ptr + + l->l_addr); + for (j = 0; j < jm; ++j) + ((init_t) addrs[j]) (argc, argv, env); + } } +} - /* Notify the debugger all new objects are now ready to go. */ - _r_debug.r_state = RT_CONSISTENT; +void +internal_function +_dl_init (struct link_map *main_map, int argc, char **argv, char **env) +{ + struct r_debug *r; + + /* Notify the debugger we have added some objects. We need to call + _dl_debug_initialize in a static program in case dynamic linking has + not been used before. */ + r = _dl_debug_initialize (0); + r->r_state = RT_ADD; _dl_debug_state (); - return 0; + /* Recursively call the constructors. */ + _dl_init_rec (main_map, argc, argv, env); + + /* Notify the debugger all new objects are now ready to go. */ + r->r_state = RT_CONSISTENT; + _dl_debug_state (); } diff --git a/elf/dl-load.c b/elf/dl-load.c index 0ec215223c..38922890d6 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -858,6 +858,7 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname, verbatim, and later correct for the run-time load address. */ case PT_DYNAMIC: l->l_ld = (void *) ph->p_vaddr; + l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn)); break; case PT_PHDR: l->l_phdr = (void *) ph->p_vaddr; diff --git a/elf/dl-open.c b/elf/dl-open.c index c497d2e34e..b419219069 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -85,8 +85,6 @@ dl_open_worker (void *a) const char *file = args->file; int mode = args->mode; struct link_map *new, *l; - ElfW(Addr) init; - struct r_debug *r; const char *dst; int lazy; @@ -194,17 +192,8 @@ dl_open_worker (void *a) l = l->l_prev; } - /* Notify the debugger we have added some objects. We need to call - _dl_debug_initialize in a static program in case dynamic linking has - not been used before. */ - r = _dl_debug_initialize (0); - r->r_state = RT_ADD; - _dl_debug_state (); - /* Run the initializer functions of new objects. */ - while ((init = _dl_init_next (&new->l_searchlist))) - (*(void (*) (int, char **, char **)) init) (__libc_argc, __libc_argv, - __environ); + _dl_init (new, __libc_argc, __libc_argv, __environ); /* Now we can make the new map available in the global scope. */ if (mode & RTLD_GLOBAL) diff --git a/elf/dl-preinit.c b/elf/dl-preinit.c index 8ef4afc5b6..fa8ce7e085 100644 --- a/elf/dl-preinit.c +++ b/elf/dl-preinit.c @@ -21,29 +21,35 @@ #include +/* Type of the initializer. */ +typedef void (*init_t) (int, char **, char **); + + /* Run initializers for MAP and its dependencies, in inverse dependency order (that is, leaf nodes first). */ -ElfW(Addr) +void internal_function -_dl_preinit_next (struct r_scope_elem *searchlist) +_dl_preinit (struct link_map *main_map, int argc, char **argv, char **env) { - struct link_map *l = searchlist->r_list[0]; - ElfW(Addr) *array; + /* Don't do anything if there is no preinit array. */ + ElfW(Dyn) *preinit_array = main_map->l_info[DT_PREINIT_ARRAYSZ]; + unsigned int max; - if (l->l_runcount >= l->l_preinitcount) + if (preinit_array != NULL + && (max = preinit_array->d_un.d_val / sizeof (ElfW(Addr))) > 0) { - /* That were all of the constructors. */ - l->l_runcount = 0; - return 0; + ElfW(Addr) *addrs; + unsigned int cnt; + + if (_dl_debug_impcalls) + _dl_debug_message (1, "\ncalling preinit: ", + main_map->l_name[0] + ? main_map->l_name : _dl_argv[0], "\n\n", NULL); + + addrs = (ElfW(Addr) *) (main_map->l_info[DT_PREINIT_ARRAY]->d_un.d_ptr + + main_map->l_addr); + for (cnt = 0; cnt < max; ++cnt) + ((init_t) addrs[cnt]) (argc, argv, env); } - - /* Print a debug message if wanted. */ - if (_dl_debug_impcalls && l->l_runcount == 0) - _dl_debug_message (1, "\ncalling preinit: ", - l->l_name[0] ? l->l_name : _dl_argv[0], - "\n\n", NULL); - - array = (ElfW(Addr) *) l->l_info[DT_PREINIT_ARRAY]->d_un.d_ptr; - return l->l_addr + array[l->l_runcount++]; } diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h index cd30656ff3..5987707e9c 100644 --- a/elf/dynamic-link.h +++ b/elf/dynamic-link.h @@ -114,19 +114,6 @@ elf_get_dynamic_info (struct link_map *l) if (flags & DF_BIND_NOW) info[DT_BIND_NOW] = info[DT_FLAGS]; } -#ifndef DL_RO_DYN_SECTION - /* Determine how many constructors there are. */ - if (info[DT_INIT_ARRAY] != NULL) - info[DT_INIT_ARRAY]->d_un.d_ptr += l_addr; -#endif - l->l_initcount = 1 + (info[DT_INIT_ARRAY] - ? (info[DT_INIT_ARRAYSZ]->d_un.d_val - / sizeof (ElfW(Addr))) - : 0); - l->l_preinitcount = (info[DT_PREINIT_ARRAY] - ? (info[DT_PREINIT_ARRAYSZ]->d_un.d_val - / sizeof (ElfW(Addr))) - : 0); if (info[DT_RUNPATH] != NULL) /* If both RUNPATH and RPATH are given, the latter is ignored. */ info[DT_RPATH] = NULL; diff --git a/elf/link.h b/elf/link.h index ac53a204a6..e4c2fea1f9 100644 --- a/elf/link.h +++ b/elf/link.h @@ -1,6 +1,6 @@ /* Data structure for communication from the run-time dynamic linker for loaded ELF shared objects. - Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + Copyright (C) 1995-1999, 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 @@ -137,6 +137,7 @@ struct link_map const ElfW(Phdr) *l_phdr; /* Pointer to program header table in core. */ ElfW(Addr) l_entry; /* Entry point location. */ ElfW(Half) l_phnum; /* Number of program header entries. */ + ElfW(Half) l_ldnum; /* Number of dynamic segment entries. */ /* Array of DT_NEEDED dependencies and their dependencies, in dependency order for symbol lookup (with and without @@ -166,6 +167,8 @@ struct link_map unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */ unsigned int l_global:1; /* Nonzero if object in _dl_global_scope. */ unsigned int l_reserved:2; /* Reserved for internal use. */ + unsigned int l_phdr_allocated:1; /* Nonzero if the data structure pointed + to by `l_phdr' is allocated. */ /* Array with version names. */ unsigned int l_nversions; @@ -200,22 +203,11 @@ struct link_map dev_t l_dev; ino_t l_ino; - /* Nonzero if the data structure pointed to by `l_phdr' is allocated. */ - int l_phdr_allocated; - - /* Counter for running constructors and destructors. */ - unsigned int l_runcount; - - /* Number of constructors. We compute this during loading to avoid - duplication of this during the possibly many calls to _dl_init_next. */ - unsigned int l_initcount; - /* Collected information about own RUNPATH directories. */ struct r_search_path_elem **l_runpath_dirs; - /* Number of pre-constructors. We compute this during loading to avoid - duplication of this during the possibly many calls to _dl_init_next. */ - unsigned int l_preinitcount; + /* List of object in order of the init and fini calls. */ + struct link_map **l_initfini; }; #endif /* link.h */ diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index bcc74b3ab2..bc4ece2a7e 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -354,11 +354,10 @@ extern int _dl_check_all_versions (struct link_map *map, int verbose) extern int _dl_check_map_versions (struct link_map *map, int verbose) internal_function; -/* Return the address of the next initializer function for SCOPE or one of - its dependencies that has not yet been run. When there are no more - initializers to be run, this returns zero. The functions are returned - in the order they should be called. */ -extern ElfW(Addr) _dl_init_next (struct r_scope_elem *scope) internal_function; +/* Initialize the object in SCOPE by calling the constructors with + ARGC, ARGV, and ENV as the parameters. */ +extern void _dl_init (struct link_map *main_map, int argc, char **argv, + char **env) internal_function; /* Call the finalizer functions of all shared objects whose initializer functions have completed. */ diff --git a/sysdeps/generic/libc-start.c b/sysdeps/generic/libc-start.c index bcb3cf3f2d..e3960ca818 100644 --- a/sysdeps/generic/libc-start.c +++ b/sysdeps/generic/libc-start.c @@ -63,12 +63,12 @@ __libc_start_main (int (*main) (int, char **, char **), int argc, if (rtld_fini != NULL) atexit (rtld_fini); - /* Call the initializer of the libc. */ -#ifdef PIC - if (_dl_debug_impcalls) - _dl_debug_message (1, "\ninitialize libc\n\n", NULL); -#endif + /* Call the initializer of the libc. This is only needed here if we + are compiling for the static library in which case we haven't + run the constructors in `_dl_start_user'. */ +#ifndef PIC __libc_init_first (argc, argv, __environ); +#endif /* Register the destructor of the program, if any. */ if (fini) diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h index 351b82af32..1256fbcdc0 100644 --- a/sysdeps/i386/dl-machine.h +++ b/sysdeps/i386/dl-machine.h @@ -1,5 +1,5 @@ /* Machine-dependent ELF dynamic relocation inline functions. i386 version. - Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997, 1998, 1999, 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 @@ -217,47 +217,33 @@ _dl_start_user:\n\ movl _dl_skip_args@GOT(%ebx), %eax\n\ movl (%eax), %eax\n\ # Pop the original argument count.\n\ - popl %ecx\n\ + popl %esi\n\ # Subtract _dl_skip_args from it.\n\ - subl %eax, %ecx\n\ + subl %eax, %esi\n\ # Adjust the stack pointer to skip _dl_skip_args words.\n\ leal (%esp,%eax,4), %esp\n\ - # Push back the modified argument count.\n\ - pushl %ecx\n\ - # Push the searchlist of the main object as argument in\n\ - # _dl_init_next call below.\n\ - movl _dl_main_searchlist@GOT(%ebx), %eax\n\ - movl (%eax), %esi\n\ - # First run the pre-initializers.\n\ -0: movl %esi,%eax\n\ - # Call _dl_init_next to return the address of an initializer\n\ - # function to run.\n\ - call _dl_preinit_next@PLT\n\ - # Check for zero return, when out of initializers.\n\ - testl %eax, %eax\n\ - jz 0f\n\ - # Call the pre-initilizer.\n\ - call *%eax\n\ - # Loop to call _dl_preinit_next for the next initializer.\n\ - jmp 0b\n\ -0: movl %esi,%eax\n\ - # Call _dl_init_next to return the address of an initializer\n\ - # function to run.\n\ - call _dl_init_next@PLT\n\ - # Check for zero return, when out of initializers.\n\ - testl %eax, %eax\n\ - jz 1f\n\ - # Call the shared object initializer function.\n\ - # NOTE: We depend only on the registers (%ebx, %esi and %edi)\n\ - # and the return address pushed by this call;\n\ - # the initializer is called with the stack just\n\ - # as it appears on entry, and it is free to move\n\ - # the stack around, as long as it winds up jumping to\n\ - # the return address on the top of the stack.\n\ - call *%eax\n\ - # Loop to call _dl_init_next for the next initializer.\n\ - jmp 0b\n\ -1: # Clear the startup flag.\n\ + # Move the argv pointer in a register.\n\ + leal 4(%esp,%esi,4), %edx\n\ + movl %esp, %ecx\n\ + pushl %edx\n\ + movl %esi, %edx\n\ + # Get the searchlist of the main object as argument for\n\ + # _dl_preinit and _dl_init calls below.\n\ + movl _dl_loaded@GOT(%ebx), %ebp\n\ + movl (%ebp), %eax\n\ + # Call the function to run the pre-initializers.\n\ + call _dl_preinit@PLT\n\ + # Load the parameters again.\n\ + leal 4(%esp,%esi,4), %edx\n\ + movl %esp, %ecx\n\ + pushl %edx\n\ + movl %esi, %edx\n\ + movl (%ebp), %eax\n\ + # Call the function to run the initializers.\n\ + call _dl_init@PLT\n\ + # Push argc back on the stack.\n\ + push %esi\n\ + # Clear the startup flag.\n\ movl _dl_starting_up@GOT(%ebx), %eax\n\ movl $0, (%eax)\n\ # Pass our finalizer function to the user in %edx, as per ELF ABI.\n\ diff --git a/sysdeps/unix/sysv/linux/Dist b/sysdeps/unix/sysv/linux/Dist index 87d9044015..06754f8a16 100644 --- a/sysdeps/unix/sysv/linux/Dist +++ b/sysdeps/unix/sysv/linux/Dist @@ -3,7 +3,6 @@ cmsg_nxthdr.c errlist.h getdirentries.c getdirentries64.c -init-first.h ipc_priv.h kernel-features.h kernel_sigaction.h diff --git a/sysdeps/unix/sysv/linux/init-first.c b/sysdeps/unix/sysv/linux/init-first.c index 9a8f3c5fb2..a04234864d 100644 --- a/sysdeps/unix/sysv/linux/init-first.c +++ b/sysdeps/unix/sysv/linux/init-first.c @@ -1,5 +1,5 @@ /* Initialization code run first thing by the ELF startup code. Linux version. - Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + Copyright (C) 1995-1999, 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 @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -156,7 +155,7 @@ init (int argc, char **argv, char **envp) #ifdef PIC -SYSDEP_CALL_INIT(_init, init); +strong_alias (init, _init); void __libc_init_first (void) diff --git a/sysdeps/unix/sysv/linux/init-first.h b/sysdeps/unix/sysv/linux/init-first.h deleted file mode 100644 index aaf8a4f4dc..0000000000 --- a/sysdeps/unix/sysv/linux/init-first.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Prepare arguments for library initialization function. - Copyright (C) 1997, 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 - 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. */ - -/* The job of this fragment it to find argc and friends for INIT. - This is done in one of two ways: either in the stack context - of program start, or having dlopen pass them in. */ - -#define SYSDEP_CALL_INIT(NAME, INIT) \ -void NAME (void *arg) \ -{ \ - int argc; \ - char **argv, **envp; \ - /* The next variable is only here to work around a bug in gcc <= 2.7.2.2. \ - If the address would be taken inside the expression the optimizer \ - would try to be too smart and throws it away. Grrr. */ \ - int *dummy_addr = &_dl_starting_up; \ - \ - __libc_multiple_libcs = dummy_addr && !_dl_starting_up; \ - \ - if (!__libc_multiple_libcs) \ - { \ - argc = (int) (long int) arg; \ - argv = (char **) &arg + 1; \ - envp = &argv[argc+1]; \ - } \ - else \ - { \ - argc = (int) (long int) arg; \ - argv = ((char ***) &arg)[1]; \ - envp = ((char ***) &arg)[2]; \ - } \ - \ - INIT (argc, argv, envp); \ -} -- cgit 1.4.1