Implement STB_GNU_UNIQUE handling.
authorUlrich Drepper <drepper@redhat.com>
Fri, 10 Jul 2009 06:52:22 +0000 (23:52 -0700)
committerUlrich Drepper <drepper@redhat.com>
Fri, 10 Jul 2009 06:52:22 +0000 (23:52 -0700)
Some symbols have to be identified process-wide by their name.  This is
particularly important for some C++ features (e.g., class local static data
and static variables in inline functions).  This cannot completely be
implemented with ELF functionality so far.  The STB_GNU_UNIQUE binding
helps by ensuring the dynamic linker will always use the same definition for
all symbols with the same name and this binding.

16 files changed:
ChangeLog
config.h.in
configure
configure.in
elf/Makefile
elf/dl-lookup.c
elf/dl-open.c
elf/do-lookup.h
elf/rtld.c
elf/tst-unique1.c [new file with mode: 0644]
elf/tst-unique1mod1.c [new file with mode: 0644]
elf/tst-unique1mod2.c [new file with mode: 0644]
elf/tst-unique2.c [new file with mode: 0644]
elf/tst-unique2mod1.c [new file with mode: 0644]
elf/tst-unique2mod2.c [new file with mode: 0644]
sysdeps/generic/ldsodefs.h

index f06f610..bdb320f 100644 (file)
--- 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>
 
index 4ddab7d..5f16874 100644 (file)
@@ -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
 
index e30778f..4e49f70 100755 (executable)
--- 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
index 216cdc9..61c8741 100644 (file)
@@ -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
index 57febea..cc5caeb 100644 (file)
@@ -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
index 707d650..2ba885a 100644 (file)
@@ -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;
index c3f0e42..b8ebfe0 100644 (file)
@@ -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;
     }
index acbc53d..782f490 100644 (file)
@@ -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;
index f97de9a..55b84c3 100644 (file)
@@ -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 (file)
index 0000000..9b7996c
--- /dev/null
@@ -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 (file)
index 0000000..16de28d
--- /dev/null
@@ -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 (file)
index 0000000..c075515
--- /dev/null
@@ -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 (file)
index 0000000..7bb0687
--- /dev/null
@@ -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 (file)
index 0000000..5e4ac4d
--- /dev/null
@@ -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 (file)
index 0000000..c075515
--- /dev/null
@@ -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
index b1af7fd..1e1bb4c 100644 (file)
@@ -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];