From 77799d9d9b3f9c937bd6de035b724b9b272c9227 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Fri, 30 Oct 2009 00:39:38 -0700 Subject: [PATCH] Implement IFUNC for PPC. --- ChangeLog | 22 ++++++++++++- elf/elf.h | 23 ++++++++------ sysdeps/powerpc/powerpc32/dl-irel.h | 45 ++++++++++++++++++++++++++ sysdeps/powerpc/powerpc32/dl-machine.c | 6 +++- sysdeps/powerpc/powerpc32/dl-machine.h | 8 +++-- sysdeps/powerpc/powerpc64/dl-irel.h | 58 ++++++++++++++++++++++++++++++++++ sysdeps/powerpc/powerpc64/dl-machine.h | 39 ++++++++++++++++++++++- 7 files changed, 187 insertions(+), 14 deletions(-) create mode 100644 sysdeps/powerpc/powerpc32/dl-irel.h create mode 100644 sysdeps/powerpc/powerpc64/dl-irel.h diff --git a/ChangeLog b/ChangeLog index 5fd0861f23..3359bc20bf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,24 @@ -2009-10-30 Ulrich Drepper +2009-07-30 Alan Modra + + * elf/elf.h (R_PPC_NUM, R_PPC64_NUM): Delete unused and incorrect. + (R_PPC_REL16*): Correct comments. + (R_PPC_IRELATIVE, R_PPC64_IRELATIVE, R_PPC64_JMP_IREL): Define. + (R_PPC64_REL16, R_PPC64_REL16_LO, R_PPC64_REL16_HI, + R_PPC64_REL16_HA): Define. + * sysdeps/powerpc/powerpc32/dl-irel.h: New file. + * sysdeps/powerpc/powerpc64/dl-irel.h: New file. + * sysdeps/powerpc/powerpc32/dl-machine.c (__elf_machine_fixup_plt): + Delete unused "reloc" param. + (__process_machine_rela): Handle R_PPC_IRELATIVE. + * sysdeps/powerpc/powerpc32/dl-machine.h (__elf_machine_fixup_plt): + Delete "reloc" param. + (elf_machine_rela): Handle STT_GNU_IFUNC functions and + R_PPC_IRELATIVE. + * sysdeps/powerpc/powerpc64/dl-machine.h (resolve_ifunc): New function. + (elf_machine_rela): Handle STT_GNU_IFUNC functions and new ifunc + relocations. + +d2009-10-30 Ulrich Drepper [BZ #10315] * sysdeps/gnu/netinet/udp.h: Define UDP_CORK, UDP_ENCAP, diff --git a/elf/elf.h b/elf/elf.h index ce6de07e91..c772ff41ad 100644 --- a/elf/elf.h +++ b/elf/elf.h @@ -2041,9 +2041,6 @@ typedef Elf32_Addr Elf32_Conflict; #define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ #define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ -/* Keep this the last entry. */ -#define R_PPC_NUM 95 - /* The remaining relocs are from the Embedded ELF ABI, and are not in the SVR4 ELF ABI. */ #define R_PPC_EMB_NADDR32 101 @@ -2071,11 +2068,14 @@ typedef Elf32_Addr Elf32_Conflict; #define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ #define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ +/* GNU extension to support local ifunc. */ +#define R_PPC_IRELATIVE 248 + /* GNU relocs used in PIC code sequences. */ -#define R_PPC_REL16 249 /* word32 (sym-.) */ -#define R_PPC_REL16_LO 250 /* half16 (sym-.)@l */ -#define R_PPC_REL16_HI 251 /* half16 (sym-.)@h */ -#define R_PPC_REL16_HA 252 /* half16 (sym-.)@ha */ +#define R_PPC_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ /* This is a phony reloc to handle any old fashioned TOC16 references that may still be in object files. */ @@ -2197,8 +2197,13 @@ typedef Elf32_Addr Elf32_Conflict; #define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ #define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ -/* Keep this the last entry. */ -#define R_PPC64_NUM 107 +/* GNU extension to support local ifunc. */ +#define R_PPC64_JMP_IREL 247 +#define R_PPC64_IRELATIVE 248 +#define R_PPC64_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ /* PowerPC64 specific values for the Dyn d_tag field. */ #define DT_PPC64_GLINK (DT_LOPROC + 0) diff --git a/sysdeps/powerpc/powerpc32/dl-irel.h b/sysdeps/powerpc/powerpc32/dl-irel.h new file mode 100644 index 0000000000..3f204cd7ae --- /dev/null +++ b/sysdeps/powerpc/powerpc32/dl-irel.h @@ -0,0 +1,45 @@ +/* Machine-dependent ELF indirect relocation inline functions. + PowerPC version. + Copyright (C) 2009 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _DL_IREL_H +#define _DL_IREL_H + +#include +#include + +#define ELF_MACHINE_IRELA 1 + +static inline void +__attribute ((always_inline)) +elf_irela (const Elf32_Rela *reloc) +{ + unsigned int r_type = ELF32_R_TYPE (reloc->r_info); + + if (__builtin_expect (r_type == R_PPC_IRELATIVE, 1)) + { + Elf32_Addr *const reloc_addr = (void *) reloc->r_offset; + Elf32_Addr value = ((Elf32_Addr (*) (void)) reloc->r_addend) (); + *reloc_addr = value; + } + else + __libc_fatal ("unexpected reloc type in static binary"); +} + +#endif /* dl-irel.h */ diff --git a/sysdeps/powerpc/powerpc32/dl-machine.c b/sysdeps/powerpc/powerpc32/dl-machine.c index 71540bd185..ee4c3e0c1c 100644 --- a/sysdeps/powerpc/powerpc32/dl-machine.c +++ b/sysdeps/powerpc/powerpc32/dl-machine.c @@ -337,7 +337,7 @@ __elf_machine_runtime_setup (struct link_map *map, int lazy, int profile) } Elf32_Addr -__elf_machine_fixup_plt (struct link_map *map, const Elf32_Rela *reloc, +__elf_machine_fixup_plt (struct link_map *map, Elf32_Addr *reloc_addr, Elf32_Addr finaladdr) { Elf32_Sword delta = finaladdr - (Elf32_Word) reloc_addr; @@ -430,6 +430,10 @@ __process_machine_rela (struct link_map *map, *reloc_addr = finaladdr; return; + case R_PPC_IRELATIVE: + *reloc_addr = ((Elf32_Addr (*) (void)) finaladdr) (); + return; + case R_PPC_UADDR32: ((char *) reloc_addr)[0] = finaladdr >> 24; ((char *) reloc_addr)[1] = finaladdr >> 16; diff --git a/sysdeps/powerpc/powerpc32/dl-machine.h b/sysdeps/powerpc/powerpc32/dl-machine.h index a50ffdd1c2..6f8d0f506e 100644 --- a/sysdeps/powerpc/powerpc32/dl-machine.h +++ b/sysdeps/powerpc/powerpc32/dl-machine.h @@ -226,7 +226,6 @@ elf_machine_runtime_setup (struct link_map *map, /* Change the PLT entry whose reloc is 'reloc' to call the actual routine. */ extern Elf32_Addr __elf_machine_fixup_plt (struct link_map *map, - const Elf32_Rela *reloc, Elf32_Addr *reloc_addr, Elf32_Addr finaladdr); @@ -237,7 +236,7 @@ elf_machine_fixup_plt (struct link_map *map, lookup_t t, { if (map->l_info[DT_PPC(GOT)] == 0) /* Handle old style PLT. */ - return __elf_machine_fixup_plt (map, reloc, reloc_addr, finaladdr); + return __elf_machine_fixup_plt (map, reloc_addr, finaladdr); *reloc_addr = finaladdr; return finaladdr; @@ -317,6 +316,11 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, value = reloc->r_addend; #endif + if (sym != NULL + && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0) + && __builtin_expect (sym->st_shndx != SHN_UNDEF, 1)) + value = ((Elf32_Addr (*) (void)) value) (); + /* A small amount of code is duplicated here for speed. In libc, more than 90% of the relocs are R_PPC_RELATIVE; in the X11 shared libraries, 60% are R_PPC_RELATIVE, 24% are R_PPC_GLOB_DAT or diff --git a/sysdeps/powerpc/powerpc64/dl-irel.h b/sysdeps/powerpc/powerpc64/dl-irel.h new file mode 100644 index 0000000000..6cded5091d --- /dev/null +++ b/sysdeps/powerpc/powerpc64/dl-irel.h @@ -0,0 +1,58 @@ +/* Machine-dependent ELF indirect relocation inline functions. + PowerPC64 version. + Copyright (C) 2009 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _DL_IREL_H +#define _DL_IREL_H + +#include +#include + +#define ELF_MACHINE_IRELA 1 + +typedef struct +{ + Elf64_Addr fd_func; + Elf64_Addr fd_toc; + Elf64_Addr fd_aux; +} Elf64_FuncDesc; + +static inline void +__attribute ((always_inline)) +elf_irela (const Elf64_Rela *reloc) +{ + unsigned int r_type = ELF64_R_TYPE (reloc->r_info); + + if (__builtin_expect (r_type == R_PPC64_IRELATIVE, 1)) + { + Elf64_Addr *const reloc_addr = (void *) reloc->r_offset; + Elf64_Addr value = ((Elf64_Addr (*) (void)) reloc->r_addend) (); + *reloc_addr = value; + } + else if (__builtin_expect (r_type == R_PPC64_JMP_IREL, 1)) + { + Elf64_Addr *const reloc_addr = (void *) reloc->r_offset; + Elf64_Addr value = ((Elf64_Addr (*) (void)) reloc->r_addend) (); + *(Elf64_FuncDesc *) reloc_addr = *(Elf64_FuncDesc *) value; + } + else + __libc_fatal ("unexpected reloc type in static binary"); +} + +#endif /* dl-irel.h */ diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h index b674dbef43..8a720ae9c2 100644 --- a/sysdeps/powerpc/powerpc64/dl-machine.h +++ b/sysdeps/powerpc/powerpc64/dl-machine.h @@ -526,6 +526,29 @@ elf_machine_tprel (struct link_map *map, } #endif +/* Call function at address VALUE (an OPD entry) to resolve ifunc relocs. */ +auto inline Elf64_Addr __attribute__ ((always_inline)) +resolve_ifunc (Elf64_Addr value, + const struct link_map *map, const struct link_map *sym_map) +{ + /* The function we are calling may not yet have its opd entry relocated. */ + Elf64_FuncDesc opd; + if (map != sym_map +#if !defined RTLD_BOOTSTRAP && defined SHARED + /* Bootstrap map doesn't have l_relocated set for it. */ + && sym_map != &GL(dl_rtld_map) +#endif + && !sym_map->l_relocated) + { + Elf64_FuncDesc *func = (Elf64_FuncDesc *) value; + opd.fd_func = func->fd_func + sym_map->l_addr; + opd.fd_toc = func->fd_toc + sym_map->l_addr; + opd.fd_aux = func->fd_aux; + value = (Elf64_Addr) &opd; + } + return ((Elf64_Addr (*) (void)) value) (); +} + /* Perform the relocation specified by RELOC and SYM (which is fully resolved). MAP is the object containing the reloc. */ auto inline void __attribute__ ((always_inline)) @@ -550,11 +573,17 @@ elf_machine_rela (struct link_map *map, if (__builtin_expect (r_type == R_PPC64_NONE, 0)) return; - /* We need SYM_MAP even in the absence of TLS, for elf_machine_fixup_plt. */ + /* We need SYM_MAP even in the absence of TLS, for elf_machine_fixup_plt + and STT_GNU_IFUNC. */ struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type); Elf64_Addr value = ((sym_map == NULL ? 0 : sym_map->l_addr + sym->st_value) + reloc->r_addend); + if (sym != NULL + && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0) + && __builtin_expect (sym->st_shndx != SHN_UNDEF, 1)) + value = resolve_ifunc (value, map, sym_map); + /* For relocs that don't edit code, return. For relocs that might edit instructions, break from the switch. */ switch (r_type) @@ -564,6 +593,14 @@ elf_machine_rela (struct link_map *map, *reloc_addr = value; return; + case R_PPC64_IRELATIVE: + value = resolve_ifunc (value, map, sym_map); + *reloc_addr = value; + return; + + case R_PPC64_JMP_IREL: + value = resolve_ifunc (value, map, sym_map); + /* Fall thru */ case R_PPC64_JMP_SLOT: #ifdef RESOLVE_CONFLICT_FIND_MAP elf_machine_plt_conflict (reloc_addr, value); -- 2.11.4.GIT