about summary refs log tree commit diff
path: root/sysdeps/powerpc/powerpc64/dl-machine.h
diff options
context:
space:
mode:
authorUlrich Weigand <Ulrich.Weigand@de.ibm.com>2013-12-04 06:51:11 -0600
committerAdhemerval Zanella <azanella@linux.vnet.ibm.com>2013-12-04 07:41:38 -0600
commit696caf1d002ff059ddd20fd5eaccd76229c14850 (patch)
tree53eff52b9b42b364f380aa3370c350ee6af8de8a /sysdeps/powerpc/powerpc64/dl-machine.h
parentd31beafa8e4ca69faa4cf362784796ef17299341 (diff)
downloadglibc-696caf1d002ff059ddd20fd5eaccd76229c14850.tar.gz
glibc-696caf1d002ff059ddd20fd5eaccd76229c14850.tar.xz
glibc-696caf1d002ff059ddd20fd5eaccd76229c14850.zip
PowerPC64 ELFv2 ABI 2/6: Remove function descriptors
This patch adds support for the ELFv2 ABI feature to remove function
descriptors.  See this GCC patch for in-depth discussion:
http://gcc.gnu.org/ml/gcc-patches/2013-11/msg01141.html

This mostly involves two types of changes: updating assembler source
files to the new logic, and updating the dynamic loader.

After the refactoring in the previous patch, most of the assembler source
changes can be handled simply by providing ELFv2 versions of the
macros in sysdep.h.   One somewhat non-obvious change is in __GI__setjmp:
this used to "fall through" to the immediately following __setjmp ENTRY
point.  This is no longer safe in the ELFv2 since ENTRY defines both
a global and a local entry point, and you cannot simply fall through
to a global entry point as it requires r12 to be set up.

Also, makecontext needs to be updated to set up registers according to
the new ABI for calling into the context's start routine.

The dynamic linker changes mostly consist of removing special code
to handle function descriptors.  We also need to support the new PLT
and glink format used by the the ELFv2 linker, see:
https://sourceware.org/ml/binutils/2013-10/msg00376.html

In addition, the dynamic linker now verifies that the dynamic libraries
it loads match its own ABI.

The hack in VDSO_IFUNC_RET to "synthesize" a function descriptor
for vDSO routines is also no longer necessary for ELFv2.
Diffstat (limited to 'sysdeps/powerpc/powerpc64/dl-machine.h')
-rw-r--r--sysdeps/powerpc/powerpc64/dl-machine.h100
1 files changed, 76 insertions, 24 deletions
diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h
index a623be5f0b..f222bb07d7 100644
--- a/sysdeps/powerpc/powerpc64/dl-machine.h
+++ b/sysdeps/powerpc/powerpc64/dl-machine.h
@@ -31,6 +31,7 @@
    in l_info array.  */
 #define DT_PPC64(x) (DT_PPC64_##x - DT_LOPROC + DT_NUM)
 
+#if _CALL_ELF != 2
 /* A PowerPC64 function descriptor.  The .plt (procedure linkage
    table) and .opd (official procedure descriptor) sections are
    arrays of these.  */
@@ -40,6 +41,7 @@ typedef struct
   Elf64_Addr fd_toc;
   Elf64_Addr fd_aux;
 } Elf64_FuncDesc;
+#endif
 
 #define ELF_MULT_MACHINES_SUPPORTED
 
@@ -47,6 +49,18 @@ typedef struct
 static inline int
 elf_machine_matches_host (const Elf64_Ehdr *ehdr)
 {
+  /* Verify that the binary matches our ABI version.  */
+  if ((ehdr->e_flags & EF_PPC64_ABI) != 0)
+    {
+#if _CALL_ELF != 2
+      if ((ehdr->e_flags & EF_PPC64_ABI) != 1)
+        return 0;
+#else
+      if ((ehdr->e_flags & EF_PPC64_ABI) != 2)
+        return 0;
+#endif
+    }
+
   return ehdr->e_machine == EM_PPC64;
 }
 
@@ -124,6 +138,7 @@ elf_machine_dynamic (void)
 "	.align	2\n"							\
 "	" ENTRY_2(_start) "\n"						\
 BODY_PREFIX "_start:\n"							\
+"	" LOCALENTRY(_start) "\n"						\
 /* We start with the following on the stack, from top:			\
    argc (4 bytes);							\
    arguments for program (terminated by NULL);				\
@@ -165,6 +180,7 @@ DL_STARTING_UP_DEF							\
    Changing these is strongly discouraged (not least because argc is	\
    passed by value!).  */						\
 BODY_PREFIX "_dl_start_user:\n"						\
