MIPS support for GNU hash

Message ID 1560962428-17477-1-git-send-email-mihailo.stojanovic@rt-rk.com
State Superseded
Headers

Commit Message

Mihailo Stojanovic June 19, 2019, 4:40 p.m. UTC
  Hello everyone,

  This patch is a reimplementation of [1], which was submitted back in
2015. Copyright issue has been sorted out [2] last year. It proposed a
new section (.gnu.xhash) and related dynamic tag (DT_GNU_XHASH). The new
section would be virtually identical to the existing .gnu.hash except
for the translation table (xlat) which would contain correct MIPS
.dynsym indexes corresponding to the hashvals in chains. This is because
MIPS ABI imposes a different ordering of the dynyms than the one
expected by the .gnu.hash section. Another addition would be a leading
word which would contain the number of entries in the translation table.

  This patch addresses the alignment issue as reported in [3] which is
caused by the leading word added to the .gnu.xhash section. Leading word
is now removed in the corresponding binutils patch, and the number of
entries in the translation table is computed using DT_MIPS_SYMTABNO
dynamic tag.

  Since the MIPS specific dl-lookup.c file was removed since the initial
patch, I opted for the definition of three new macros in the common
dl-lookup.c. ELF_MACHINE_GNU_HASH can be redefined to select the type of
GNU hash which the machine uses (in this case, DT_GNU_XHASH).
ELF_MACHINE_HASH_SYMIDX is used to calculate the index of a symbol in
GNU hash. On MIPS, it is redefined to look up the symbol index in the
translation table. ELF_MACHINE_XHASH_SETUP does nothing for all
machines, except MIPS, on which it initializes the .gnu.xhash pointer in
the link_map_machine_struct.

  I kept the new, MIPS specific, dl-addr.c file, though this is
something on which I would like comments from the community.

  The other major change is reserving MIPS ABI version 5 for .gnu.xhash,
suggesting that the dynamic linker now supports .gnu.xhash. This is also
something of which I am not sure of, especially after reading [4]. I am
confused on whether this ABI version is reserved for IFUNC, or it can be
used for this purpose.

  The patch was tested by running the glibc testsuite for the three MIPS
ABIs (o32, n32 and n64).

  The corresponding binutils patch can be found here:
https://sourceware.org/ml/binutils/2019-06/msg00133.html

Regards,
Mihailo

[1] https://sourceware.org/ml/binutils/2015-10/msg00057.html
[2] https://sourceware.org/ml/binutils/2018-03/msg00025.html
[3] https://sourceware.org/ml/binutils/2016-01/msg00006.html
[4] https://sourceware.org/ml/libc-alpha/2016-12/msg00853.html

        * elf/dl-lookup.c (ELF_MACHINE_GNU_HASH): New macro.
        (ELF_MACHINE_HASH_SYMIDX): Ditto.
        (ELF_MACHINE_XHASH_SETUP): Ditto.
        (do_lookup_x): Calculate the symbol index using the newly
        defined ELF_MACHINE_HASH_SYMIDX macro.
        (_dl_setup_hash): Replace the uses of DT_GNU_HASH macro wit the
        newly defined ELF_MACHINE_GNU_HASH macro.
        * elf/elf.h (SHT_GNU_XHASH): New macro.
        (DT_GNU_XHASH): New macro.
        * elf/get-dynamic-info.h (elf_get_dynamic_info): Call
        ADJUST_DYN_INFO on the new DT_GNU_XHASH dynamic tag.
        * sysdeps/mips/dl-addr.c: New file.
        * sysdeps/mips/dl-machine.h (ELF_MACHINE_GNU_HASH): New macro.
        (ELF_MACHINE_HASH_SYMIDX): Ditto.
        (ELF_MACHINE_XHASH_SETUP): Ditto.
        * sysdeps/mips/linkmap.h (struct link_map_machine): New member.
        * sysdeps/unix/sysv/linux/mips/ldsodefs.h: Increment valid ABI
        version.
        * sysdeps/unix/sysv/linux/mips/libc-abis: New ABI version.
