about summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile28
-rw-r--r--elf/dl-close.c127
-rw-r--r--elf/dl-deps.c6
-rw-r--r--elf/dl-lookup.c27
-rw-r--r--elf/nodelete2.c16
-rw-r--r--elf/reldep6.c12
-rw-r--r--elf/reldep9.c16
-rw-r--r--elf/reldep9mod1.c23
-rw-r--r--elf/reldep9mod2.c3
-rw-r--r--elf/reldep9mod3.c1
-rw-r--r--elf/tls-macros.h32
11 files changed, 229 insertions, 62 deletions
diff --git a/elf/Makefile b/elf/Makefile
index e9090d3044..38819a3884 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 1995-1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+# Copyright (C) 1995-2002, 2003 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
@@ -73,7 +73,9 @@ distribute	:= rtld-Rules \
 		   circlemod1.c circlemod1a.c circlemod2.c circlemod2a.c \
 		   circlemod3.c circlemod3a.c nodlopenmod2.c \
 		   tls-macros.h \
-		   reldep8mod1.c reldep8mod2.c reldep8mod3.c
+		   reldep8mod1.c reldep8mod2.c reldep8mod3.c \
+		   nodel2mod1.c nodel2mod2.c nodel2mod3.c \
+		   reldep9mod1.c reldep9mod2.c reldep9mod3.c
 
 include ../Makeconfig
 
@@ -138,9 +140,10 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \
 	 neededtest3 neededtest4 unload2 lateglobal initfirst global \
 	 restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \
 	 circleload1 tst-tls3 tst-tls4 tst-tls5 tst-tls6 tst-tls7 tst-tls8
+#	 reldep9
 test-srcs = tst-pathopt
 tests-vis-yes = vismain
-tests-nodelete-yes = nodelete
+tests-nodelete-yes = nodelete nodelete2
 tests-nodlopen-yes = nodlopen nodlopen2
 endif
 modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
@@ -160,12 +163,14 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		tst-tlsmod5 tst-tlsmod6 \
 		circlemod1 circlemod1a circlemod2 circlemod2a \
 		circlemod3 circlemod3a \
-		reldep8mod1 reldep8mod2 reldep8mod3
+		reldep8mod1 reldep8mod2 reldep8mod3 \
+		reldep9mod1 reldep9mod2 reldep9mod3
 ifeq (yes,$(have-initfini-array))
 modules-names += tst-array2dep
 endif
 modules-vis-yes = vismod1 vismod2 vismod3
-modules-nodelete-yes = nodelmod1 nodelmod2 nodelmod3 nodelmod4
+modules-nodelete-yes = nodelmod1 nodelmod2 nodelmod3 nodelmod4 \
+		       nodel2mod1 nodel2mod2 nodel2mod3
 modules-nodlopen-yes = nodlopenmod nodlopenmod2
 extra-objs += $(addsuffix .os,$(strip $(modules-names)))
 # We need this variable to be sure the test modules get the right CPPFLAGS.
@@ -363,7 +368,13 @@ $(objpfx)reldep6mod2.so: $(objpfx)reldep6mod1.so
 $(objpfx)reldep6mod3.so: $(objpfx)reldep6mod2.so
 $(objpfx)reldep6mod4.so: $(objpfx)reldep6mod1.so
 $(objpfx)tst-tlsmod3.so: $(objpfx)tst-tlsmod2.so
+# For tst-tls9-static, make sure the modules it dlopens have libc.so in DT_NEEDED
+$(objpfx)tst-tlsmod5.so: $(common-objpfx)libc.so
+$(objpfx)tst-tlsmod6.so: $(common-objpfx)libc.so
 $(objpfx)reldep8mod3.so: $(objpfx)reldep8mod1.so $(objpfx)reldep8mod2.so
+$(objpfx)nodel2mod3.so: $(objpfx)nodel2mod1.so $(objpfx)nodel2mod2.so
+$(objpfx)reldep9mod2.so: $(objpfx)reldep9mod1.so
+$(objpfx)reldep9mod3.so: $(objpfx)reldep9mod1.so $(objpfx)reldep9mod2.so
 
 LDFLAGS-tst-tlsmod5.so = -nostdlib
 LDFLAGS-tst-tlsmod6.so = -nostdlib
