diff mbox series

[ARM-FDPIC,04/12,ARM] Implement FDPIC relocations.

Message ID 20180322143850.1766-5-christophe.lyon@st.com
State New
Headers show
Series FDPIC ABI for ARM | expand

Commit Message

Christophe Lyon March 22, 2018, 2:38 p.m. UTC
This is the main BFD patch, that enables the linker to actually handle
the FDPIC relocation.

2018-XX-XX  Christophe Lyon  <christophe.lyon@st.com>
	Mickaël Guêné  <mickael.guene@st.com>

	bfd/
	* elf32-arm.c (struct fdpic_local): New.
	(elf_arm_obj_tdata): Add local_fdpic_cnts field.
	(elf32_arm_local_fdpic_cnts): New.
	(struct fdpic_global): New.
	(elf32_arm_link_hash_entry): Add fdpic_cnts field.
	(elf32_arm_link_hash_table): Add srofixup field.
	(arm_elf_add_rofixup): New.
	(arm_elf_fill_funcdesc): New.
	(elf32_arm_link_hash_newfunc): Handle fdpic_cnts.
	(elf32_arm_allocate_local_sym_info): Likewise.
	(create_got_section): Create .rofixup section.
	(elf32_arm_copy_indirect_symbol): Handle fdpic_cnts.
	(bfd_elf32_arm_set_target_params): Handle FDPIC.
	(elf32_arm_final_link_relocate): Likewise.
	(elf32_arm_check_relocs): Likewise.
	(allocate_dynrelocs_for_symbol): Likewise.
	(elf32_arm_size_dynamic_sections): Likewise.
	(elf32_arm_finish_dynamic_sections): Likewise.
	(elf32_arm_output_arch_local_syms): Likewise.
	(elf32_arm_fdpic_omit_section_dynsym): New.

	ld/
	* emulparams/armelf_linux_fdpiceabi.sh: Add .rofixup section.
---
 bfd/elf32-arm.c                         | 728 ++++++++++++++++++++++++++++++--
 ld/emulparams/armelf_linux_fdpiceabi.sh |   8 +-
 2 files changed, 705 insertions(+), 31 deletions(-)

-- 
2.6.3
diff mbox series

Patch

diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c
index 3805f71..5892255 100644
--- a/bfd/elf32-arm.c
+++ b/bfd/elf32-arm.c
@@ -3001,6 +3001,13 @@  struct arm_local_iplt_info
   struct elf_dyn_relocs *dyn_relocs;
 };
 
+/* Structure to handle FDPIC support for local functions.  */
+struct fdpic_local {
+  unsigned int funcdesc_cnt;
+  unsigned int gotofffuncdesc_cnt;
+  int funcdesc_offset;
+};
+
 struct elf_arm_obj_tdata
 {
   struct elf_obj_tdata root;
@@ -3019,6 +3026,9 @@  struct elf_arm_obj_tdata
 
   /* Zero to warn when linking objects with incompatible wchar_t sizes.  */
   int no_wchar_size_warning;
+
+  /* Maintains FDPIC counters and funcdesc info.  */
+  struct fdpic_local *local_fdpic_cnts;
 };
 
 #define elf_arm_tdata(bfd) \
@@ -3033,6 +3043,9 @@  struct elf_arm_obj_tdata
 #define elf32_arm_local_iplt(bfd) \
   (elf_arm_tdata (bfd)->local_iplt)
 
+#define elf32_arm_local_fdpic_cnts(bfd) \
+  (elf_arm_tdata (bfd)->local_fdpic_cnts)
+
 #define is_arm_elf(bfd) \
   (bfd_get_flavour (bfd) == bfd_target_elf_flavour \
    && elf_tdata (bfd) != NULL \
@@ -3047,6 +3060,15 @@  elf32_arm_mkobject (bfd *abfd)
 
 #define elf32_arm_hash_entry(ent) ((struct elf32_arm_link_hash_entry *)(ent))
 
+/* Structure to handle FDPIC support for extern functions.  */
+struct fdpic_global {
+  unsigned int gotofffuncdesc_cnt;
+  unsigned int gotfuncdesc_cnt;
+  unsigned int funcdesc_cnt;
+  int funcdesc_offset;
+  int gotfuncdesc_offset;
+};
+
 /* Arm ELF linker hash entry.  */
 struct elf32_arm_link_hash_entry
 {
@@ -3082,6 +3104,9 @@  struct elf32_arm_link_hash_entry
   /* A pointer to the most recently used stub hash entry against this
      symbol.  */
   struct elf32_arm_stub_hash_entry *stub_cache;
+
+  /* Counter for FDPIC relocations against this symbol.  */
+  struct fdpic_global fdpic_cnts;
 };
 
 /* Traverse an arm ELF linker hash table.  */
@@ -3288,8 +3313,22 @@  struct elf32_arm_link_hash_table
 
   /* True if the target system uses FDPIC. */
   int fdpic_p;
+
+  /* Fixup section. Used for FDPIC.  */
+  asection *srofixup;
 };
 
+/* Add an FDPIC read-only fixup.  */
+static void
+arm_elf_add_rofixup (bfd *output_bfd, asection *srofixup, bfd_vma offset)
+{
+  bfd_vma fixup_offset;
+
+  fixup_offset = srofixup->reloc_count++ * 4;
+  BFD_ASSERT (fixup_offset < srofixup->size);
+  bfd_put_32 (output_bfd, offset, srofixup->contents + fixup_offset);
+}
+
 static inline int
 ctz (unsigned int mask)
 {
@@ -3327,6 +3366,57 @@  elf32_arm_popcount (unsigned int mask)
 #endif
 }
 
