summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog24
-rw-r--r--config.h.in3
-rwxr-xr-xconfigure26
-rw-r--r--configure.in17
-rw-r--r--elf/Makefile18
-rw-r--r--elf/dl-lookup.c6
-rw-r--r--elf/dl-open.c7
-rw-r--r--elf/do-lookup.h118
-rw-r--r--elf/rtld.c7
-rw-r--r--elf/tst-unique1.c40
-rw-r--r--elf/tst-unique1mod1.c21
-rw-r--r--elf/tst-unique1mod2.c20
-rw-r--r--elf/tst-unique2.c32
-rw-r--r--elf/tst-unique2mod1.c13
-rw-r--r--elf/tst-unique2mod2.c20
-rw-r--r--sysdeps/generic/ldsodefs.h15
16 files changed, 375 insertions, 12 deletions
diff --git a/ChangeLog b/ChangeLog
index f06f610d36..bdb320fd25 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2009-07-09  Ulrich Drepper  <drepper@redhat.com>
+
+	* configure.in: Check for gnu_unique_symbol symbol type.
+	* config.h.in: Add HAVE_ASM_UNIQUE_OBJECT entry.
+	* elf/do-lookup.h (do_lookup_x): Take new parameter with link map of
+	the undefined symbol.  Handle STB_GNU_UNIQUE binding of found symbol.
+	* elf/dl-lookup.c (_dl_lookup_symbol_x): Adjust callers for do_lookup_x
+	change.
+	* sysdeps/generic/ldsodefs.h (struct rtld_global): Add definitions for
+	unique symbol table.
+	* elf/rtld.c (rtld_global): Initialize lock of unique symbol hash table
+	for first namespace.
+	* elf/dl-open.c (_dl_open): For new namespace, initialize lock for
+	unique symbol hash table.
+	* elf/Makefile: Add rules to build and run tst-unique1 and tst-unique2.
+	* elf/tst-unique1.c: New file.
+	* elf/tst-unique1mod1.c: New file.
+	* elf/tst-unique1mod2.c: New file.
+	* elf/tst-unique2.c: New file.
+	* elf/tst-unique2mod1.c: New file.
+	* elf/tst-unique2mod2.c: New file.
+
 2009-07-07  Ulrich Drepper  <drepper@redhat.com>
 
 	* elf/elf.h (STB_GNU_UNIQUE): Define.
@@ -9,7 +31,7 @@
 
 2009-07-06  Ulrich Drepper  <drepper@redhat.com>
 
-	* elf/do-lookup.h (ALLOWED_STT): Optimize test for valid symbol types.
+	* elf/do-lookup.h (do_lookup_x): Optimize test for valid symbol types.
 
 2009-07-03  Andreas Schwab  <aschwab@redhat.com>
 
diff --git a/config.h.in b/config.h.in
index 4ddab7d775..5f16874584 100644
--- a/config.h.in
+++ b/config.h.in
@@ -59,6 +59,9 @@
    assembler's `.type' directive, if it has one.  */
 #undef	ASM_TYPE_DIRECTIVE_PREFIX
 
+/* Define if the assembler supports the gnu_unique_object symbol type.  */
+#undef	HAVE_ASM_UNIQUE_OBJECT
+
 /* Define a symbol_name as a global .symbol_name for ld.  */
 #undef	HAVE_ASM_GLOBAL_DOT_NAME
 
diff --git a/configure b/configure
index e30778fd94..4e49f7068b 100755
--- a/configure
+++ b/configure
@@ -5994,6 +5994,32 @@ _ACEOF
 
 fi
 
+{ $as_echo "$as_me:$LINENO: checking for assembler gnu_unique_object symbol type" >&5
+$as_echo_n "checking for assembler gnu_unique_object symbol type... " >&6; }
+if test "${libc_cv_asm_unique_object+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat > conftest.s <<EOF
+${libc_cv_dot_text}
+_sym:
+.type _sym, ${libc_cv_asm_type_prefix}gnu_unique_object
+EOF
+if ${CC-cc} -c $ASFLAGS conftest.s 1>&5 2>&5; then
+  libc_cv_asm_unique_object=yes
+else
+  libc_cv_asm_unique_object=no
+fi
+rm -f conftest*
+fi
+{ $as_echo "$as_me:$LINENO: result: $libc_cv_asm_unique_object" >&5
+$as_echo "$libc_cv_asm_unique_object" >&6; }
+if test $libc_cv_asm_unique_object = yes; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_ASM_UNIQUE_OBJECT 1
+_ACEOF
+
+fi
+
 # For the multi-arch option we need support in the assembler.
 if test "$multi_arch" = yes; then
   if test "x$libc_cv_asm_type_prefix" != xno; then
