b1dca6
commit 23c1c256ae7b0f010d0fcaff60682b620887b164
b1dca6
Author: Mihailo Stojanovic <mihailo.stojanovic@rt-rk.com>
b1dca6
Date:   Thu Aug 29 20:11:42 2019 +0000
b1dca6
b1dca6
    MIPS support for GNU hash
b1dca6
    
b1dca6
    This patch is a reimplementation of [1], which was submitted back in
b1dca6
    2015. Copyright issue has been sorted [2] last year. It proposed a new
b1dca6
    section (.gnu.xhash) and related dynamic tag (GT_GNU_XHASH). The new
b1dca6
    section would be virtually identical to the existing .gnu.hash except
b1dca6
    for the translation table (xlat) which would contain correct MIPS
b1dca6
    .dynsym indexes corresponding to the hashvals in chains. This is because
b1dca6
    MIPS ABI imposes a different ordering of the dynsyms than the one
b1dca6
    expected by the .gnu.hash section. Another addition would be a leading
b1dca6
    word at the beggining of the section, which would contain the number of
b1dca6
    entries in the translation table.
b1dca6
    
b1dca6
    In this patch, the new section name and dynamic tag are changed to
b1dca6
    reflect the fact that the section should be treated as MIPS specific
b1dca6
    (.MIPS.xhash and DT_MIPS_XHASH).
b1dca6
    
b1dca6
    This patch addresses the alignment issue reported in [3] which is caused
b1dca6
    by the leading word of the .MIPS.xhash section. Leading word is now
b1dca6
    removed in the corresponding binutils patch, and the number of entries
b1dca6
    in the translation table is computed using DT_MIPS_SYMTABNO dynamic tag.
b1dca6
    
b1dca6
    Since the MIPS specific dl-lookup.c file was removed following the
b1dca6
    initial patch submission, I opted for the definition of three new macros
b1dca6
    in the generic ldsodefs.h. ELF_MACHINE_GNU_HASH_ADDRIDX defines the
b1dca6
    index of the dynamic tag in the l_info array. ELF_MACHINE_HASH_SYMIDX is
b1dca6
    used to calculate the index of a symbol in GNU hash. On MIPS, it is
b1dca6
    defined to look up the symbol index in the translation table.
b1dca6
    ELF_MACHINE_XHASH_SETUP is defined for MIPS only. It initializes the
b1dca6
    .MIPS.xhash pointer in the link_map_machine struct.
b1dca6
    
b1dca6
    The other major change is bumping the highest EI_ABIVERSION value for
b1dca6
    MIPS to suggest that the dynamic linker now supports GNU hash.
b1dca6
    
b1dca6
    The patch was tested by running the glibc testsuite for the three MIPS
b1dca6
    ABIs (o32, n32 and n64) and for x86_64-linux-gnu.
b1dca6
    
b1dca6
    [1] https://sourceware.org/ml/binutils/2015-10/msg00057.html
b1dca6
    [2] https://sourceware.org/ml/binutils/2018-03/msg00025.html
b1dca6
    [3] https://sourceware.org/ml/binutils/2016-01/msg00006.html
b1dca6
    
b1dca6
            * elf/dl-addr.c (determine_info): Calculate the symbol index
b1dca6
            using the newly defined ELF_MACHINE_HASH_SYMIDX macro.
b1dca6
            * elf/dl-lookup.c (do_lookup_x): Ditto.
b1dca6
            (_dl_setup_hash): Initialize MIPS xhash translation table.
b1dca6
            * elf/elf.h (SHT_MIPS_XHASH): New define.
b1dca6
            (DT_MIPS_XHASH): New define.
b1dca6
            * sysdeps/generic/ldsodefs.h (ELF_MACHINE_GNU_HASH_ADDRIDX): New
b1dca6
            define.
b1dca6
            (ELF_MACHINE_HASH_SYMIDX): Ditto.
b1dca6
            (ELF_MACHINE_XHASH_SETUP): Ditto.
b1dca6
            * sysdeps/mips/ldsodefs.h (ELF_MACHINE_GNU_HASH_ADDRIDX): New
b1dca6
            define.
b1dca6
            (ELF_MACHINE_HASH_SYMIDX): Ditto.
b1dca6
            (ELF_MACHINE_XHASH_SETUP): Ditto.
