From patchwork Fri Jul 31 12:52:00 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Faraz Shahbazker X-Patchwork-Id: 502538 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 723A51402A2 for ; Fri, 31 Jul 2015 22:52:32 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.b=W7y6arH0; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:subject:date:message-id:content-type :mime-version; q=dns; s=default; b=AqXSfkMhgip2Ju3TVFJcoWCV9xZKY r+i6g/B1R1GUXk5lkSjRax5oaf+ICoYHASrnlxY1Q1LTz1t6Mp6CzMsZ1j7HejhH d7gTcHv7FHbxSYN7sfrLBhNm6gHc7/cQGZipHiZ7lQzjgMqLVijphb1JBc1iHGv4 zrsXiLPuxM/DoQ= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:subject:date:message-id:content-type :mime-version; s=default; bh=Ps9N1AbmlYH4IW4NaEse4NEzqX0=; b=W7y 6arH0Y14Psp29NcvdNBTHx3IJUagkl0A3vaLzTRrS1U19C9kqAS1BHG6SWKx8ObP Cgxz5vOvU0GY7CjMuG58kXIn8VZqWnfH8aqKoWkRVigrdAPwGEe2elkpLDNS2TsV yP1TwF3buDSVLuthAb3r3Ia2hmMNR6j0QtBYOh1k= Received: (qmail 66404 invoked by alias); 31 Jul 2015 12:52:19 -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 66170 invoked by uid 89); 31 Jul 2015 12:52:17 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.2 required=5.0 tests=AWL, BAYES_50, RCVD_IN_DNSWL_LOW, RP_MATCHES_RCVD, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mailapp01.imgtec.com From: Faraz Shahbazker To: "libc-alpha@sourceware.org" Subject: [RFC] Add IFUNC support for MIPS Date: Fri, 31 Jul 2015 12:52:00 +0000 Message-ID: MIME-Version: 1.0 Hi, This is a follow up on IFUNC support for MIPS. Previous patch discussion on this list was: https://sourceware.org/ml/libc-ports/2013-08/msg00007.html This patch is intended to work with the corresponding binutils patch, on-going review at: https://sourceware.org/ml/binutils/2015-07/msg00213.html Detailed description is in the attached text file. Please comment. Thanks, Faraz Shahbazker 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. * 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 in case symbol is IFUNC and perform IFUNC indirection. (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. Use DT_MIPS_GENERAL_GOTNO tag, if present, to find the start of the normally relocated GOT. Skip normal GOT relocation for global GOT entries corresponding to IFUNCs. * dl-trampoline.c: (__dl_runtime_resolve): Add check for STT_GNU_IFUNC. --- elf/elf.h | 6 ++- sysdeps/mips/dl-irel.h | 63 +++++++++++++++++++++++++++++++ sysdeps/mips/dl-machine.h | 86 ++++++++++++++++++++++++++++++++++++------ sysdeps/mips/dl-trampoline.c | 4 ++ 4 files changed, 145 insertions(+), 14 deletions(-) create mode 100644 sysdeps/mips/dl-irel.h diff --git a/elf/elf.h b/elf/elf.h index fbadda4..cfcf72c 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,8 @@ 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 +#define DT_MIPS_GENERAL_GOTNO 0x70000036 /* Number of relocated GOT entries */ +#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..7e1fdc4 --- /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 (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS, 0)) + { + 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 (__builtin_expect (r_type == R_MIPS_IRELATIVE, 1)) + *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 8738564..e7e0dc4 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 (__glibc_unlikely (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; @@ -599,6 +604,41 @@ elf_machine_reloc (struct link_map *map, ElfW(Addr) r_info, #endif reloc_value += sym->st_value + map->l_addr; } +#ifndef RTLD_BOOTSTRAP + /* Resolve IFUNC symbols with pre-emption. */ + else if (sym + && __glibc_unlikely (ELFW(ST_TYPE) (sym->st_info) + == STT_GNU_IFUNC) + && !skip_ifunc) + { + struct link_map *rmap = RESOLVE_MAP (&sym, version, r_type); + + /* Symbol pre-empted, use value from GOT. + 2nd part of condition is redundant, explicit for clarity. */ + if (__glibc_unlikely (rmap->l_relocated) + && __glibc_unlikely (rmap != map)) + { + const ElfW(Addr) *got + = (const ElfW(Addr) *) D_PTR (rmap, l_info[DT_PLTGOT]); + const ElfW(Word) local_gotno + = (const ElfW(Word)) + rmap->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val; + symidx = (sym + - (ElfW(Sym) *) D_PTR(rmap, l_info[DT_SYMTAB])) + / sizeof (sym); + reloc_value = got[symidx + local_gotno - gotsym]; + } + /* Symbol pre-empted by not yet relocated, use best guess. */ + else if (__glibc_unlikely (rmap != map)) + reloc_value = sym->st_value + rmap->l_addr; + /* Resolve IFUNC in this link unit. */ + else if (__glibc_likely (ELFW(ST_TYPE) (sym->st_info) + == STT_GNU_IFUNC)) + reloc_value = elf_ifunc_invoke (sym->st_value + map->l_addr); + else + reloc_value += sym->st_value + map->l_addr; + } +#endif else { #ifndef RTLD_BOOTSTRAP @@ -698,6 +738,14 @@ elf_machine_reloc (struct link_map *map, ElfW(Addr) r_info, break; } + case R_MIPS_IRELATIVE: + /* 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. */ + *addr_field = elf_ifunc_invoke (map->l_addr + *addr_field); + break; + #if _MIPS_SIM == _ABI64 case R_MIPS_64: /* For full compliance with the ELF64 ABI, one must precede the @@ -727,7 +775,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 +817,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 +844,18 @@ 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 \ + && sym_map != map \ + && sym_map->l_relocated)) \ + value = elf_ifunc_invoke (value); \ + } \ + value; \ }) if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL) @@ -810,9 +869,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 (__glibc_unlikely(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. */ @@ -866,7 +928,7 @@ elf_machine_got_rel (struct link_map *map, int lazy) if (sym->st_other == 0) *got += map->l_addr; } - else + else if (ELFW(ST_TYPE) (sym->st_info) != STT_GNU_IFUNC) *got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_32); ++got; diff --git a/sysdeps/mips/dl-trampoline.c b/sysdeps/mips/dl-trampoline.c index 25b1709..d5f8891 100644 --- a/sysdeps/mips/dl-trampoline.c +++ b/sysdeps/mips/dl-trampoline.c @@ -193,6 +193,10 @@ __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 + && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) + == STT_GNU_IFUNC, 0)) + value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value)); } else /* We already found the symbol. The module (and therefore its load