RFC: Supporting multiple relocs per section

Message ID 877e0pmpew.fsf@redhat.com
State New
Headers show
Series
  • RFC: Supporting multiple relocs per section
Related show

Commit Message

Nick Clifton Feb. 14, 2020, 10:30 a.m.
Hi Guys,

  Traditionally the BFD library has supported a one-to-one relationship
  between relocation sections and the sections that they relocate.  The
  ELF portions of the library do support a two-to-one relationship if
  one of the reloc sections uses REL relocs and the other uses RELA
  relocs, but that is a bit of a hack.  The ELF standard however does
  not prohibit a many-to-one relationship and I now have a need to
  support such a feature[1].

  Initially I looked at reworking the internals of the BFD library to
  accommodate a list of reloc sections attached to a normal section but
  this quickly spiralled out of control and broke far too many things.
  So I have now come up with an implementation that adds a few new
  functions to the elf_backend_data structure, and some hooks to call
  these functions.  This encapsulates the new code and helps separate it
  from the rest of the library.  It does however mean that, currently,
  these extra reloc sections are not processed when linking.  For the
  use case I have in mind however (objcopy/strip) this does not matter.

  As part of this process I also discovered that the BFD library would
  unconditionally convert OS-specific and PROC-specific section indices
  into SHN_ABS indices when writing out symbol tables, so I also added a
  hook to allow backends to override this behaviour if they so desire.

  I am hoping to add the code to the upstream sources, but I would like
  seek out any comments or suggestions that people might have before I
  do so.  So ... any thoughts ?

Cheers
  Nick

PS. During testing I also encountered a build failure with the score
  architecture because that defines target_size=64 during configuration
  but it does not include the 64-bit generic elf functions found in
  elf64.o.  So the patch fixes that as well.

[1] This is to support kernel live patch modules used by Fedora and RHEL
  and possibly other distributions too.

bfd/ChangeLog
2020-02-14  Nick Clifton  <nickc@redhat.com>

	* elf-bfd.h (struct elf_backend_data): Add new fields:
	init_secondary_reloc_section, slurp_secondary_reloc_section,
	write_secondary_reloc_section, symbol_section_index.
	(_bfd_elf_init_secondary_reloc_section): Prototype.
	(_bfd_elf_slurp_secondary_reloc_section): Prototype.
	(_bfd_elf_write_secondary_reloc_section): Prototype.
	(_bfd_elf_symbol_section_index): Prototype.
	* elf.c ( bfd_section_from_shdr): Invoke the new
	init_secondary_reloc_section backend function, if defined, when a
	second reloc section is encountered.
	(swap_out_syms): Invoke the new symbol_section_index function, if
	defined, when computing the section index of an OS/PROC specific
	symbol.
	(_bfd_elf_init_secondary_reloc_section): New function.
	(_bfd_elf_slurp_secondary_reloc_section): New function.
	(_bfd_elf_write_secondary_reloc_section): New function.
	(_bfd_elf_symbol_section_index): New function.
	(_bfd_elf_copy_special_section_fields): New function.
	* elfcode.h (elf_write_relocs): Invoke the new
	write_secondary_relocs function, if defined, in order to emit
	secondary relocs.
	(elf_slurp_reloc_table): Invoke the new slurp_secondary_relocs
	function, if defined, in order to read in secondary relocs.
	* elfxx-target.h (elf_backend_copy_special_section_fields):
	Provide a non-NULL default definition.
	(elf_backend_init_secondary_reloc_section): Likewise.
	(elf_backend_slurp_secondary_reloc_section): Likewise.
	(elf_backend_write_secondary_reloc_section): Likewise.
	(elf_backend_symbol_section_index): Likewise.
	(struct elf_backend_data elfNN_bed): Add initialisers for the new
	fields.
        * configure.ac (score_elf32_[bl]e_vec): Add elf64.lo
        * configure: Regenerate.

Comments

Alan Modra Feb. 18, 2020, 12:20 a.m. | #1
On Fri, Feb 14, 2020 at 10:30:47AM +0000, Nick Clifton wrote:
>   As part of this process I also discovered that the BFD library would

>   unconditionally convert OS-specific and PROC-specific section indices

>   into SHN_ABS indices when writing out symbol tables, so I also added a

>   hook to allow backends to override this behaviour if they so desire.


That sounds like a bug that should be fixed for all ELF targets and
not via a target hook.  Note how we shift section indices in the
reserved range from 0xff00..0xfffe to 0xffffff00..0xfffffffe in
elf_swap_symbol_in.  There may well be some target specific work
involved but likely only in targets that want the linker to create
such symbols.  Separate patch anyway, please.

> [1] This is to support kernel live patch modules used by Fedora and RHEL

>   and possibly other distributions too.


Can the need for multiple reloc sections be avoided?  Or at least
any processing on extra reloc sections?  The current support as such
for multiple reloc sections in bfd is by treating any extra reloc
sections as normal sections, ie. just a blob of data in the section.
Or at least that is what is supposed to happen.

