about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--elf/dl-addr.c1
-rw-r--r--elf/dl-lookup.c4
-rw-r--r--elf/dl-reloc.c3
-rw-r--r--sysdeps/generic/ldsodefs.h13
5 files changed, 29 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index a7ca1ff16e..29a92f5a5b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2016-07-01  Maciej W. Rozycki  <macro@imgtec.com>
+
+	* sysdeps/generic/ldsodefs.h
+	(dl_symbol_visibility_binds_local_p): New inline function.
+	* elf/dl-addr.c (determine_info): Treat hidden and internal
+	symbols as local.
+	* elf/dl-lookup.c (do_lookup_x): Likewise.
+	* elf/dl-reloc.c (RESOLVE_MAP): Likewise.
+
 2016-07-01  Aurelien Jarno  <aurelien@aurel32.net>
 
 	* sparc/sparc32/sparcv9/fpu/s_nearbyint.S (__nearbyint): Trigger an
diff --git a/elf/dl-addr.c b/elf/dl-addr.c
index 291ff55816..1b16a58ced 100644
--- a/elf/dl-addr.c
+++ b/elf/dl-addr.c
@@ -88,6 +88,7 @@ determine_info (const ElfW(Addr) addr, struct link_map *match, Dl_info *info,
       for (; (void *) symtab < (void *) symtabend; ++symtab)
 	if ((ELFW(ST_BIND) (symtab->st_info) == STB_GLOBAL
 	     || ELFW(ST_BIND) (symtab->st_info) == STB_WEAK)
+	    && __glibc_likely (!dl_symbol_visibility_binds_local_p (symtab))
 	    && ELFW(ST_TYPE) (symtab->st_info) != STT_TLS
 	    && (symtab->st_shndx != SHN_UNDEF
 		|| symtab->st_value != 0)
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 6d299c1097..52c994e16c 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -516,6 +516,10 @@ do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
 #endif
 	    }
 
+	  /* Hidden and internal symbols are local, ignore them.  */
+	  if (__glibc_unlikely (dl_symbol_visibility_binds_local_p (sym)))
+	    goto skip;
+
 	  switch (ELFW(ST_BIND) (sym->st_info))
 	    {
 	    case STB_WEAK:
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 14709f960d..42bddc1e2c 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -233,7 +233,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
 
     /* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code.  */
 #define RESOLVE_MAP(ref, version, r_type) \
-    (ELFW(ST_BIND) ((*ref)->st_info) != STB_LOCAL			      \
+    ((ELFW(ST_BIND) ((*ref)->st_info) != STB_LOCAL			      \
+      && __glibc_likely (!dl_symbol_visibility_binds_local_p (*ref)))	      \
      ? ((__builtin_expect ((*ref) == l->l_lookup_cache.sym, 0)		      \
 	 && elf_machine_type_class (r_type) == l->l_lookup_cache.type_class)  \
 	? (bump_num_cache_relocations (),				      \
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index ddec0be12c..f68fdf4501 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -88,6 +88,19 @@ typedef struct link_map *lookup_t;
        || (ADDR) < (L)->l_addr + (SYM)->st_value + (SYM)->st_size)	\
    && ((MATCHSYM) == NULL || (MATCHSYM)->st_value < (SYM)->st_value))
 
+/* According to the ELF gABI no STV_HIDDEN or STV_INTERNAL symbols are
+   expected to be present in dynamic symbol tables as they should have
+   been either removed or converted to STB_LOCAL binding by the static
+   linker.  However some GNU binutils versions produce such symbols in
+   some cases.  To prevent such symbols present in a buggy binary from
+   preempting global symbols we filter them out with this predicate.  */
+static __always_inline bool
+dl_symbol_visibility_binds_local_p (const ElfW(Sym) *sym)
+{
+  return (ELFW(ST_VISIBILITY) (sym->st_other) == STV_HIDDEN
+	  || ELFW(ST_VISIBILITY) (sym->st_other) == STV_INTERNAL);
+}
+
 /* Unmap a loaded object, called by _dl_close (). */
 #ifndef DL_UNMAP_IS_SPECIAL
 # define DL_UNMAP(map)	_dl_unmap_segments (map)