From c4bb124a75b23d1a82cee232005dc7e8c43a4276 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sat, 8 Sep 2001 16:27:08 +0000 Subject: [PATCH] Update. 2001-09-08 Ulrich Drepper * elf/dl-close.c (_dl_close): If object has no r_list (i.e., wasn't loaded directly) determine length if l_initfini list by iterating over its elements. Minor optimizations. * elf/dl-deps.c (_dl_map_object_deps): Always add own map to l_initfini for dependency objects. If object was already loaded check whether any of the dependencies is already on the relocation dependency list. If yes, remove the latter. Minor optimizations. * elf/dl-lookup.c (add_dependency): Add check for self reference of maps here. Search l_initfini list only if the object was loaded directly and not only as a dependency. (_dl_lookup_symbol): Add relocation dependency also if object is not in global scope. Remove test for self-reference here. (_dl_lookup_versioned_symbol): Likewise. * elf/dl-object (_dl_new_object): Cleanup. Initialize dont_free element of first name record. * elf/loadtest.c: Add some more test to recognize early if an object wasn't unloaded. * elf/Makefile: Add rules to build and run reldep5. * elf/reldep5.c: New file. * elf/reldepmod5.c: New file. * elf/reldepmod6.c: New file. * elf/reldep2.c: Fix typo. * elf/dl-object.c (_dl_new_object): Initialize l_scope and l_scope_max. --- ChangeLog | 29 ++++++++- elf/Makefile | 10 +++- elf/dl-close.c | 24 ++++---- elf/dl-deps.c | 34 +++++++++-- elf/dl-lookup.c | 150 +++++++++++++++++++++++------------------------ elf/dl-object.c | 12 ++-- elf/loadtest.c | 22 ++++++- elf/reldep5.c | 71 ++++++++++++++++++++++ elf/reldepmod5.c | 5 ++ elf/reldepmod6.c | 7 +++ 10 files changed, 264 insertions(+), 100 deletions(-) create mode 100644 elf/reldep5.c create mode 100644 elf/reldepmod5.c create mode 100644 elf/reldepmod6.c diff --git a/ChangeLog b/ChangeLog index 21653dc4d9..81ba6caab6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,35 @@ +2001-09-08 Ulrich Drepper + + * elf/dl-close.c (_dl_close): If object has no r_list (i.e., wasn't + loaded directly) determine length if l_initfini list by iterating + over its elements. Minor optimizations. + * elf/dl-deps.c (_dl_map_object_deps): Always add own map to l_initfini + for dependency objects. + If object was already loaded check whether any of the dependencies + is already on the relocation dependency list. If yes, remove the + latter. Minor optimizations. + * elf/dl-lookup.c (add_dependency): Add check for self reference of + maps here. Search l_initfini list only if the object was loaded + directly and not only as a dependency. + (_dl_lookup_symbol): Add relocation dependency also if object + is not in global scope. Remove test for self-reference here. + (_dl_lookup_versioned_symbol): Likewise. + * elf/dl-object (_dl_new_object): Cleanup. Initialize dont_free + element of first name record. + * elf/loadtest.c: Add some more test to recognize early if an object + wasn't unloaded. + * elf/Makefile: Add rules to build and run reldep5. + * elf/reldep5.c: New file. + * elf/reldepmod5.c: New file. + * elf/reldepmod6.c: New file. + + * elf/reldep2.c: Fix typo. + 2001-09-07 Ulrich Drepper * include/link.h (struct link_map): Add l_scope_mem and l_scope_max elements. Change l_scope to be a pointer only. - * elf/dl-object.c (_dl_new_ojbect): Initialize l_scope and l_scope_max. + * elf/dl-object.c (_dl_new_object): Initialize l_scope and l_scope_max. * elf/dl-open.c (dl_open_worker): If dependency wasn't just opened here add searchlist of newly open file to the dependency's scope. * elf/dl-close.c (_dl_close): If dependency is used otherwise remove diff --git a/elf/Makefile b/elf/Makefile index 160d901526..d4fc54e010 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -55,6 +55,7 @@ distribute := $(rtld-routines:=.c) dynamic-link.h do-rel.h dl-machine.h \ nodlopenmod.c nodelete.c nodelmod1.c nodelmod2.c \ nodelmod3.c nodelmod4.c nodlopen.c dl-osinfo.h \ reldepmod1.c reldepmod2.c reldepmod3.c reldepmod4.c \ + reldepmod5.c reldepmod6.c \ reldep4mod1.c reldep4mod2.c reldep4mod3.c reldep4mod4.c \ nextmod1.c nextmod2.c pathoptobj.c tst-pathopt.sh \ neededobj1.c neededobj2.c neededobj3.c neededobj4.c \ @@ -106,7 +107,7 @@ tests = loadtest restest1 preloadtest loadfail multiload origtest resolvfail \ reldep reldep2 reldep3 reldep4 $(tests-nodelete-$(have-z-nodelete)) \ $(tests-nodlopen-$(have-z-nodlopen)) neededtest neededtest2 \ neededtest3 neededtest4 unload2 lateglobal initfirst global \ - restest2 next dblload dblunload + restest2 next dblload dblunload reldep5 test-srcs = tst-pathopt tests-vis-yes = vismain tests-nodelete-yes = nodelete @@ -122,7 +123,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ neededobj1 neededobj2 neededobj3 neededobj4 \ neededobj5 neededobj6 firstobj globalmod1 \ unload2mod unload2dep ltglobmod1 ltglobmod2 pathoptobj \ - dblloadmod1 dblloadmod2 dblloadmod3 + dblloadmod1 dblloadmod2 dblloadmod3 reldepmod5 reldepmod6 modules-vis-yes = vismod1 vismod2 vismod3 modules-nodelete-yes = nodelmod1 nodelmod2 nodelmod3 nodelmod4 modules-nodlopen-yes = nodlopenmod @@ -285,6 +286,8 @@ $(objpfx)reldep4mod1.so: $(objpfx)reldep4mod3.so $(objpfx)reldep4mod2.so: $(objpfx)reldep4mod4.so $(objpfx)dblloadmod1.so: $(objpfx)dblloadmod3.so $(objpfx)dblloadmod2.so: $(objpfx)dblloadmod3.so +$(objpfx)reldepmod5.so: $(objpfx)reldepmod2.so +$(objpfx)reldepmod6.so: $(objpfx)reldepmod2.so # filtmod1.so has a special rule $(filter-out $(objpfx)filtmod1.so, $(test-modules)): $(objpfx)%.so: $(objpfx)%.os @@ -423,3 +426,6 @@ $(objpfx)dblload.out: $(objpfx)dblloadmod1.so $(objpfx)dblloadmod2.so $(objpfx)dblunload: $(libdl) $(objpfx)dblunload.out: $(objpfx)dblloadmod1.so $(objpfx)dblloadmod2.so + +$(objpfx)reldep5: $(libdl) +$(objpfx)reldep5.out: $(objpfx)reldepmod5.so $(objpfx)reldepmod5.so diff --git a/elf/dl-close.c b/elf/dl-close.c index dfc204d478..31b4863afe 100644 --- a/elf/dl-close.c +++ b/elf/dl-close.c @@ -48,7 +48,8 @@ _dl_close (void *_map) unsigned int *new_opencount; /* First see whether we can remove the object at all. */ - if ((map->l_flags_1 & DF_1_NODELETE) && map->l_init_called) + if (__builtin_expect (map->l_flags_1 & DF_1_NODELETE, 0) + && map->l_init_called) /* Nope. Do nothing. */ return; @@ -63,14 +64,8 @@ _dl_close (void *_map) { /* There are still references to this object. Do nothing more. */ if (__builtin_expect (_dl_debug_mask & DL_DEBUG_FILES, 0)) - { - char buf[20]; - - buf[sizeof buf - 1] = '\0'; - - _dl_debug_printf ("\nclosing file=%s; opencount == %u\n", - map->l_name, map->l_opencount); - } + _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; @@ -82,8 +77,15 @@ _dl_close (void *_map) list = map->l_initfini; /* Compute the new l_opencount values. */ - new_opencount = (unsigned int *) alloca (map->l_searchlist.r_nlist - * sizeof (unsigned int)); + 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; diff --git a/elf/dl-deps.c b/elf/dl-deps.c index c4bc9874cc..9d91d5ef00 100644 --- a/elf/dl-deps.c +++ b/elf/dl-deps.c @@ -448,11 +448,13 @@ _dl_map_object_deps (struct link_map *map, { needed[nneeded++] = NULL; - l->l_initfini = malloc (nneeded * sizeof needed[0]); + l->l_initfini = (struct link_map **) + malloc ((nneeded + 1) * sizeof needed[0]); if (l->l_initfini == NULL) _dl_signal_error (ENOMEM, map->l_name, NULL, N_("cannot allocate dependency list")); - memcpy (l->l_initfini, needed, nneeded * sizeof needed[0]); + l->l_initfini[0] = l; + memcpy (&l->l_initfini[1], needed, nneeded * sizeof needed[0]); } /* If we have no auxiliary objects just go on to the next map. */ @@ -462,7 +464,7 @@ _dl_map_object_deps (struct link_map *map, while (runp != NULL && runp->done); } -out: + out: if (errno == 0 && errno_saved != 0) __set_errno (errno_saved); @@ -489,7 +491,7 @@ out: for (nlist = 0, runp = known; runp; runp = runp->next) { - if (trace_mode && runp->map->l_faked) + if (__builtin_expect (trace_mode, 0) && runp->map->l_faked) /* This can happen when we trace the loading. */ --map->l_searchlist.r_nlist; else @@ -500,6 +502,30 @@ out: runp->map->l_reserved = 0; } + /* Maybe we can remove some relocation dependencies now. */ + assert (map->l_searchlist.r_list[0] == map); + for (i = 0; i < map->l_reldepsact; ++i) + { + unsigned int j; + + for (j = 1; j < nlist; ++j) + if (map->l_searchlist.r_list[j] == map->l_reldeps[i]) + { + /* A direct or transitive dependency is also on the list + of relocation dependencies. Remove the latter. */ + --map->l_reldeps[i]->l_opencount; + + for (j = i + 1; j < map->l_reldepsact; ++j) + map->l_reldeps[j - 1] = map->l_reldeps[j]; + + --map->l_reldepsact; + + /* Account for the '++i' performed by the 'for'. */ + --i; + break; + } + } + /* Now determine the order in which the initialization has to happen. */ memcpy (map->l_initfini, map->l_searchlist.r_list, nlist * sizeof (struct link_map *)); diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c index 2faa9fb99c..26c839caa7 100644 --- a/elf/dl-lookup.c +++ b/elf/dl-lookup.c @@ -79,92 +79,94 @@ internal_function add_dependency (struct link_map *undef_map, struct link_map *map) { struct link_map **list; + struct link_map *runp; unsigned int act; unsigned int i; int result = 0; + /* Avoid self-references. */ + if (undef_map == map) + return 0; + /* Make sure nobody can unload the object while we are at it. */ __libc_lock_lock_recursive (_dl_load_lock); /* Determine whether UNDEF_MAP already has a reference to MAP. First look in the normal dependencies. */ - list = undef_map->l_initfini; - - for (i = 0; list[i] != NULL; ++i) - if (list[i] == map) - break; - - if (__builtin_expect (list[i] == NULL, 1)) + if (undef_map->l_searchlist.r_list != NULL) { - /* No normal dependency. See whether we already had to add it - to the special list of dynamic dependencies. */ - list = undef_map->l_reldeps; - act = undef_map->l_reldepsact; + list = undef_map->l_initfini; - for (i = 0; i < act; ++i) + for (i = 0; list[i] != NULL; ++i) if (list[i] == map) - break; - - if (i == act) - { - /* The object is not yet in the dependency list. Before we add - it make sure just one more time the object we are about to - reference is still available. There is a brief period in - which the object could have been removed since we found the - definition. */ - struct link_map *runp = _dl_loaded; - - while (runp != NULL && runp != map) - runp = runp->l_next; - - if (runp != NULL) - { - /* The object is still available. Add the reference now. */ - if (__builtin_expect (act >= undef_map->l_reldepsmax, 0)) - { - /* Allocate more memory for the dependency list. Since - this can never happen during the startup phase we can - use `realloc'. */ - void *newp; - - undef_map->l_reldepsmax += 5; - newp = realloc (undef_map->l_reldeps, - undef_map->l_reldepsmax - * sizeof(struct link_map *)); - - if (__builtin_expect (newp != NULL, 1)) - undef_map->l_reldeps = (struct link_map **) newp; - else - /* Correct the addition. */ - undef_map->l_reldepsmax -= 5; - } - - /* If we didn't manage to allocate memory for the list this - is no fatal mistake. We simply increment the use counter - of the referenced object and don't record the dependencies. - This means this increment can never be reverted and the - object will never be unloaded. This is semantically the - correct behaviour. */ - if (__builtin_expect (act < undef_map->l_reldepsmax, 1)) - undef_map->l_reldeps[undef_map->l_reldepsact++] = map; - - /* And increment the counter in the referenced object. */ - ++map->l_opencount; - - /* Display information if we are debugging. */ - if (__builtin_expect (_dl_debug_mask & DL_DEBUG_FILES, 0)) - _dl_debug_printf ("\ -\nfile=%s; needed by %s (relocation dependency)\n\n", - map->l_name[0] ? map->l_name : _dl_argv[0], - undef_map->l_name[0] - ? undef_map->l_name : _dl_argv[0]); - } - else - /* Whoa, that was bad luck. We have to search again. */ - result = -1; - } + goto out; } + /* No normal dependency. See whether we already had to add it + to the special list of dynamic dependencies. */ + list = undef_map->l_reldeps; + act = undef_map->l_reldepsact; + + for (i = 0; i < act; ++i) + if (list[i] == map) + goto out; + + /* The object is not yet in the dependency list. Before we add + it make sure just one more time the object we are about to + reference is still available. There is a brief period in + which the object could have been removed since we found the + definition. */ + runp = _dl_loaded; + while (runp != NULL && runp != map) + runp = runp->l_next; + + if (runp != NULL) + { + /* The object is still available. Add the reference now. */ + if (__builtin_expect (act >= undef_map->l_reldepsmax, 0)) + { + /* Allocate more memory for the dependency list. Since this + can never happen during the startup phase we can use + `realloc'. */ + void *newp; + + undef_map->l_reldepsmax += 5; + newp = realloc (undef_map->l_reldeps, + undef_map->l_reldepsmax + * sizeof (struct link_map *)); + + if (__builtin_expect (newp != NULL, 1)) + undef_map->l_reldeps = (struct link_map **) newp; + else + /* Correct the addition. */ + undef_map->l_reldepsmax -= 5; + } + + /* If we didn't manage to allocate memory for the list this is + no fatal mistake. We simply increment the use counter of the + referenced object and don't record the dependencies. This + means this increment can never be reverted and the object + will never be unloaded. This is semantically the correct + behaviour. */ + if (__builtin_expect (act < undef_map->l_reldepsmax, 1)) + undef_map->l_reldeps[undef_map->l_reldepsact++] = map; + + /* And increment the counter in the referenced object. */ + ++map->l_opencount; + + /* Display information if we are debugging. */ + if (__builtin_expect (_dl_debug_mask & DL_DEBUG_FILES, 0)) + _dl_debug_printf ("\ +\nfile=%s; needed by %s (relocation dependency)\n\n", + map->l_name[0] ? map->l_name : _dl_argv[0], + undef_map->l_name[0] + ? undef_map->l_name : _dl_argv[0]); + } + else + /* Whoa, that was bad luck. We have to search again. */ + result = -1; + + out: /* Release the lock. */ __libc_lock_unlock_recursive (_dl_load_lock); @@ -212,8 +214,6 @@ _dl_lookup_symbol (const char *undef_name, struct link_map *undef_map, we have to prevent the latter from being unloaded unless the UNDEF_MAP object is also unloaded. */ if (__builtin_expect (current_value.m->l_type == lt_loaded, 0) - && current_value.m->l_global - && undef_map != current_value.m /* Don't do this for explicit lookups as opposed to implicit runtime lookups. */ && ! explicit @@ -395,8 +395,6 @@ _dl_lookup_versioned_symbol (const char *undef_name, we have to prevent the latter from being unloaded unless the UNDEF_MAP object is also unloaded. */ if (__builtin_expect (current_value.m->l_type == lt_loaded, 0) - && current_value.m->l_global - && undef_map != current_value.m /* Don't do this for explicit lookups as opposed to implicit runtime lookups. */ && ! explicit diff --git a/elf/dl-object.c b/elf/dl-object.c index 9c32c08ff2..65e90d66a0 100644 --- a/elf/dl-object.c +++ b/elf/dl-object.c @@ -37,14 +37,18 @@ _dl_new_object (char *realname, const char *libname, int type, struct link_map *l; int idx; size_t libname_len = strlen (libname) + 1; - struct link_map *new = calloc (sizeof *new, 1); - struct libname_list *newname = malloc (sizeof *newname + libname_len); - if (! new || ! newname) + struct link_map *new; + struct libname_list *newname; + + new = (struct link_map *) calloc (sizeof *new, 1); + newname = (struct libname_list *) malloc (sizeof *newname + libname_len); + if (new == NULL || newname == NULL) return NULL; new->l_name = realname; - newname->name = memcpy (newname + 1, libname, libname_len); + newname->name = (char *) memcpy (newname + 1, libname, libname_len); newname->next = NULL; + newname->dont_free = 0; new->l_libname = newname; new->l_type = type; new->l_loader = loader; diff --git a/elf/loadtest.c b/elf/loadtest.c index 863dc536e7..6b8f4bb7d0 100644 --- a/elf/loadtest.c +++ b/elf/loadtest.c @@ -5,6 +5,7 @@ #include #include #include +#include /* How many load/unload operations do we do. */ @@ -139,6 +140,23 @@ main (int argc, char *argv[]) testobjs[index].name, testobjs[index].handle); testobjs[index].handle = NULL; + + if (testobjs[0].handle == NULL + && testobjs[1].handle == NULL + && testobjs[5].handle == NULL) + { + /* In this case none of the objects above should be + present. */ + for (map = _r_debug.r_map; map != NULL; map = map->l_next) + if (map->l_type == lt_loaded + && (strstr (map->l_name, testobjs[0].name) != NULL + || strstr (map->l_name, testobjs[1].name) != NULL + || strstr (map->l_name, testobjs[5].name) != NULL)) + { + printf ("`%s' is still loaded\n", map->l_name); + result = 1; + } + } } if (debug) @@ -151,8 +169,8 @@ main (int argc, char *argv[]) { printf ("\nclose: %s: l_initfini = %p, l_versions = %p\n", testobjs[count].name, - ((struct link_map*)testobjs[count].handle)->l_initfini, - ((struct link_map*)testobjs[count].handle)->l_versions); + ((struct link_map *) testobjs[count].handle)->l_initfini, + ((struct link_map *) testobjs[count].handle)->l_versions); if (dlclose (testobjs[count].handle) != 0) { diff --git a/elf/reldep5.c b/elf/reldep5.c new file mode 100644 index 0000000000..982265f251 --- /dev/null +++ b/elf/reldep5.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include + +int +main (void) +{ + void *h1; + void *h2; + int (*fp) (void); + int *vp; + + mtrace (); + + /* Open the two objects. */ + h1 = dlopen ("reldepmod5.so", RTLD_LAZY); + if (h1 == NULL) + { + printf ("cannot open reldepmod5.so: %s\n", dlerror ()); + exit (1); + } + h2 = dlopen ("reldepmod6.so", RTLD_LAZY); + if (h2 == NULL) + { + printf ("cannot open reldepmod6.so: %s\n", dlerror ()); + exit (1); + } + + /* Get the address of the variable in reldepmod1.so. */ + fp = dlsym (h2, "bar"); + if (fp == NULL) + { + printf ("cannot get address of \"bar\": %s\n", dlerror ()); + exit (1); + } + + /* Call the function. */ + puts ("calling fp for the first time"); + if (fp () != 0) + { + puts ("function \"call_me\" returned wrong result"); + exit (1); + } + + /* Now close the first object. It must still be around since we have + an implicit dependency. */ + if (dlclose (h1) != 0) + { + printf ("closing h1 failed: %s\n", dlerror ()); + exit (1); + } + + /* Calling the function must still work. */ + puts ("calling fp for the second time"); + if (fp () != 0) + { + puts ("function \"call_me\" the second time returned wrong result"); + exit (1); + } + puts ("second call suceeded as well"); + + /* Close the second object, we are done. */ + if (dlclose (h2) != 0) + { + printf ("closing h2 failed: %s\n", dlerror ()); + exit (1); + } + + return 0; +} diff --git a/elf/reldepmod5.c b/elf/reldepmod5.c new file mode 100644 index 0000000000..eae70da1e2 --- /dev/null +++ b/elf/reldepmod5.c @@ -0,0 +1,5 @@ +int +foo (void) +{ + return 42; +} diff --git a/elf/reldepmod6.c b/elf/reldepmod6.c new file mode 100644 index 0000000000..95c18d4cdb --- /dev/null +++ b/elf/reldepmod6.c @@ -0,0 +1,7 @@ +extern int call_me (void); + +int +bar (void) +{ + return call_me (); +}