Hmm, I see your patch updates reloc symbols in the extra reloc
sections.  Am I correct in guessing that the underlying problem is
that objcopy/strip renumber symbols?  And I suppose there isn't any
way to convince people not to objcopy/strip files with extra reloc
sections?  What about requiring those extra reloc sections only have
relocs using a zero symbol index?

-- 
Alan Modra
Australia Development Lab, IBM
Nick Clifton Feb. 19, 2020, 4:48 p.m. | #2
Hi Alan,

>>   As part of this process I also discovered that the BFD library would

>>   unconditionally convert OS-specific and PROC-specific section indices

>>   into SHN_ABS indices when writing out symbol tables,


> That sounds like a bug that should be fixed for all ELF targets and

> not via a target hook.


A good point - I will do that.



>> [1] This is to support kernel live patch modules used by Fedora and RHEL

>>   and possibly other distributions too.

> 

> Can the need for multiple reloc sections be avoided?  Or at least

> any processing on extra reloc sections? 


Not now.  The scheme has been in place for a couple of years now, so
I think that it is too late to change it.

> The current support as such

> for multiple reloc sections in bfd is by treating any extra reloc

> sections as normal sections, ie. just a blob of data in the section.

> Or at least that is what is supposed to happen.


I thought so too, but it turns out not to be so.  The extra reloc 
sections are dropped, rather than being preserved in transit from
input to output.
 
> Hmm, I see your patch updates reloc symbols in the extra reloc

> sections.  Am I correct in guessing that the underlying problem is

> that objcopy/strip renumber symbols?


Yup. :-)

>  And I suppose there isn't any

> way to convince people not to objcopy/strip files with extra reloc

> sections? 


No.  The actual issue came about because the kernel team are trying to
update their practices and rather than create and distribute these
live update modules by hand they are now trying to use the brew/koji
build systems.  Which use objcopy and strip from the binutils.  Which
corrupts the modules...

> What about requiring those extra reloc sections only have

> relocs using a zero symbol index?


No, they need specific symbol indicies in order for the relocations
to be applied by the kernel's live patcher...

Cheers
  Nick
Nick Clifton Feb. 19, 2020, 5:05 p.m. | #3
Hi Alan,

>>   As part of this process I also discovered that the BFD library would

>>   unconditionally convert OS-specific and PROC-specific section indices

>>   into SHN_ABS indices when writing out symbol tables,


> That sounds like a bug that should be fixed for all ELF targets and

> not via a target hook.


A good point - I will do that.



>> [1] This is to support kernel live patch modules used by Fedora and RHEL

>>   and possibly other distributions too.

> 

> Can the need for multiple reloc sections be avoided?  Or at least

> any processing on extra reloc sections? 


Not now.  The scheme has been in place for a couple of years now, so
I think that it is too late to change it.

> The current support as such

> for multiple reloc sections in bfd is by treating any extra reloc

> sections as normal sections, ie. just a blob of data in the section.

> Or at least that is what is supposed to happen.


I thought so too, but it turns out not to be so.  The extra reloc 
sections are dropped, rather than being preserved in transit from
input to output.
 
> Hmm, I see your patch updates reloc symbols in the extra reloc

> sections.  Am I correct in guessing that the underlying problem is

> that objcopy/strip renumber symbols?


Yup. :-)

>  And I suppose there isn't any

> way to convince people not to objcopy/strip files with extra reloc

> sections? 


No.  The actual issue came about because the kernel team are trying to
update their practices and rather than create and distribute these
live update modules by hand they are now trying to use the brew/koji
build systems.  Which use objcopy and strip from the binutils.  Which
corrupts the modules...

> What about requiring those extra reloc sections only have

> relocs using a zero symbol index?


No, they need specific symbol indicies in order for the relocations
to be applied by the kernel's live patcher...

Cheers
  Nick
Nick Clifton Feb. 20, 2020, 1:11 p.m. | #4
Hi Alan,

> On Fri, Feb 14, 2020 at 10:30:47AM +0000, Nick Clifton wrote:

>>   As part of this process I also discovered that the BFD library would

>>   unconditionally convert OS-specific and PROC-specific section indices

>>   into SHN_ABS indices when writing out symbol tables,


> That sounds like a bug that should be fixed for all ELF targets and

> not via a target hook.  

I have checked in this patch to resolve this particular problem.  I do not 
think that the code has to worry about the sign extension of the special
values as this has been handled by the definitions in elf/internal.h.

Cheers
  Nick