@@ -531,6 +542,13 @@ $(objpfx)reldep7.out: $(objpfx)reldep7mod1.so $(objpfx)reldep7mod2.so
 $(objpfx)reldep8: $(libdl)
 $(objpfx)reldep8.out: $(objpfx)reldep8mod3.so
 
+LDFLAGS-nodel2mod2.so = -Wl,--enable-new-dtags,-z,nodelete
+$(objpfx)nodelete2: $(libdl)
+$(objpfx)nodelete2.out: $(objpfx)nodel2mod3.so
+
+$(objpfx)reldep9: $(libdl)
+$(objpfx)reldep9.out: $(objpfx)reldep9mod3.so
+
 $(objpfx)tst-tls3: $(objpfx)tst-tlsmod1.so
 
 $(objpfx)tst-tls4: $(libdl)
diff --git a/elf/dl-close.c b/elf/dl-close.c
index 7e4626e3d6..0953fab210 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-2001, 2002 Free Software Foundation, Inc.
+   Copyright (C) 1996-2002, 2003 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
@@ -138,7 +138,7 @@ _dl_close (void *_map)
 	_dl_debug_printf ("\nclosing file=%s; opencount == %u\n",
 			  map->l_name, map->l_opencount);
 
-      /* One decrement the object itself, not the dependencies.  */
+      /* Decrement the object's reference counter, not the dependencies'.  */
       --map->l_opencount;
 
       __rtld_lock_unlock_recursive (GL(dl_load_lock));
@@ -165,7 +165,7 @@ _dl_close (void *_map)
     }
   --new_opencount[0];
   for (i = 1; list[i] != NULL; ++i)
-    if ((! (list[i]->l_flags_1 & DF_1_NODELETE) || ! list[i]->l_init_called)
+    if ((list[i]->l_flags_1 & DF_1_NODELETE) == 0
 	/* Decrement counter.  */
 	&& --new_opencount[i] == 0)
       {
@@ -185,7 +185,10 @@ _dl_close (void *_map)
 		{
 		  assert (dep_list[j]->l_idx < map->l_searchlist.r_nlist);
 		  if (--new_opencount[dep_list[j]->l_idx] == 0)
-		    mark_removed (dep_list[j]);
+		    {
+		      assert (dep_list[j]->l_type == lt_loaded);
+		      mark_removed (dep_list[j]);
+		    }
 		}
 	      }
 
@@ -200,8 +203,11 @@ _dl_close (void *_map)
 			    == remmap->l_reldeps[j]))
 		      /* Yes, it is.  */
 		      if (--new_opencount[remmap->l_reldeps[j]->l_idx] == 0)
-			/* This one is now gone, too.  */
-			mark_removed (remmap->l_reldeps[j]);
+			{
+			  /* This one is now gone, too.  */
+			  assert (remmap->l_reldeps[j]->l_type == lt_loaded);
+			  mark_removed (remmap->l_reldeps[j]);
+			}
 		  }
 	      }
 	  }
@@ -215,57 +221,98 @@ _dl_close (void *_map)
     {
       struct link_map *imap = list[i];
       if (new_opencount[i] == 0 && imap->l_type == lt_loaded
-	  && (imap->l_info[DT_FINI] || imap->l_info[DT_FINI_ARRAY])
-	  && (! (imap->l_flags_1 & DF_1_NODELETE) || ! imap->l_init_called)
-	  /* Skip any half-cooked objects that were never initialized.  */
-	  && imap->l_init_called)
+	  && (imap->l_flags_1 & DF_1_NODELETE) == 0)
 	{
 	  /* When debugging print a message first.  */
 	  if (__builtin_expect (GL(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
 	    _dl_debug_printf ("\ncalling fini: %s\n\n", imap->l_name);
 
-	  /* Call its termination function.  */
-	  if (imap->l_info[DT_FINI_ARRAY] != NULL)
+	  /* Call its termination function.  Do not do it for
+	     half-cooked objects.  */
+	  if (imap->l_init_called)
 	    {
-	      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])) ();
+	      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)) DL_DT_FINI_ADDRESS
+		 (imap, ((void *) imap->l_addr
+			 + imap->l_info[DT_FINI]->d_un.d_ptr))) ();
 	    }
 