+"	" LOCALENTRY(_dl_start_user) "\n"				\
 /* the address of _start in r30.  */					\
 "	mr	30,3\n"							\
 /* &_dl_argc in 29, &_dl_argv in 27, and _dl_loaded in 28.  */		\
@@ -256,8 +272,22 @@ BODY_PREFIX "_dl_start_user:\n"						\
    relocations behave "normally", ie. always use the real address
    like PLT relocations.  So always set ELF_RTYPE_CLASS_PLT.  */
 
+#if _CALL_ELF != 2
 #define elf_machine_type_class(type) \
   (ELF_RTYPE_CLASS_PLT | (((type) == R_PPC64_COPY) * ELF_RTYPE_CLASS_COPY))
+#else
+/* And now that you have read that large comment, you can disregard it
+   all for ELFv2.  ELFv2 does need the special SHN_UNDEF treatment.  */
+#define IS_PPC64_TLS_RELOC(R)						\
+  (((R) >= R_PPC64_TLS && (R) <= R_PPC64_DTPREL16_HIGHESTA)		\
+   || ((R) >= R_PPC64_TPREL16_HIGH && (R) <= R_PPC64_DTPREL16_HIGHA))
+
+#define elf_machine_type_class(type) \
+  ((((type) == R_PPC64_JMP_SLOT					\
+     || (type) == R_PPC64_ADDR24				\
+     || IS_PPC64_TLS_RELOC (type)) * ELF_RTYPE_CLASS_PLT)	\
+   | (((type) == R_PPC64_COPY) * ELF_RTYPE_CLASS_COPY))
+#endif
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT	R_PPC64_JMP_SLOT
@@ -266,8 +296,19 @@ BODY_PREFIX "_dl_start_user:\n"						\
 #define ELF_MACHINE_NO_REL 1
 
 /* Stuff for the PLT.  */
+#if _CALL_ELF != 2
 #define PLT_INITIAL_ENTRY_WORDS 3
+#define PLT_ENTRY_WORDS 3
+#define GLINK_INITIAL_ENTRY_WORDS 8
+/* The first 32k entries of glink can set an index and branch using two
+   instructions; past that point, glink uses three instructions.  */
+#define GLINK_ENTRY_WORDS(I) (((I) < 0x8000)? 2 : 3)
+#else
+#define PLT_INITIAL_ENTRY_WORDS 2
+#define PLT_ENTRY_WORDS 1
 #define GLINK_INITIAL_ENTRY_WORDS 8
+#define GLINK_ENTRY_WORDS(I) 1
+#endif
 
 #define PPC_DCBST(where) asm volatile ("dcbst 0,%0" : : "r"(where) : "memory")
 #define PPC_DCBT(where) asm volatile ("dcbt 0,%0" : : "r"(where) : "memory")