bfd/ChangeLog
2020-02-20  Nick Clifton  <nickc@redhat.com>

	* elf-bfd.h (struct elf_backend_data): Add symbol_section_index
	callback.
	* elfxx-target.h (elf_backend_symbol_section_index): Provide
	default definition.
	(elfNN_bed): Initialise the symbol_section_index field.
	* elf.c (swap_out_syms): Call symbol_section_index, if defined, on
	OS and PROC specific section indicies.  Warn if converting other
	reserved incidies to SHN_ABS.
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index cbbba153f4..7d36e23ea1 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -1503,6 +1503,12 @@ struct elf_backend_data
   /* Opcode representing no unwind.  */
   int (*cant_unwind_opcode) (struct bfd_link_info *);
 
+  /* Called when emitting an ELF symbol whoes input version had an
+     ST_SHNDX field set to a value in the range SHN_LOPROC..SHN_HIOS.
+     Returns the value to be installed in the ST_SHNDX field of the
+     emitted symbol.  If not defined, the value is left unchanged.  */
+  unsigned int (*symbol_section_index) (bfd *, elf_symbol_type *);
+  
   /* This is non-zero if static TLS segments require a special alignment.  */
   unsigned static_tls_alignment;
 
diff --git a/bfd/elf.c b/bfd/elf.c
index deb93b0a5a..4342e84752 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -8194,9 +8194,26 @@ swap_out_syms (bfd *abfd,
 		  if (elf_symtab_shndx_list (abfd))
 		    shndx = elf_symtab_shndx_list (abfd)->ndx;
 		  break;
-		default:
+		case SHN_COMMON:
+		case SHN_ABS:
 		  shndx = SHN_ABS;
 		  break;
+		default:
+		  if (shndx >= SHN_LOPROC && shndx <= SHN_HIOS)
+		    {
+		      if (bed->symbol_section_index)
+			shndx = bed->symbol_section_index (abfd, type_ptr);
+		      /* Otherwise just leave the index alone.  */
+		    }
+		  else
+		    {
+		      if (shndx > SHN_HIOS && shndx < SHN_HIRESERVE)
+			_bfd_error_handler (_("%pB: \
+Unable to handle section index %x in ELF symbol.  Using ABS instead."),
+					  abfd, shndx);
+		      shndx = SHN_ABS;
+		    }
+		  break;
 		}
 	    }
 	  else
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index caca83f5c9..e9cac0a535 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -769,6 +769,10 @@
 #define elf_backend_cant_unwind_opcode 0
 #endif
 
+#ifndef elf_backend_symbol_section_index
+#define elf_backend_symbol_section_index NULL
+#endif
+ 
 #ifndef elf_match_priority
 #define elf_match_priority \
   (ELF_ARCH == bfd_arch_unknown ? 2 : ELF_OSABI == ELFOSABI_NONE ? 1 : 0)
@@ -895,6 +899,7 @@ static struct elf_backend_data elfNN_bed =
   elf_backend_fixup_gnu_properties,
   elf_backend_compact_eh_encoding,
   elf_backend_cant_unwind_opcode,
+  elf_backend_symbol_section_index,
   elf_backend_static_tls_alignment,
   elf_backend_stack_align,
   elf_backend_strtab_flags,
Fangrui Song Feb. 24, 2020, 6:38 a.m. | #5
Hi Nick,

On 2020-02-19, Nick Clifton wrote:
>Hi Alan,

>

>>>   As part of this process I also discovered that the BFD library would

>>>   unconditionally convert OS-specific and PROC-specific section indices

>>>   into SHN_ABS indices when writing out symbol tables,

>

>> That sounds like a bug that should be fixed for all ELF targets and

>> not via a target hook.

>

>A good point - I will do that.

>

>

>

>>> [1] This is to support kernel live patch modules used by Fedora and RHEL

>>>   and possibly other distributions too.

>>

>> Can the need for multiple reloc sections be avoided?  Or at least

>> any processing on extra reloc sections?


(I wanted to ask the same question...)

>Not now.  The scheme has been in place for a couple of years now, so

>I think that it is too late to change it.


>> The current support as such

>> for multiple reloc sections in bfd is by treating any extra reloc

>> sections as normal sections, ie. just a blob of data in the section.

>> Or@least that is what is supposed to happen.

>

>I thought so too, but it turns out not to be so.  The extra reloc

>sections are dropped, rather than being preserved in transit from

>input to output.

>

>> Hmm, I see your patch updates reloc symbols in the extra reloc

>> sections.  Am I correct in guessing that the underlying problem is

>> that objcopy/strip renumber symbols?

>

>Yup. :-)

>

>>  And I suppose there isn't any

>> way to convince people not to objcopy/strip files with extra reloc

>> sections?

>

>No.  The actual issue came about because the kernel team are trying to

>update their practices and rather than create and distribute these

>live update modules by hand they are now trying to use the brew/koji

>build systems.  Which use objcopy and strip from the binutils.  Which

>corrupts the modules...

>

>> What about requiring those extra reloc sections only have

>> relocs using a zero symbol index?

>

>No, they need specific symbol indicies in order for the relocations

>to be applied by the kernel's live patcher...

>

>Cheers

>  Nick