-	  /* Next try the old-style destructor.  */
-	  if (imap->l_info[DT_FINI] != NULL)
-	    (*(void (*) (void)) DL_DT_FINI_ADDRESS
-	      (imap, (void *) imap->l_addr
-		     + imap->l_info[DT_FINI]->d_un.d_ptr)) ();
+	  /* This object must not be used anymore.  We must remove the
+	     reference from the scope.  */
+	  unsigned int j;
+	  struct link_map **searchlist = map->l_searchlist.r_list;
+	  unsigned int nsearchlist = map->l_searchlist.r_nlist;
+
+#ifndef NDEBUG
+	  bool found = false;
+#endif
+	  for (j = 0; j < nsearchlist; ++j)
+	    if (imap == searchlist[j])
+	      {
+		/* This is the object to remove.  Copy all the
+		   following ones.  */
+		while (++j < nsearchlist)
+		  searchlist[j - 1] = searchlist[j];
+
+		searchlist[j - 1] = NULL;
+
+		--map->l_searchlist.r_nlist;
+
+#ifndef NDEBUG
+		found = true;
+#endif
+		break;
+	      }
+	  assert (found);
 	}
-      else if (new_opencount[i] != 0 && imap->l_type == lt_loaded)
+      else if (new_opencount[i] != 0 && imap->l_type == lt_loaded
+	       && imap->l_searchlist.r_list == NULL
+	       && imap->l_initfini != NULL)
 	{
-	  /* The object is still used.  But the object we are unloading
-	     right now is responsible for loading it and therefore we
-	     have the search list of the current object in its scope.
-	     Remove it.  */
-	  struct r_scope_elem **runp = imap->l_scope;
-
-	  while (*runp != NULL)
-	    if (*runp == &map->l_searchlist)
+	  /* The object is still used.  But the object we are
+	     unloading right now is responsible for loading it.  If
+	     the current object does not have it's own scope yet we
+	     have to create one.  This has to be done before running
+	     the finalizers.
+
+	     To do this count the number of dependencies.  */
+	  unsigned int cnt;
+	  for (cnt = 1; imap->l_initfini[cnt] != NULL; ++cnt)
+	    if (imap->l_initfini[cnt]->l_idx >= i
+		&& imap->l_initfini[cnt]->l_idx < nopencount)
+	      ++new_opencount[imap->l_initfini[cnt]->l_idx];
+	    else
+	      ++imap->l_initfini[cnt]->l_opencount;
+
+	  /* We simply reuse the l_initfini list.  */
+	  imap->l_searchlist.r_list = &imap->l_initfini[cnt + 1];
+	  imap->l_searchlist.r_nlist = cnt;
+
+	  for (cnt = 0; imap->l_scope[cnt] != NULL; ++cnt)
+	    if (imap->l_scope[cnt] = &map->l_searchlist)
 	      {
-		/* Copy all later elements.  */
-		while ((runp[0] = runp[1]) != NULL)
-		  ++runp;
+		imap->l_scope[cnt] = &imap->l_searchlist;
 		break;
 	      }
-	    else
-	      ++runp;
 	}
 
       /* Store the new l_opencount value.  */
       imap->l_opencount = new_opencount[i];
+
       /* Just a sanity check.  */
       assert (imap->l_type == lt_loaded || imap->l_opencount > 0);
     }
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
index 9fd2dd23ef..0a9183faee 100644
--- a/elf/dl-deps.c
+++ b/elf/dl-deps.c
@@ -1,5 +1,5 @@
 /* Load the dependencies of a mapped object.
-   Copyright (C) 1996-2001, 2002 Free Software Foundation, Inc.
+   Copyright (C) 1996-2001, 2002, 2003 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
@@ -455,12 +455,14 @@ _dl_map_object_deps (struct link_map *map,
 	  needed[nneeded++] = NULL;
 
 	  l->l_initfini = (struct link_map **)
-	    malloc ((nneeded + 1) * sizeof needed[0]);
+	    malloc ((2 * nneeded + 1) * sizeof needed[0]);
 	  if (l->l_initfini == NULL)
 	    INTUSE(_dl_signal_error) (ENOMEM, map->l_name, NULL,
 				      N_("cannot allocate dependency list"));
 	  l->l_initfini[0] = l;
 	  memcpy (&l->l_initfini[1], needed, nneeded * sizeof needed[0]);
+	  memcpy (&l->l_initfini[nneeded + 1], l->l_initfini,
+		  nneeded * sizeof needed[0]);
 	}
 
       /* If we have no auxiliary objects just go on to the next map.  */
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 39b3a3d013..4603761383 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -1,5 +1,5 @@
 /* Look up a symbol in the loaded objects.
-   Copyright (C) 1995,96,97,98,99,2000,2001,2002 Free Software Foundation, Inc.
+   Copyright (C) 1995-2002, 2003 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
@@ -90,17 +90,34 @@ add_dependency (struct link_map *undef_map, struct link_map *map)
   unsigned int i;
   int result = 0;
 
-  /* Avoid self-references.  */
+  /* Avoid self-references and references to objects which cannot be
+     unloaded anyway.  */
   if (undef_map == map)
     return 0;
 