diff --git a/configure.in b/configure.in
index 216cdc9d07..61c87418e6 100644
--- a/configure.in
+++ b/configure.in
@@ -1211,6 +1211,23 @@ if test "x$libc_cv_asm_type_prefix" != xno; then
   AC_DEFINE_UNQUOTED(ASM_TYPE_DIRECTIVE_PREFIX, ${libc_cv_asm_type_prefix})
 fi
 
+AC_CACHE_CHECK(for assembler gnu_unique_object symbol type,
+	       libc_cv_asm_unique_object, [dnl
+cat > conftest.s <<EOF
+${libc_cv_dot_text}
+_sym:
+.type _sym, ${libc_cv_asm_type_prefix}gnu_unique_object
+EOF
+if ${CC-cc} -c $ASFLAGS conftest.s 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD; then
+  libc_cv_asm_unique_object=yes
+else
+  libc_cv_asm_unique_object=no
+fi
+rm -f conftest*])
+if test $libc_cv_asm_unique_object = yes; then
+  AC_DEFINE(HAVE_ASM_UNIQUE_OBJECT)
+fi
+
 # For the multi-arch option we need support in the assembler.
 if test "$multi_arch" = yes; then
   if test "x$libc_cv_asm_type_prefix" != xno; then
diff --git a/elf/Makefile b/elf/Makefile
index 57febea483..cc5caeb521 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -111,7 +111,9 @@ distribute	:= rtld-Rules \
 		   ifuncdep5.c ifuncdep5pic.c ifuncmod5.c \
 		   ifuncmain6pie.c ifuncmod6.c \
 		   ifuncmain7.c ifuncmain7pic.c ifuncmain7picstatic.c \
-		   ifuncmain7pie.c ifuncmain7static.c
+		   ifuncmain7pie.c ifuncmain7static.c \
+		   tst-unique1.c tst-unique1mod1.c tst-unique1mod2.c \
+		   tst-unique2.c tst-unique2mod1.c tst-unique2mod2.c
 
 CFLAGS-dl-runtime.c = -fexceptions -fasynchronous-unwind-tables
 CFLAGS-dl-lookup.c = -fexceptions -fasynchronous-unwind-tables
@@ -190,7 +192,8 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \
 	 tst-dlmopen1 tst-dlmopen2 tst-dlmopen3 \
 	 unload3 unload4 unload5 unload6 unload7 tst-global1 order2 \
 	 tst-audit1 tst-audit2 \
-	 tst-stackguard1 tst-addr1 tst-thrlock
+	 tst-stackguard1 tst-addr1 tst-thrlock \
+	 tst-unique1 tst-unique2
 #	 reldep9
 test-srcs = tst-pathopt
 tests-execstack-yes = tst-execstack tst-execstack-needed tst-execstack-prog
@@ -239,7 +242,9 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		unload4mod1 unload4mod2 unload4mod3 unload4mod4 \
 		unload6mod1 unload6mod2 unload6mod3 \
 		unload7mod1 unload7mod2 \
-		order2mod1 order2mod2 order2mod3 order2mod4
+		order2mod1 order2mod2 order2mod3 order2mod4 \
+		tst-unique1mod1 tst-unique1mod2 \
+		tst-unique2mod1 tst-unique2mod2
 ifeq (yes,$(have-initfini-array))
 modules-names += tst-array2dep tst-array5dep
 endif
@@ -1103,3 +1108,10 @@ $(objpfx)ifuncmain5pic: $(addprefix $(objpfx),ifuncmod5.so)
 $(objpfx)ifuncmain5static: $(addprefix $(objpfx),ifuncdep5.o)
 $(objpfx)ifuncmain5staticpic: $(addprefix $(objpfx),ifuncdep5pic.o)
 $(objpfx)ifuncmain5picstatic: $(addprefix $(objpfx),ifuncdep5pic.o)
+
+$(objpfx)tst-unique1: $(libdl)
+$(objpfx)tst-unique1.out: $(objpfx)tst-unique1mod1.so \
+			  $(objpfx)tst-unique1mod2.so
+
+$(objpfx)tst-unique2: $(libdl) $(objpfx)tst-unique2mod1.so
+$(objpfx)tst-unique2.out: $(objpfx)tst-unique2mod2.so
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 707d650719..2ba885a639 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -337,7 +337,7 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
     {
       int res = do_lookup_x (undef_name, new_hash, &old_hash, *ref,
 			     &current_value, *scope, start, version, flags,
-			     skip_map, type_class);
+			     skip_map, type_class, undef_map);
       if (res > 0)
 	break;
 
