From 77799d9d9b3f9c937bd6de035b724b9b272c9227 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Fri, 30 Oct 2009 00:39:38 -0700 Subject: Implement IFUNC for PPC. --- 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 ++++++++++++++++++++++- 5 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 sysdeps/powerpc/powerpc32/dl-irel.h create mode 100644 sysdeps/powerpc/powerpc64/dl-irel.h (limited to 'sysdeps') 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); -- cgit v1.2.3