+  /* Make sure nobody can unload the object while we are at it.  */
+  __rtld_lock_lock_recursive (GL(dl_load_lock));
+
   /* Don't create cross-reference between modules which are
      dynamically loaded by the same dlopen() call.  */
   if (undef_map->l_opencount == 0 && map->l_opencount == 0)
-    return 0;
+    goto out;
 
-  /* Make sure nobody can unload the object while we are at it.  */
-  __rtld_lock_lock_recursive (GL(dl_load_lock));
+  /* Avoid references to objects which cannot be unloaded anyway.  */
+  if (map->l_type != lt_loaded
+      || (map->l_flags_1 & DF_1_NODELETE) != 0)
+    goto out;
+
+  /* If the object with the undefined reference cannot be removed ever
+     just make sure the same is true for the object which contains the
+     definition.  */
+  if (undef_map->l_type != lt_loaded
+      || (undef_map->l_flags_1 & DF_1_NODELETE) != 0)
+    {
+      ++map->l_opencount;
+      map->l_flags |= DF_1_NODELETE;
+      goto out;
+    }
 
   /* Determine whether UNDEF_MAP already has a reference to MAP.  First
      look in the normal dependencies.  */
diff --git a/elf/nodelete2.c b/elf/nodelete2.c
new file mode 100644
index 0000000000..b3d7e31a08
--- /dev/null
+++ b/elf/nodelete2.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+int
+main (void)
+{
+  void *handle = dlopen ("nodel2mod3.so", RTLD_LAZY);
+  if (handle == NULL)
+    {
+      printf ("%s\n", dlerror ());
+      exit (1);
+    }
+  dlclose (handle);
+  exit (1);
+}
diff --git a/elf/reldep6.c b/elf/reldep6.c
index bf80ec5773..1eeec6c862 100644
--- a/elf/reldep6.c
+++ b/elf/reldep6.c
@@ -48,6 +48,18 @@ main (void)
       exit (1);
     }
 
