From 415ac3df9b10ae426d4f71f9d48003f6a3c7bd8d Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Thu, 9 Jul 2009 23:52:22 -0700 Subject: [PATCH] Implement STB_GNU_UNIQUE handling. 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. --- ChangeLog | 24 ++++++++- config.h.in | 3 ++ configure | 26 ++++++++++ configure.in | 17 +++++++ elf/Makefile | 18 +++++-- elf/dl-lookup.c | 6 +-- elf/dl-open.c | 7 ++- elf/do-lookup.h | 118 ++++++++++++++++++++++++++++++++++++++++++++- elf/rtld.c | 7 ++- elf/tst-unique1.c | 40 +++++++++++++++ elf/tst-unique1mod1.c | 21 ++++++++ elf/tst-unique1mod2.c | 20 ++++++++ elf/tst-unique2.c | 32 ++++++++++++ elf/tst-unique2mod1.c | 13 +++++ elf/tst-unique2mod2.c | 20 ++++++++ sysdeps/generic/ldsodefs.h | 15 ++++++ 16 files changed, 375 insertions(+), 12 deletions(-) create mode 100644 elf/tst-unique1.c create mode 100644 elf/tst-unique1mod1.c create mode 100644 elf/tst-unique1mod2.c create mode 100644 elf/tst-unique2.c create mode 100644 elf/tst-unique2mod1.c create mode 100644 elf/tst-unique2mod2.c diff --git a/ChangeLog b/ChangeLog index f06f610..bdb320f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2009-07-09 Ulrich Drepper + + * 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 * elf/elf.h (STB_GNU_UNIQUE): Define. @@ -9,7 +31,7 @@ 2009-07-06 Ulrich Drepper - * 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 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 <&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 <&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, ¤t_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 +#include +#include + +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 + +#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 + +#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 +#include +#include + +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 + +#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 + +#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]; -- 2.7.4