---
 elf/dl-lookup.c                         |  27 +++++-
 elf/elf.h                               |   4 +-
 elf/get-dynamic-info.h                  |   1 +
 sysdeps/mips/dl-addr.c                  | 148 ++++++++++++++++++++++++++++++++
 sysdeps/mips/dl-machine.h               |  10 +++
 sysdeps/mips/linkmap.h                  |   1 +
 sysdeps/unix/sysv/linux/mips/ldsodefs.h |   2 +-
 sysdeps/unix/sysv/linux/mips/libc-abis  |   3 +
 8 files changed, 191 insertions(+), 5 deletions(-)
 create mode 100644 sysdeps/mips/dl-addr.c
  

Comments

Joseph Myers June 20, 2019, 3:13 p.m. UTC | #1
On Wed, 19 Jun 2019, Mihailo Stojanovic wrote:

>   I kept the new, MIPS specific, dl-addr.c file, though this is
> something on which I would like comments from the community.

That's not maintainable.  Situations where one architecture has an 
architecture-specific modified duplicate of an architecture-independent 
file like this have to be avoided, because people will never reliably 
remember to update the architecture-specific copy when changing the 
architecture-independent version.  The general direction in glibc is more 
unification of sources for different architectures, not less.
  
Mihailo Stojanovic June 21, 2019, 2:03 p.m. UTC | #2
Thank you for the input. The approach I took for dl-lookup.c (defining 
MIPS specific macros in dl-machine.h and using them in 
architecture-independent dl-lookup.c) will not work for 
architecture-independent dl-addr.c, as it must not include dl-machine.h.

These MIPS specific macros could possibly be moved to 
sysdeps/mips/ldsodefs.h, and their generic counterparts moved to 
architecture-independent sysdeps/generic/ldsodefs.h. This way they could 
be accessed by both dl-lookup.c and dl-addr.c. Is this OK? If not, could 
you suggest another approach?

Regards,

Mihailo

On 20.6.19. 17:13, Joseph Myers wrote:
> On Wed, 19 Jun 2019, Mihailo Stojanovic wrote:
>
>>    I kept the new, MIPS specific, dl-addr.c file, though this is
>> something on which I would like comments from the community.
> That's not maintainable.  Situations where one architecture has an
> architecture-specific modified duplicate of an architecture-independent
> file like this have to be avoided, because people will never reliably
> remember to update the architecture-specific copy when changing the
> architecture-independent version.  The general direction in glibc is more
> unification of sources for different architectures, not less.
>
  
Joseph Myers July 8, 2019, 9:58 a.m. UTC | #3
On Fri, 21 Jun 2019, Mihailo Stojanović wrote:

> Thank you for the input. The approach I took for dl-lookup.c (defining MIPS
> specific macros in dl-machine.h and using them in architecture-independent
> dl-lookup.c) will not work for architecture-independent dl-addr.c, as it must
> not include dl-machine.h.
> 
> These MIPS specific macros could possibly be moved to sysdeps/mips/ldsodefs.h,
> and their generic counterparts moved to architecture-independent
> sysdeps/generic/ldsodefs.h. This way they could be accessed by both
> dl-lookup.c and dl-addr.c. Is this OK? If not, could you suggest another
> approach?

I have no advice on which sysdeps header is most appropriate for this, as 
long as you don't duplicate code between architecture-specific and 
architecture-independent files (so the MIPS-specific file only defines 
things that are genuinely different from the architecture-independent 
defaults, without having its own copies of any of those default 
definitions).
  

Patch

diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index e3f42a1..65a52ca 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -37,6 +37,24 @@ 
 # define ELF_MACHINE_SYM_NO_MATCH(sym) 0
 #endif
 
+/* Type of GNU hash which the machine uses.  An architecture can redefine
+   this if it uses GNU_XHASH.  */
+#ifndef ELF_MACHINE_GNU_HASH
+# define ELF_MACHINE_GNU_HASH DT_GNU_HASH
+#endif
+
+/* Calculate the index of a symbol in GNU_HASH.  An architecture can redefine
+   this if it uses GNU_XHASH.  */
+#ifndef ELF_MACHINE_HASH_SYMIDX
+# define ELF_MACHINE_HASH_SYMIDX(map, hasharr) \
+  ((hasharr) - (map)->l_gnu_chain_zero)
+#endif
+
+/* Setup GNU_XHASH.  An architecture can redefine this if it uses GNU_XHASH.  */
+#ifndef ELF_MACHINE_XHASH_SETUP
+# define ELF_MACHINE_XHASH_SETUP(hash32, symbias, map)
+#endif
+
 #define VERSTAG(tag)	(DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (tag))
 
 