+static void elf32_arm_add_dynreloc (bfd *output_bfd, struct bfd_link_info *info,
+				    asection *sreloc, Elf_Internal_Rela *rel);
+
+static void
+arm_elf_fill_funcdesc(bfd *output_bfd,
+		      struct bfd_link_info *info,
+		      int *funcdesc_offset,
+		      int dynindx,
+		      int offset,
+		      bfd_vma addr,
+		      bfd_vma dynreloc_value,
+		      bfd_vma seg)
+{
+  if ((*funcdesc_offset & 1) == 0)
+    {
+      struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (info);
+      asection *sgot = globals->root.sgot;
+
+      if (bfd_link_pic(info))
+	{
+	  asection *srelgot = globals->root.srelgot;
+	  Elf_Internal_Rela outrel;
+
+	  outrel.r_info = ELF32_R_INFO (dynindx, R_ARM_FUNCDESC_VALUE);
+	  outrel.r_offset = sgot->output_section->vma + sgot->output_offset + offset;
+	  outrel.r_addend = 0;
+
+	  elf32_arm_add_dynreloc (output_bfd, info, srelgot, &outrel);
+	  bfd_put_32 (output_bfd, addr, sgot->contents + offset);
+	  bfd_put_32 (output_bfd, seg, sgot->contents + offset + 4);
+	}
+      else
+	{
+	  struct elf_link_hash_entry *hgot = globals->root.hgot;
+	  bfd_vma got_value = hgot->root.u.def.value
+	    + hgot->root.u.def.section->output_section->vma
+	    + hgot->root.u.def.section->output_offset;
+
+	  arm_elf_add_rofixup(output_bfd, globals->srofixup,
+			      sgot->output_section->vma + sgot->output_offset
+			      + offset);
+	  arm_elf_add_rofixup(output_bfd, globals->srofixup,
+			      sgot->output_section->vma + sgot->output_offset
+			      + offset + 4);
+	  bfd_put_32 (output_bfd, dynreloc_value, sgot->contents + offset);
+	  bfd_put_32 (output_bfd, got_value, sgot->contents + offset + 4);
+	}
+      *funcdesc_offset |= 1;
+    }
+}
+
 /* Create an entry in an ARM ELF linker hash table.  */
 
 static struct bfd_hash_entry *
@@ -3362,6 +3452,12 @@  elf32_arm_link_hash_newfunc (struct bfd_hash_entry * entry,
       ret->export_glue = NULL;
 
       ret->stub_cache = NULL;
+
+      ret->fdpic_cnts.gotofffuncdesc_cnt = 0;
+      ret->fdpic_cnts.gotfuncdesc_cnt = 0;
+      ret->fdpic_cnts.funcdesc_cnt = 0;
+      ret->fdpic_cnts.funcdesc_offset = -1;
+      ret->fdpic_cnts.gotfuncdesc_offset = -1;
     }
 
   return (struct bfd_hash_entry *) ret;
@@ -3383,11 +3479,15 @@  elf32_arm_allocate_local_sym_info (bfd *abfd)
       size = num_syms * (sizeof (bfd_signed_vma)
 			 + sizeof (struct arm_local_iplt_info *)
 			 + sizeof (bfd_vma)
-			 + sizeof (char));
+			 + sizeof (char)
+			 + sizeof (struct fdpic_local));
       data = bfd_zalloc (abfd, size);
       if (data == NULL)
 	return FALSE;
 
+      elf32_arm_local_fdpic_cnts (abfd) = (struct fdpic_local *) data;
+      data += num_syms * sizeof (struct fdpic_local);
+
       elf_local_got_refcounts (abfd) = (bfd_signed_vma *) data;
       data += num_syms * sizeof (bfd_signed_vma);
 
@@ -3569,6 +3669,16 @@  create_got_section (bfd *dynobj, struct bfd_link_info *info)
   if (! _bfd_elf_create_got_section (dynobj, info))
     return FALSE;
 
+  /* Also create .rofixup.  */
+  if (htab->fdpic_p)
+    {
+      htab->srofixup = bfd_make_section_with_flags (dynobj, ".rofixup",
+						    (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
+						     | SEC_IN_MEMORY | SEC_LINKER_CREATED | SEC_READONLY));
+      if (htab->srofixup == NULL || ! bfd_set_section_alignment (dynobj, htab->srofixup, 2))
+	return FALSE;
+    }
+
   return TRUE;
 }
 
