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

View 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

26
configure vendored
View file

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

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})
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

View 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

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,
&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;

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)
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;
}

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,
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;

View 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

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
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];