about summary refs log tree commit diff
path: root/sysdeps/powerpc
diff options
context:
space:
mode:
authorAlan Modra <amodra@bigpond.net.au>2009-10-30 00:39:38 -0700
committerUlrich Drepper <drepper@redhat.com>2009-10-30 00:39:38 -0700
commit77799d9d9b3f9c937bd6de035b724b9b272c9227 (patch)
tree097d09d8ef5885e1e72e7368de69da986ce9c8db /sysdeps/powerpc
parent9fd76770c304ac30a344150d6a56bd6f873b2be0 (diff)
downloadglibc-77799d9d9b3f9c937bd6de035b724b9b272c9227.tar.gz
glibc-77799d9d9b3f9c937bd6de035b724b9b272c9227.tar.xz
glibc-77799d9d9b3f9c937bd6de035b724b9b272c9227.zip
Implement IFUNC for PPC.
Diffstat (limited to 'sysdeps/powerpc')
-rw-r--r--sysdeps/powerpc/powerpc32/dl-irel.h45
-rw-r--r--sysdeps/powerpc/powerpc32/dl-machine.c6
-rw-r--r--sysdeps/powerpc/powerpc32/dl-machine.h8
-rw-r--r--sysdeps/powerpc/powerpc64/dl-irel.h58
-rw-r--r--sysdeps/powerpc/powerpc64/dl-machine.h39
5 files changed, 152 insertions, 4 deletions
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 <stdio.h>
+#include <unistd.h>
+
+#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 <stdio.h>
+#include <unistd.h>
+
+#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);