b1dca6
            * sysdeps/mips/linkmap.h (struct link_map_machine): New member.
b1dca6
            * sysdeps/unix/sysv/linux/mips/ldsodefs.h: Increment valid ABI
b1dca6
            version.
b1dca6
            * sysdeps/unix/sysv/linux/mips/libc-abis: New ABI version.
b1dca6
b1dca6
diff --git a/elf/dl-addr.c b/elf/dl-addr.c
b1dca6
index e6c7d020945c51d2..b146fed09a46ff76 100644
b1dca6
--- a/elf/dl-addr.c
b1dca6
+++ b/elf/dl-addr.c
b1dca6
@@ -42,7 +42,7 @@ determine_info (const ElfW(Addr) addr, struct link_map *match, Dl_info *info,
b1dca6
   ElfW(Word) strtabsize = match->l_info[DT_STRSZ]->d_un.d_val;
b1dca6
 
b1dca6
   const ElfW(Sym) *matchsym = NULL;
b1dca6
-  if (match->l_info[ADDRIDX (DT_GNU_HASH)] != NULL)
b1dca6
+  if (match->l_info[ELF_MACHINE_GNU_HASH_ADDRIDX] != NULL)
b1dca6
     {
b1dca6
       /* We look at all symbol table entries referenced by the hash
b1dca6
 	 table.  */
b1dca6
@@ -57,6 +57,7 @@ determine_info (const ElfW(Addr) addr, struct link_map *match, Dl_info *info,
b1dca6
 		{
b1dca6
 		  /* The hash table never references local symbols so
b1dca6
 		     we can omit that test here.  */
b1dca6
+		  symndx = ELF_MACHINE_HASH_SYMIDX (match, hasharr);
b1dca6
 		  if ((symtab[symndx].st_shndx != SHN_UNDEF
b1dca6
 		       || symtab[symndx].st_value != 0)
b1dca6
 		      && symtab[symndx].st_shndx != SHN_ABS
b1dca6
@@ -65,8 +66,6 @@ determine_info (const ElfW(Addr) addr, struct link_map *match, Dl_info *info,
b1dca6
 					    matchsym, addr)
b1dca6
 		      && symtab[symndx].st_name < strtabsize)
b1dca6
 		    matchsym = (ElfW(Sym) *) &symtab[symndx];
b1dca6
-
b1dca6
-		  ++symndx;
b1dca6
 		}
b1dca6
 	      while ((*hasharr++ & 1u) == 0);
b1dca6
 	    }
b1dca6
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
b1dca6
index 01724a54f8840f9f..42fdaed99296137f 100644
b1dca6
--- a/elf/dl-lookup.c
b1dca6
+++ b/elf/dl-lookup.c
b1dca6
@@ -432,7 +432,7 @@ do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
b1dca6
 		  do
b1dca6
 		    if (((*hasharr ^ new_hash) >> 1) == 0)
b1dca6
 		      {
b1dca6
-			symidx = hasharr - map->l_gnu_chain_zero;
b1dca6
+			symidx = ELF_MACHINE_HASH_SYMIDX (map, hasharr);
b1dca6
 			sym = check_match (undef_name, ref, version, flags,
b1dca6
 					   type_class, &symtab[symidx], symidx,
b1dca6
 					   strtab, map, &versioned_sym,
b1dca6
@@ -961,10 +961,10 @@ _dl_setup_hash (struct link_map *map)
b1dca6
 {
b1dca6
   Elf_Symndx *hash;
b1dca6
 
b1dca6
-  if (__glibc_likely (map->l_info[ADDRIDX (DT_GNU_HASH)] != NULL))
b1dca6
+  if (__glibc_likely (map->l_info[ELF_MACHINE_GNU_HASH_ADDRIDX] != NULL))
b1dca6
     {
b1dca6
       Elf32_Word *hash32
b1dca6
-	= (void *) D_PTR (map, l_info[ADDRIDX (DT_GNU_HASH)]);
b1dca6
+	= (void *) D_PTR (map, l_info[ELF_MACHINE_GNU_HASH_ADDRIDX]);
b1dca6
       map->l_nbuckets = *hash32++;
b1dca6
       Elf32_Word symbias = *hash32++;
b1dca6
       Elf32_Word bitmask_nwords = *hash32++;
b1dca6
@@ -979,6 +979,10 @@ _dl_setup_hash (struct link_map *map)
b1dca6
       map->l_gnu_buckets = hash32;
b1dca6
       hash32 += map->l_nbuckets;
b1dca6
       map->l_gnu_chain_zero = hash32 - symbias;
b1dca6
+
b1dca6
+      /* Initialize MIPS xhash translation table.  */
b1dca6
+      ELF_MACHINE_XHASH_SETUP (hash32, symbias, map);
b1dca6
+
b1dca6
       return;
b1dca6
     }
b1dca6
 
b1dca6
diff --git a/elf/elf.h b/elf/elf.h
b1dca6
index 74f7f479ce817040..d6506ea1c7160dea 100644
b1dca6
--- a/elf/elf.h
b1dca6
+++ b/elf/elf.h
b1dca6
@@ -1698,6 +1698,7 @@ typedef struct
b1dca6
 #define SHT_MIPS_EH_REGION	0x70000027
b1dca6
 #define SHT_MIPS_XLATE_OLD	0x70000028
b1dca6
 #define SHT_MIPS_PDR_EXCEPTION	0x70000029
b1dca6
+#define SHT_MIPS_XHASH		0x7000002b
b1dca6
 
b1dca6
 /* Legal values for sh_flags field of Elf32_Shdr.  */
b1dca6
 
b1dca6
@@ -1945,7 +1946,9 @@ typedef struct
b1dca6
    in a PIE as it stores a relative offset from the address of the tag
b1dca6
    rather than an absolute address.  */
b1dca6
 #define DT_MIPS_RLD_MAP_REL  0x70000035
b1dca6
-#define DT_MIPS_NUM	     0x36
b1dca6
+/* GNU-style hash table with xlat.  */
b1dca6
+#define DT_MIPS_XHASH	     0x70000036
b1dca6
+#define DT_MIPS_NUM	     0x37
b1dca6
 
b1dca6
 /* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry.  */
b1dca6
 
b1dca6
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
b1dca6
index f0185ce0d16c0f69..3bdbdd6e67dacc85 100644
b1dca6
--- a/sysdeps/generic/ldsodefs.h
b1dca6
+++ b/sysdeps/generic/ldsodefs.h
b1dca6
@@ -47,6 +47,23 @@ __BEGIN_DECLS
b1dca6
 #define ADDRIDX(tag)	(DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \
b1dca6
 			 + DT_EXTRANUM + DT_VALNUM + DT_ADDRTAGIDX (tag))
b1dca6
 
b1dca6
+/* Type of GNU hash which the machine uses.  */
b1dca6
+#ifndef ELF_MACHINE_GNU_HASH_ADDRIDX
b1dca6
+# define ELF_MACHINE_GNU_HASH_ADDRIDX ADDRIDX (DT_GNU_HASH)
b1dca6
+#endif
b1dca6
+
b1dca6
+/* Calculate the index of a symbol in GNU hash.  */
b1dca6
+#ifndef ELF_MACHINE_HASH_SYMIDX
b1dca6
+# define ELF_MACHINE_HASH_SYMIDX(map, hasharr) \
b1dca6
+  ((hasharr) - (map)->l_gnu_chain_zero)
b1dca6
+#endif
b1dca6
+
b1dca6
+/* Setup MIPS xhash.  Defined only for MIPS.  */
b1dca6
+#ifndef ELF_MACHINE_XHASH_SETUP
b1dca6
+# define ELF_MACHINE_XHASH_SETUP(hash32, symbias, map) \
b1dca6
+  ((void) (hash32), (void) (symbias), (void) (map))
b1dca6
+#endif
b1dca6
+
b1dca6
 /* We use this macro to refer to ELF types independent of the native wordsize.
b1dca6
    `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'.  */
b1dca6
 #define ELFW(type)	_ElfW (ELF, __ELF_NATIVE_CLASS, type)
b1dca6
diff --git a/sysdeps/mips/ldsodefs.h b/sysdeps/mips/ldsodefs.h
b1dca6
index c6e5ce7e660325c1..35043b7c6d416c50 100644
b1dca6
--- a/sysdeps/mips/ldsodefs.h
b1dca6
+++ b/sysdeps/mips/ldsodefs.h
b1dca6
@@ -26,6 +26,21 @@ struct La_mips_32_retval;
b1dca6
 struct La_mips_64_regs;
b1dca6
 struct La_mips_64_retval;
b1dca6
 
b1dca6
+#define ELF_MACHINE_GNU_HASH_ADDRIDX (DT_MIPS_XHASH - DT_LOPROC + DT_NUM)
b1dca6
+
b1dca6
+/* Calculate the index of a symbol in MIPS xhash.  */
b1dca6
+#define ELF_MACHINE_HASH_SYMIDX(map, hasharr) \
b1dca6
+  ((map)->l_mach.mips_xlat_zero[(hasharr) - (map)->l_gnu_chain_zero])
b1dca6
+
b1dca6
+/* Setup MIPS xhash.  */
b1dca6
+#define ELF_MACHINE_XHASH_SETUP(hash32, symbias, map)			    \
b1dca6
+  do									    \
b1dca6
+    {									    \
b1dca6
+      (hash32) += (map)->l_info[DT_MIPS (SYMTABNO)]->d_un.d_val - (symbias); \
b1dca6
+      (map)->l_mach.mips_xlat_zero = (hash32) - (symbias);		    \
b1dca6
+    }									    \
b1dca6
+  while (0)
b1dca6
+
b1dca6
 #define ARCH_PLTENTER_MEMBERS						    \
b1dca6
     Elf32_Addr (*mips_o32_gnu_pltenter) (Elf32_Sym *, unsigned int,	    \
b1dca6
 					 uintptr_t *, uintptr_t *,	    \
b1dca6
diff --git a/sysdeps/mips/linkmap.h b/sysdeps/mips/linkmap.h
b1dca6
index 1fb9678a6d1625fd..1e640c3ba9bd18e4 100644
b1dca6
--- a/sysdeps/mips/linkmap.h
b1dca6
+++ b/sysdeps/mips/linkmap.h
b1dca6
@@ -3,4 +3,5 @@ struct link_map_machine
b1dca6
     ElfW(Addr) plt; /* Address of .plt */
b1dca6
     ElfW(Word) fpabi; /* FP ABI of the object */
b1dca6
     unsigned int odd_spreg; /* Does the object require odd_spreg support? */
b1dca6
+    const Elf32_Word *mips_xlat_zero; /* .MIPS.xhash */
b1dca6
   };
b1dca6
diff --git a/sysdeps/unix/sysv/linux/mips/ldsodefs.h b/sysdeps/unix/sysv/linux/mips/ldsodefs.h
b1dca6
index d2912cadabfd6877..03f3e12f202a0563 100644
b1dca6
--- a/sysdeps/unix/sysv/linux/mips/ldsodefs.h
b1dca6
+++ b/sysdeps/unix/sysv/linux/mips/ldsodefs.h
b1dca6
@@ -34,7 +34,7 @@ extern void _dl_static_init (struct link_map *map);
b1dca6
 #undef VALID_ELF_ABIVERSION
b1dca6
 #define VALID_ELF_ABIVERSION(osabi,ver)			\
b1dca6
   (ver == 0						\
b1dca6
-   || (osabi == ELFOSABI_SYSV && ver < 5)		\
b1dca6
+   || (osabi == ELFOSABI_SYSV && ver < 6)		\
b1dca6
    || (osabi == ELFOSABI_GNU && ver < LIBC_ABI_MAX))
b1dca6
 
b1dca6
 #endif /* ldsodefs.h */
b1dca6
diff --git a/sysdeps/unix/sysv/linux/mips/libc-abis b/sysdeps/unix/sysv/linux/mips/libc-abis
b1dca6
index eaea558720f42a48..c0b67dae3ece1511 100644
b1dca6
--- a/sysdeps/unix/sysv/linux/mips/libc-abis
b1dca6
+++ b/sysdeps/unix/sysv/linux/mips/libc-abis
b1dca6
@@ -16,3 +16,5 @@ UNIQUE
b1dca6
 MIPS_O32_FP64   mips*-*-linux*
b1dca6
 # Absolute (SHN_ABS) symbols working correctly.
b1dca6
 ABSOLUTE
b1dca6
+# GNU-style hash table with translation table.
b1dca6
+MIPS_XHASH