@@ -312,38 +353,45 @@ elf_machine_runtime_setup (struct link_map *map, int lazy, int profile)
 
       if (lazy)
 	{
-	  /* The function descriptor of the appropriate trampoline
-	     routine is used to set the 1st and 2nd doubleword of the
-	     plt_reserve.  */
-	  Elf64_FuncDesc *resolve_fd;
 	  Elf64_Word glink_offset;
-	  /* the plt_reserve area is the 1st 3 doublewords of the PLT */
-	  Elf64_FuncDesc *plt_reserve = (Elf64_FuncDesc *) plt;
 	  Elf64_Word offset;
+	  Elf64_Addr dlrr;
 
-	  resolve_fd = (Elf64_FuncDesc *) (profile ? _dl_profile_resolve
-					   : _dl_runtime_resolve);
+	  dlrr = (Elf64_Addr) (profile ? _dl_profile_resolve
+				       : _dl_runtime_resolve);
 	  if (profile && GLRO(dl_profile) != NULL
 	      && _dl_name_match_p (GLRO(dl_profile), map))
 	    /* This is the object we are looking for.  Say that we really
 	       want profiling and the timers are started.  */
 	    GL(dl_profile_map) = map;
 
-
+#if _CALL_ELF != 2
 	  /* We need to stuff the address/TOC of _dl_runtime_resolve
 	     into doublewords 0 and 1 of plt_reserve.  Then we need to
 	     stuff the map address into doubleword 2 of plt_reserve.
 	     This allows the GLINK0 code to transfer control to the
 	     correct trampoline which will transfer control to fixup
 	     in dl-machine.c.  */
-	  plt_reserve->fd_func = resolve_fd->fd_func;
-	  plt_reserve->fd_toc  = resolve_fd->fd_toc;
-	  plt_reserve->fd_aux  = (Elf64_Addr) map;
+	  {
+	    /* The plt_reserve area is the 1st 3 doublewords of the PLT.  */
+	    Elf64_FuncDesc *plt_reserve = (Elf64_FuncDesc *) plt;
+	    Elf64_FuncDesc *resolve_fd = (Elf64_FuncDesc *) dlrr;
+	    plt_reserve->fd_func = resolve_fd->fd_func;
+	    plt_reserve->fd_toc  = resolve_fd->fd_toc;
+	    plt_reserve->fd_aux  = (Elf64_Addr) map;
 #ifdef RTLD_BOOTSTRAP
-	  /* When we're bootstrapping, the opd entry will not have
-	     been relocated yet.  */
-	  plt_reserve->fd_func += l_addr;
-	  plt_reserve->fd_toc  += l_addr;
+	    /* When we're bootstrapping, the opd entry will not have
+	       been relocated yet.  */
+	    plt_reserve->fd_func += l_addr;
+	    plt_reserve->fd_toc  += l_addr;
+#endif
+	  }
+#else
+	  /* When we don't have function descriptors, the first doubleword
+	     of the PLT holds the address of _dl_runtime_resolve, and the
+	     second doubleword holds the map address.  */
+	  plt[0] = dlrr;
+	  plt[1] = (Elf64_Addr) map;
 #endif
 
 	  /* Set up the lazy PLT entries.  */
@@ -354,14 +402,8 @@ elf_machine_runtime_setup (struct link_map *map, int lazy, int profile)
 	    {
 
 	      plt[offset] = (Elf64_Xword) &glink[glink_offset];
-	      offset += 3;
-	      /* The first 32k entries of glink can set an index and
-		 branch using two instructions;  Past that point,
-		 glink uses three instructions.  */
-	      if (i < 0x8000)
-		glink_offset += 2;
-	      else
-		glink_offset += 3;
+	      offset += PLT_ENTRY_WORDS;
+	      glink_offset += GLINK_ENTRY_WORDS (i);
 	    }
 
 	  /* Now, we've modified data.  We need to write the changes from
@@ -389,6 +431,7 @@ elf_machine_fixup_plt (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;
   Elf64_FuncDesc *rel = (Elf64_FuncDesc *) finaladdr;
   Elf64_Addr offset = 0;
@@ -426,6 +469,9 @@ elf_machine_fixup_plt (struct link_map *map, lookup_t sym_map,
   plt->fd_func = rel->fd_func + offset;
   PPC_DCBST (&plt->fd_func);
   PPC_ISYNC;
+#else
+  *reloc_addr = finaladdr;
+#endif
 
   return finaladdr;
 }
@@ -433,6 +479,7 @@ 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)
 {
+#if _CALL_ELF != 2
   Elf64_FuncDesc *plt = (Elf64_FuncDesc *) reloc_addr;
   Elf64_FuncDesc *rel = (Elf64_FuncDesc *) finaladdr;
 
@@ -443,6 +490,9 @@ elf_machine_plt_conflict (Elf64_Addr *reloc_addr, Elf64_Addr finaladdr)
   PPC_DCBST (&plt->fd_aux);
   PPC_DCBST (&plt->fd_toc);
   PPC_SYNC;
+#else
+  *reloc_addr = finaladdr;
+#endif
 }
 
 /* Return the final value of a plt relocation.  */
@@ -512,6 +562,7 @@ auto inline Elf64_Addr __attribute__ ((always_inline))
 resolve_ifunc (Elf64_Addr value,
 	       const struct link_map *map, const struct link_map *sym_map)
 {
+#if _CALL_ELF != 2
 #ifndef RESOLVE_CONFLICT_FIND_MAP
   /* The function we are calling may not yet have its opd entry relocated.  */
   Elf64_FuncDesc opd;
@@ -529,6 +580,7 @@ resolve_ifunc (Elf64_Addr value,
       value = (Elf64_Addr) &opd;
     }
 #endif
+#endif
   return ((Elf64_Addr (*) (unsigned long int)) value) (GLRO(dl_hwcap));
 }