Where can we find more information about the kernel side implementation,
if it is open?
Nick Clifton Feb. 24, 2020, 12:16 p.m. | #6
Hi Fangrui,

> Where can we find more information about the kernel side implementation,

> if it is open?


Good question.  I believe that it is being used in Fedora, so it should
be open.  I will ask the kernel team for any pointers.

Cheers
  Nick
Nick Clifton Feb. 24, 2020, 5:11 p.m. | #7
Hi Fangrui,

> Where can we find more information about the kernel side implementation,

> if it is open?


You might like to start with these articles:

  https://access.redhat.com/articles/2475321
  https://www.redhat.com/en/blog/live-kernel-patching-update

There is also a mailing list where you can post questions:

  live-patching@vger.kernel.org

FYI Joe Lawrence - the kernel engineer who has been looking 
into this - did try using the LLVM toolset but he found that
it did not like their use of reserved st_shndx symbol values
and the processor specific values in the link field in their 
relocation sections...

Cheers
  Nick

Patch

diff --git a/bfd/configure.ac b/bfd/configure.ac
index af4d4b8c13..3426ded094 100644
--- a/bfd/configure.ac
+++ b/bfd/configure.ac
@@ -619,8 +619,8 @@  do
     rx_elf32_linux_le_vec)	 tb="$tb elf32-rx.lo elf32.lo $elf" ;;
     s390_elf32_vec)		 tb="$tb elf32-s390.lo elf32.lo $elf" ;;
     s390_elf64_vec)		 tb="$tb elf64-s390.lo elf64.lo $elf"; target_size=64 ;;
-    score_elf32_be_vec)	 tb="$tb elf32-score.lo elf32-score7.lo elf32.lo $elf"; want64=true; target_size=64 ;;
-    score_elf32_le_vec)	 tb="$tb elf32-score.lo elf32-score7.lo elf32.lo $elf"; want64=true; target_size=64 ;;
+    score_elf32_be_vec)	 tb="$tb elf32-score.lo elf32-score7.lo elf32.lo elf64.lo $elf"; want64=true; target_size=64 ;;
+    score_elf32_le_vec)	 tb="$tb elf32-score.lo elf32-score7.lo elf32.lo elf64.lo $elf"; want64=true; target_size=64 ;;
     sh_coff_vec)		 tb="$tb coff-sh.lo $coff" ;;
     sh_coff_le_vec)		 tb="$tb coff-sh.lo $coff" ;;
     sh_coff_small_vec)		 tb="$tb coff-sh.lo $coff" ;;
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index cbbba153f4..a930d8278d 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -1503,6 +1503,19 @@  struct elf_backend_data
   /* Opcode representing no unwind.  */
   int (*cant_unwind_opcode) (struct bfd_link_info *);
 
+  /* Called when a section has extra reloc sections.  */
+  bfd_boolean (*init_secondary_reloc_section) (bfd *, Elf_Internal_Shdr *, const char *, unsigned int);
+
+  /* Called when after loading the normal relocs for a section.  */
+  bfd_boolean (*slurp_secondary_relocs) (bfd *, asection *, asymbol **);
+
+  /* Called after writing the normal relocs for a section.  */
+  bfd_boolean (*write_secondary_relocs) (bfd *, asection *);
+
+  /* Called to return the value to set in the ST_SHNDX field of an ELF symbol
+     from an iternal symbol which does not map to any known section.  */
+  unsigned int (*symbol_section_index) (bfd *, elf_symbol_type *);
+  
   /* This is non-zero if static TLS segments require a special alignment.  */
   unsigned static_tls_alignment;
 
@@ -2830,6 +2843,19 @@  extern bfd_vma elf32_r_sym (bfd_vma);
 
 extern bfd_boolean is_debuginfo_file (bfd *);
 
+
+extern bfd_boolean _bfd_elf_init_secondary_reloc_section
+  (bfd *, Elf_Internal_Shdr *, const char *, unsigned int);
+extern bfd_boolean _bfd_elf_slurp_secondary_reloc_section
+  (bfd *, asection *, asymbol **);
+extern bfd_boolean _bfd_elf_copy_special_section_fields
+  (const bfd *, bfd *, const Elf_Internal_Shdr *, Elf_Internal_Shdr *);
+extern bfd_boolean _bfd_elf_write_secondary_reloc_section
+  (bfd *, asection *);
+extern unsigned int _bfd_elf_symbol_section_index
+  (bfd *, elf_symbol_type *);
+
+
 /* Large common section.  */
 extern asection _bfd_elf_large_com_section;
 
diff --git a/bfd/elf.c b/bfd/elf.c
index c85face630..2c1b6fe67d 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -1619,7 +1619,7 @@  _bfd_elf_copy_private_bfd_data (bfd *ibfd, bfd *obfd)
 	  /* Final attempt.  Call the backend copy function
 	     with a NULL input section.  */
 	  if (bed->elf_backend_copy_special_section_fields != NULL)
