aboutsummaryrefslogtreecommitdiff
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 d229f33..668fe03 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 7a9855c..08b4ed8 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 f222bb0..eccfbb3 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 bffc4cb..e31311c 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)