Treat STV_HIDDEN and STV_INTERNAL symbols as STB_LOCAL

Message ID alpine.DEB.2.00.1604042030500.21846@tp.orcam.me.uk
State Superseded
Headers

Commit Message

Maciej W. Rozycki April 4, 2016, 8:35 p.m. UTC
  In a reference to PR ld/19908 make ld.so respect symbol export classes 
aka visibility and treat STV_HIDDEN and STV_INTERNAL symbols as local, 
preventing such symbols from preempting exported symbols.

According to the ELF gABI[1] neither STV_HIDDEN nor STV_INTERNAL symbols 
are supposed to be present in linked binaries:

"A hidden symbol contained in a relocatable object must be either
removed or converted to STB_LOCAL binding by the link-editor when the
relocatable object is included in an executable file or shared object."

"An internal symbol contained in a relocatable object must be either
removed or converted to STB_LOCAL binding by the link-editor when the
relocatable object is included in an executable file or shared object."

however some GNU binutils versions produce such symbols in some cases.  
PR ld/19908 is one and we also have this note in scripts/abilist.awk:

# binutils versions up through at least 2.23 have some bugs that
# caused STV_HIDDEN symbols to appear in .dynsym, though that is useless.

so clearly there is linked code out there which contains such symbols 
which is prone to symbol table misinterpretation, and it'll be more 
productive if we handle this gracefully, under the Robustness Principle: 
"be liberal in what you accept, and conservative in what you produce", 
especially as this is a simple (STV_HIDDEN|STV_INTERNAL) => STB_LOCAL 
mapping.

References:

[1] "System V Application Binary Interface - DRAFT - 24 April 2001",
    The Santa Cruz Operation, Inc., "Symbol Table",
    <http://www.sco.com/developers/gabi/2001-04-24/ch4.symtab.html>

2016-04-04  Maciej W. Rozycki  <macro@imgtec.com>

	* elf/dl-addr.c (determine_info): Treat STV_HIDDEN and
	STV_INTERNAL symbols as local.
	* elf/dl-lookup.c (do_lookup_x): Likewise.
	* elf/dl-reloc.c (RESOLVE_MAP): Likewise.
---
Hi,

 This has passed testing with the mips-linux-gnu target, little-endian, 
o32 ABI, with no regressions.

 Any reason why this change would not be The Right Thing To Do?

 NB I've noticed in many if not most places we do not respect the GNU 
Coding Standard WRT the `ELFW' macro and the space required before the 
following opening parenthesis.  I decided to ignore this practice and 
respect the standard regardless, however I did not correct any nearby use 
except where the line affected had to be changed anyway.  Please let me 
know if there is a compelling reason to do otherwise and I'll follow 
accordingly.

  Maciej

glibc-elf-export-class-local.diff
  

Comments

Roland McGrath April 4, 2016, 8:56 p.m. UTC | #1
The code should have comments explaining why visibility is being checked
despite the fact that it ought to be irrelevant.  The cleanest way to do
this would be to introduce a macro for the "treat as local?" predicate and
make the cases you touched all use that.

The ELFW and ElfW macros are used with no space before the paren as a
special exception, because they are not normal function-like macros but
rather ElfW(Foo) stands in for Elf32_Foo and ELFW(ST_FOO) stands in for
ELF32_ST_FOO so making them look more like a single token fits better.
  

Patch

Index: glibc/elf/dl-addr.c
===================================================================
--- glibc.orig/elf/dl-addr.c	2016-01-07 13:55:59.000000000 +0000
+++ glibc/elf/dl-addr.c	2016-03-06 06:57:12.384634956 +0000
@@ -88,6 +88,8 @@  determine_info (const ElfW(Addr) addr, s
       for (; (void *) symtab < (void *) symtabend; ++symtab)
 	if ((ELFW(ST_BIND) (symtab->st_info) == STB_GLOBAL
 	     || ELFW(ST_BIND) (symtab->st_info) == STB_WEAK)
+	    && ELFW (ST_VISIBILITY) (symtab->st_other) != STV_HIDDEN
+	    && ELFW (ST_VISIBILITY) (symtab->st_other) != STV_INTERNAL
 	    && ELFW(ST_TYPE) (symtab->st_info) != STT_TLS
 	    && (symtab->st_shndx != SHN_UNDEF
 		|| symtab->st_value != 0)
Index: glibc/elf/dl-lookup.c
===================================================================
--- glibc.orig/elf/dl-lookup.c	2016-03-06 05:18:09.098564495 +0000
+++ glibc/elf/dl-lookup.c	2016-03-06 05:18:38.910397550 +0000
@@ -516,6 +516,12 @@  do_lookup_x (const char *undef_name, uin
 #endif
 	    }
 
+	  /* Hidden and internal symbols are local, ignore them.  */
+	  unsigned char visibility = ELFW (ST_VISIBILITY) (sym->st_other);
+	  if (__glibc_unlikely (visibility == STV_HIDDEN
+				|| visibility == STV_INTERNAL))
+	    goto skip;
+
 	  switch (ELFW(ST_BIND) (sym->st_info))
 	    {
 	    case STB_WEAK:
Index: glibc/elf/dl-reloc.c
===================================================================
--- glibc.orig/elf/dl-reloc.c	2016-03-05 02:29:52.306589738 +0000
+++ glibc/elf/dl-reloc.c	2016-03-06 06:57:58.050263097 +0000
@@ -233,7 +233,9 @@  _dl_relocate_object (struct link_map *l,
 
     /* 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			      \
+      && ELFW (ST_VISIBILITY) ((*ref)->st_other) != STV_HIDDEN		      \
+      && ELFW (ST_VISIBILITY) ((*ref)->st_other) != STV_INTERNAL)	      \
      ? ((__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 (),				      \