@@ -403,7 +421,7 @@  do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
 		  do
 		    if (((*hasharr ^ new_hash) >> 1) == 0)
 		      {
-			symidx = hasharr - map->l_gnu_chain_zero;
+			symidx = ELF_MACHINE_HASH_SYMIDX (map, hasharr);
 			sym = check_match (undef_name, ref, version, flags,
 					   type_class, &symtab[symidx], symidx,
 					   strtab, map, &versioned_sym,
@@ -937,10 +955,10 @@  _dl_setup_hash (struct link_map *map)
 {
   Elf_Symndx *hash;
 
-  if (__glibc_likely (map->l_info[ADDRIDX (DT_GNU_HASH)] != NULL))
+  if (__glibc_likely (map->l_info[ADDRIDX (ELF_MACHINE_GNU_HASH)] != NULL))
     {
       Elf32_Word *hash32
-	= (void *) D_PTR (map, l_info[ADDRIDX (DT_GNU_HASH)]);
+	= (void *) D_PTR (map, l_info[ADDRIDX (ELF_MACHINE_GNU_HASH)]);
       map->l_nbuckets = *hash32++;
       Elf32_Word symbias = *hash32++;
       Elf32_Word bitmask_nwords = *hash32++;
@@ -955,6 +973,9 @@  _dl_setup_hash (struct link_map *map)
       map->l_gnu_buckets = hash32;
       hash32 += map->l_nbuckets;
       map->l_gnu_chain_zero = hash32 - symbias;
+
+      ELF_MACHINE_XHASH_SETUP (hash32, symbias, map)
+
       return;
     }
 
diff --git a/elf/elf.h b/elf/elf.h
index bb13c87..fdf8a4b 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -448,6 +448,7 @@  typedef struct
 #define SHT_SYMTAB_SHNDX  18		/* Extended section indeces */
 #define	SHT_NUM		  19		/* Number of defined types.  */
 #define SHT_LOOS	  0x60000000	/* Start OS-specific.  */
+#define SHT_GNU_XHASH	  0x6ffffff4	/* GNU-style hash table with xlat.  */
 #define SHT_GNU_ATTRIBUTES 0x6ffffff5	/* Object attributes.  */
 #define SHT_GNU_HASH	  0x6ffffff6	/* GNU-style hash table.  */
 #define SHT_GNU_LIBLIST	  0x6ffffff7	/* Prelink library list */
@@ -917,6 +918,7 @@  typedef struct
    If any adjustment is made to the ELF object after it has been
    built these entries will need to be adjusted.  */
 #define DT_ADDRRNGLO	0x6ffffe00
+#define DT_GNU_XHASH	0x6ffffef4	/* GNU-style hash table with xlat.  */
 #define DT_GNU_HASH	0x6ffffef5	/* GNU-style hash table.  */
 #define DT_TLSDESC_PLT	0x6ffffef6
 #define DT_TLSDESC_GOT	0x6ffffef7
@@ -930,7 +932,7 @@  typedef struct
 #define DT_SYMINFO	0x6ffffeff	/* Syminfo table.  */
 #define DT_ADDRRNGHI	0x6ffffeff
 #define DT_ADDRTAGIDX(tag)	(DT_ADDRRNGHI - (tag))	/* Reverse order! */
-#define DT_ADDRNUM 11
+#define DT_ADDRNUM 12
 
 /* The versioning entry types.  The next are defined as part of the
    GNU extension.  */
diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h
index 75fbb88..97336b8 100644
--- a/elf/get-dynamic-info.h
+++ b/elf/get-dynamic-info.h
@@ -111,6 +111,7 @@  elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
       ADJUST_DYN_INFO (DT_JMPREL);
       ADJUST_DYN_INFO (VERSYMIDX (DT_VERSYM));
       ADJUST_DYN_INFO (ADDRIDX (DT_GNU_HASH));
+      ADJUST_DYN_INFO (ADDRIDX (DT_GNU_XHASH));
 # undef ADJUST_DYN_INFO
       assert (cnt <= DL_RO_DYN_TEMP_CNT);
     }
diff --git a/sysdeps/mips/dl-addr.c b/sysdeps/mips/dl-addr.c
new file mode 100644
index 0000000..af72460
--- /dev/null
+++ b/sysdeps/mips/dl-addr.c
@@ -0,0 +1,148 @@ 
+/* Locate the shared object symbol nearest a given address.  MIPS version.
+   Copyright (C) 1996-2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <stddef.h>
+#include <ldsodefs.h>
+
+
+static inline void
+__attribute ((always_inline))
+determine_info (const ElfW(Addr) addr, struct link_map *match, Dl_info *info,
+		struct link_map **mapp, const ElfW(Sym) **symbolp)
+{
+  /* Now we know what object the address lies in.  */
+  info->dli_fname = match->l_name;
+  info->dli_fbase = (void *) match->l_map_start;
+
+  /* If this is the main program the information is incomplete.  */
+  if (__builtin_expect (match->l_name[0], 'a') == '\0'
+      && match->l_type == lt_executable)
+    info->dli_fname = _dl_argv[0];
+
+  const ElfW(Sym) *symtab
+    = (const ElfW(Sym) *) D_PTR (match, l_info[DT_SYMTAB]);
+  const char *strtab = (const char *) D_PTR (match, l_info[DT_STRTAB]);
+
+  ElfW(Word) strtabsize = match->l_info[DT_STRSZ]->d_un.d_val;
+
+  const ElfW(Sym) *matchsym = NULL;
+  if (match->l_info[ADDRIDX (DT_GNU_XHASH)] != NULL)
+    {
+      /* We look at all symbol table entries referenced by the hash
+	 table.  */
+      for (Elf_Symndx bucket = 0; bucket < match->l_nbuckets; ++bucket)
+	{
+	  Elf32_Word symndx = match->l_gnu_buckets[bucket];
+	  if (symndx != 0)
+	    {
+	      const Elf32_Word *hasharr = &match->l_gnu_chain_zero[symndx];
+
+	      do
+		{
+		  /* The hash table never references local symbols so
+		     we can omit that test here.  */
+		  symndx = match->l_mach.gnu_xlat_zero[hasharr
+						       - match->l_gnu_chain_zero];
+		  if ((symtab[symndx].st_shndx != SHN_UNDEF
+		       || symtab[symndx].st_value != 0)
+		      && symtab[symndx].st_shndx != SHN_ABS
+		      && ELFW(ST_TYPE) (symtab[symndx].st_info) != STT_TLS
+		      && DL_ADDR_SYM_MATCH (match, &symtab[symndx],
+					    matchsym, addr)
+		      && symtab[symndx].st_name < strtabsize)
+		    matchsym = (ElfW(Sym) *) &symtab[symndx];
+
+		  ++symndx;
+		}
+	      while ((*hasharr++ & 1u) == 0);
+	    }
+	}
+    }
+  else
+    {
+      const ElfW(Sym) *symtabend;
+      if (match->l_info[DT_HASH] != NULL)
+	symtabend = (symtab
+		     + ((Elf_Symndx *) D_PTR (match, l_info[DT_HASH]))[1]);
+      else
+	/* There is no direct way to determine the number of symbols in the
+	   dynamic symbol table and no hash table is present.  The ELF
+	   binary is ill-formed but what shall we do?  Use the beginning of
+	   the string table which generally follows the symbol table.  */
+	symtabend = (const ElfW(Sym) *) strtab;
+
+      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)
+	    && symtab->st_shndx != SHN_ABS
+	    && DL_ADDR_SYM_MATCH (match, symtab, matchsym, addr)
+	    && symtab->st_name < strtabsize)
+	  matchsym = (ElfW(Sym) *) symtab;
+    }
+
+  if (mapp)
+    *mapp = match;
+  if (symbolp)
+    *symbolp = matchsym;
+
+  if (matchsym)
+    {
+      /* We found a symbol close by.  Fill in its name and exact
+	 address.  */
+      lookup_t matchl = LOOKUP_VALUE (match);
+
+      info->dli_sname = strtab + matchsym->st_name;
+      info->dli_saddr = DL_SYMBOL_ADDRESS (matchl, matchsym);
+    }
+  else
+    {
+      /* No symbol matches.  We return only the containing object.  */
+      info->dli_sname = NULL;
+      info->dli_saddr = NULL;
+    }
+}
+
+
+int
+_dl_addr (const void *address, Dl_info *info,
+	  struct link_map **mapp, const ElfW(Sym) **symbolp)
+{
+  const ElfW(Addr) addr = DL_LOOKUP_ADDRESS (address);
+  int result = 0;
+
+  /* Protect against concurrent loads and unloads.  */
+  __rtld_lock_lock_recursive (GL(dl_load_lock));
+
+  struct link_map *l = _dl_find_dso_for_object (addr);
+
+  if (l)
+    {
+      determine_info (addr, l, info, mapp, symbolp);
+      result = 1;
+    }
+
+  __rtld_lock_unlock_recursive (GL(dl_load_lock));
+
+  return result;
+}
+libc_hidden_def (_dl_addr)
diff --git a/sysdeps/mips/dl-machine.h b/sysdeps/mips/dl-machine.h
index f9e7e90..b7c42df 100644
--- a/sysdeps/mips/dl-machine.h
+++ b/sysdeps/mips/dl-machine.h
@@ -22,6 +22,7 @@ 
 #define dl_machine_h
 
 #define ELF_MACHINE_NAME "MIPS"