-	    bed->elf_backend_copy_special_section_fields (ibfd, obfd, NULL, oheader);
+	    (void) bed->elf_backend_copy_special_section_fields (ibfd, obfd, NULL, oheader);
 	}
     }
 
@@ -2468,13 +2468,17 @@  bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
 	   sections.  */
 	if (*p_hdr != NULL)
 	  {
-	    _bfd_error_handler
-	      /* xgettext:c-format */
-	      (_("%pB: warning: multiple relocation sections for section %pA \
-found - ignoring all but the first"),
-	       abfd, target_sect);
+	    if (bed->init_secondary_reloc_section == NULL
+		|| ! bed->init_secondary_reloc_section (abfd, hdr, name, shindex))
+	      {
+		_bfd_error_handler
+		  /* xgettext:c-format */
+		  (_("%pB: warning: secondary relocation section '%s' for section %pA found - ignoring"),
+		   abfd, name, target_sect);
+	      }
 	    goto success;
 	  }
+
 	hdr2 = (Elf_Internal_Shdr *) bfd_alloc (abfd, sizeof (*hdr2));
 	if (hdr2 == NULL)
 	  goto fail;
@@ -8199,9 +8203,20 @@  error_return:
 		  if (elf_symtab_shndx_list (abfd))
 		    shndx = elf_symtab_shndx_list (abfd)->ndx;
 		  break;
-		default:
+		case SHN_COMMON:
+		case SHN_ABS:
 		  shndx = SHN_ABS;
 		  break;
+		default:
+		  if (bed->symbol_section_index)
+		    shndx = bed->symbol_section_index (abfd, type_ptr);
+		  else
+		    {
+		      _bfd_error_handler (_("%pB: Unable to handle section index %x in ELF symbol.  Using ABS instead. (%x)"),
+					  abfd, shndx, SHN_COMMON);
+		      shndx = SHN_ABS;
+		    }
+		  break;
 		}
 	    }
 	  else
@@ -12289,3 +12304,355 @@  _bfd_elf_maybe_function_sym (const asymbol *sym, asection *sec,
     size = 1;
   return size;
 }
