From patchwork Wed Jun 19 16:40:28 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mihailo Stojanovic X-Patchwork-Id: 33219 Received: (qmail 63215 invoked by alias); 19 Jun 2019 16:40:37 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 63207 invoked by uid 89); 19 Jun 2019 16:40:37 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.2 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, KAM_STOCKGEN, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.1 spammy=locate, opted, order! X-HELO: mail.rt-rk.com From: Mihailo Stojanovic To: libc-alpha@sourceware.org Cc: Dragan Mladjenovic , petar.jovanovic@rt-rk.com, Chenghua Xu , Joseph Myers , "Maciej W . Rozycki" , Mihailo Stojanovic Subject: [PATCH] MIPS support for GNU hash Date: Wed, 19 Jun 2019 18:40:28 +0200 Message-Id: <1560962428-17477-1-git-send-email-mihailo.stojanovic@rt-rk.com> 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 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 + . */ + +#include +#include +#include + + +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 @@ -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 +