@@ -3808,6 +3918,11 @@  elf32_arm_copy_indirect_symbol (struct bfd_link_info *info,
       edir->plt.noncall_refcount += eind->plt.noncall_refcount;
       eind->plt.noncall_refcount = 0;
 
+      /* Copy FDPIC counters.  */
+      edir->fdpic_cnts.gotofffuncdesc_cnt += eind->fdpic_cnts.gotofffuncdesc_cnt;
+      edir->fdpic_cnts.gotfuncdesc_cnt += eind->fdpic_cnts.gotfuncdesc_cnt;
+      edir->fdpic_cnts.funcdesc_cnt += eind->fdpic_cnts.funcdesc_cnt;
+
       /* We should only allocate a function to .iplt once the final
 	 symbol information is known.  */
       BFD_ASSERT (!eind->is_iplt);
@@ -8788,7 +8903,10 @@  bfd_elf32_arm_set_target_params (struct bfd *output_bfd,
   globals->use_blx |= params->use_blx;
   globals->vfp11_fix = params->vfp11_denorm_fix;
   globals->stm32l4xx_fix = params->stm32l4xx_fix;
-  globals->pic_veneer = params->pic_veneer;
+  if (globals->fdpic_p)
+    globals->pic_veneer = 1;
+  else
+    globals->pic_veneer = params->pic_veneer;
   globals->fix_cortex_a8 = params->fix_cortex_a8;
   globals->fix_arm1176 = params->fix_arm1176;
   globals->cmse_implib = params->cmse_implib;
@@ -10147,7 +10265,8 @@  elf32_arm_final_link_relocate (reloc_howto_type *	    howto,
 	 relocations are copied into the output file to be resolved at
 	 run time.  */
       if ((bfd_link_pic (info)
-	   || globals->root.is_relocatable_executable)
+	   || globals->root.is_relocatable_executable
+	   || globals->fdpic_p)
 	  && (input_section->flags & SEC_ALLOC)
 	  && !(globals->vxworks_p
 	       && strcmp (input_section->output_section->name,
@@ -10168,6 +10287,7 @@  elf32_arm_final_link_relocate (reloc_howto_type *	    howto,
 	{
 	  Elf_Internal_Rela outrel;
 	  bfd_boolean skip, relocate;
+	  int isrofixup = 0;
 
 	  if ((r_type == R_ARM_REL32 || r_type == R_ARM_REL32_NOI)
 	      && !h->def_regular)
@@ -10223,7 +10343,8 @@  elf32_arm_final_link_relocate (reloc_howto_type *	    howto,
 	      int symbol;
 
 	      /* This symbol is local, or marked to become local.  */
-	      BFD_ASSERT (r_type == R_ARM_ABS32 || r_type == R_ARM_ABS32_NOI);
+	      BFD_ASSERT (r_type == R_ARM_ABS32 || r_type == R_ARM_ABS32_NOI
+			  || (globals->fdpic_p && !bfd_link_pic(info)));
 	      if (globals->symbian_p)
 		{
 		  asection *osec;
@@ -10269,6 +10390,8 @@  elf32_arm_final_link_relocate (reloc_howto_type *	    howto,
 		   must use an R_ARM_IRELATIVE relocation to obtain the
 		   correct run-time address.  */
 		outrel.r_info = ELF32_R_INFO (symbol, R_ARM_IRELATIVE);
+	      else if (globals->fdpic_p && !bfd_link_pic(info))
+		isrofixup = 1;
 	      else
 		outrel.r_info = ELF32_R_INFO (symbol, R_ARM_RELATIVE);
 	      if (globals->use_rel)
@@ -10277,7 +10400,10 @@  elf32_arm_final_link_relocate (reloc_howto_type *	    howto,
 		outrel.r_addend += dynreloc_value;
 	    }
 
-	  elf32_arm_add_dynreloc (output_bfd, info, sreloc, &outrel);
+	  if (isrofixup)
+	    arm_elf_add_rofixup(output_bfd, globals->srofixup, outrel.r_offset);
+	  else
+	    elf32_arm_add_dynreloc (output_bfd, info, sreloc, &outrel);
 
 	  /* If this reloc is against an external symbol, we do not want to
 	     fiddle with the addend.  Otherwise, we need to include the symbol
@@ -11153,8 +11279,10 @@  elf32_arm_final_link_relocate (reloc_howto_type *	    howto,
 	  else
 	    {
 	      Elf_Internal_Rela outrel;
+	      int isrofixup = 0;
 
-	      if (h->dynindx != -1 && !SYMBOL_REFERENCES_LOCAL (info, h))
+	      if (((h->dynindx != -1) || globals->fdpic_p)
+		  && !SYMBOL_REFERENCES_LOCAL (info, h))
 		{
 		  /* If the symbol doesn't resolve locally in a static
 		     object, we have an undefined reference.  If the
@@ -11177,6 +11305,8 @@  elf32_arm_final_link_relocate (reloc_howto_type *	    howto,
 			   && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
 			       || h->root.type != bfd_link_hash_undefweak))
 		    outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
+		  else if (globals->fdpic_p)
+		    isrofixup = 1;
 		  else
 		    outrel.r_info = 0;
 		  outrel.r_addend = dynreloc_value;
@@ -11185,20 +11315,27 @@  elf32_arm_final_link_relocate (reloc_howto_type *	    howto,
 	      /* The GOT entry is initialized to zero by default.
 		 See if we should install a different value.  */
 	      if (outrel.r_addend != 0
-		  && (outrel.r_info == 0 || globals->use_rel))
+		  && (outrel.r_info == 0 || globals->use_rel || isrofixup))
 		{
 		  bfd_put_32 (output_bfd, outrel.r_addend,
 			      sgot->contents + off);
 		  outrel.r_addend = 0;
 		}
 
-	      if (outrel.r_info != 0)
+	      if (outrel.r_info != 0 && !isrofixup)
 		{
 		  outrel.r_offset = (sgot->output_section->vma
 				     + sgot->output_offset
 				     + off);
 		  elf32_arm_add_dynreloc (output_bfd, info, srelgot, &outrel);
 		}
+	      else if (isrofixup)
+		{
+		  arm_elf_add_rofixup(output_bfd,
+				      elf32_arm_hash_table(info)->srofixup,
+				      sgot->output_section->vma
+				      + sgot->output_offset + off);
+		}
 	      h->got.offset |= 1;
 	    }
 	  value = sgot->output_offset + off;
@@ -11236,6 +11373,14 @@  elf32_arm_final_link_relocate (reloc_howto_type *	    howto,
 		    outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
 		  elf32_arm_add_dynreloc (output_bfd, info, srelgot, &outrel);
 		}
+	      else if (globals->fdpic_p)
+		{
+		  /* For FDPIC executables, we use rofixup to fix
+		     address at runtime.  */
+		  arm_elf_add_rofixup(output_bfd, globals->srofixup,
+				      sgot->output_section->vma + sgot->output_offset
+				      + off);
+		}
 
 	      local_got_offsets[r_symndx] |= 1;
 	    }
@@ -11295,12 +11440,24 @@  elf32_arm_final_link_relocate (reloc_howto_type *	    howto,
 	    globals->tls_ldm_got.offset |= 1;
 	  }
 
-	value = sgot->output_section->vma + sgot->output_offset + off
-	  - (input_section->output_section->vma + input_section->output_offset + rel->r_offset);
+	if (globals->fdpic_p)
+	  {
+	    bfd_put_32(output_bfd,
+		       globals->root.sgot->output_offset + off,
+		       contents + rel->r_offset);
+
+	    return bfd_reloc_ok;
+	  }
+	else
+	  {
+	    value = sgot->output_section->vma + sgot->output_offset + off
+	      - (input_section->output_section->vma
+		 + input_section->output_offset + rel->r_offset);
 
-	return _bfd_final_link_relocate (howto, input_bfd, input_section,
-					 contents, rel->r_offset, value,
-					 rel->r_addend);
+	    return _bfd_final_link_relocate (howto, input_bfd, input_section,
+					     contents, rel->r_offset, value,
+					     rel->r_addend);
+	  }
       }
 
     case R_ARM_TLS_CALL:
@@ -11653,9 +11810,23 @@  elf32_arm_final_link_relocate (reloc_howto_type *	    howto,
 		   - (input_section->output_section->vma
 		      + input_section->output_offset + rel->r_offset));
 
-	return _bfd_final_link_relocate (howto, input_bfd, input_section,
-					 contents, rel->r_offset, value,
-					 rel->r_addend);
+	if (globals->fdpic_p && (r_type == R_ARM_TLS_GD32 ||
+				 r_type == R_ARM_TLS_IE32))
+	  {
+	    /* For FDPIC relocations, resolve to the offset of the GOT
+	       entry from the start of GOT.  */
+	    bfd_put_32(output_bfd,
+		       globals->root.sgot->output_offset + off,
+		       contents + rel->r_offset);
+
+	    return bfd_reloc_ok;
+	  }
+	else
+	  {
+	    return _bfd_final_link_relocate (howto, input_bfd, input_section,
+					     contents, rel->r_offset, value,
+					     rel->r_addend);
+	  }
       }
 
     case R_ARM_TLS_LE32:
@@ -12237,6 +12408,244 @@  elf32_arm_final_link_relocate (reloc_howto_type *	    howto,
 	*unresolved_reloc_p = FALSE;
 	return bfd_reloc_ok;
 
+    case R_ARM_GOTOFFFUNCDESC:
+      {
+        if (h == NULL)
+	  {
+	    struct fdpic_local *local_fdpic_cnts = elf32_arm_local_fdpic_cnts(input_bfd);
+	    int dynindx = elf_section_data (sym_sec->output_section)->dynindx;
+	    int offset = local_fdpic_cnts[r_symndx].funcdesc_offset & ~1;
+	    bfd_vma addr = dynreloc_value - sym_sec->output_section->vma;
+	    bfd_vma seg = -1;
+
+	    if (bfd_link_pic(info) && dynindx == 0)
+	      abort();
+
+	    /* Resolve relocation.  */
+	    bfd_put_32(output_bfd, (offset + sgot->output_offset)
+		       , contents + rel->r_offset);
+	    /* Emit R_ARM_FUNCDESC_VALUE or two fixups on funcdesc if
+	       not done yet.  */
+	    arm_elf_fill_funcdesc(output_bfd, info,
+				  &local_fdpic_cnts[r_symndx].funcdesc_offset,
+				  dynindx, offset, addr, dynreloc_value, seg);
+	  }
+	else
+	  {
+	    int dynindx;
+	    int offset = eh->fdpic_cnts.funcdesc_offset & ~1;
+	    bfd_vma addr;
+	    bfd_vma seg = -1;
+
+	    /* For static binaries, sym_sec can be null.  */
+	    if (sym_sec)
+	      {
+		dynindx = elf_section_data (sym_sec->output_section)->dynindx;
+		addr = dynreloc_value - sym_sec->output_section->vma;
+	      }
+	    else
+	      {
+		dynindx = 0;
+		addr = 0;
+	      }
+
+	    if (bfd_link_pic(info) && dynindx == 0)
+	      abort();
+
+	    /* This case cannot occur since funcdesc is allocated by
+	       the dynamic loader so we cannot resolve the relocation.  */
+	    if (h->dynindx != -1)
+	      abort();
+
+	    /* Resolve relocation.  */
+	    bfd_put_32(output_bfd, (offset + sgot->output_offset),
+		       contents + rel->r_offset);
+	    /* Emit R_ARM_FUNCDESC_VALUE on funcdesc if not done yet.  */
+	    arm_elf_fill_funcdesc(output_bfd, info,
+				  &eh->fdpic_cnts.funcdesc_offset,
+				  dynindx, offset, addr, dynreloc_value, seg);
+	  }
+      }
+      *unresolved_reloc_p = FALSE;
+      return bfd_reloc_ok;
+
+    case R_ARM_GOTFUNCDESC:
+      {
+        if (h != NULL)
+	  {
+	    Elf_Internal_Rela outrel;
+
+	    /* Resolve relocation.  */
+	    bfd_put_32(output_bfd, ((eh->fdpic_cnts.gotfuncdesc_offset & ~1)
+				    + sgot->output_offset),
+		       contents + rel->r_offset);
+	    /* Add funcdesc and associated R_ARM_FUNCDESC_VALUE.  */
+	    if(h->dynindx == -1)
+	      {
+		int dynindx;
+		int offset = eh->fdpic_cnts.funcdesc_offset & ~1;
+		bfd_vma addr;
+		bfd_vma seg = -1;
+
+		/* For static binaries sym_sec can be null.  */
+		if (sym_sec)
+		  {
+		    dynindx = elf_section_data (sym_sec->output_section)->dynindx;
+		    addr = dynreloc_value - sym_sec->output_section->vma;
+		  }
+		else
+		  {
+		    dynindx = 0;
+		    addr = 0;
+		  }
+
+		if (bfd_link_pic(info) && dynindx == 0)
+		  abort();
+
+		/* Emit R_ARM_FUNCDESC_VALUE on funcdesc if not done yet.  */
+		arm_elf_fill_funcdesc(output_bfd, info,
+				      &eh->fdpic_cnts.funcdesc_offset,
+				      dynindx, offset, addr, dynreloc_value, seg);
+	      }
+
+	    /* Add a dynamic relocation on GOT entry if not already done.  */
+	    if ((eh->fdpic_cnts.gotfuncdesc_offset & 1) == 0)
+	      {
+		if (h->dynindx == -1)
+		  {
+		    outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
+		    if (h->root.type == bfd_link_hash_undefweak)
+		      bfd_put_32(output_bfd, 0, sgot->contents
+				 + (eh->fdpic_cnts.gotfuncdesc_offset & ~1));
+		    else
+		      bfd_put_32(output_bfd, sgot->output_section->vma
+				 + sgot->output_offset
+				 + (eh->fdpic_cnts.funcdesc_offset & ~1),
+				 sgot->contents
+				 + (eh->fdpic_cnts.gotfuncdesc_offset & ~1));
+		  }
+		else
+		  {
+		    outrel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_FUNCDESC);
+		  }
+		outrel.r_offset = sgot->output_section->vma
+		  + sgot->output_offset
+		  + (eh->fdpic_cnts.gotfuncdesc_offset & ~1);
+		outrel.r_addend = 0;
+		if (h->dynindx == -1 && !bfd_link_pic(info))
+		  if (h->root.type == bfd_link_hash_undefweak)
+                    arm_elf_add_rofixup(output_bfd, globals->srofixup, -1);
+		  else
+                    arm_elf_add_rofixup(output_bfd, globals->srofixup, outrel.r_offset);
+		else
+		  elf32_arm_add_dynreloc (output_bfd, info, srelgot, &outrel);
+		eh->fdpic_cnts.gotfuncdesc_offset |= 1;
+	      }
+	  }
+	else
+	  {
+	    /* Such relocation on static function should not have been
+	       emitted by the compiler.  */
+	    abort();
+	  }
+      }
+      *unresolved_reloc_p = FALSE;
+      return bfd_reloc_ok;
+
+    case R_ARM_FUNCDESC:
+      {
+        if (h == NULL)
+	  {
+	    struct fdpic_local *local_fdpic_cnts = elf32_arm_local_fdpic_cnts(input_bfd);
+	    Elf_Internal_Rela outrel;
+	    int dynindx = elf_section_data (sym_sec->output_section)->dynindx;
+	    int offset = local_fdpic_cnts[r_symndx].funcdesc_offset & ~1;
+	    bfd_vma addr = dynreloc_value - sym_sec->output_section->vma;
+	    bfd_vma seg = -1;
+
+	    if (bfd_link_pic(info) && dynindx == 0)
+	      abort();
+
+	    /* Replace static FUNCDESC relocation with a
+	       R_ARM_RELATIVE dynamic relocation or with a rofixup for
+	       executable.  */
+	    outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
+	    outrel.r_offset = input_section->output_section->vma
+	      + input_section->output_offset + rel->r_offset;
+	    outrel.r_addend = 0;
+	    if (bfd_link_pic(info))
+	      elf32_arm_add_dynreloc (output_bfd, info, srelgot, &outrel);
+	    else
+	      arm_elf_add_rofixup(output_bfd, globals->srofixup, outrel.r_offset);
+
+	    bfd_put_32 (input_bfd, sgot->output_section->vma
+			+ sgot->output_offset + offset, hit_data);
+
+	    /* Emit R_ARM_FUNCDESC_VALUE on funcdesc if not done yet.  */
+	    arm_elf_fill_funcdesc(output_bfd, info,
+				  &local_fdpic_cnts[r_symndx].funcdesc_offset,
+				  dynindx, offset, addr, dynreloc_value, seg);
+	  }
+	else
+	  {
+	    if (h->dynindx == -1)
+	      {
+		int dynindx;
+		int offset = eh->fdpic_cnts.funcdesc_offset & ~1;
+		bfd_vma addr;
+		bfd_vma seg = -1;
+		Elf_Internal_Rela outrel;
+
+		/* For static binaries sym_sec can be null.  */
+		if (sym_sec)
+		  {
+		    dynindx = elf_section_data (sym_sec->output_section)->dynindx;
+		    addr = dynreloc_value - sym_sec->output_section->vma;
+		  }
+		else
+		  {
+		    dynindx = 0;
+		    addr = 0;
+		  }
+
+		if (bfd_link_pic(info) && dynindx == 0)
+		  abort();
+
+		/* Replace static FUNCDESC relocation with a
+		   R_ARM_RELATIVE dynamic relocation.  */
+		outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
+		outrel.r_offset = input_section->output_section->vma
+		  + input_section->output_offset + rel->r_offset;
+		outrel.r_addend = 0;
+		if (bfd_link_pic(info))
+		  elf32_arm_add_dynreloc (output_bfd, info, srelgot, &outrel);
+		else
+		  arm_elf_add_rofixup(output_bfd, globals->srofixup, outrel.r_offset);
+
+		bfd_put_32 (input_bfd, sgot->output_section->vma
+			    + sgot->output_offset + offset, hit_data);
+
+		/* Emit R_ARM_FUNCDESC_VALUE on funcdesc if not done yet.  */
+		arm_elf_fill_funcdesc(output_bfd, info,
+				      &eh->fdpic_cnts.funcdesc_offset,
+				      dynindx, offset, addr, dynreloc_value, seg);
+	      }
+	    else
+	      {
+		Elf_Internal_Rela outrel;
+
+		/* Add a dynamic relocation.  */
+		outrel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_FUNCDESC);
+		outrel.r_offset = input_section->output_section->vma
+		  + input_section->output_offset + rel->r_offset;
+		outrel.r_addend = 0;
+		elf32_arm_add_dynreloc (output_bfd, info, srelgot, &outrel);
+	      }
+	  }
+      }
+      *unresolved_reloc_p = FALSE;
+      return bfd_reloc_ok;
+
     default:
       return bfd_reloc_notsupported;
     }
@@ -14499,6 +14908,54 @@  elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
       r_type = elf32_arm_tls_transition (info, r_type, h);
       switch (r_type)
 	{
+	case R_ARM_GOTOFFFUNCDESC:
+	  {
+	    if (h == NULL)
+	      {
+		if (!elf32_arm_allocate_local_sym_info (abfd))
+		  return FALSE;
+		elf32_arm_local_fdpic_cnts(abfd)[r_symndx].gotofffuncdesc_cnt += 1;
+		elf32_arm_local_fdpic_cnts(abfd)[r_symndx].funcdesc_offset = -1;
+	      }
+	    else
+	      {
+		eh->fdpic_cnts.gotofffuncdesc_cnt++;
+	      }
+	  }
+	  break;
+
+	case R_ARM_GOTFUNCDESC:
+	  {
+	    if (h == NULL)
+	      {
+		/* Such a relocation is not supposed to be generated
+		   by gcc on a static function. */
+		/* Anyway if needed it could be handled.  */
+		abort();
+	      }
+	    else
+	      {
+		eh->fdpic_cnts.gotfuncdesc_cnt++;
+	      }
+	  }
+	  break;
+
+	case R_ARM_FUNCDESC:
+	  {
+	    if (h == NULL)
+	      {
+		if (!elf32_arm_allocate_local_sym_info (abfd))
+		  return FALSE;
+		elf32_arm_local_fdpic_cnts(abfd)[r_symndx].funcdesc_cnt += 1;
+		elf32_arm_local_fdpic_cnts(abfd)[r_symndx].funcdesc_offset = -1;
+	      }
+	    else
+	      {
+		eh->fdpic_cnts.funcdesc_cnt++;
+	      }
+	  }
+	  break;
+
 	  case R_ARM_GOT32:
 	  case R_ARM_GOT_PREL:
 	  case R_ARM_TLS_GD32:
@@ -14640,7 +15097,8 @@  elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	  case R_ARM_THM_MOVT_PREL:
 
 	    /* Should the interworking branches be listed here?  */
-	    if ((bfd_link_pic (info) || htab->root.is_relocatable_executable)
+	    if ((bfd_link_pic (info) || htab->root.is_relocatable_executable
+		 || htab->fdpic_p)
 		&& (sec->flags & SEC_ALLOC) != 0)
 	      {
 		if (h == NULL
@@ -14793,6 +15251,18 @@  elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	  if (elf32_arm_howto_from_type (r_type)->pc_relative)
 	    p->pc_count += 1;
 	  p->count += 1;
+	  if (h == NULL && htab->fdpic_p && !bfd_link_pic(info)
+	      && r_type != R_ARM_ABS32 && r_type != R_ARM_ABS32_NOI) {
+	    /* Here we only support R_ARM_ABS32 and R_ARM_ABS32_NOI
+	       that will become rofixup.  */
+	    /* This is due to the fact that we suppose all will become rofixup.  */
+	    fprintf(stderr, "FDPIC does not yet support %d relocation to become dynamic for executable\n", r_type);
+	    _bfd_error_handler
+	      (_("FDPIC does not yet support %s relocation"
+		 " to become dynamic for executable"),
+	       elf32_arm_howto_table_1[r_type].name);
+	    abort();
+	  }
 	}
     }
 
@@ -15455,7 +15925,7 @@  allocate_dynrelocs_for_symbol (struct elf_link_hash_entry *h, void * inf)
 
       /* Make sure this symbol is output as a dynamic symbol.
 	 Undefined weak syms won't yet be marked as dynamic.  */
-      if (h->dynindx == -1 && !h->forced_local
+      if (htab->root.dynamic_sections_created && h->dynindx == -1 && !h->forced_local
 	  && h->root.type == bfd_link_hash_undefweak)
 	{
 	  if (! bfd_elf_link_record_dynamic_symbol (info, h))
@@ -15535,7 +16005,8 @@  allocate_dynrelocs_for_symbol (struct elf_link_hash_entry *h, void * inf)
 	      if ((tls_type & GOT_TLS_GD) && indx != 0)
 		elf32_arm_allocate_dynrelocs (info, htab->root.srelgot, 1);
 	    }
-	  else if (indx != -1 && !SYMBOL_REFERENCES_LOCAL (info, h))
+	  else if (((indx != -1) || htab->fdpic_p)
+		   && !SYMBOL_REFERENCES_LOCAL (info, h))
 	    {
 	      if (htab->root.dynamic_sections_created)
 		/* Reserve room for the GOT entry's R_ARM_GLOB_DAT relocation.  */
@@ -15552,11 +16023,110 @@  allocate_dynrelocs_for_symbol (struct elf_link_hash_entry *h, void * inf)
 		       || h->root.type != bfd_link_hash_undefweak))
 	    /* Reserve room for the GOT entry's R_ARM_RELATIVE relocation.  */
 	    elf32_arm_allocate_dynrelocs (info, htab->root.srelgot, 1);
+	  else if (htab->fdpic_p && tls_type == GOT_NORMAL)
+	    /* Reserve room for rofixup for FDPIC executable.  */
+	    /* TLS relocs do not need space since they are completely
+	       resolved.  */
+	    htab->srofixup->size += 4;
 	}
     }
   else
     h->got.offset = (bfd_vma) -1;
 
+  /* FDPIC support.  */
+  if (eh->fdpic_cnts.gotofffuncdesc_cnt > 0)
+    {
+      /* Symbol musn't be exported.  */
+      if (h->dynindx != -1)
+	abort();
+
+      /* We only allocate one function descriptor with its associated relocation.  */
+      if (eh->fdpic_cnts.funcdesc_offset == -1)
+	{
+	  asection *s = htab->root.sgot;
+
+	  eh->fdpic_cnts.funcdesc_offset = s->size;
+	  s->size += 8;
+	  /* We will add an R_ARM_FUNCDESC_VALUE relocation or two rofixups.  */
+	  if (bfd_link_pic(info))
+	    elf32_arm_allocate_dynrelocs (info, htab->root.srelgot, 1);
+	  else
+	    htab->srofixup->size += 8;
+	}
+    }
+
+  if (eh->fdpic_cnts.gotfuncdesc_cnt > 0)
+    {
+      asection *s = htab->root.sgot;
+
+      if (htab->root.dynamic_sections_created && h->dynindx == -1
+	  && !h->forced_local)
+	if (! bfd_elf_link_record_dynamic_symbol (info, h))
+	  return FALSE;
+
+      if (h->dynindx == -1)
+	{
+	  /* We only allocate one function descriptor with its associated relocation. q */
+	  if (eh->fdpic_cnts.funcdesc_offset == -1)
+	    {
+
+	      eh->fdpic_cnts.funcdesc_offset = s->size;
+	      s->size += 8;
+	      /* We will add an R_ARM_FUNCDESC_VALUE relocation or two rofixups.  */
+	      if (bfd_link_pic(info))
+		elf32_arm_allocate_dynrelocs (info, htab->root.srelgot, 1);
+	      else
+		htab->srofixup->size += 8;
+	    }
+	}
+
+      /* Add one entry into the GOT and a R_ARM_FUNCDESC or
+	 R_ARM_RELATIVE/rofixup relocation on it.  */
+      eh->fdpic_cnts.gotfuncdesc_offset = s->size;
+      s->size += 4;
+      if (h->dynindx == -1 && !bfd_link_pic(info))
+        htab->srofixup->size += 4;
+      else
+        elf32_arm_allocate_dynrelocs (info, htab->root.srelgot, 1);
+    }
+
+  if (eh->fdpic_cnts.funcdesc_cnt > 0)
+    {
+      if (htab->root.dynamic_sections_created && h->dynindx == -1
+	  && !h->forced_local)
+	if (! bfd_elf_link_record_dynamic_symbol (info, h))
+	  return FALSE;
+
+      if (h->dynindx == -1)
+	{
+	  /* We only allocate one function descriptor with its associated relocation.  */
+	  if (eh->fdpic_cnts.funcdesc_offset == -1)
+	    {
+	      asection *s = htab->root.sgot;
+
+	      eh->fdpic_cnts.funcdesc_offset = s->size;
+	      s->size += 8;
+	      /* We will add an R_ARM_FUNCDESC_VALUE relocation or two rofixups.  */
+	      if (bfd_link_pic(info))
+		elf32_arm_allocate_dynrelocs (info, htab->root.srelgot, 1);
+	      else
+		htab->srofixup->size += 8;
+	    }
+	}
+      if (h->dynindx == -1 && !bfd_link_pic(info))
+	{
+	  /* For FDPIC executable we replace R_ARM_RELATIVE with a rofixup.  */
+	  htab->srofixup->size += 4 * eh->fdpic_cnts.funcdesc_cnt;
+	}
+      else
+	{
+	  /* Will need one dynamic reloc per reference. will be either
+	     R_ARM_FUNCDESC or R_ARM_RELATIVE for hidden symbols.  */
+	  elf32_arm_allocate_dynrelocs (info, htab->root.srelgot,
+					eh->fdpic_cnts.funcdesc_cnt);
+	}
+    }
+
   /* Allocate stubs for exported Thumb functions on v4t.  */
   if (!htab->use_blx && h->dynindx != -1
       && h->def_regular
@@ -15599,7 +16169,7 @@  allocate_dynrelocs_for_symbol (struct elf_link_hash_entry *h, void * inf)
      space for pc-relative relocs that have become local due to symbol
      visibility changes.  */
 
-  if (bfd_link_pic (info) || htab->root.is_relocatable_executable)
+  if (bfd_link_pic (info) || htab->root.is_relocatable_executable || htab->fdpic_p)
     {
       /* Relocs that use pc_count are PC-relative forms, which will appear
 	 on something like ".long foo - ." or "movw REG, foo - .".  We want
@@ -15646,7 +16216,7 @@  allocate_dynrelocs_for_symbol (struct elf_link_hash_entry *h, void * inf)
 
 	  /* Make sure undefined weak symbols are output as a dynamic
 	     symbol in PIEs.  */
-	  else if (h->dynindx == -1
+	  else if (htab->root.dynamic_sections_created && h->dynindx == -1
 		   && !h->forced_local)
 	    {
 	      if (! bfd_elf_link_record_dynamic_symbol (info, h))
@@ -15702,10 +16272,15 @@  allocate_dynrelocs_for_symbol (struct elf_link_hash_entry *h, void * inf)
   for (p = eh->dyn_relocs; p != NULL; p = p->next)
     {
       asection *sreloc = elf_section_data (p->sec)->sreloc;
+
       if (h->type == STT_GNU_IFUNC
 	  && eh->plt.noncall_refcount == 0
 	  && SYMBOL_REFERENCES_LOCAL (info, h))
 	elf32_arm_allocate_irelocs (info, sreloc, p->count);
+      else if (h->dynindx != -1 && (!bfd_link_pic(info) || !info->symbolic || !h->def_regular))
+	elf32_arm_allocate_dynrelocs (info, sreloc, p->count);
+      else if (htab->fdpic_p && !bfd_link_pic(info))
+	htab->srofixup->size += 4 * p->count;
       else
 	elf32_arm_allocate_dynrelocs (info, sreloc, p->count);
     }
@@ -15800,6 +16375,7 @@  elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
       asection *srel;
       bfd_boolean is_vxworks = htab->vxworks_p;
       unsigned int symndx;
+      struct fdpic_local *local_fdpic_cnts;
 
       if (! is_arm_elf (ibfd))
 	continue;
@@ -15829,7 +16405,10 @@  elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
 	      else if (p->count != 0)
 		{
 		  srel = elf_section_data (p->sec)->sreloc;
-		  elf32_arm_allocate_dynrelocs (info, srel, p->count);
+		  if (htab->fdpic_p && !bfd_link_pic(info))
+		    htab->srofixup->size += 4 * p->count;
+		  else
+		    elf32_arm_allocate_dynrelocs (info, srel, p->count);
 		  if ((p->sec->output_section->flags & SEC_READONLY) != 0)
 		    info->flags |= DF_TEXTREL;
 		}
@@ -15846,15 +16425,54 @@  elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
       local_iplt_ptr = elf32_arm_local_iplt (ibfd);
       local_tls_type = elf32_arm_local_got_tls_type (ibfd);
       local_tlsdesc_gotent = elf32_arm_local_tlsdesc_gotent (ibfd);
+      local_fdpic_cnts = elf32_arm_local_fdpic_cnts (ibfd);
       symndx = 0;
       s = htab->root.sgot;
       srel = htab->root.srelgot;
       for (; local_got < end_local_got;
 	   ++local_got, ++local_iplt_ptr, ++local_tls_type,
-	   ++local_tlsdesc_gotent, ++symndx)
+	   ++local_tlsdesc_gotent, ++symndx, ++local_fdpic_cnts)
 	{
 	  *local_tlsdesc_gotent = (bfd_vma) -1;
 	  local_iplt = *local_iplt_ptr;
+
+	  /* FDPIC support.  */
+	  if (local_fdpic_cnts->gotofffuncdesc_cnt > 0)
+	    {
+	      if (local_fdpic_cnts->funcdesc_offset == -1)
+		{
+		  local_fdpic_cnts->funcdesc_offset = s->size;
+		  s->size += 8;
+
+		  /* We will add an R_ARM_FUNCDESC_VALUE relocation or two rofixups.  */
+		  if (bfd_link_pic(info))
+		    elf32_arm_allocate_dynrelocs (info, srel, 1);
+		  else
+		    htab->srofixup->size += 8;
+		}
+	    }
+
+	  if (local_fdpic_cnts->funcdesc_cnt > 0)
+	    {
+	      if (local_fdpic_cnts->funcdesc_offset == -1)
+		{
+		  local_fdpic_cnts->funcdesc_offset = s->size;
+		  s->size += 8;
+
+		  /* We will add an R_ARM_FUNCDESC_VALUE relocation or two rofixups.  */
+		  if (bfd_link_pic(info))
+		    elf32_arm_allocate_dynrelocs (info, srel, 1);
+		  else
+		    htab->srofixup->size += 8;
+		}
+
+	      /* We will add n R_ARM_RELATIVE relocations or n rofixups.  */
+	      if (bfd_link_pic(info))
+		elf32_arm_allocate_dynrelocs (info, srel, local_fdpic_cnts->funcdesc_cnt);
+	      else
+		htab->srofixup->size += 4 * local_fdpic_cnts->funcdesc_cnt;
+	    }
+
 	  if (local_iplt != NULL)
 	    {
 	      struct elf_dyn_relocs *p;
@@ -15929,13 +16547,15 @@  elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
 		  && (local_iplt == NULL
 		      || local_iplt->arm.noncall_refcount == 0))
 		elf32_arm_allocate_irelocs (info, srel, 1);
-	      else if (bfd_link_pic (info) || output_bfd->flags & DYNAMIC)
+	      else if (bfd_link_pic (info) || output_bfd->flags & DYNAMIC || htab->fdpic_p)
 		{
-		  if ((bfd_link_pic (info) && !(*local_tls_type & GOT_TLS_GDESC))
-		      || *local_tls_type & GOT_TLS_GD)
+		  if ((bfd_link_pic (info) && !(*local_tls_type & GOT_TLS_GDESC)))
 		    elf32_arm_allocate_dynrelocs (info, srel, 1);
+		  else if (htab->fdpic_p && *local_tls_type & GOT_NORMAL)
+		    htab->srofixup->size += 4;
 
-		  if (bfd_link_pic (info) && *local_tls_type & GOT_TLS_GDESC)
+		  if ((bfd_link_pic (info) || htab->fdpic_p)
+		      && *local_tls_type & GOT_TLS_GDESC)
 		    {
 		      elf32_arm_allocate_dynrelocs (info,
 						    htab->root.srelplt, 1);
@@ -15960,6 +16580,11 @@  elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
   else
     htab->tls_ldm_got.offset = -1;
 
+  /* At the very end of the .rofixup section is a pointer to the GOT,
+     reserve space for it. */
+  if (htab->fdpic_p && htab->srofixup != NULL)
+    htab->srofixup->size += 4;
+
   /* Allocate global sym .plt and .got entries, and space for global
      sym dynamic relocs.  */
   elf_link_hash_traverse (& htab->root, allocate_dynrelocs_for_symbol, info);
@@ -16050,7 +16675,8 @@  elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
 	       && s != htab->root.iplt
 	       && s != htab->root.igotplt
 	       && s != htab->root.sdynbss
-	       && s != htab->root.sdynrelro)
+	       && s != htab->root.sdynrelro
+	       && s != htab->srofixup)
 	{
 	  /* It's not one of our sections, so don't allocate space.  */
 	  continue;
@@ -16682,6 +17308,21 @@  elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
       elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4;
     }
 
+  /* At the very end of the .rofixup section is a pointer to the GOT.  */
+  if (htab->fdpic_p && htab->srofixup != NULL)
+    {
+      struct elf_link_hash_entry *hgot = htab->root.hgot;
+
+      bfd_vma got_value = hgot->root.u.def.value
+	+ hgot->root.u.def.section->output_section->vma
+	+ hgot->root.u.def.section->output_offset;
+
+      arm_elf_add_rofixup(output_bfd, htab->srofixup, got_value);
+
+      /* Make sure we allocated and generated the same number of fixups.  */
+      BFD_ASSERT (htab->srofixup->reloc_count * 4 == htab->srofixup->size);
+    }
+
   return TRUE;
 }
 
@@ -17301,7 +17942,7 @@  elf32_arm_output_arch_local_syms (bfd *output_bfd,
 	  if (!elf32_arm_output_map_sym (&osi, ARM_MAP_THUMB, 16))
 	    return FALSE;
 	}
-      else if (!htab->symbian_p)
+      else if (!htab->symbian_p && !htab->fdpic_p)
 	{
 	  if (!elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, 0))
 	    return FALSE;
@@ -19514,15 +20155,44 @@  elf32_arm_fdpic_link_hash_table_create (bfd *abfd)
   return ret;
 }
 
+/* We need dynamic symbols for every section, since segments can
+   relocate independently.  */
+static bfd_boolean
+elf32_arm_fdpic_omit_section_dynsym (bfd *output_bfd ATTRIBUTE_UNUSED,
+				    struct bfd_link_info *info
+				    ATTRIBUTE_UNUSED,
+				    asection *p ATTRIBUTE_UNUSED)
+{
+  switch (elf_section_data (p)->this_hdr.sh_type)
+    {
+    case SHT_PROGBITS:
+    case SHT_NOBITS:
+      /* If sh_type is yet undecided, assume it could be
+	 SHT_PROGBITS/SHT_NOBITS.  */
+    case SHT_NULL:
+      return FALSE;
+
+      /* There shouldn't be section relative relocations
+	 against any other section.  */
+    default:
+      return TRUE;
+    }
+}
+
 #undef  elf32_bed
 #define elf32_bed				elf32_arm_fdpic_bed
 
 #undef  bfd_elf32_bfd_link_hash_table_create
 #define bfd_elf32_bfd_link_hash_table_create 	elf32_arm_fdpic_link_hash_table_create
 
+#undef elf_backend_omit_section_dynsym
+#define elf_backend_omit_section_dynsym		elf32_arm_fdpic_omit_section_dynsym
+
 #include "elf32-target.h"
+
 #undef elf_match_priority
 #undef ELF_OSABI
+#undef elf_backend_omit_section_dynsym
 
 /* VxWorks Targets.  */
 
diff --git a/ld/emulparams/armelf_linux_fdpiceabi.sh b/ld/emulparams/armelf_linux_fdpiceabi.sh
index 104cf38..aa604aa 100644
--- a/ld/emulparams/armelf_linux_fdpiceabi.sh
+++ b/ld/emulparams/armelf_linux_fdpiceabi.sh
@@ -9,5 +9,9 @@  OTHER_READONLY_SECTIONS="
   .ARM.extab ${RELOCATING-0} : { *(.ARM.extab${RELOCATING+* .gnu.linkonce.armextab.*}) }
   ${RELOCATING+ PROVIDE_HIDDEN (__exidx_start = .); }
   .ARM.exidx ${RELOCATING-0} : { *(.ARM.exidx${RELOCATING+* .gnu.linkonce.armexidx.*}) }
-  ${RELOCATING+ PROVIDE_HIDDEN (__exidx_end = .); }"
-
+  ${RELOCATING+ PROVIDE_HIDDEN (__exidx_end = .); }
+  .rofixup : {
+	${RELOCATING+__ROFIXUP_LIST__ = .;}
+	*(.rofixup)
+	${RELOCATING+__ROFIXUP_END__ = .;}
+}"