about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog17
-rw-r--r--elf/elf.h11
-rw-r--r--sysdeps/powerpc/powerpc64/dl-machine.h44
-rw-r--r--sysdeps/powerpc/powerpc64/dl-trampoline.S8
4 files changed, 78 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index d229f33151..668fe03d2b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,23 @@
 2013-12-04  Ulrich Weigand  <Ulrich.Weigand@de.ibm.com>
 	    Alan Modra  <amodra@gmail.com>
 
+	* elf/elf.h (DT_PPC64_OPT, PPC64_OPT_TLS, PPC64_OPT_MULTI_TOC):
+	Define.
+	(STO_PPC64_LOCAL_BIT, STO_PPC64_LOCAL_MASK,
+	PPC64_LOCAL_ENTRY_OFFSET): Define.
+	* sysdeps/powerpc/powerpc64/dl-machine.h (ppc64_local_entry_offset):
+	New function.
+	(elf_machine_fixup_plt): Call it.
+	(elf_machine_plt_conflict): Likewise.  Add map, sym_map, and
+	reloc arguments.
+	(elf_machine_rela): Update call to elf_machine_plt_conflict.
+        * sysdeps/powerpc/powerpc64/dl-trampoline.S (_dl_runtime_resolve,
+        _dl_profile_resolve) [_CALL_ELF == 2]: Restore caller's TOC into
+	r2 before calling target.
+
+2013-12-04  Ulrich Weigand  <Ulrich.Weigand@de.ibm.com>
+	    Alan Modra  <amodra@gmail.com>
+
 	* sysdeps/powerpc/powerpc64/sysdep.h [_CALL_ELF == 2]
 	(PPC64_LOAD_FUNCPTR, DOT_LABEL, BODY_LABEL, ENTRY_2, END_2): New
 	versions of macros to support ELFv2 ABI.
diff --git a/elf/elf.h b/elf/elf.h
index 7a9855c850..08b4ed8893 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -2282,8 +2282,19 @@ typedef Elf32_Addr Elf32_Conflict;
 #define DT_PPC64_GLINK  (DT_LOPROC + 0)
 #define DT_PPC64_OPD	(DT_LOPROC + 1)
 #define DT_PPC64_OPDSZ	(DT_LOPROC + 2)
+#define DT_PPC64_OPT	(DT_LOPROC + 3)
 #define DT_PPC64_NUM    3
 
+/* PowerPC64 specific values for the DT_PPC64_OPT Dyn entry.  */
+#define PPC64_OPT_TLS		1
+#define PPC64_OPT_MULTI_TOC	2
+
+/* PowerPC64 specific values for the Elf64_Sym st_other field.  */
+#define STO_PPC64_LOCAL_BIT	5
+#define STO_PPC64_LOCAL_MASK	(7 << STO_PPC64_LOCAL_BIT)
+#define PPC64_LOCAL_ENTRY_OFFSET(other)				\
+ (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2)
+
 
 /* ARM specific declarations */
 
diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h
index f222bb07d7..eccfbb3563 100644
--- a/sysdeps/powerpc/powerpc64/dl-machine.h
+++ b/sysdeps/powerpc/powerpc64/dl-machine.h
@@ -424,6 +424,42 @@ elf_machine_runtime_setup (struct link_map *map, int lazy, int profile)
   return lazy;
 }
 
+#if _CALL_ELF == 2
+/* If the PLT entry whose reloc is 'reloc' resolves to a function in
+   the same object, return the target function's local entry point
+   offset if usable.  */
+static inline Elf64_Addr __attribute__ ((always_inline))
+ppc64_local_entry_offset (struct link_map *map, lookup_t sym_map,
+			  const Elf64_Rela *reloc)
+{
+  const Elf64_Sym *symtab;
+  const Elf64_Sym *sym;
+
+  /* If the target function is in a different object, we cannot
+     use the local entry point.  */
+  if (sym_map != map)
+    return 0;
+
+  /* If the linker inserted multiple TOCs, we cannot use the
+     local entry point.  */
+  if (map->l_info[DT_PPC64(OPT)]
+      && (map->l_info[DT_PPC64(OPT)]->d_un.d_val & PPC64_OPT_MULTI_TOC))
+    return 0;
+
+  /* Otherwise, we can use the local entry point.  Retrieve its offset
+     from the symbol's ELF st_other field.  */
+  symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
+  sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
+
+  /* If the target function is an ifunc then the local entry offset is
+     for the resolver, not the final destination.  */
+  if (__builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0))
+    return 0;
+
+  return PPC64_LOCAL_ENTRY_OFFSET (sym->st_other);
+}
+#endif
+
 /* Change the PLT entry whose reloc is 'reloc' to call the actual
    routine.  */
 static inline Elf64_Addr __attribute__ ((always_inline))
