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