@@ -410,7 +410,7 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
 	  for (scope = symbol_scope; *scope != NULL; i = 0, ++scope)
 	    if (do_lookup_x (undef_name, new_hash, &old_hash, *ref,
 			     &protected_value, *scope, i, version, flags,
-			     skip_map, ELF_RTYPE_CLASS_PLT) != 0)
+			     skip_map, ELF_RTYPE_CLASS_PLT, NULL) != 0)
 	      break;
 
 	  if (protected_value.s != NULL && protected_value.m != undef_map)
@@ -536,7 +536,7 @@ _dl_debug_bindings (const char *undef_name, struct link_map *undef_map,
 
 	  do_lookup_x (undef_name, new_hash, &old_hash, *ref, &val,
 		       undef_map->l_local_scope[0], 0, version, 0, NULL,
-		       type_class);
+		       type_class, undef_map);
 
 	  if (val.s != value->s || val.m != value->m)
 	    conflict = 1;
diff --git a/elf/dl-open.c b/elf/dl-open.c
index c3f0e42d5e..b8ebfe0e60 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -569,7 +569,7 @@ _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
 	if (GL(dl_ns)[nsid]._ns_loaded == NULL)
 	  break;
 
-      if (nsid == DL_NNS)
+      if (__builtin_expect (nsid == DL_NNS, 0))
 	{
 	  /* No more namespace available.  */
 	  __rtld_lock_unlock_recursive (GL(dl_load_lock));
@@ -579,7 +579,10 @@ no more namespaces available for dlmopen()"));
 	}
 
       if (nsid == GL(dl_nns))
-	++GL(dl_nns);
+	{
+	  __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock);
+	  ++GL(dl_nns);
+	}
 
       _dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT;
     }
diff --git a/elf/do-lookup.h b/elf/do-lookup.h
index acbc53dbbe..782f490964 100644
--- a/elf/do-lookup.h
+++ b/elf/do-lookup.h
@@ -27,7 +27,7 @@ do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
 	     unsigned long int *old_hash, const ElfW(Sym) *ref,
 	     struct sym_val *result, struct r_scope_elem *scope, size_t i,
 	     const struct r_found_version *const version, int flags,
-	     struct link_map *skip, int type_class)
+	     struct link_map *skip, int type_class, struct link_map *undef_map)
 {
   size_t n = scope->r_nlist;
   /* Make sure we read the value before proceeding.  Otherwise we
@@ -233,7 +233,7 @@ do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
       if (sym != NULL)
 	{
 	found_it:
-	  switch (ELFW(ST_BIND) (sym->st_info))
+	  switch (__builtin_expect (ELFW(ST_BIND) (sym->st_info), STB_GLOBAL))
 	    {
 	    case STB_WEAK:
 	      /* Weak definition.  Use this value if we don't find another.  */
@@ -248,10 +248,124 @@ do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
 		}
 	      /* FALLTHROUGH */
 	    case STB_GLOBAL:
+	    success:
 	      /* Global definition.  Just what we need.  */
 	      result->s = sym;
 	      result->m = (struct link_map *) map;
 	      return 1;
