From patchwork Thu Sep 3 22:03:40 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Faraz Shahbazker X-Patchwork-Id: 8570 Received: (qmail 98807 invoked by alias); 3 Sep 2015 22:03:53 -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 98782 invoked by uid 89); 3 Sep 2015 22:03:50 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.0 required=5.0 tests=AWL, BAYES_50, RCVD_IN_DNSWL_LOW, SPF_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mailapp01.imgtec.com Message-ID: <55E8C3BC.8040606@imgtec.com> Date: Thu, 3 Sep 2015 15:03:40 -0700 From: Faraz Shahbazker User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.2.0 MIME-Version: 1.0 To: "libc-alpha@sourceware.org" CC: Subject: [RFC 1/2] Add IFUNC support for MIPS (v5) References: <87k2tdn5xt.fsf@googlemail.com> <55BFC10F.2050503@imgtec.com> <87k2tapwq0.fsf@googlemail.com> <55CE5217.5020902@imgtec.com> <87io8f2gc9.fsf@googlemail.com> <55D23368.1070705@imgtec.com> <87io8dhegb.fsf@googlemail.com> <55D269D2.2030208@imgtec.com> <87si7h6oyv.fsf@googlemail.com> <55D7A3C5.8050600@imgtec.com> <87vbc7u1df.fsf@googlemail.com> In-Reply-To: <87vbc7u1df.fsf@googlemail.com> Changes with respect to previous patch: - Retain REL32 handling for STB_LOCAL symbols - Added/modified comments - Add bias to resolved function address if resolver is not yet relocated. glibc suite cross-tested on mips32 little endian, summary: 324 FAIL 1537 PASS 9 XFAIL All ifunc* tests are passing. If this looks good, I invite comments on the prototype to be used for the IFUNC resolver function (rfc 2 of 2) ChangeLog: * elf/elf.h (R_MIPS_IRELATIVE): New relocation type. (R_MIPS_NUM): Bump up to 129. (DT_MIPS_GENERAL_GOTNO): New dynamic tags. (DT_MIPS_NUM): Bump to 0x37. * sysdeps/mips/dl-irel.h: New file. (elf_ifunc_invoke): New function. (elf_irel): Likewise. * sysdeps/mips/dl-machine.h Include new dl-irel.h (ELF_MACHINE_BEFORE_RTLD_RELOC): Use DT_MIPS_GENERAL_GOTNO tag, if present, to find the start of the normally relocated GOT. (elf_machine_reloc): Add skip_ifunc to parameter. Add case for R_MIPS_IRELATIVE. Modify REL32 to check for pre-emption if symbol is IFUNC and then perform IFUNC indirection. Modify JUMP relocation to perform IFUNC resolution if necessary. (elf_machine_rel): Add skip_ifunc to call to elf_machine_reloc(). (elf_machine_rela):Add skip_ifunc to call to elf_machine_reloc(). (RESOLVE_GOTSYM): Add check for STT_GNU_IFUNC. (elf_machine_got_rel): Add check for STT_GNU_IFUNC and IFUNC resolution step. Use DT_MIPS_GENERAL_GOTNO tag, if present, to find the start of the normally relocated GOT. * sysdeps/mips/dl-trampoline.c (__dl_runtime_resolve): Add check for STT_GNU_IFUNC. * sysdeps/unix/sysv/linux/mips/ldsodefs.h (VALID_ELF_ABIVERSION): Upped allowed version number to 4. * sysdeps/unix/sysv/linux/mips/libc-abis (IFUNC): New ABI compatibility level. --- elf/elf.h | 7 +- sysdeps/mips/dl-irel.h | 63 ++++++++++++++++ sysdeps/mips/dl-machine.h | 126 ++++++++++++++++++++++++------- sysdeps/mips/dl-trampoline.c | 3 + sysdeps/unix/sysv/linux/mips/ldsodefs.h | 2 +- sysdeps/unix/sysv/linux/mips/libc-abis | 3 + 6 files changed, 173 insertions(+), 31 deletions(-) create mode 100644 sysdeps/mips/dl-irel.h diff --git a/elf/elf.h b/elf/elf.h index fbadda4..960834a 100644 --- a/elf/elf.h +++ b/elf/elf.h @@ -1653,8 +1653,9 @@ typedef struct #define R_MIPS_GLOB_DAT 51 #define R_MIPS_COPY 126 #define R_MIPS_JUMP_SLOT 127 +#define R_MIPS_IRELATIVE 128 /* Keep this the last entry. */ -#define R_MIPS_NUM 128 +#define R_MIPS_NUM 129 /* Legal values for p_type field of Elf32_Phdr. */ @@ -1731,7 +1732,9 @@ typedef struct in a PIE as it stores a relative offset from the address of the tag rather than an absolute address. */ #define DT_MIPS_RLD_MAP_REL 0x70000035 -#define DT_MIPS_NUM 0x36 + /* Number of explicitly relocated GOT entries */ +#define DT_MIPS_GENERAL_GOTNO 0x70000036 +#define DT_MIPS_NUM 0x37 /* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ diff --git a/sysdeps/mips/dl-irel.h b/sysdeps/mips/dl-irel.h new file mode 100644 index 0000000..47f3257 --- /dev/null +++ b/sysdeps/mips/dl-irel.h @@ -0,0 +1,63 @@ +/* Machine-dependent ELF indirect relocation inline functions. + MIPS version. + Copyright (C) 2015 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 + . */ + +#ifndef _DL_IREL_H +#define _DL_IREL_H + +#include +#include +#include +#include +#include +#include + +#define ELF_MACHINE_IREL 1 + +static inline ElfW(Addr) +__attribute ((always_inline)) +elf_ifunc_invoke (ElfW(Addr) addr) +{ + /* Print some debugging info if wanted. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS)) + { + ElfW(Addr) t_addr = + ((ElfW(Addr) (*) (unsigned long int)) (addr)) (GLRO(dl_hwcap)); + GLRO(dl_debug_printf) ("In elf_ifunc_invoke(0x%lx), return(0x%lx)\n", + (unsigned long int)addr, + (unsigned long int)t_addr); + } + + return ((ElfW(Addr) (*) (unsigned long int)) (addr)) (GLRO(dl_hwcap)); +} + +/* Allow either R_MIPS_RELATIVE or the nop R_MIPS_NONE. */ +static inline void +__attribute ((always_inline)) +elf_irel (const ElfW(Rel) *reloc) +{ + ElfW(Addr) *const reloc_addr = (void *) reloc->r_offset; + const unsigned long int r_type = ELFW(R_TYPE) (reloc->r_info); + + if (__glibc_likely (r_type == R_MIPS_IRELATIVE)) + *reloc_addr = elf_ifunc_invoke (*reloc_addr); + else if (r_type) + __libc_fatal ("unexpected reloc type in static binary"); +} + +#endif /* dl-irel.h */ diff --git a/sysdeps/mips/dl-machine.h b/sysdeps/mips/dl-machine.h index 52cd742..3749770 100644 --- a/sysdeps/mips/dl-machine.h +++ b/sysdeps/mips/dl-machine.h @@ -33,6 +33,7 @@ #include #include #include +#include /* The offset of gp from GOT might be system-dependent. It's set by ld. The same value is also */ @@ -200,10 +201,13 @@ do { \ if (__builtin_expect (map->l_addr == 0, 1)) \ break; \ \ - /* got[0] is reserved. got[1] is also reserved for the dynamic object \ - generated by gnu ld. Skip these reserved entries from \ - relocation. */ \ - i = (got[1] & ELF_MIPS_GNU_GOT1_MASK)? 2 : 1; \ + if (map->l_info[DT_MIPS (GENERAL_GOTNO)] != NULL) \ + i = map->l_info[DT_MIPS (GENERAL_GOTNO)]->d_un.d_val; \ + else \ + /* got[0] is reserved. got[1] is also reserved for the dynamic \ + object generated by gnu ld. Skip these reserved entries from \ + relocation. */ \ + i = (got[1] & ELF_MIPS_GNU_GOT1_MASK)? 2 : 1; \ n = map->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val; \ \ /* Add the run-time displacement to all local got entries. */ \ @@ -493,7 +497,8 @@ auto inline void __attribute__ ((always_inline)) elf_machine_reloc (struct link_map *map, ElfW(Addr) r_info, const ElfW(Sym) *sym, const struct r_found_version *version, - void *reloc_addr, ElfW(Addr) r_addend, int inplace_p) + void *reloc_addr, ElfW(Addr) r_addend, int inplace_p, + int skip_ifunc) { const unsigned long int r_type = ELFW(R_TYPE) (r_info); ElfW(Addr) *addr_field = (ElfW(Addr) *) reloc_addr; @@ -579,25 +584,60 @@ elf_machine_reloc (struct link_map *map, ElfW(Addr) r_info, if ((ElfW(Word))symidx < gotsym) { - /* This wouldn't work for a symbol imported from other - libraries for which there's no GOT entry, but MIPS - requires every symbol referenced in a dynamic - relocation to have a GOT entry in the primary GOT, - so we only get here for locally-defined symbols. - For section symbols, we should *NOT* be adding - sym->st_value (per the definition of the meaning of - S in reloc expressions in the ELF64 MIPS ABI), - since it should have already been added to - reloc_value by the linker, but older versions of - GNU ld didn't add it, and newer versions don't emit - useless relocations to section symbols any more, so - it is safe to keep on adding sym->st_value, even - though it's not ABI compliant. Some day we should - bite the bullet and stop doing this. */ + if (ELFW(ST_BIND) (sym->st_info) == STB_LOCAL) + { + /* For section symbols, we should *NOT* be adding + sym->st_value (per the definition of the meaning of S + in reloc expressions in the ELF64 MIPS ABI), since it + should have already been added to reloc_value by the + linker, but older versions of GNU ld didn't add it, and + newer versions don't emit useless relocations to + section symbols any more, so it is safe to keep on + adding sym->st_value, even though it's not ABI + compliant. */ #ifndef RTLD_BOOTSTRAP - if (map != &GL(dl_rtld_map)) + if (map != &GL(dl_rtld_map)) +#endif + reloc_value += sym->st_value + map->l_addr; + } +#ifndef RTLD_BOOTSTRAP + /* The original MIPS ABI required every global symbol used in + a relocation to be in the global GOT. We would then only + expect to get here for local symbols. This restriction is + removed for objects that use DT_MIPS_GENERAL_GOTNO, since + newer relocations and symbol types do not fit easily in the + original ABI scheme. Relocations against symbols below + DT_MIPS_GOTSYM bind in just the same way as relocations + against symbols in the global GOT; the only difference is + that we are not able to use the global GOT as a + directly-indexed lookup cache. Symbols below + DT_MIPS_GOTSYM might be in the general GOT region or might + not have a GOT entry at all. */ + else if (__glibc_unlikely (map->l_info[DT_MIPS (GENERAL_GOTNO)] + == NULL)) + { + const char *strtab; + strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + + _dl_error_printf ("\ +%s: Explicitly relocated symbol `%s' requires dynamic tag MIPS_GENERAL_GOTNO\n", + RTLD_PROGNAME, strtab + sym->st_name); + } + else + { + struct link_map *rmap = RESOLVE_MAP (&sym, version, r_type); + if ((ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)) + reloc_value = elf_ifunc_invoke (sym->st_value + + rmap->l_addr); + else + /* The behavior for section symbols described above + is now so firmly established that it is explicitly + adopted by objects with DT_MIPS_GLOBAL_GOTNO. + We therefore don't have a special case for + section symbols. */ + reloc_value = sym->st_value + rmap->l_addr; + } #endif - reloc_value += sym->st_value + map->l_addr; } else { @@ -663,6 +703,9 @@ elf_machine_reloc (struct link_map *map, ElfW(Addr) r_info, sym_map = RESOLVE_MAP (&sym, version, r_type); value = sym_map == NULL ? 0 : sym_map->l_addr + sym->st_value; + if ((ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC) && !skip_ifunc) + value = elf_ifunc_invoke (value); + *addr_field = value; break; @@ -698,6 +741,17 @@ elf_machine_reloc (struct link_map *map, ElfW(Addr) r_info, break; } + case R_MIPS_IRELATIVE: +#ifndef RTLD_BOOTSTRAP + /* The resolver routine is the symbol referenced by this relocation. + To get the address of the function to use at runtime, the resolver + routine is called and its return value is the address of the target + functon which is final relocation value. */ + if (!skip_ifunc) + *addr_field = elf_ifunc_invoke (map->l_addr + *addr_field); +#endif + break; + #if _MIPS_SIM == _ABI64 case R_MIPS_64: /* For full compliance with the ELF64 ABI, one must precede the @@ -727,7 +781,8 @@ elf_machine_rel (struct link_map *map, const ElfW(Rel) *reloc, const ElfW(Sym) *sym, const struct r_found_version *version, void *const reloc_addr, int skip_ifunc) { - elf_machine_reloc (map, reloc->r_info, sym, version, reloc_addr, 0, 1); + elf_machine_reloc (map, reloc->r_info, sym, version, reloc_addr, 0, 1, + skip_ifunc); } auto inline void @@ -768,7 +823,7 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc, void *const reloc_addr, int skip_ifunc) { elf_machine_reloc (map, reloc->r_info, sym, version, reloc_addr, - reloc->r_addend, 0); + reloc->r_addend, 0, skip_ifunc); } auto inline void @@ -795,8 +850,20 @@ elf_machine_got_rel (struct link_map *map, int lazy) const struct r_found_version *version __attribute__ ((unused)) \ = vernum ? &map->l_versions[vernum[sym_index] & 0x7fff] : NULL; \ struct link_map *sym_map; \ + ElfW(Addr) value = 0; \ sym_map = RESOLVE_MAP (&ref, version, reloc); \ - ref ? sym_map->l_addr + ref->st_value : 0; \ + if (__glibc_likely (ref != NULL)) \ + { \ + value = sym_map->l_addr + ref->st_value; \ + if (__glibc_unlikely (ELFW(ST_TYPE) (ref->st_info) \ + == STT_GNU_IFUNC)) \ + { \ + value = elf_ifunc_invoke (value); \ + if (!sym_map->l_relocated) \ + value += sym_map->l_addr; \ + } \ + } \ + value; \ }) if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL) @@ -810,9 +877,12 @@ elf_machine_got_rel (struct link_map *map, int lazy) /* The dynamic linker's local got entries have already been relocated. */ if (map != &GL(dl_rtld_map)) { - /* got[0] is reserved. got[1] is also reserved for the dynamic object - generated by gnu ld. Skip these reserved entries from relocation. */ - i = (got[1] & ELF_MIPS_GNU_GOT1_MASK)? 2 : 1; + if (map->l_info[DT_MIPS (GENERAL_GOTNO)] != NULL) + i = map->l_info[DT_MIPS (GENERAL_GOTNO)]->d_un.d_val; + else + /* got[0] is reserved. got[1] is also reserved for the dynamic object + generated by gnu ld. Skip these reserved entries from relocation. */ + i = (got[1] & ELF_MIPS_GNU_GOT1_MASK)? 2 : 1; /* Add the run-time displacement to all local got entries if needed. */ diff --git a/sysdeps/mips/dl-trampoline.c b/sysdeps/mips/dl-trampoline.c index 25b1709..0f1829c1 100644 --- a/sysdeps/mips/dl-trampoline.c +++ b/sysdeps/mips/dl-trampoline.c @@ -193,6 +193,9 @@ __dl_runtime_resolve (ElfW(Word) sym_index, /* Currently value contains the base load address of the object that defines sym. Now add in the symbol offset. */ value = (sym ? sym_map->l_addr + sym->st_value : 0); + if (sym != NULL + && __glibc_unlikely (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)) + value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value)); } else /* We already found the symbol. The module (and therefore its load diff --git a/sysdeps/unix/sysv/linux/mips/ldsodefs.h b/sysdeps/unix/sysv/linux/mips/ldsodefs.h index 42b92b3..385db69 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 < 5) \ || (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 14ff603..0644720 100644 --- a/sysdeps/unix/sysv/linux/mips/libc-abis +++ b/sysdeps/unix/sysv/linux/mips/libc-abis @@ -14,3 +14,6 @@ UNIQUE # # MIPS O32 FP64 MIPS_O32_FP64 mips*-*-linux* +# +# MIPS IFUNC +IFUNC mips*-*-linux*