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.
This commit is contained in:
Ulrich Drepper 2009-07-09 23:52:22 -07:00
parent b4f55afd03
commit 415ac3df9b
16 changed files with 375 additions and 12 deletions

View File

@ -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> 2009-07-07 Ulrich Drepper <drepper@redhat.com>
* elf/elf.h (STB_GNU_UNIQUE): Define. * elf/elf.h (STB_GNU_UNIQUE): Define.
@ -9,7 +31,7 @@
2009-07-06 Ulrich Drepper <drepper@redhat.com> 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> 2009-07-03 Andreas Schwab <aschwab@redhat.com>

View File

@ -59,6 +59,9 @@
assembler's `.type' directive, if it has one. */ assembler's `.type' directive, if it has one. */
#undef ASM_TYPE_DIRECTIVE_PREFIX #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. */ /* Define a symbol_name as a global .symbol_name for ld. */
#undef HAVE_ASM_GLOBAL_DOT_NAME #undef HAVE_ASM_GLOBAL_DOT_NAME

26
configure vendored
View File

@ -5994,6 +5994,32 @@ _ACEOF
fi 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. # For the multi-arch option we need support in the assembler.
if test "$multi_arch" = yes; then if test "$multi_arch" = yes; then
if test "x$libc_cv_asm_type_prefix" != xno; then if test "x$libc_cv_asm_type_prefix" != xno; then

View 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}) AC_DEFINE_UNQUOTED(ASM_TYPE_DIRECTIVE_PREFIX, ${libc_cv_asm_type_prefix})
fi 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. # For the multi-arch option we need support in the assembler.
if test "$multi_arch" = yes; then if test "$multi_arch" = yes; then
if test "x$libc_cv_asm_type_prefix" != xno; then if test "x$libc_cv_asm_type_prefix" != xno; then

View File

@ -111,7 +111,9 @@ distribute := rtld-Rules \
ifuncdep5.c ifuncdep5pic.c ifuncmod5.c \ ifuncdep5.c ifuncdep5pic.c ifuncmod5.c \
ifuncmain6pie.c ifuncmod6.c \ ifuncmain6pie.c ifuncmod6.c \
ifuncmain7.c ifuncmain7pic.c ifuncmain7picstatic.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-runtime.c = -fexceptions -fasynchronous-unwind-tables
CFLAGS-dl-lookup.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 \ tst-dlmopen1 tst-dlmopen2 tst-dlmopen3 \
unload3 unload4 unload5 unload6 unload7 tst-global1 order2 \ unload3 unload4 unload5 unload6 unload7 tst-global1 order2 \
tst-audit1 tst-audit2 \ tst-audit1 tst-audit2 \
tst-stackguard1 tst-addr1 tst-thrlock tst-stackguard1 tst-addr1 tst-thrlock \
tst-unique1 tst-unique2
# reldep9 # reldep9
test-srcs = tst-pathopt test-srcs = tst-pathopt
tests-execstack-yes = tst-execstack tst-execstack-needed tst-execstack-prog 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 \ unload4mod1 unload4mod2 unload4mod3 unload4mod4 \
unload6mod1 unload6mod2 unload6mod3 \ unload6mod1 unload6mod2 unload6mod3 \
unload7mod1 unload7mod2 \ unload7mod1 unload7mod2 \
order2mod1 order2mod2 order2mod3 order2mod4 order2mod1 order2mod2 order2mod3 order2mod4 \
tst-unique1mod1 tst-unique1mod2 \
tst-unique2mod1 tst-unique2mod2
ifeq (yes,$(have-initfini-array)) ifeq (yes,$(have-initfini-array))
modules-names += tst-array2dep tst-array5dep modules-names += tst-array2dep tst-array5dep
endif endif
@ -1103,3 +1108,10 @@ $(objpfx)ifuncmain5pic: $(addprefix $(objpfx),ifuncmod5.so)
$(objpfx)ifuncmain5static: $(addprefix $(objpfx),ifuncdep5.o) $(objpfx)ifuncmain5static: $(addprefix $(objpfx),ifuncdep5.o)
$(objpfx)ifuncmain5staticpic: $(addprefix $(objpfx),ifuncdep5pic.o) $(objpfx)ifuncmain5staticpic: $(addprefix $(objpfx),ifuncdep5pic.o)
$(objpfx)ifuncmain5picstatic: $(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

View 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, int res = do_lookup_x (undef_name, new_hash, &old_hash, *ref,
&current_value, *scope, start, version, flags, &current_value, *scope, start, version, flags,
skip_map, type_class); skip_map, type_class, undef_map);
if (res > 0) if (res > 0)
break; 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) for (scope = symbol_scope; *scope != NULL; i = 0, ++scope)
if (do_lookup_x (undef_name, new_hash, &old_hash, *ref, if (do_lookup_x (undef_name, new_hash, &old_hash, *ref,
&protected_value, *scope, i, version, flags, &protected_value, *scope, i, version, flags,
skip_map, ELF_RTYPE_CLASS_PLT) != 0) skip_map, ELF_RTYPE_CLASS_PLT, NULL) != 0)
break; break;
if (protected_value.s != NULL && protected_value.m != undef_map) 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, do_lookup_x (undef_name, new_hash, &old_hash, *ref, &val,
undef_map->l_local_scope[0], 0, version, 0, NULL, 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) if (val.s != value->s || val.m != value->m)
conflict = 1; conflict = 1;