+
+/* Set to non-zero to enable some debug messages.  */
+#define DEBUG_SECONDARY_RELOCS	 0
+
+/* An internal-to-the-bfd-library only section type
+   used to indicate a cached secondary reloc section.  */
+#define SHT_SECONDARY_RELOC	 (SHT_LOOS + SHT_RELA)
+
+/* Create a BFD section to hold a secondary reloc section.  */
+
+bfd_boolean
+_bfd_elf_init_secondary_reloc_section (bfd * abfd,
+				       Elf_Internal_Shdr *hdr,
+				       const char * name,
+				       unsigned int shindex)
+{
+  /* We only support RELA secondary relocs.  */
+  if (hdr->sh_type != SHT_RELA)
+    return FALSE;
+
+#if DEBUG_SECONDARY_RELOCS
+  fprintf (stderr, "secondary reloc section %s encountered\n", name);
+#endif
+  hdr->sh_type = SHT_SECONDARY_RELOC;
+  return _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex);
+}
+
+/* Read in any secondary relocs associated with SEC.  */
+
+bfd_boolean
+_bfd_elf_slurp_secondary_reloc_section (bfd *      abfd,
+					asection * sec,
+					asymbol ** symbols)
+{
+  const struct elf_backend_data * const ebd = get_elf_backend_data (abfd);
+  asection * relsec;
+  bfd_boolean result = TRUE;
+  bfd_vma (*r_sym) (bfd_vma);
+
+  BFD_ASSERT (ebd->elf_info_to_howto != NULL);
+
+#ifdef BFD64
+  if (bfd_arch_bits_per_address (abfd) != 32)
+    r_sym = elf64_r_sym;
+  else
+#endif
+    r_sym = elf32_r_sym;
+  
+  /* Discover if there are any secondary reloc sections
+     associated with SEC.  */
+  for (relsec = abfd->sections; relsec != NULL; relsec = relsec->next)
+    {
+      Elf_Internal_Shdr * hdr = & elf_section_data (relsec)->this_hdr;
+
+      if (hdr->sh_type == SHT_SECONDARY_RELOC
+	  && hdr->sh_info == (unsigned) elf_section_data (sec)->this_idx)
+	{
+	  bfd_byte * native_relocs;
+	  bfd_byte * native_reloc;
+	  arelent * internal_relocs;
+	  arelent * internal_reloc;
+	  unsigned int i;
+	  unsigned int entsize;
+	  unsigned int symcount;
+	  unsigned int reloc_count;
+
+#if DEBUG_SECONDARY_RELOCS
+	  fprintf (stderr, "read secondary relocs for %s from %s\n", sec->name, relsec->name);
+#endif
+	  entsize = hdr->sh_entsize;
+
+	  native_relocs = bfd_malloc (hdr->sh_size);
+	  if (native_relocs == NULL)
+	    {
+	      result = FALSE;
+	      continue;
+	    }
+
+	  reloc_count = NUM_SHDR_ENTRIES (hdr);
+	  internal_relocs = (arelent *) bfd_alloc2 (abfd, reloc_count, sizeof (arelent));
+	  if (internal_relocs == NULL)
+	    {
+	      free (native_relocs);
+	      result = FALSE;
+	      continue;
+	    }
+
+	  if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0
+	      || (bfd_bread (native_relocs, hdr->sh_size, abfd) != hdr->sh_size))
+	    {
+	      free (native_relocs);
+	      free (internal_relocs);
+	      result = FALSE;
+	      continue;
+	    }
+
+	  symcount = bfd_get_symcount (abfd);
+
+	  for (i = 0, internal_reloc = internal_relocs, native_reloc = native_relocs;
+	       i < reloc_count;
+	       i++, internal_reloc++, native_reloc += entsize)
+	    {
+	      bfd_boolean res;
+	      Elf_Internal_Rela rela;
+
+	      ebd->s->swap_reloca_in (abfd, native_reloc, & rela);
+
+	      /* The address of an ELF reloc is section relative for an object
+		 file, and absolute for an executable file or shared library.
+		 The address of a normal BFD reloc is always section relative,
+		 and the address of a dynamic reloc is absolute..  */
+	      if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
+		internal_reloc->address = rela.r_offset;
+	      else
+		internal_reloc->address = rela.r_offset - sec->vma;
+
+	      if (r_sym (rela.r_info) == STN_UNDEF)
+		{
+		  /* FIXME: This and the error case below mean that we
+		     have a symbol on relocs that is not elf_symbol_type.  */
+		  internal_reloc->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
+		}
+	      else if (r_sym (rela.r_info) > symcount)
+		{
+		  _bfd_error_handler
+		    /* xgettext:c-format */
+		    (_("%pB(%pA): relocation %d has invalid symbol index %ld"),
+		     abfd, sec, i, (long) r_sym (rela.r_info));
+		  bfd_set_error (bfd_error_bad_value);
+		  internal_reloc->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
+		  result = FALSE;
+		}
+	      else
+		{
+		  asymbol **ps;
+
+		  ps = symbols + r_sym (rela.r_info) - 1;
+
+		  internal_reloc->sym_ptr_ptr = ps;
+		  /* Make sure that this symbol is not removed by strip.  */
+		  (*ps)->flags |= BSF_KEEP;
+		}
+
+	      internal_reloc->addend = rela.r_addend;
+
+	      res = ebd->elf_info_to_howto (abfd, internal_reloc, & rela);
+	      if (! res || internal_reloc->howto == NULL)
+		{
+#if DEBUG_SECONDARY_RELOCS
+		  fprintf (stderr, "there is no howto associated with reloc %lx\n", rela.r_info);
+#endif
+		  result = FALSE;
+		}
+	    }
+
+	  free (native_relocs);
+	  /* Store the internal relocs.  */
+	  elf_section_data (relsec)->sec_info = internal_relocs;
+	}
+    }
+
+  return result;
+}
+
+/* Set the ELF section header fields of an output secondary reloc section.  */
+
+bfd_boolean
+_bfd_elf_copy_special_section_fields (const bfd *               ibfd ATTRIBUTE_UNUSED,
+				      bfd *                     obfd ATTRIBUTE_UNUSED,
+				      const Elf_Internal_Shdr * isection,
+				      Elf_Internal_Shdr *       osection)
+{
+  if (isection->sh_type != SHT_SECONDARY_RELOC)
+    return TRUE;
+
+  if (isection == NULL)
+    return FALSE;
+
+  asection * isec = isection->bfd_section;
+  if (isec == NULL)
+    return FALSE;
+
+  asection * osec = osection->bfd_section;
+  if (osec == NULL)
+    return FALSE;
+
+  BFD_ASSERT (elf_section_data (osec)->sec_info == NULL);
+  elf_section_data (osec)->sec_info = elf_section_data (isec)->sec_info;
+  osection->sh_type = SHT_RELA;
+  osection->sh_link = elf_onesymtab (obfd);
+  if (osection->sh_link == 0)
+    {
+      /* There is no symbol table - we are hosed...  */
+      _bfd_error_handler
+	/* xgettext:c-format */
+	(_("%pB(%pA): link section cannot be set because the output file does not have a symbol table"),
+	obfd, osec);
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
+
+  /* Find the output section that corresponds to the isection's sh_info link.  */
+  BFD_ASSERT (isection->sh_info > 0 && isection->sh_info < elf_numsections (ibfd));
+  isection = elf_elfsections (ibfd)[isection->sh_info];
+
+  BFD_ASSERT (isection != NULL);
+  BFD_ASSERT (isection->bfd_section != NULL);
+  BFD_ASSERT (isection->bfd_section->output_section != NULL);
+  osection->sh_info = elf_section_data (isection->bfd_section->output_section)->this_idx;
+
+#if DEBUG_SECONDARY_RELOCS
+  fprintf (stderr, "update header of %s, sh_link = %u, sh_info = %u\n",
+	   osec->name, osection->sh_link, osection->sh_info);
+#endif
+
+  return TRUE;
+}
+
+/* Write out a secondary reloc section.  */
+
+bfd_boolean
+_bfd_elf_write_secondary_reloc_section (bfd *abfd, asection *sec)
+{
+  const struct elf_backend_data * const ebd = get_elf_backend_data (abfd);
+  bfd_vma addr_offset;
+  asection * relsec;
+  bfd_vma (*r_info) (bfd_vma, bfd_vma);
+
+#ifdef BFD64
+  if (bfd_arch_bits_per_address (abfd) != 32)
+    r_info = elf64_r_info;
+  else
+#endif
+    r_info = elf32_r_info;
+
+  if (sec == NULL)
+    return FALSE;
+
+  /* The address of an ELF reloc is section relative for an object
+     file, and absolute for an executable file or shared library.
+     The address of a BFD reloc is always section relative.  */
+  addr_offset = 0;
+  if ((abfd->flags & (EXEC_P | DYNAMIC)) != 0)
+    addr_offset = sec->vma;
+
+  /* Discover if there are any secondary reloc sections
+     associated with SEC.  */
+  for (relsec = abfd->sections; relsec != NULL; relsec = relsec->next)
+    {
+      const struct bfd_elf_section_data * const esd = elf_section_data (relsec);
+      Elf_Internal_Shdr * const hdr = (Elf_Internal_Shdr *) & esd->this_hdr;
+
+      if (hdr->sh_type == SHT_RELA
+	  && hdr->sh_info == (unsigned) elf_section_data (sec)->this_idx)
+	{
+	  asymbol *    last_sym;
+	  int          last_sym_idx;
+	  unsigned int reloc_count;
+	  unsigned int idx;
+	  arelent *    src_irel;
+	  bfd_byte *   dst_rela;
+
+	  BFD_ASSERT (hdr->contents == NULL);
+
+	  reloc_count = hdr->sh_size / hdr->sh_entsize;
+	  BFD_ASSERT (reloc_count > 0);
+
+	  hdr->contents = bfd_alloc (abfd, hdr->sh_size);
+	  if (hdr->contents == NULL)
+	    continue;
+
+#if DEBUG_SECONDARY_RELOCS
+	  fprintf (stderr, "write %u secondary relocs for %s from %s\n", reloc_count, sec->name, relsec->name);
+#endif
+	  last_sym = NULL;
+	  last_sym_idx = 0;
+	  dst_rela = hdr->contents;
+	  src_irel = (arelent *) esd->sec_info;
+	  BFD_ASSERT (src_irel != NULL);
+
+	  for (idx = 0; idx < reloc_count; idx++, dst_rela += hdr->sh_entsize)
+	    {
+	      Elf_Internal_Rela src_rela;
+	      arelent *ptr;
+	      asymbol *sym;
+	      int n;
+
+	      ptr = src_irel + idx;
+	      sym = *ptr->sym_ptr_ptr;
+
+	      if (sym == last_sym)
+		n = last_sym_idx;
+	      else
+		{
+		  last_sym = sym;
+		  n = _bfd_elf_symbol_from_bfd_symbol (abfd, & sym);
+		  if (n < 0)
+		    {
+#if DEBUG_SECONDARY_RELOCS
+		      fprintf (stderr, "failed to find symbol %s whilst rewriting relocs\n",
+			       sym->name);
+#endif
+		      /* FIXME: Signal failure somehow.  */
+		      n = 0;
+		    }
+		  last_sym_idx = n;
+		}
+
+	      if ((*ptr->sym_ptr_ptr)->the_bfd != NULL
+		  && (*ptr->sym_ptr_ptr)->the_bfd->xvec != abfd->xvec
+		  && ! _bfd_elf_validate_reloc (abfd, ptr))
+		{
+#if DEBUG_SECONDARY_RELOCS
+		  fprintf (stderr, "symbol %s is not in the output bfd\n",
+			   sym->name);
+#endif
+		  /* FIXME: Signal failure somehow.  */
+		  n = 0;
+		}
+
+	      if (ptr->howto == NULL)
+		{
+#if DEBUG_SECONDARY_RELOCS
+		  fprintf (stderr, "reloc for symbol %s does not have a howto associated with it\n",
+			   sym->name);
+#endif
+		  /* FIXME: Signal failure somehow.  */
+		  n = 0;
+		}
+
+	      src_rela.r_offset = ptr->address + addr_offset;
+	      src_rela.r_info = r_info (n, ptr->howto->type);
+	      src_rela.r_addend = ptr->addend;
+	      ebd->s->swap_reloca_out (abfd, &src_rela, dst_rela);
+	    }
+	}
+    }
+
+  return TRUE;
+}
+
+/* Preserve any OS or PROCESSOR specific section indicies.  */
+
+unsigned int
+_bfd_elf_symbol_section_index (bfd * abfd ATTRIBUTE_UNUSED,
+			       elf_symbol_type * sym)
+{
+  unsigned int shndx = sym->internal_elf_sym.st_shndx;
+
+  /* Preserve special section indicies.  */
+  return shndx >= SHN_LORESERVE ? shndx : SHN_ABS;
+}
diff --git a/bfd/elfcode.h b/bfd/elfcode.h
index e1e89cf78f..27f6b81b1e 100644
--- a/bfd/elfcode.h
+++ b/bfd/elfcode.h
@@ -865,6 +865,7 @@  elf_object_p (bfd *abfd)
 void
 elf_write_relocs (bfd *abfd, asection *sec, void *data)
 {
+  const struct elf_backend_data * const bed = get_elf_backend_data (abfd);
   bfd_boolean *failedp = (bfd_boolean *) data;
   Elf_Internal_Shdr *rela_hdr;
   bfd_vma addr_offset;
@@ -980,6 +981,13 @@  elf_write_relocs (bfd *abfd, asection *sec, void *data)
       src_rela.r_addend = ptr->addend;
       (*swap_out) (abfd, &src_rela, dst_rela);
     }
+
+  if (bed->write_secondary_relocs != NULL)
+    if (! bed->write_secondary_relocs (abfd, sec))
+      {
+	*failedp = TRUE;
+	return;
+      }
 }
 
 /* Write out the program headers.  */