@@ -470,6 +506,7 @@ elf_machine_fixup_plt (struct link_map *map, lookup_t sym_map,
   PPC_DCBST (&plt->fd_func);
   PPC_ISYNC;
 #else
+  finaladdr += ppc64_local_entry_offset (map, sym_map, reloc);
   *reloc_addr = finaladdr;
 #endif
 
@@ -477,7 +514,9 @@ elf_machine_fixup_plt (struct link_map *map, lookup_t sym_map,
 }
 
 static inline void __attribute__ ((always_inline))
-elf_machine_plt_conflict (Elf64_Addr *reloc_addr, Elf64_Addr finaladdr)
+elf_machine_plt_conflict (struct link_map *map, lookup_t sym_map,
+			  const Elf64_Rela *reloc,
+			  Elf64_Addr *reloc_addr, Elf64_Addr finaladdr)
 {
 #if _CALL_ELF != 2
   Elf64_FuncDesc *plt = (Elf64_FuncDesc *) reloc_addr;
@@ -491,6 +530,7 @@ elf_machine_plt_conflict (Elf64_Addr *reloc_addr, Elf64_Addr finaladdr)
   PPC_DCBST (&plt->fd_toc);
   PPC_SYNC;
 #else
+  finaladdr += ppc64_local_entry_offset (map, sym_map, reloc);
   *reloc_addr = finaladdr;
 #endif
 }
@@ -646,7 +686,7 @@ elf_machine_rela (struct link_map *map,
       /* Fall thru */
     case R_PPC64_JMP_SLOT:
 #ifdef RESOLVE_CONFLICT_FIND_MAP
-      elf_machine_plt_conflict (reloc_addr, value);
+      elf_machine_plt_conflict (map, sym_map, reloc, reloc_addr, value);
 #else
       elf_machine_fixup_plt (map, sym_map, reloc, reloc_addr, value);
 #endif
diff --git a/sysdeps/powerpc/powerpc64/dl-trampoline.S b/sysdeps/powerpc/powerpc64/dl-trampoline.S
index bffc4cbab6..e31311cf18 100644
--- a/sysdeps/powerpc/powerpc64/dl-trampoline.S
+++ b/sysdeps/powerpc/powerpc64/dl-trampoline.S
@@ -74,6 +74,10 @@ EALIGN(_dl_runtime_resolve, 4, 0)
 /* Prepare for calling the function returned by fixup.  */
 	PPC64_LOAD_FUNCPTR r3
 	ld	r3,INT_PARMS+0(r1)
+#if _CALL_ELF == 2
+/* Restore the caller's TOC in case we jump to a local entry point.  */
+	ld	r2,FRAME_SIZE+40(r1)
+#endif
 /* Unwind the stack frame, and jump.  */
 	addi	r1,r1,FRAME_SIZE
 	bctr
@@ -321,6 +325,10 @@ L(restoreFXR):
 /* Prepare for calling the function returned by fixup.  */
 	PPC64_LOAD_FUNCPTR r3
 	ld	r3,INT_PARMS+0(r1)
+#if _CALL_ELF == 2
+/* Restore the caller's TOC in case we jump to a local entry point.  */
+	ld	r2,FRAME_SIZE+40(r1)
+#endif
 /* Load the floating point registers.  */
 	lfd	fp1,FPR_PARMS+0(r1)
 	lfd	fp2,FPR_PARMS+8(r1)