+  baz = dlsym (h2, "baz");
+  if (baz == NULL)
+    {
+      printf ("cannot get address of \"baz\": %s\n", dlerror ());
+      exit (1);
+    }
+  if (baz () != 31)
+    {
+      printf ("baz() did not return 31\n");
+      exit (1);
+    }
+
   if (dlclose (h1) != 0)
     {
       printf ("closing h1 failed: %s\n", dlerror ());
diff --git a/elf/reldep9.c b/elf/reldep9.c
new file mode 100644
index 0000000000..51c7a8bb9e
--- /dev/null
+++ b/elf/reldep9.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+int
+main (void)
+{
+  void *handle = dlopen ("reldep9mod3.so", RTLD_LAZY);
+  if (handle == NULL)
+    {
+      printf ("%s\n", dlerror ());
+      exit (1);
+    }
+  dlclose (handle);
+  abort ();
+}
diff --git a/elf/reldep9mod1.c b/elf/reldep9mod1.c
new file mode 100644
index 0000000000..249a2bae1c
--- /dev/null
+++ b/elf/reldep9mod1.c
@@ -0,0 +1,23 @@
+#include <stdlib.h>
+void
+foo (void)
+{
+  exit (0);
+}
+
+void
+__attribute__((destructor))
+bar (void)
+{
+  static int i;
+  foo ();
+  ++i;
+}
+
+void
+__attribute__((constructor))
+destr (void)
+{
+  extern void baz (void);
+  baz ();
+}
diff --git a/elf/reldep9mod2.c b/elf/reldep9mod2.c
new file mode 100644
index 0000000000..090966e3e3
--- /dev/null
+++ b/elf/reldep9mod2.c
@@ -0,0 +1,3 @@
+void baz (void)
+{
+}
diff --git a/elf/reldep9mod3.c b/elf/reldep9mod3.c
new file mode 100644
index 0000000000..6d1a0d47b7
--- /dev/null
+++ b/elf/reldep9mod3.c
@@ -0,0 +1 @@
+int x;
diff --git a/elf/tls-macros.h b/elf/tls-macros.h
index 6b40dedf88..046a7d4c92 100644
--- a/elf/tls-macros.h
+++ b/elf/tls-macros.h
@@ -243,47 +243,59 @@ register void *__gp __asm__("$29");
 
 # define TLS_LE(x) \
   ({ void *__l;								      \
-     asm ("ld8 r2=tp\n\t"						      \
+     asm ("mov r2=r13\n\t"						      \
          ";;\n\t"							      \
-         "addl %0=@tpre1(" #x "),r2\n\t"				      \
+         "addl %0=@tprel(" #x "),r2\n\t"				      \
          : "=r" (__l) : : "r2"  ); __l; })
 
 # define TLS_IE(x) \
   ({ void *__l;								      \
-     asm ("addl r16=@ltoff(@tprel(" #x ")),gp\n\t"			      \
+     asm (";;\n\t"							      \
+	 "addl r16=@ltoff(@tprel(" #x ")),gp\n\t"			      \
          ";;\n\t"							      \
          "ld8 r17=[r16]\n\t"						      \
          ";;\n\t"							      \
-         "add %0=tp,r17\n\t"						      \
+         "add %0=r13,r17\n\t"						      \
          : "=r" (__l) : : "r16", "r17" ); __l; })
 
+# define __TLS_CALL_CLOBBERS \
+  "r2", "r3", "r8", "r9", "r10", "r11", "r14", "r15", "r16", "r17",	      \
+  "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26",	      \
+  "r27", "r28", "r29", "r30", "r31",					      \
+  "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15",	      \
+  "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",	      \
+  "b6", "b7",								      \
+  "out0", "out1", "out2", "out3", "out4", "out5", "out6", "out7"
+
 # define TLS_LD(x) \
   ({ void *__l;								      \
-     asm ("mov loc0=gp\n\t"						      \
+     asm (";;\n\t"							      \
+	 "mov loc0=gp\n\t"						      \
          "addl r16=@ltoff(@dtpmod(" #x ")),gp\n\t"			      \
          "addl out1=@dtprel(" #x "),r0\n\t"				      \
          ";;\n\t"							      \
          "ld8 out0=[r16]\n\t"						      \
-         "br.callrp=__tls_get_addr"					      \
+         "br.call.sptk.many b0=__tls_get_addr"				      \
          ";;\n\t"							      \
          "mov gp=loc0\n\t"						      \
          "mov %0=r8\n\t"						      \
-         : "=r" (__l) : : "r16" , "loc0" , "out0" , "out1" , "r8" );	      \
+         : "=r" (__l) : : "loc0", __TLS_CALL_CLOBBERS);			      \
      __l; })
 
 # define TLS_GD(x) \
   ({ void *__l;								      \
-     asm ("mov loc0=gp\n\t"						      \
+     asm (";;\n\t"							      \
+	 "mov loc0=gp\n\t"						      \
          "addl r16=@ltoff(@dtpmod(" #x ")),gp\n\t"			      \
          "addl r17=@ltoff(@dtprel(" #x ")),gp\n\t"			      \
          ";;\n\t"							      \
          "ld8 out0=[r16]\n\t"						      \
          "ld8 out1=[r17]\n\t"						      \
-         "br.callrp=__tls_get_addr"					      \
+         "br.call.sptk.many b0=__tls_get_addr"				      \
          ";;\n\t"							      \
          "mov gp=loc0\n\t"						      \
          "mov %0=r8\n\t"						      \
-          : "=r" (__l) : : "r16", "r17" , "loc0" , "out0", "out1" , "r8");    \
+          : "=r" (__l) : : "loc0", __TLS_CALL_CLOBBERS);		      \
      __l; })
 
 #else