@@ -1281,7 +1289,10 @@  elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bfd_boolean dynamic)
 		{
 		  /* This symbol is in a section for which we did not
 		     create a BFD section.  Just use bfd_abs_section,
-		     although it is wrong.  FIXME.  */
+		     although it is wrong.  FIXME.  Note - there is
+		     code in elf.c:swap_out_syms that calls
+		     symbol_section_index() in the elf backend for
+		     cases like this.  */
 		  sym->symbol.section = bfd_abs_section_ptr;
 		}
 	    }
@@ -1509,6 +1520,7 @@  elf_slurp_reloc_table (bfd *abfd,
 		       asymbol **symbols,
 		       bfd_boolean dynamic)
 {
+  const struct elf_backend_data * const bed = get_elf_backend_data (abfd);
   struct bfd_elf_section_data * const d = elf_section_data (asect);
   Elf_Internal_Shdr *rel_hdr;
   Elf_Internal_Shdr *rel_hdr2;
@@ -1571,6 +1583,10 @@  elf_slurp_reloc_table (bfd *abfd,
 					      symbols, dynamic))
     return FALSE;
 
+  if (bed->slurp_secondary_relocs != NULL
+      && ! bed->slurp_secondary_relocs (abfd, asect, symbols))
+    return FALSE;
+
   asect->relocation = relents;
   return TRUE;
 }
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index caca83f5c9..8fe21eb671 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -758,7 +758,7 @@ 
 #endif
 
 #ifndef elf_backend_copy_special_section_fields
