summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog29
-rw-r--r--elf/Makefile10
-rw-r--r--elf/dl-close.c24
-rw-r--r--elf/dl-deps.c34
-rw-r--r--elf/dl-lookup.c142
-rw-r--r--elf/dl-object.c12
-rw-r--r--elf/loadtest.c22
-rw-r--r--elf/reldep5.c71
-rw-r--r--elf/reldepmod5.c5
-rw-r--r--elf/reldepmod6.c7
10 files changed, 260 insertions, 96 deletions
diff --git a/ChangeLog b/ChangeLog
index 21653dc4d9..81ba6caab6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,35 @@
+2001-09-08  Ulrich Drepper  <drepper@redhat.com>
+
+	* 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  <drepper@redhat.com>
 
 	* 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;
+	  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;
 
-      if (i == act)
+  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))
 	{
-	  /* 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]);
-	    }
+	  /* 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
-	    /* Whoa, that was bad luck.  We have to search again.  */
-	    result = -1;
+	    /* 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 <mcheck.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 
 /* 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 <dlfcn.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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 ();
+}