+
+	    case STB_GNU_UNIQUE:;
+	      /* We have to determine whether we already found a
+		 symbol with this name before.  If not then we have to
+		 add it to the search table.  If we already found a
+		 definition we have to use it.  */
+	      void enter (struct unique_sym *table, size_t size,
+			  unsigned int hash, const char *name,
+			  const ElfW(Sym) *sym, const struct link_map *map)
+	      {
+		size_t idx = hash % size;
+		size_t hash2 = 1 + hash % (size - 2);
+		while (1)
+		  {
+		    if (table[idx].hashval == 0)
+		      {
+			table[idx].hashval = hash;
+			table[idx].name = strtab + sym->st_name;
+			if ((type_class & ELF_RTYPE_CLASS_COPY) != 0)
+			  {
+			    table[idx].sym = ref;
+			    table[idx].map = undef_map;
+			  }
+			else
+			  {
+			    table[idx].sym = sym;
+			    table[idx].map = map;
+			  }
+			return;
+		      }
+
+		    idx += hash2;
+		    if (idx >= size)
+		      idx -= size;
+		  }
+	      }
+
+	      struct unique_sym_table *tab
+		= &GL(dl_ns)[map->l_ns]._ns_unique_sym_table;
+
+	      __rtld_lock_lock_recursive (tab->lock);
+
+	      struct unique_sym *entries = tab->entries;
+	      size_t size = tab->size;
+	      if (entries != NULL)
+		{
+		  size_t idx = new_hash % size;
+		  size_t hash2 = 1 + new_hash % (size - 2);
+		  while (1)
+		    {
+		      if (entries[idx].hashval == new_hash
+			  && strcmp (entries[idx].name, undef_name) == 0)
+			{
+			  result->s = entries[idx].sym;
+			  result->m = (struct link_map *) entries[idx].map;
+			  __rtld_lock_unlock_recursive (tab->lock);
+			  return 1;
+			}
+
+		      if (entries[idx].hashval == 0
+			  && entries[idx].name == NULL)
+			break;
+
+		      idx += hash2;
+		      if (idx >= size)
+			idx -= size;
+		    }
+
+		  if (size * 3 <= tab->n_elements)
+		    {
+		      /* Expand the table.  */
+		      size_t newsize = _dl_higher_prime_number (size);
+		      struct unique_sym *newentries
+			= calloc (sizeof (struct unique_sym), newsize);
+		      if (newentries == NULL)
+			{
+			nomem:
+			  __rtld_lock_unlock_recursive (tab->lock);
+			  _dl_fatal_printf ("out of memory\n");
+			}
+
+		      for (idx = 0; idx < size; ++idx)
+			if (entries[idx].hashval != 0)
+			  enter (newentries, newsize, entries[idx].hashval,
+				 entries[idx].name, entries[idx].sym,
+				 entries[idx].map);
+
+		      tab->free (entries);
+		      tab->size = newsize;
+		      entries = tab->entries = newentries;
+		      tab->free = free;
+		    }
+		}
+	      else
+		{
+#define INITIAL_NUNIQUE_SYM_TABLE 31
+		  size = INITIAL_NUNIQUE_SYM_TABLE;
+		  entries = calloc (sizeof (struct unique_sym), size);
+		  if (entries == NULL)
+		    goto nomem;
+
+		  tab->entries = entries;
+		  tab->size = size;
+		  tab->free = free;
+		}
+
+	      enter (entries, size, new_hash, strtab + sym->st_name, sym, map);
+	      ++tab->n_elements;
+
+	      __rtld_lock_unlock_recursive (tab->lock);
+
+	      goto success;
+
 	    default:
 	      /* Local symbols are ignored.  */
 	      break;
diff --git a/elf/rtld.c b/elf/rtld.c
index f97de9ac08..55b84c3bf4 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -127,7 +127,12 @@ struct rtld_global _rtld_global =
 #ifdef _LIBC_REENTRANT
     ._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
 #endif