-#define elf_backend_copy_special_section_fields NULL
+#define elf_backend_copy_special_section_fields _bfd_elf_copy_special_section_fields
 #endif
 
 #ifndef elf_backend_compact_eh_encoding
@@ -766,7 +766,23 @@ 
 #endif
 
 #ifndef elf_backend_cant_unwind_opcode
-#define elf_backend_cant_unwind_opcode 0
+#define elf_backend_cant_unwind_opcode NULL
+#endif
+
+#ifndef elf_backend_init_secondary_reloc_section
+#define elf_backend_init_secondary_reloc_section _bfd_elf_init_secondary_reloc_section
+#endif
+
+#ifndef elf_backend_slurp_secondary_reloc_section
+#define elf_backend_slurp_secondary_reloc_section _bfd_elf_slurp_secondary_reloc_section
+#endif
+
+#ifndef elf_backend_write_secondary_reloc_section
+#define elf_backend_write_secondary_reloc_section _bfd_elf_write_secondary_reloc_section
+#endif
+
+#ifndef elf_backend_symbol_section_index
+#define elf_backend_symbol_section_index _bfd_elf_symbol_section_index
 #endif
 
 #ifndef elf_match_priority
@@ -895,6 +911,10 @@  static struct elf_backend_data elfNN_bed =
   elf_backend_fixup_gnu_properties,
   elf_backend_compact_eh_encoding,
   elf_backend_cant_unwind_opcode,
+  elf_backend_init_secondary_reloc_section,
+  elf_backend_slurp_secondary_reloc_section,
+  elf_backend_write_secondary_reloc_section,
+  elf_backend_symbol_section_index,
   elf_backend_static_tls_alignment,
   elf_backend_stack_align,
   elf_backend_strtab_flags,