+#define ELF_MACHINE_GNU_HASH DT_GNU_XHASH
 
 #include <entry.h>
 
@@ -482,6 +483,15 @@  elf_machine_plt_value (struct link_map *map, const ElfW(Rel) *reloc,
 #define ELF_MACHINE_SYM_NO_MATCH(sym) \
   ((sym)->st_shndx == SHN_UNDEF && !((sym)->st_other & STO_MIPS_PLT))
 
+/* Calculate the index of a symbol in GNU_XHASH.  */
+#define ELF_MACHINE_HASH_SYMIDX(map, hasharr) \
+  ((map)->l_mach.gnu_xlat_zero[(hasharr) - (map)->l_gnu_chain_zero])
+
+/* Setup GNU_XHASH.  */
+#define ELF_MACHINE_XHASH_SETUP(hash32, symbias, map) \
+  (hash32) += (map)->l_info[DT_MIPS (SYMTABNO)]->d_un.d_val - (symbias); \
+  (map)->l_mach.gnu_xlat_zero = (hash32) - (symbias);
+
 #endif /* !dl_machine_h */
 
 #ifdef RESOLVE_MAP
diff --git a/sysdeps/mips/linkmap.h b/sysdeps/mips/linkmap.h
index 1fb9678..d29f751 100644
--- a/sysdeps/mips/linkmap.h
+++ b/sysdeps/mips/linkmap.h
@@ -3,4 +3,5 @@  struct link_map_machine
     ElfW(Addr) plt; /* Address of .plt */
     ElfW(Word) fpabi; /* FP ABI of the object */
     unsigned int odd_spreg; /* Does the object require odd_spreg support? */
+    const Elf32_Word *gnu_xlat_zero; /* .gnu.xhash */
   };