-    ._dl_nns = 1
+    ._dl_nns = 1,
+    ._dl_ns =
+    {
+      [LM_ID_BASE] = { ._ns_unique_sym_table
+		       = { .lock = _RTLD_LOCK_RECURSIVE_INITIALIZER } }
+    }
   };
 /* If we would use strong_alias here the compiler would see a
    non-hidden definition.  This would undo the effect of the previous
diff --git a/elf/tst-unique1.c b/elf/tst-unique1.c
new file mode 100644
index 0000000000..9b7996cd96
--- /dev/null
+++ b/elf/tst-unique1.c
@@ -0,0 +1,40 @@
+#include <config.h>
+#include <dlfcn.h>
+#include <stdio.h>
+
+static int
+do_test (void)
+{
+#ifdef HAVE_ASM_UNIQUE_OBJECT
+  void *h1 = dlopen ("tst-unique1mod1.so", RTLD_LAZY);
+  if (h1 == NULL)
+    {
+      puts ("cannot load tst-unique1mod1");
+      return 1;
+    }
+  int *(*f1) (void) = dlsym (h1, "f");
+  if (f1 == NULL)
+    {
+      puts ("cannot locate f in tst-unique1mod1");
+      return 1;
+    }
+  void *h2 = dlopen ("tst-unique1mod2.so", RTLD_LAZY);
+  if (h2 == NULL)
+    {
+      puts ("cannot load tst-unique1mod2");
+      return 1;
+    }
+  int (*f2) (int *) = dlsym (h2, "f");
+  if (f2 == NULL)
+    {
+      puts ("cannot locate f in tst-unique1mod2");
+      return 1;
+    }
+  return f2 (f1 ());
+#else
+  return 0;
+#endif
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/elf/tst-unique1mod1.c b/elf/tst-unique1mod1.c
new file mode 100644
index 0000000000..16de28d25e
--- /dev/null
+++ b/elf/tst-unique1mod1.c
@@ -0,0 +1,21 @@
+#include <config.h>
+
+#ifdef HAVE_ASM_UNIQUE_OBJECT
+# define S(s) _S (s)
+# define _S(s) #s
+
+asm (".data;"
+     S (ASM_GLOBAL_DIRECTIVE) " var\n"
+     ".type var, " S (ASM_TYPE_DIRECTIVE_PREFIX) "gnu_unique_object\n"
+     ".size var, 4\n"
+     "var:.zero 4\n"
+     ".previous");
+extern int var;
+
+int *
+f (void)
+{
+  var = 1;
+  return &var;
+}
+#endif
diff --git a/elf/tst-unique1mod2.c b/elf/tst-unique1mod2.c
new file mode 100644
index 0000000000..c075515827
--- /dev/null
+++ b/elf/tst-unique1mod2.c
@@ -0,0 +1,20 @@
+#include <config.h>
+
+#ifdef HAVE_ASM_UNIQUE_OBJECT
+# define S(s) _S (s)
+# define _S(s) #s
+
+asm (".data;"
+     S (ASM_GLOBAL_DIRECTIVE) " var\n"
+     ".type var, " S (ASM_TYPE_DIRECTIVE_PREFIX) "gnu_unique_object\n"
+     ".size var, 4\n"
+     "var:.zero 4\n"
+     ".previous");
+extern int var;
+
+int
+f (int *p)
+{
+  return &var != p || *p != 1;
+}
+#endif
diff --git a/elf/tst-unique2.c b/elf/tst-unique2.c
new file mode 100644
index 0000000000..7bb0687364
--- /dev/null
+++ b/elf/tst-unique2.c
@@ -0,0 +1,32 @@
+#include <config.h>
+#include <dlfcn.h>
+#include <stdio.h>
+
+extern int var;
+
+static int
+do_test (void)
+{
+#ifdef HAVE_ASM_UNIQUE_OBJECT
+  var = 1;
+
+  void *h = dlopen ("tst-unique2mod2.so", RTLD_LAZY);
+  if (h == NULL)
+    {
+      puts ("cannot load tst-unique2mod2");
+      return 1;
+    }
+  int (*f) (int *) = dlsym (h, "f");
+  if (f == NULL)
+    {
+      puts ("cannot locate f in tst-unique2mod2");
+      return 1;
+    }
+  return f (&var);
+#else
+  return 0;
+#endif
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/elf/tst-unique2mod1.c b/elf/tst-unique2mod1.c
new file mode 100644
index 0000000000..5e4ac4d68c
--- /dev/null
+++ b/elf/tst-unique2mod1.c
@@ -0,0 +1,13 @@
+#include <config.h>
+
+#ifdef HAVE_ASM_UNIQUE_OBJECT
+# define S(s) _S (s)
+# define _S(s) #s
+
+asm (".data;"
+     S (ASM_GLOBAL_DIRECTIVE) " var\n"
+     ".type var, " S (ASM_TYPE_DIRECTIVE_PREFIX) "gnu_unique_object\n"
+     ".size var, 4\n"
+     "var:.zero 4\n"
+     ".previous");
+#endif
diff --git a/elf/tst-unique2mod2.c b/elf/tst-unique2mod2.c
new file mode 100644
index 0000000000..c075515827
--- /dev/null
+++ b/elf/tst-unique2mod2.c
@@ -0,0 +1,20 @@
+#include <config.h>
+
+#ifdef HAVE_ASM_UNIQUE_OBJECT
+# define S(s) _S (s)
+# define _S(s) #s
+
+asm (".data;"
+     S (ASM_GLOBAL_DIRECTIVE) " var\n"
+     ".type var, " S (ASM_TYPE_DIRECTIVE_PREFIX) "gnu_unique_object\n"
+     ".size var, 4\n"
+     "var:.zero 4\n"
+     ".previous");
+extern int var;
+
+int
+f (int *p)
+{
+  return &var != p || *p != 1;
+}
+#endif
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index b1af7fde0a..1e1bb4ccd4 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -383,6 +383,21 @@ struct rtld_global
        allocated by rtld.  Later it keeps the size of the map.  It might be
        reset if in _dl_close if the last global object is removed.  */
     size_t _ns_global_scope_alloc;
+    /* Search table for unique objects.  */
+    struct unique_sym_table
+    {
+      __rtld_lock_recursive_t lock;
+      struct unique_sym
+      {
+	uint32_t hashval;
+	const char *name;
+	const ElfW(Sym) *sym;
+	const struct link_map *map;
+      } *entries;
+      size_t size;
+      size_t n_elements;
+      void (*free) (void *);
+    } _ns_unique_sym_table;
     /* Keep track of changes to each namespace' list.  */
     struct r_debug _ns_debug;
   } _dl_ns[DL_NNS];