View 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) if (GL(dl_ns)[nsid]._ns_loaded == NULL)
break; break;
if (nsid == DL_NNS) if (__builtin_expect (nsid == DL_NNS, 0))
{ {
/* No more namespace available. */ /* No more namespace available. */
__rtld_lock_unlock_recursive (GL(dl_load_lock)); __rtld_lock_unlock_recursive (GL(dl_load_lock));
@ -579,7 +579,10 @@ no more namespaces available for dlmopen()"));
} }
if (nsid == GL(dl_nns)) 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; _dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT;
} }

View 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, unsigned long int *old_hash, const ElfW(Sym) *ref,
struct sym_val *result, struct r_scope_elem *scope, size_t i, struct sym_val *result, struct r_scope_elem *scope, size_t i,
const struct r_found_version *const version, int flags, 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; size_t n = scope->r_nlist;
/* Make sure we read the value before proceeding. Otherwise we /* 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) if (sym != NULL)
{ {
found_it: found_it:
switch (ELFW(ST_BIND) (sym->st_info)) switch (__builtin_expect (ELFW(ST_BIND) (sym->st_info), STB_GLOBAL))
{ {
case STB_WEAK: case STB_WEAK:
/* Weak definition. Use this value if we don't find another. */ /* 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 */ /* FALLTHROUGH */
case STB_GLOBAL: case STB_GLOBAL:
success:
/* Global definition. Just what we need. */ /* Global definition. Just what we need. */
result->s = sym; result->s = sym;
result->m = (struct link_map *) map; result->m = (struct link_map *) map;
return 1; 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: default:
/* Local symbols are ignored. */ /* Local symbols are ignored. */
break; break;

View File

@ -127,7 +127,12 @@ struct rtld_global _rtld_global =
#ifdef _LIBC_REENTRANT #ifdef _LIBC_REENTRANT
._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER, ._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
#endif #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 /* If we would use strong_alias here the compiler would see a
non-hidden definition. This would undo the effect of the previous non-hidden definition. This would undo the effect of the previous

40
elf/tst-unique1.c Normal file
View File

@ -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"

21
elf/tst-unique1mod1.c Normal file
View File

@ -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

20
elf/tst-unique1mod2.c Normal file
View File

@ -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

32
elf/tst-unique2.c Normal file
View File

@ -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"

13
elf/tst-unique2mod1.c Normal file
View File

@ -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

20
elf/tst-unique2mod2.c Normal file
View File

@ -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

View File

@ -383,6 +383,21 @@ struct rtld_global
allocated by rtld. Later it keeps the size of the map. It might be 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. */ reset if in _dl_close if the last global object is removed. */
size_t _ns_global_scope_alloc; 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. */ /* Keep track of changes to each namespace' list. */
struct r_debug _ns_debug; struct r_debug _ns_debug;
} _dl_ns[DL_NNS]; } _dl_ns[DL_NNS];