diff --git a/sysdeps/unix/sysv/linux/mips/ldsodefs.h b/sysdeps/unix/sysv/linux/mips/ldsodefs.h
index 8dde855..ce7b2f9 100644
--- a/sysdeps/unix/sysv/linux/mips/ldsodefs.h
+++ b/sysdeps/unix/sysv/linux/mips/ldsodefs.h
@@ -34,7 +34,7 @@  extern void _dl_static_init (struct link_map *map);
 #undef VALID_ELF_ABIVERSION
 #define VALID_ELF_ABIVERSION(osabi,ver)			\
   (ver == 0						\
-   || (osabi == ELFOSABI_SYSV && ver < 4)		\
+   || (osabi == ELFOSABI_SYSV && ver < 6)		\
    || (osabi == ELFOSABI_GNU && ver < LIBC_ABI_MAX))
 
 #endif /* ldsodefs.h */
diff --git a/sysdeps/unix/sysv/linux/mips/libc-abis b/sysdeps/unix/sysv/linux/mips/libc-abis
index eaea558..4044355 100644
--- a/sysdeps/unix/sysv/linux/mips/libc-abis
+++ b/sysdeps/unix/sysv/linux/mips/libc-abis
@@ -16,3 +16,6 @@  UNIQUE
 MIPS_O32_FP64   mips*-*-linux*
 # Absolute (SHN_ABS) symbols working correctly.
 ABSOLUTE
+# .gnu.xhash working correctly.
+GNU_XHASH
+