public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] ld: Check ELF relocs before allocation
@ 2021-12-28  2:48 H.J. Lu
  2021-12-29  7:46 ` Alan Modra
  0 siblings, 1 reply; 4+ messages in thread
From: H.J. Lu @ 2021-12-28  2:48 UTC (permalink / raw)
  To: binutils

To prepare for DT_RELR implemenation, delay checking ELF relocations
before allocation so that all input sections have been mapped to output
sections when relocations are checked.  This is only enabled for x86
targets.

Since relocations are checked after __start, __stop, .startof. and
.sizeof. symbols have been finalized on x86, __[start|stop]_SECNAME for
--gc-sections -z start-stop-gc are now zero when all SECNAME sections
been garbage collected.  This is no need for elf_x86_start_stop_gc_p.

bfd/

	* aout-target.h (MY_bfd_link_make_reloc_sections): New.
	* binary.c (binary_bfd_link_make_reloc_sections): Likewise.
	* coff-alpha.c (_bfd_ecoff_bfd_link_make_reloc_sections):
	Likewise.
	* coff-rs6000.c (_bfd_xcoff_bfd_link_make_reloc_sections):
	Likewise.
	* coffcode.h (coff_bfd_link_make_reloc_sections): Likewise.
	* i386msdos.c (msdos_bfd_link_make_reloc_sections): Likewise.
	* ihex.c (ihex_bfd_link_make_reloc_sections): Likewise.
	* libbfd-in.h (_bfd_nolink_bfd_link_make_reloc_sections):
	Likewise.
	* libecoff.h (_bfd_ecoff_bfd_link_make_reloc_sections): Likewise.
	* linker.c (bfd_link_make_reloc_sections): Likewise.
	(_bfd_generic_link_make_reloc_sections): Likewise.
	* mach-o-target.c (bfd_mach_o_bfd_link_make_reloc_sections):
	Likewise.
	* mmo.c (mmo_bfd_link_make_reloc_sections): Likewise.
	* pef.c (bfd_pef_bfd_link_make_reloc_sections): Likewise.
	* plugin.c (bfd_plugin_bfd_link_make_reloc_sections): Likewise.
	* ppcboot.c (ppcboot_bfd_link_make_reloc_sections): Likewise.
	* som.c (som_bfd_link_make_reloc_sections): Likewise.
	* srec.c (srec_bfd_link_make_reloc_sections): Likewise.
	* tekhex.c (tekhex_bfd_link_make_reloc_sections): Likewise.
	* vms-alpha.c (alpha_vms_bfd_link_make_reloc_sections): Likewise.
	* xsym.c (bfd_sym_bfd_link_make_reloc_sections): Likewise.
	* coff64-rs6000.c (rs6000_xcoff64_vec): Add
	_bfd_generic_link_make_reloc_sections.
	(rs6000_xcoff64_aix_vec): Likewise.
	* elf-bfd.h (elf_backend_data): Add make_reloc_section.
	(_bfd_elf_link_make_reloc_sections): New.
	* elf32-i386.c (elf_i386_convert_load_reloc): Don't call
	elf_x86_start_stop_gc_p.
	(elf_i386_check_relocs): Don't call
	_bfd_elf_make_dynamic_reloc_section.
	(elf_i386_make_reloc_section): New.
	(elf_backend_make_reloc_section): Likewise.
	* elf64-x86-64.c (elf_x86_64_convert_load_reloc): Likewise.
	(elf_x86_64_check_relocs): Don't call
	_bfd_elf_make_dynamic_reloc_section.
	(elf_x86_64_make_reloc_section): New.
	(elf_backend_make_reloc_section): Likewise.
	* elflink.c (_bfd_elf_link_check_relocs_or_make_reloc_sections):
	New.  Renamed and modified from _bfd_elf_link_check_relocs.
	(_bfd_elf_link_check_relocs): New.
	(_bfd_elf_link_make_reloc_sections): Likewise.
	* elfxx-target.h (bfd_elfNN_bfd_link_make_reloc_sections): New.
	(elf_backend_make_reloc_section): Likewise.
	(elfNN_bed): Add elf_backend_make_reloc_section.
	* elfxx-x86.h (bfd_elf64_bfd_link_make_reloc_sections): Likewise.
	(bfd_elf32_bfd_link_make_reloc_sections): Likewise.
	(elf_x86_start_stop_gc_p): Removed.
	* targets.c (BFD_JUMP_TABLE_LINK): Add
	NAME##_bfd_link_make_reloc_sections.
	(bfd_target): Add _bfd_link_make_reloc_sections.
	* bfd-in2.h: Regenerate.
	* libbfd.h: Likewise.

include/

	* bfdlink.h (bfd_link_check_relocs_phase): New enum.
	(bfd_link_info): Rename check_relocs_after_open_input to
	bfd_link_check_relocs_phase.

ld/

	* ldlang.c (lang_check_relocs): Removed.
	(check_relocs): New.
	(lang_check_relocs_after_open_input): Likewise.
	(lang_check_relocs_before_allocation): Likewise.
	(lang_process): Call lang_check_relocs_after_open_input instead
	of lang_check_relocs.  Don't call ldemul_after_check_relocs.
	Call lang_check_relocs_before_allocation before calling
	ldemul_before_allocation.
	* emulparams/elf32_x86_64.sh (CHECK_RELOCS_PHASE): New.
	* emulparams/elf_i386.sh (CHECK_RELOCS_PHASE): Likewise.
	* emulparams/elf_i386_be.sh (CHECK_RELOCS_PHASE): Likewise.
	* emulparams/elf_i386_ldso.sh (CHECK_RELOCS_PHASE): Likewise.
	* emulparams/elf_i386_vxworks.sh (CHECK_RELOCS_PHASE): Likewise.
	* emulparams/elf_x86_64.sh (CHECK_RELOCS_PHASE): Likewise.
	* emultempl/aarch64elf.em (gld${EMULATION_NAME}_before_parse):
	Set link_info.check_relocs_phase instead of
	link_info.check_relocs_after_open_input.
	* emultempl/armelf.em (gld${EMULATION_NAME}_before_parse):
	Likewise.
	* emultempl/mmix-elfnmmo.em (gld${EMULATION_NAME}_before_parse):
	Likewise.
	* emultempl/scoreelf.em (gld${EMULATION_NAME}_before_parse):
	Likewise.
	* emultempl/elf.em (CHECK_RELOCS_PHASE): New.
	(gld${EMULATION_NAME}_before_parse): Set
	link_info.check_relocs_phase instead of
	link_info.check_relocs_after_open_input.
	* testsuite/ld-i386/pr27491-1a.d: Updated.
	* testsuite/ld-x86-64/pr27491-1a.d: Likewise.
---
 bfd/aout-target.h                   |   4 +
 bfd/bfd-in2.h                       |  10 +++
 bfd/binary.c                        |   1 +
 bfd/coff-alpha.c                    |   2 +
 bfd/coff-rs6000.c                   |   2 +
 bfd/coff64-rs6000.c                 |   2 +
 bfd/coffcode.h                      |   3 +
 bfd/elf-bfd.h                       |   8 ++
 bfd/elf32-i386.c                    | 117 +++++++++++++++++++++-----
 bfd/elf64-x86-64.c                  | 126 +++++++++++++++++++++++-----
 bfd/elflink.c                       |  32 ++++++-
 bfd/elfxx-target.h                  |   9 ++
 bfd/elfxx-x86.h                     |  37 +-------
 bfd/i386msdos.c                     |   2 +
 bfd/ihex.c                          |   1 +
 bfd/libbfd-in.h                     |   2 +
 bfd/libbfd.h                        |   2 +
 bfd/libecoff.h                      |   2 +
 bfd/linker.c                        |  42 ++++++++++
 bfd/mach-o-target.c                 |   2 +
 bfd/mmo.c                           |   2 +
 bfd/pef.c                           |   1 +
 bfd/plugin.c                        |   1 +
 bfd/ppcboot.c                       |   2 +
 bfd/som.c                           |   1 +
 bfd/srec.c                          |   1 +
 bfd/targets.c                       |   4 +
 bfd/tekhex.c                        |   1 +
 bfd/vms-alpha.c                     |   2 +
 bfd/xsym.c                          |   2 +
 include/bfdlink.h                   |  16 +++-
 ld/emulparams/elf32_x86_64.sh       |   1 +
 ld/emulparams/elf_i386.sh           |   1 +
 ld/emulparams/elf_i386_be.sh        |   1 +
 ld/emulparams/elf_i386_ldso.sh      |   1 +
 ld/emulparams/elf_i386_vxworks.sh   |   1 +
 ld/emulparams/elf_x86_64.sh         |   1 +
 ld/emultempl/aarch64elf.em          |   2 +-
 ld/emultempl/armelf.em              |   2 +-
 ld/emultempl/elf.em                 |   6 +-
 ld/emultempl/mmix-elfnmmo.em        |   2 +-
 ld/emultempl/scoreelf.em            |   2 +-
 ld/ldlang.c                         |  57 +++++++++++--
 ld/testsuite/ld-i386/pr27491-1a.d   |   4 +-
 ld/testsuite/ld-x86-64/pr27491-1a.d |   4 +-
 45 files changed, 421 insertions(+), 103 deletions(-)

diff --git a/bfd/aout-target.h b/bfd/aout-target.h
index b5155ac36c5..4fff19cc541 100644
--- a/bfd/aout-target.h
+++ b/bfd/aout-target.h
@@ -558,6 +558,10 @@ MY_bfd_final_link (bfd *abfd, struct bfd_link_info *info)
 #define MY_bfd_link_check_relocs   _bfd_generic_link_check_relocs
 #endif
 
+#ifndef MY_bfd_link_make_reloc_sections
+#define MY_bfd_link_make_reloc_sections _bfd_generic_link_check_relocs
+#endif
+
 #ifndef MY_bfd_copy_private_bfd_data
 #define MY_bfd_copy_private_bfd_data _bfd_generic_bfd_copy_private_bfd_data
 #endif
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 7b7a329cd62..5b94764a25b 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -7658,6 +7658,7 @@ typedef struct bfd_target
   NAME##_bfd_final_link, \
   NAME##_bfd_link_split_section, \
   NAME##_bfd_link_check_relocs, \
+  NAME##_bfd_link_make_reloc_sections, \
   NAME##_bfd_gc_sections, \
   NAME##_bfd_lookup_section_flags, \
   NAME##_bfd_merge_sections, \
@@ -7709,6 +7710,9 @@ typedef struct bfd_target
   /* Check the relocations in the bfd for validity.  */
   bool (* _bfd_link_check_relocs)(bfd *, struct bfd_link_info *);
 
+  /* Make reloc sections in the bfd.  */
+  bool (* _bfd_link_make_reloc_sections)(bfd *, struct bfd_link_info *);
+
   /* Remove sections that are not referenced from the output.  */
   bool (*_bfd_gc_sections) (bfd *, struct bfd_link_info *);
 
@@ -7933,9 +7937,15 @@ bool bfd_hide_sym_by_version
 bool bfd_link_check_relocs
    (bfd *abfd, struct bfd_link_info *info);
 
+bool bfd_link_make_reloc_sections
+   (bfd *abfd, struct bfd_link_info *info);
+
 bool _bfd_generic_link_check_relocs
    (bfd *abfd, struct bfd_link_info *info);
 
+bool _bfd_generic_link_make_reloc_sections
+   (bfd *abfd, struct bfd_link_info *info);
+
 bool bfd_merge_private_bfd_data
    (bfd *ibfd, struct bfd_link_info *info);
 
diff --git a/bfd/binary.c b/bfd/binary.c
index 0761a0ba899..a71b7ff3f1c 100644
--- a/bfd/binary.c
+++ b/bfd/binary.c
@@ -321,6 +321,7 @@ binary_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED,
 #define binary_bfd_link_split_section		  _bfd_generic_link_split_section
 #define binary_get_section_contents_in_window	  _bfd_generic_get_section_contents_in_window
 #define binary_bfd_link_check_relocs		  _bfd_generic_link_check_relocs
+#define binary_bfd_link_make_reloc_sections	  _bfd_generic_link_make_reloc_sections
 
 const bfd_target binary_vec =
 {
diff --git a/bfd/coff-alpha.c b/bfd/coff-alpha.c
index 15fea1fde34..cb678478805 100644
--- a/bfd/coff-alpha.c
+++ b/bfd/coff-alpha.c
@@ -2412,6 +2412,8 @@ static const struct ecoff_backend_data alpha_ecoff_backend_data =
 #define _bfd_ecoff_bfd_link_hide_symbol _bfd_generic_link_hide_symbol
 #define _bfd_ecoff_bfd_define_start_stop    bfd_generic_define_start_stop
 #define _bfd_ecoff_bfd_link_check_relocs    _bfd_generic_link_check_relocs
+#define _bfd_ecoff_bfd_link_make_reloc_sections \
+  _bfd_generic_link_make_reloc_sections
 
 /* Installing internal relocations in a section is also generic.  */
 #define _bfd_ecoff_set_reloc _bfd_generic_set_reloc
diff --git a/bfd/coff-rs6000.c b/bfd/coff-rs6000.c
index 1cc2162e7d8..576ae67e884 100644
--- a/bfd/coff-rs6000.c
+++ b/bfd/coff-rs6000.c
@@ -4407,6 +4407,8 @@ const struct xcoff_dwsect_name xcoff_dwsect_names[] = {
 #define _bfd_xcoff_bfd_link_hide_symbol _bfd_generic_link_hide_symbol
 #define _bfd_xcoff_bfd_define_start_stop    bfd_generic_define_start_stop
 #define _bfd_xcoff_bfd_link_check_relocs    _bfd_generic_link_check_relocs
+#define _bfd_xcoff_bfd_link_make_reloc_sections \
+  _bfd_generic_link_make_reloc_sections
 
 /* For dynamic symbols and relocs entry points.  */
 #define _bfd_xcoff_get_synthetic_symtab _bfd_nodynamic_get_synthetic_symtab
diff --git a/bfd/coff64-rs6000.c b/bfd/coff64-rs6000.c
index 95a31b606c8..ff613f310db 100644
--- a/bfd/coff64-rs6000.c
+++ b/bfd/coff64-rs6000.c
@@ -2630,6 +2630,7 @@ const bfd_target rs6000_xcoff64_vec =
     _bfd_xcoff_bfd_final_link,
     _bfd_generic_link_split_section,
     _bfd_generic_link_check_relocs,
+    _bfd_generic_link_make_reloc_sections,
     bfd_generic_gc_sections,
     bfd_generic_lookup_section_flags,
     bfd_generic_merge_sections,
@@ -2894,6 +2895,7 @@ const bfd_target rs6000_xcoff64_aix_vec =
     _bfd_xcoff_bfd_final_link,
     _bfd_generic_link_split_section,
     _bfd_generic_link_check_relocs,
+    _bfd_generic_link_make_reloc_sections,
     bfd_generic_gc_sections,
     bfd_generic_lookup_section_flags,
     bfd_generic_merge_sections,
diff --git a/bfd/coffcode.h b/bfd/coffcode.h
index 4405c9fe5ea..ff37ee03ef0 100644
--- a/bfd/coffcode.h
+++ b/bfd/coffcode.h
@@ -5345,6 +5345,9 @@ dummy_reloc16_extra_cases (bfd *abfd ATTRIBUTE_UNUSED,
 
 #define coff_bfd_link_check_relocs   _bfd_generic_link_check_relocs
 
+#define coff_bfd_link_make_reloc_sections \
+  _bfd_generic_link_make_reloc_sections
+
 #ifndef coff_start_final_link
 #define coff_start_final_link NULL
 #endif
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index be2eb38ea6a..b4c52af6d73 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -1100,6 +1100,12 @@ struct elf_backend_data
     (bfd *abfd, struct bfd_link_info *info, asection *o,
      const Elf_Internal_Rela *relocs);
 
+  /* The MAKE_RELOC_SECTION function is called to make the dynamic reloc
+     section associated with the input section.  */
+  bool (*make_reloc_section)
+    (bfd *abfd, struct bfd_link_info *info, asection *o,
+     const Elf_Internal_Rela *relocs);
+
   /* The CHECK_DIRECTIVES function is called once per input file by
      the add_symbols phase of the ELF backend linker.  The function
      must inspect the bfd and create any additional symbols according
@@ -2628,6 +2634,8 @@ extern int bfd_elf_add_dt_needed_tag
   (bfd *, struct bfd_link_info *);
 extern bool _bfd_elf_link_check_relocs
   (bfd *, struct bfd_link_info *);
+extern bool _bfd_elf_link_make_reloc_sections
+  (bfd *, struct bfd_link_info *);
 
 extern bool bfd_elf_link_record_dynamic_symbol
   (struct bfd_link_info *, struct elf_link_hash_entry *);
diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c
index db6d1accdbc..54e51bfbb5c 100644
--- a/bfd/elf32-i386.c
+++ b/bfd/elf32-i386.c
@@ -1393,11 +1393,6 @@ elf_i386_convert_load_reloc (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
 	       || h->root.type == bfd_link_hash_defweak)
 	      && local_ref))
 	{
-	  /* Skip __start_SECNAME/__stop_SECNAME when --gc-sections
-	     -z start-stop-gc are used.  */
-	  if (elf_x86_start_stop_gc_p (link_info, h))
-	    return true;
-
 	convert_load:
 	  if (opcode == 0x8b)
 	    {
@@ -1462,8 +1457,8 @@ elf_i386_convert_load_reloc (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
 #define check_relocs_failed	sec_flg0
 
 /* Look through the relocs for a section during the first phase, and
-   calculate needed space in the global offset table, procedure linkage
-   table, and dynamic reloc sections.  */
+   calculate needed space in the global offset table, and procedure
+   linkage table.  */
 
 static bool
 elf_i386_check_relocs (bfd *abfd,
@@ -1476,7 +1471,6 @@ elf_i386_check_relocs (bfd *abfd,
   struct elf_link_hash_entry **sym_hashes;
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
-  asection *sreloc;
   bfd_byte *contents;
   bool converted;
 
@@ -1506,8 +1500,6 @@ elf_i386_check_relocs (bfd *abfd,
 
   converted = false;
 
-  sreloc = NULL;
-
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
@@ -1841,18 +1833,6 @@ elf_i386_check_relocs (bfd *abfd,
 	      struct elf_dyn_relocs *p;
 	      struct elf_dyn_relocs **head;
 
-	      /* We must copy these reloc types into the output file.
-		 Create a reloc section in dynobj and make room for
-		 this reloc.  */
-	      if (sreloc == NULL)
-		{
-		  sreloc = _bfd_elf_make_dynamic_reloc_section
-		    (sec, htab->elf.dynobj, 2, abfd, /*rela?*/ false);
-
-		  if (sreloc == NULL)
-		    goto error_return;
-		}
-
 	      /* If this is a global symbol, we count the number of
 		 relocations we need for this symbol.  */
 	      if (h != NULL)
@@ -1947,6 +1927,97 @@ elf_i386_check_relocs (bfd *abfd,
   return false;
 }
 
+/* Look through the relocs for a section during the first phase and
+   make the dynamic reloc section.  */
+
+static bool
+elf_i386_make_reloc_section (bfd *abfd,
+			     struct bfd_link_info *info,
+			     asection *sec,
+			     const Elf_Internal_Rela *relocs)
+{
+  struct elf_x86_link_hash_table *htab;
+  Elf_Internal_Shdr *symtab_hdr;
+  struct elf_link_hash_entry **sym_hashes;
+  const Elf_Internal_Rela *rel;
+  const Elf_Internal_Rela *rel_end;
+  asection *sreloc;
+
+  if (bfd_link_relocatable (info))
+    return true;
+
+  htab = elf_x86_hash_table (info, I386_ELF_DATA);
+  if (htab == NULL)
+    {
+      sec->check_relocs_failed = 1;
+      return false;
+    }
+
+  BFD_ASSERT (is_x86_elf (abfd, htab));
+
+  symtab_hdr = &elf_symtab_hdr (abfd);
+  sym_hashes = elf_sym_hashes (abfd);
+
+  rel_end = relocs + sec->reloc_count;
+  for (rel = relocs; rel < rel_end; rel++)
+    {
+      unsigned int r_type;
+      unsigned int r_symndx;
+      struct elf_link_hash_entry *h;
+
+      r_symndx = htab->r_sym (rel->r_info);
+      r_type = ELF32_R_TYPE (rel->r_info);
+
+      if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
+	{
+	  /* xgettext:c-format */
+	  _bfd_error_handler (_("%pB: bad symbol index: %d"),
+			      abfd, r_symndx);
+	  goto error_return;
+	}
+
+      if (r_symndx < symtab_hdr->sh_info)
+	h = NULL;
+      else
+	{
+	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+	  while (h->root.type == bfd_link_hash_indirect
+		 || h->root.type == bfd_link_hash_warning)
+	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+	}
+
+      switch (r_type)
+	{
+	default:
+	  break;
+	case R_386_SIZE32:
+	case R_386_TLS_LE_32:
+	case R_386_TLS_LE:
+	case R_386_32:
+	case R_386_PC32:
+	  if (NEED_DYNAMIC_RELOCATION_P (info, true, h, sec, r_type,
+					 htab->pointer_r_type))
+	    {
+	      /* We may copy these reloc types into the output file.
+		 Create a reloc section in dynobj and make room for
+		 this reloc.  */
+	      sreloc = _bfd_elf_make_dynamic_reloc_section
+		(sec, htab->elf.dynobj, 2, abfd, /*rela?*/ false);
+
+	      if (sreloc != NULL)
+		return true;
+
+  error_return:
+	      sec->check_relocs_failed = 1;
+	      return false;
+	    }
+	  break;
+	}
+    }
+
+  return true;
+}
+
 /* Set the correct type for an x86 ELF section.  We do this by the
    section name, which is a hack, but ought to work.  */
 
@@ -4424,6 +4495,8 @@ elf_i386_link_setup_gnu_properties (struct bfd_link_info *info)
 #define elf_backend_relocate_section	      elf_i386_relocate_section
 #define elf_backend_setup_gnu_properties      elf_i386_link_setup_gnu_properties
 #define elf_backend_hide_symbol		      _bfd_x86_elf_hide_symbol
+#define elf_backend_make_reloc_section \
+  elf_i386_make_reloc_section
 
 #define elf_backend_linux_prpsinfo32_ugid16	true
 
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index bb6df798d7b..67b773e90fa 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1643,11 +1643,6 @@ elf_x86_64_convert_load_reloc (bfd *abfd,
 			   || h->root.type == bfd_link_hash_defweak)
 			  && h->root.u.def.section == bfd_und_section_ptr))))
 	    {
-	      /* Skip __start_SECNAME/__stop_SECNAME when --gc-sections
-	         -z start-stop-gc are used.  */
-	      if (elf_x86_start_stop_gc_p (link_info, h))
-		return true;
-
 	      /* Skip since R_X86_64_32/R_X86_64_32S may overflow.  */
 	      if (no_overflow)
 		return true;
@@ -1838,8 +1833,8 @@ elf_x86_64_convert_load_reloc (bfd *abfd,
 }
 
 /* Look through the relocs for a section during the first phase, and
-   calculate needed space in the global offset table, procedure
-   linkage table, and dynamic reloc sections.  */
+   calculate needed space in the global offset table, and procedure
+   linkage table.  */
 
 static bool
 elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
@@ -1851,7 +1846,6 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
   struct elf_link_hash_entry **sym_hashes;
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
-  asection *sreloc;
   bfd_byte *contents;
   bool converted;
 
@@ -1881,8 +1875,6 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
   converted = false;
 
-  sreloc = NULL;
-
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
@@ -2290,19 +2282,6 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	      struct elf_dyn_relocs *p;
 	      struct elf_dyn_relocs **head;
 
-	      /* We must copy these reloc types into the output file.
-		 Create a reloc section in dynobj and make room for
-		 this reloc.  */
-	      if (sreloc == NULL)
-		{
-		  sreloc = _bfd_elf_make_dynamic_reloc_section
-		    (sec, htab->elf.dynobj, ABI_64_P (abfd) ? 3 : 2,
-		     abfd, /*rela?*/ true);
-
-		  if (sreloc == NULL)
-		    goto error_return;
-		}
-
 	      /* If this is a global symbol, we count the number of
 		 relocations we need for this symbol.  */
 	      if (h != NULL)
@@ -2398,6 +2377,105 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
   return false;
 }
 
+/* Look through the relocs for a section during the first phase and
+   make the dynamic reloc section.  */
+
+static bool
+elf_x86_64_make_reloc_section (bfd *abfd,
+			       struct bfd_link_info *info,
+			       asection *sec,
+			       const Elf_Internal_Rela *relocs)
+{
+  struct elf_x86_link_hash_table *htab;
+  Elf_Internal_Shdr *symtab_hdr;
+  struct elf_link_hash_entry **sym_hashes;
+  const Elf_Internal_Rela *rel;
+  const Elf_Internal_Rela *rel_end;
+  asection *sreloc;
+
+  if (bfd_link_relocatable (info))
+    return true;
+
+  htab = elf_x86_hash_table (info, X86_64_ELF_DATA);
+  if (htab == NULL)
+    {
+      sec->check_relocs_failed = 1;
+      return false;
+    }
+
+  BFD_ASSERT (is_x86_elf (abfd, htab));
+
+  symtab_hdr = &elf_symtab_hdr (abfd);
+  sym_hashes = elf_sym_hashes (abfd);
+
+  rel_end = relocs + sec->reloc_count;
+  for (rel = relocs; rel < rel_end; rel++)
+    {
+      unsigned int r_type;
+      unsigned int r_symndx;
+      struct elf_link_hash_entry *h;
+
+      r_symndx = htab->r_sym (rel->r_info);
+      r_type = ELF32_R_TYPE (rel->r_info);
+
+      if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
+	{
+	  /* xgettext:c-format */
+	  _bfd_error_handler (_("%pB: bad symbol index: %d"),
+			      abfd, r_symndx);
+	  goto error_return;
+	}
+
+      if (r_symndx < symtab_hdr->sh_info)
+	h = NULL;
+      else
+	{
+	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+	  while (h->root.type == bfd_link_hash_indirect
+		 || h->root.type == bfd_link_hash_warning)
+	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+	}
+
+      switch (r_type)
+	{
+	default:
+	  break;
+	case R_X86_64_SIZE32:
+	case R_X86_64_SIZE64:
+	case R_X86_64_32:
+	case R_X86_64_8:
+	case R_X86_64_16:
+	case R_X86_64_32S:
+	case R_X86_64_PC8:
+	case R_X86_64_PC16:
+	case R_X86_64_PC32:
+	case R_X86_64_PC32_BND:
+	case R_X86_64_PC64:
+	case R_X86_64_64:
+	  if (NEED_DYNAMIC_RELOCATION_P (info, true, h, sec, r_type,
+					 htab->pointer_r_type))
+	    {
+	      /* We may copy these reloc types into the output file.
+		 Create a reloc section in dynobj and make room for
+		 this reloc.  */
+	      sreloc = _bfd_elf_make_dynamic_reloc_section
+		(sec, htab->elf.dynobj, ABI_64_P (abfd) ? 3 : 2,
+		 abfd, /*rela?*/ true);
+
+	      if (sreloc != NULL)
+		return true;
+
+  error_return:
+	      sec->check_relocs_failed = 1;
+	      return false;
+	    }
+	  break;
+	}
+    }
+
+  return true;
+}
+
 /* Return the relocation value for @tpoff relocation
    if STT_TLS virtual address is ADDRESS.  */
 
@@ -5306,6 +5384,8 @@ elf_x86_64_special_sections[]=
   elf_x86_64_link_setup_gnu_properties
 #define elf_backend_hide_symbol \
   _bfd_x86_elf_hide_symbol
+#define elf_backend_make_reloc_section \
+  elf_x86_64_make_reloc_section
 
 #undef	elf64_bed
 #define elf64_bed elf64_x86_64_bed
diff --git a/bfd/elflink.c b/bfd/elflink.c
index dc38548b23b..c4e7cba8ca1 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -4008,10 +4008,13 @@ _bfd_elf_notice_as_needed (bfd *ibfd,
   return (*info->callbacks->notice) (info, NULL, NULL, ibfd, NULL, act, 0);
 }
 
-/* Check relocations an ELF object file.  */
+/* Check relocations or make reloc sections in an ELF object file.  */
 
-bool
-_bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
+static bool
+_bfd_elf_link_check_relocs_or_make_reloc_sections
+  (bfd *abfd, struct bfd_link_info *info,
+   bool (*action) (bfd *, struct bfd_link_info *, asection *,
+		   const Elf_Internal_Rela *))
 {
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
   struct elf_link_hash_table *htab = elf_hash_table (info);
@@ -4070,7 +4073,7 @@ _bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
 	  if (internal_relocs == NULL)
 	    return false;
 
-	  ok = (*bed->check_relocs) (abfd, info, o, internal_relocs);
+	  ok = action (abfd, info, o, internal_relocs);
 
 	  if (elf_section_data (o)->relocs != internal_relocs)
 	    free (internal_relocs);
@@ -4083,6 +4086,27 @@ _bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
   return true;
 }
 
+/* Check relocations in an ELF object file.  */
+
+bool
+_bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
+{
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  return _bfd_elf_link_check_relocs_or_make_reloc_sections
+    (abfd, info, bed->check_relocs);
+}
+
+/* Make reloc sections in an ELF object file.  */
+
+bool
+_bfd_elf_link_make_reloc_sections (bfd *abfd,
+				   struct bfd_link_info *info)
+{
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  return _bfd_elf_link_check_relocs_or_make_reloc_sections
+    (abfd, info, bed->make_reloc_section);
+}
+
 /* Add symbols from an ELF object file to the linker hash table.  */
 
 static bool
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index 4c6b1f20340..8b3134ec33e 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -318,6 +318,11 @@
 #define bfd_elfNN_bfd_link_check_relocs  _bfd_elf_link_check_relocs
 #endif
 
+#ifndef bfd_elfNN_bfd_link_make_reloc_sections
+#define bfd_elfNN_bfd_link_make_reloc_sections \
+  _bfd_generic_link_make_reloc_sections
+#endif
+
 #ifndef bfd_elfNN_archive_p
 #define bfd_elfNN_archive_p bfd_generic_archive_p
 #endif
@@ -474,6 +479,9 @@
 #ifndef elf_backend_check_relocs
 #define elf_backend_check_relocs	0
 #endif
+#ifndef elf_backend_make_reloc_section
+#define elf_backend_make_reloc_section	0
+#endif
 #ifndef elf_backend_check_directives
 #define elf_backend_check_directives	0
 #endif
@@ -837,6 +845,7 @@ static const struct elf_backend_data elfNN_bed =
   elf_backend_omit_section_dynsym,
   elf_backend_relocs_compatible,
   elf_backend_check_relocs,
+  elf_backend_make_reloc_section,
   elf_backend_check_directives,
   elf_backend_notice_as_needed,
   elf_backend_adjust_dynamic_symbol,
diff --git a/bfd/elfxx-x86.h b/bfd/elfxx-x86.h
index aab641ab751..9d03f167876 100644
--- a/bfd/elfxx-x86.h
+++ b/bfd/elfxx-x86.h
@@ -706,6 +706,10 @@ extern void _bfd_x86_elf_link_report_relative_reloc
   _bfd_x86_elf_link_check_relocs
 #define bfd_elf32_bfd_link_check_relocs \
   _bfd_x86_elf_link_check_relocs
+#define bfd_elf64_bfd_link_make_reloc_sections \
+  _bfd_elf_link_make_reloc_sections
+#define bfd_elf32_bfd_link_make_reloc_sections \
+  _bfd_elf_link_make_reloc_sections
 
 #define elf_backend_size_dynamic_sections \
   _bfd_x86_elf_size_dynamic_sections
@@ -731,36 +735,3 @@ extern void _bfd_x86_elf_link_report_relative_reloc
   _bfd_x86_elf_merge_gnu_properties
 #define elf_backend_fixup_gnu_properties \
   _bfd_x86_elf_link_fixup_gnu_properties
-
-/* Return true if H is a __start_SECNAME/__stop_SECNAME symbol for the
-   SECNAME section which has been garbage collected by --gc-sections
-   -z start-stop-gc.  */
-
-static inline bool
-elf_x86_start_stop_gc_p (struct bfd_link_info *link_info,
-			 struct elf_link_hash_entry *h)
-{
-  if (h->start_stop
-      && link_info->gc_sections
-      && link_info->start_stop_gc)
-    {
-      asection *s = h->root.u.def.section;
-
-      do
-	{
-	  /* Return false if any SECNAME section is kept.  */
-	  if (s->gc_mark)
-	    return false;
-	  s = bfd_get_next_section_by_name (s->owner, s);
-	}
-      while (s != NULL);
-
-      /* Return true only if all SECNAME sections have been garbage
-	 collected.  */
-      return true;
-    }
-
-  /* Return false if H isn't a __start_SECNAME/__stop_SECNAME symbol or
-     --gc-sections or -z start-stop-gc isn't used.  */
-  return false;
-}
diff --git a/bfd/i386msdos.c b/bfd/i386msdos.c
index 349e8d2bd55..b12de3d3287 100644
--- a/bfd/i386msdos.c
+++ b/bfd/i386msdos.c
@@ -244,6 +244,8 @@ msdos_set_section_contents (bfd *abfd,
 #define msdos_bfd_link_split_section _bfd_generic_link_split_section
 #define msdos_set_arch_mach _bfd_generic_set_arch_mach
 #define msdos_bfd_link_check_relocs _bfd_generic_link_check_relocs
+#define msdos_bfd_link_make_reloc_sections \
+  _bfd_generic_link_make_reloc_sections
 
 #define msdos_get_symtab_upper_bound _bfd_nosymbols_get_symtab_upper_bound
 #define msdos_canonicalize_symtab _bfd_nosymbols_canonicalize_symtab
diff --git a/bfd/ihex.c b/bfd/ihex.c
index 1e8563e6110..305366b3560 100644
--- a/bfd/ihex.c
+++ b/bfd/ihex.c
@@ -966,6 +966,7 @@ ihex_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED,
 #define ihex_bfd_final_link			  _bfd_generic_final_link
 #define ihex_bfd_link_split_section		  _bfd_generic_link_split_section
 #define ihex_bfd_link_check_relocs		  _bfd_generic_link_check_relocs
+#define ihex_bfd_link_make_reloc_sections	  _bfd_generic_link_make_reloc_sections
 
 /* The Intel Hex target vector.  */
 
diff --git a/bfd/libbfd-in.h b/bfd/libbfd-in.h
index 89d2997aedf..fe2eaa6f382 100644
--- a/bfd/libbfd-in.h
+++ b/bfd/libbfd-in.h
@@ -534,6 +534,8 @@ extern struct bfd_link_hash_entry *_bfd_nolink_bfd_define_start_stop
   (struct bfd_link_info *, const char *, asection *) ATTRIBUTE_HIDDEN;
 #define _bfd_nolink_bfd_link_check_relocs \
   _bfd_generic_link_check_relocs
+#define _bfd_nolink_bfd_link_make_reloc_sections \
+  _bfd_generic_link_make_reloc_sections
 
 /* Routines to use for BFD_JUMP_TABLE_DYNAMIC for targets which do not
    have dynamic symbols or relocs.  Use BFD_JUMP_TABLE_DYNAMIC
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 382a574d954..30c2bd0e16a 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -539,6 +539,8 @@ extern struct bfd_link_hash_entry *_bfd_nolink_bfd_define_start_stop
   (struct bfd_link_info *, const char *, asection *) ATTRIBUTE_HIDDEN;
 #define _bfd_nolink_bfd_link_check_relocs \
   _bfd_generic_link_check_relocs
+#define _bfd_nolink_bfd_link_make_reloc_sections \
+  _bfd_generic_link_make_reloc_sections
 
 /* Routines to use for BFD_JUMP_TABLE_DYNAMIC for targets which do not
    have dynamic symbols or relocs.  Use BFD_JUMP_TABLE_DYNAMIC
diff --git a/bfd/libecoff.h b/bfd/libecoff.h
index c7eaba533b9..a223cbcb253 100644
--- a/bfd/libecoff.h
+++ b/bfd/libecoff.h
@@ -243,6 +243,8 @@ extern bool _bfd_ecoff_get_section_contents
 
 #define _bfd_ecoff_bfd_link_split_section _bfd_generic_link_split_section
 #define _bfd_ecoff_bfd_link_check_relocs  _bfd_generic_link_check_relocs
+#define _bfd_ecoff_bfd_link_make_reloc_sections \
+    _bfd_generic_link_make_reloc_sections
 
 extern bool _bfd_ecoff_bfd_copy_private_bfd_data
   (bfd *, bfd *);
diff --git a/bfd/linker.c b/bfd/linker.c
index 3019daea3a5..e22f156f015 100644
--- a/bfd/linker.c
+++ b/bfd/linker.c
@@ -3350,6 +3350,26 @@ bfd_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
   return BFD_SEND (abfd, _bfd_link_check_relocs, (abfd, info));
 }
 
+/*
+FUNCTION
+	bfd_link_make_reloc_sections
+
+SYNOPSIS
+	bool bfd_link_make_reloc_sections
+	  (bfd *abfd, struct bfd_link_info *info);
+
+DESCRIPTION
+	Make the reloc sections in ABFD.  Does not execute the relocs.
+	Return TRUE if everything is OK, FALSE otherwise.
+	This is the external entry point to this code.
+*/
+
+bool
+bfd_link_make_reloc_sections (bfd *abfd, struct bfd_link_info *info)
+{
+  return BFD_SEND (abfd, _bfd_link_make_reloc_sections, (abfd, info));
+}
+
 /*
 FUNCTION
 	_bfd_generic_link_check_relocs
@@ -3372,6 +3392,28 @@ _bfd_generic_link_check_relocs (bfd *abfd ATTRIBUTE_UNUSED,
   return true;
 }
 
+/*
+FUNCTION
+	_bfd_generic_link_make_reloc_sections
+
+SYNOPSIS
+	bool _bfd_generic_link_make_reloc_sections
+	  (bfd *abfd, struct bfd_link_info *info);
+
+DESCRIPTION
+	Stub function for targets that do not make the reloc sections.
+	Return TRUE.
+	This is an internal function.  It should not be called from
+	outside the BFD library.
+*/
+
+bool
+_bfd_generic_link_make_reloc_sections (bfd *abfd ATTRIBUTE_UNUSED,
+				       struct bfd_link_info *info ATTRIBUTE_UNUSED)
+{
+  return true;
+}
+
 /*
 FUNCTION
 	bfd_merge_private_bfd_data
diff --git a/bfd/mach-o-target.c b/bfd/mach-o-target.c
index 3b454336539..c8f48277bcd 100644
--- a/bfd/mach-o-target.c
+++ b/bfd/mach-o-target.c
@@ -45,6 +45,8 @@
 #define bfd_mach_o_bfd_final_link		      _bfd_generic_final_link
 #define bfd_mach_o_bfd_link_split_section	      _bfd_generic_link_split_section
 #define bfd_mach_o_bfd_link_check_relocs	      _bfd_generic_link_check_relocs
+#define bfd_mach_o_bfd_link_make_reloc_sections	\
+  _bfd_generic_link_make_reloc_sections
 #define bfd_mach_o_bfd_merge_private_bfd_data	      _bfd_generic_bfd_merge_private_bfd_data
 #define bfd_mach_o_bfd_set_private_flags	      bfd_mach_o_bfd_set_private_flags
 #define bfd_mach_o_get_section_contents		      _bfd_generic_get_section_contents
diff --git a/bfd/mmo.c b/bfd/mmo.c
index d80cb06f746..616e90e974a 100644
--- a/bfd/mmo.c
+++ b/bfd/mmo.c
@@ -3343,6 +3343,8 @@ mmo_write_object_contents (bfd *abfd)
 #define mmo_bfd_final_link _bfd_generic_final_link
 #define mmo_bfd_link_split_section _bfd_generic_link_split_section
 #define mmo_bfd_link_check_relocs  _bfd_generic_link_check_relocs
+#define mmo_bfd_link_make_reloc_sections \
+  _bfd_generic_link_make_reloc_sections
 
 /* Strictly speaking, only MMIX uses this restricted format, but let's not
    stop anybody from shooting themselves in the foot.  */
diff --git a/bfd/pef.c b/bfd/pef.c
index f55e532b6fd..3bea39ad57e 100644
--- a/bfd/pef.c
+++ b/bfd/pef.c
@@ -71,6 +71,7 @@
 #define bfd_pef_bfd_link_split_section		    _bfd_generic_link_split_section
 #define bfd_pef_get_section_contents_in_window	    _bfd_generic_get_section_contents_in_window
 #define bfd_pef_bfd_link_check_relocs		    _bfd_generic_link_check_relocs
+#define bfd_pef_bfd_link_make_reloc_sections	    _bfd_generic_link_make_reloc_sections
 
 static int
 bfd_pef_parse_traceback_table (bfd *abfd,
diff --git a/bfd/plugin.c b/bfd/plugin.c
index 3bab8febe88..7cf2c954479 100644
--- a/bfd/plugin.c
+++ b/bfd/plugin.c
@@ -110,6 +110,7 @@ dlerror (void)
 #define bfd_plugin_bfd_define_start_stop	      bfd_generic_define_start_stop
 #define bfd_plugin_bfd_copy_link_hash_symbol_type     _bfd_generic_copy_link_hash_symbol_type
 #define bfd_plugin_bfd_link_check_relocs	      _bfd_generic_link_check_relocs
+#define bfd_plugin_bfd_link_make_reloc_sections	      _bfd_generic_link_make_reloc_sections
 
 static enum ld_plugin_status
 message (int level ATTRIBUTE_UNUSED,
diff --git a/bfd/ppcboot.c b/bfd/ppcboot.c
index 4768de3018c..a06ce00fd54 100644
--- a/bfd/ppcboot.c
+++ b/bfd/ppcboot.c
@@ -473,6 +473,8 @@ ppcboot_bfd_print_private_bfd_data (bfd *abfd, void * farg)
 #define ppcboot_get_section_contents_in_window \
   _bfd_generic_get_section_contents_in_window
 #define ppcboot_bfd_link_check_relocs _bfd_generic_link_check_relocs
+#define ppcboot_bfd_link_make_reloc_sections \
+  _bfd_generic_link_make_reloc_sections
 
 #define ppcboot_bfd_copy_private_bfd_data _bfd_generic_bfd_copy_private_bfd_data
 #define ppcboot_bfd_merge_private_bfd_data _bfd_generic_bfd_merge_private_bfd_data
diff --git a/bfd/som.c b/bfd/som.c
index 42ecc765945..93f85ee49a3 100644
--- a/bfd/som.c
+++ b/bfd/som.c
@@ -6855,6 +6855,7 @@ som_bfd_link_split_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
 #define som_bfd_set_private_flags		_bfd_generic_bfd_set_private_flags
 #define som_find_inliner_info			_bfd_nosymbols_find_inliner_info
 #define som_bfd_link_check_relocs		_bfd_generic_link_check_relocs
+#define som_bfd_link_make_reloc_sections	_bfd_generic_link_make_reloc_sections
 #define som_set_reloc				_bfd_generic_set_reloc
 
 const bfd_target hppa_som_vec =
diff --git a/bfd/srec.c b/bfd/srec.c
index 9628691ad8f..643d01c5125 100644
--- a/bfd/srec.c
+++ b/bfd/srec.c
@@ -1283,6 +1283,7 @@ srec_print_symbol (bfd *abfd,
 #define srec_bfd_final_link			  _bfd_generic_final_link
 #define srec_bfd_link_split_section		  _bfd_generic_link_split_section
 #define srec_bfd_link_check_relocs		  _bfd_generic_link_check_relocs
+#define srec_bfd_link_make_reloc_sections	  _bfd_generic_link_make_reloc_sections
 
 const bfd_target srec_vec =
 {
diff --git a/bfd/targets.c b/bfd/targets.c
index 672dc2bb1a4..8332dcbac2c 100644
--- a/bfd/targets.c
+++ b/bfd/targets.c
@@ -465,6 +465,7 @@ BFD_JUMP_TABLE macros.
 .  NAME##_bfd_final_link, \
 .  NAME##_bfd_link_split_section, \
 .  NAME##_bfd_link_check_relocs, \
+.  NAME##_bfd_link_make_reloc_sections, \
 .  NAME##_bfd_gc_sections, \
 .  NAME##_bfd_lookup_section_flags, \
 .  NAME##_bfd_merge_sections, \
@@ -516,6 +517,9 @@ BFD_JUMP_TABLE macros.
 .  {* Check the relocations in the bfd for validity.  *}
 .  bool (* _bfd_link_check_relocs)(bfd *, struct bfd_link_info *);
 .
+.  {* Make reloc sections in the bfd.  *}
+.  bool (* _bfd_link_make_reloc_sections)(bfd *, struct bfd_link_info *);
+.
 .  {* Remove sections that are not referenced from the output.  *}
 .  bool (*_bfd_gc_sections) (bfd *, struct bfd_link_info *);
 .
diff --git a/bfd/tekhex.c b/bfd/tekhex.c
index 008fd8da107..de4da83709a 100644
--- a/bfd/tekhex.c
+++ b/bfd/tekhex.c
@@ -992,6 +992,7 @@ tekhex_print_symbol (bfd *abfd,
 #define tekhex_bfd_link_split_section		    _bfd_generic_link_split_section
 #define tekhex_get_section_contents_in_window	    _bfd_generic_get_section_contents_in_window
 #define tekhex_bfd_link_check_relocs		    _bfd_generic_link_check_relocs
+#define tekhex_bfd_link_make_reloc_sections	    _bfd_generic_link_make_reloc_sections
 
 const bfd_target tekhex_vec =
 {
diff --git a/bfd/vms-alpha.c b/bfd/vms-alpha.c
index f4f16ef116a..156d585641f 100644
--- a/bfd/vms-alpha.c
+++ b/bfd/vms-alpha.c
@@ -10023,6 +10023,8 @@ bfd_vms_get_data (bfd *abfd)
 #define alpha_vms_canonicalize_dynamic_reloc \
   _bfd_nodynamic_canonicalize_dynamic_reloc
 #define alpha_vms_bfd_link_check_relocs		     _bfd_generic_link_check_relocs
+#define alpha_vms_bfd_link_make_reloc_sections \
+  _bfd_generic_link_make_reloc_sections
 
 const bfd_target alpha_vms_vec =
 {
diff --git a/bfd/xsym.c b/bfd/xsym.c
index 822cb85eb6b..be7ca1c7015 100644
--- a/bfd/xsym.c
+++ b/bfd/xsym.c
@@ -62,6 +62,8 @@
 #define bfd_sym_bfd_link_split_section		    _bfd_generic_link_split_section
 #define bfd_sym_get_section_contents_in_window	    _bfd_generic_get_section_contents_in_window
 #define bfd_sym_bfd_link_check_relocs		    _bfd_generic_link_check_relocs
+#define bfd_sym_bfd_link_make_reloc_sections \
+  _bfd_generic_link_make_reloc_sections
 
 extern const bfd_target sym_vec;
 
diff --git a/include/bfdlink.h b/include/bfdlink.h
index 566529ee644..7ddbe6c8161 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -284,6 +284,17 @@ enum textrel_check_method
 #define bfd_link_textrel_check(info) \
   (info->textrel_check != textrel_check_none)
 
+/* When to check relocations.  */
+enum bfd_link_check_relocs_phase
+{
+  /* Check relocations before all input files have been opened.  */
+  check_relocs_before_open_input = 0,
+  /* Check relocations after all input files have been opened.  */
+  check_relocs_after_open_input,
+  /* Check relocations before allocation.  */
+  check_relocs_before_allocation
+};
+
 typedef enum {with_flags, without_flags} flag_type;
 
 /* A section flag list.  */
@@ -501,9 +512,8 @@ struct bfd_link_info
   /* TRUE if program headers ought to be loaded.  */
   unsigned int load_phdrs: 1;
 
-  /* TRUE if we should check relocations after all input files have
-     been opened.  */
-  unsigned int check_relocs_after_open_input: 1;
+  /* When to check relocations.  */
+  ENUM_BITFIELD (bfd_link_check_relocs_phase) check_relocs_phase: 2;
 
   /* TRUE if generation of .interp/PT_INTERP should be suppressed.  */
   unsigned int nointerp: 1;
diff --git a/ld/emulparams/elf32_x86_64.sh b/ld/emulparams/elf32_x86_64.sh
index ac0a7aa6dcf..d5a72faa8b0 100644
--- a/ld/emulparams/elf32_x86_64.sh
+++ b/ld/emulparams/elf32_x86_64.sh
@@ -10,6 +10,7 @@ source_sh ${srcdir}/emulparams/static.sh
 SCRIPT_NAME=elf
 ELFSIZE=32
 OUTPUT_FORMAT="elf32-x86-64"
+CHECK_RELOCS_PHASE=check_relocs_before_allocation
 NO_REL_RELOCS=yes
 TEXT_START_ADDR=0x400000
 MAXPAGESIZE="CONSTANT (MAXPAGESIZE)"
diff --git a/ld/emulparams/elf_i386.sh b/ld/emulparams/elf_i386.sh
index 98532e5edbc..11574299c56 100644
--- a/ld/emulparams/elf_i386.sh
+++ b/ld/emulparams/elf_i386.sh
@@ -8,6 +8,7 @@ source_sh ${srcdir}/emulparams/x86-64-level.sh
 source_sh ${srcdir}/emulparams/static.sh
 SCRIPT_NAME=elf
 OUTPUT_FORMAT="elf32-i386"
+CHECK_RELOCS_PHASE=check_relocs_before_allocation
 NO_RELA_RELOCS=yes
 TEXT_START_ADDR=0x08048000
 MAXPAGESIZE="CONSTANT (MAXPAGESIZE)"
diff --git a/ld/emulparams/elf_i386_be.sh b/ld/emulparams/elf_i386_be.sh
index dbe68e99e63..01e4b026ce1 100644
--- a/ld/emulparams/elf_i386_be.sh
+++ b/ld/emulparams/elf_i386_be.sh
@@ -4,6 +4,7 @@ source_sh ${srcdir}/emulparams/call_nop.sh
 SCRIPT_NAME=elf
 OUTPUT_FORMAT="elf32-i386"
 EXTRA_EM_FILE="elf-x86"
+CHECK_RELOCS_PHASE=check_relocs_before_allocation
 NO_RELA_RELOCS=yes
 TEXT_START_ADDR=0x80000000
 MAXPAGESIZE="CONSTANT (MAXPAGESIZE)"
diff --git a/ld/emulparams/elf_i386_ldso.sh b/ld/emulparams/elf_i386_ldso.sh
index 081de5f8e71..e62e5adf370 100644
--- a/ld/emulparams/elf_i386_ldso.sh
+++ b/ld/emulparams/elf_i386_ldso.sh
@@ -5,6 +5,7 @@ source_sh ${srcdir}/emulparams/call_nop.sh
 SCRIPT_NAME=elf
 OUTPUT_FORMAT="elf32-i386"
 EXTRA_EM_FILE="elf-x86"
+CHECK_RELOCS_PHASE=check_relocs_before_allocation
 NO_RELA_RELOCS=yes
 TEXT_START_ADDR=0x08048000
 MAXPAGESIZE="CONSTANT (MAXPAGESIZE)"
diff --git a/ld/emulparams/elf_i386_vxworks.sh b/ld/emulparams/elf_i386_vxworks.sh
index 40c809263d1..4a5a2d288cc 100644
--- a/ld/emulparams/elf_i386_vxworks.sh
+++ b/ld/emulparams/elf_i386_vxworks.sh
@@ -1,5 +1,6 @@
 SCRIPT_NAME=elf
 OUTPUT_FORMAT="elf32-i386-vxworks"
+CHECK_RELOCS_PHASE=check_relocs_before_allocation
 NO_RELA_RELOCS=yes
 TEXT_START_ADDR=0x08048000
 MAXPAGESIZE="CONSTANT (MAXPAGESIZE)"
diff --git a/ld/emulparams/elf_x86_64.sh b/ld/emulparams/elf_x86_64.sh
index 48d0974711b..fe1c18f6405 100644
--- a/ld/emulparams/elf_x86_64.sh
+++ b/ld/emulparams/elf_x86_64.sh
@@ -11,6 +11,7 @@ source_sh ${srcdir}/emulparams/static.sh
 SCRIPT_NAME=elf
 ELFSIZE=64
 OUTPUT_FORMAT="elf64-x86-64"
+CHECK_RELOCS_PHASE=check_relocs_before_allocation
 NO_REL_RELOCS=yes
 TEXT_START_ADDR=0x400000
 MAXPAGESIZE="CONSTANT (MAXPAGESIZE)"
diff --git a/ld/emultempl/aarch64elf.em b/ld/emultempl/aarch64elf.em
index 4e1d8f8a240..933ca205454 100644
--- a/ld/emultempl/aarch64elf.em
+++ b/ld/emultempl/aarch64elf.em
@@ -46,7 +46,7 @@ gld${EMULATION_NAME}_before_parse (void)
   input_flags.dynamic = ${DYNAMIC_LINK-true};
   config.has_shared = `if test -n "$GENERATE_SHLIB_SCRIPT" ; then echo true ; else echo false ; fi`;
   config.separate_code = `if test "x${SEPARATE_CODE}" = xyes ; then echo true ; else echo false ; fi`;
-  link_info.check_relocs_after_open_input = true;
+  link_info.check_relocs_phase = $CHECK_RELOCS_PHASE;
 EOF
 if test -n "$COMMONPAGESIZE"; then
 fragment <<EOF
diff --git a/ld/emultempl/armelf.em b/ld/emultempl/armelf.em
index 7aec17e5ede..a04ee2d70be 100644
--- a/ld/emultempl/armelf.em
+++ b/ld/emultempl/armelf.em
@@ -59,7 +59,7 @@ gld${EMULATION_NAME}_before_parse (void)
   input_flags.dynamic = ${DYNAMIC_LINK-true};
   config.has_shared = `if test -n "$GENERATE_SHLIB_SCRIPT" ; then echo true ; else echo false ; fi`;
   config.separate_code = `if test "x${SEPARATE_CODE}" = xyes ; then echo true ; else echo false ; fi`;
-  link_info.check_relocs_after_open_input = true;
+  link_info.check_relocs_phase = $CHECK_RELOCS_PHASE;
 EOF
 if test -n "$COMMONPAGESIZE"; then
 fragment <<EOF
diff --git a/ld/emultempl/elf.em b/ld/emultempl/elf.em
index bfaf8130a3e..c2ce87f3a03 100644
--- a/ld/emultempl/elf.em
+++ b/ld/emultempl/elf.em
@@ -58,6 +58,10 @@ static void gld${EMULATION_NAME}_before_allocation (void);
 static void gld${EMULATION_NAME}_after_allocation (void);
 EOF
 
+if test -z "$CHECK_RELOCS_PHASE"; then
+CHECK_RELOCS_PHASE=check_relocs_after_open_input
+fi
+
 # Import any needed special functions and/or overrides.
 #
 source_em ${srcdir}/emultempl/elf-generic.em
@@ -81,7 +85,7 @@ gld${EMULATION_NAME}_before_parse (void)
   input_flags.dynamic = ${DYNAMIC_LINK-true};
   config.has_shared = `if test -n "$GENERATE_SHLIB_SCRIPT" ; then echo true ; else echo false ; fi`;
   config.separate_code = `if test "x${SEPARATE_CODE}" = xyes ; then echo true ; else echo false ; fi`;
-  link_info.check_relocs_after_open_input = true;
+  link_info.check_relocs_phase = $CHECK_RELOCS_PHASE;
 EOF
 if test -n "$COMMONPAGESIZE"; then
 fragment <<EOF
diff --git a/ld/emultempl/mmix-elfnmmo.em b/ld/emultempl/mmix-elfnmmo.em
index a88fc5a6596..20f7df20eb0 100644
--- a/ld/emultempl/mmix-elfnmmo.em
+++ b/ld/emultempl/mmix-elfnmmo.em
@@ -30,7 +30,7 @@ static void gld${EMULATION_NAME}_before_parse (void);
 static void
 mmix_before_parse (void)
 {
-  link_info.check_relocs_after_open_input = true;
+  link_info.check_relocs_phase = check_relocs_after_open_input;
   gld${EMULATION_NAME}_before_parse ();
 }
 
diff --git a/ld/emultempl/scoreelf.em b/ld/emultempl/scoreelf.em
index 9aea76a8f51..770ce21f3e1 100644
--- a/ld/emultempl/scoreelf.em
+++ b/ld/emultempl/scoreelf.em
@@ -39,7 +39,7 @@ gld${EMULATION_NAME}_before_parse (void)
   input_flags.dynamic = ${DYNAMIC_LINK-true};
   config.has_shared = `if test -n "$GENERATE_SHLIB_SCRIPT" ; then echo true ; else echo false ; fi`;
   config.separate_code = `if test "x${SEPARATE_CODE}" = xyes ; then echo true ; else echo false ; fi`;
-  link_info.check_relocs_after_open_input = true;
+  link_info.check_relocs_phase = $CHECK_RELOCS_PHASE;
 EOF
 if test -n "$COMMONPAGESIZE"; then
 fragment <<EOF
diff --git a/ld/ldlang.c b/ld/ldlang.c
index a0ff1229344..e95d32d9c6e 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -7915,15 +7915,42 @@ lang_add_gc_name (const char *name)
 /* Check relocations.  */
 
 static void
-lang_check_relocs (void)
+check_relocs (void)
 {
-  if (link_info.check_relocs_after_open_input)
-    {
-      bfd *abfd;
+  bfd *abfd;
+
+  for (abfd = link_info.input_bfds;
+       abfd != (bfd *) NULL; abfd = abfd->link.next)
+    if (!bfd_link_check_relocs (abfd, &link_info))
+      {
+	/* No object output, fail return.  */
+	config.make_executable = false;
+	/* Note: we do not abort the loop, but rather
+	   continue the scan in case there are other
+	   bad relocations to report.  */
+      }
+
+  ldemul_after_check_relocs ();
+}
+
+/* Check relocations after all input files have been opened.  */
+
+static void
+lang_check_relocs_after_open_input (void)
+{
+  bfd *abfd;
 
+  switch (link_info.check_relocs_phase)
+    {
+    case check_relocs_before_open_input:
+      break;
+    case check_relocs_after_open_input:
+      check_relocs ();
+      break;
+    case check_relocs_before_allocation:
       for (abfd = link_info.input_bfds;
 	   abfd != (bfd *) NULL; abfd = abfd->link.next)
-	if (!bfd_link_check_relocs (abfd, &link_info))
+	if (!bfd_link_make_reloc_sections (abfd, &link_info))
 	  {
 	    /* No object output, fail return.  */
 	    config.make_executable = false;
@@ -7931,9 +7958,20 @@ lang_check_relocs (void)
 	       continue the scan in case there are other
 	       bad relocations to report.  */
 	  }
+
+      break;
     }
 }
 
+/* Check relocations before allocation.  */
+
+static void
+lang_check_relocs_before_allocation (void)
+{
+  if (link_info.check_relocs_phase == check_relocs_before_allocation)
+    check_relocs ();
+}
+
 /* Look through all output sections looking for places where we can
    propagate forward the lma region.  */
 
@@ -8165,10 +8203,8 @@ lang_process (void)
 
   lang_mark_undefineds ();
 
-  /* Check relocations.  */
-  lang_check_relocs ();
-
-  ldemul_after_check_relocs ();
+  /* Check relocations after all input files have been opened.  */
+  lang_check_relocs_after_open_input ();
 
   /* Update wild statements.  */
   update_wild_statements (statement_list.head);
@@ -8231,6 +8267,9 @@ lang_process (void)
   if (!bfd_link_relocatable (&link_info))
     lang_init_startof_sizeof ();
 
+  /* Check relocations before allocation.  */
+  lang_check_relocs_before_allocation ();
+
   /* Do anything special before sizing sections.  This is where ELF
      and other back-ends size dynamic sections.  */
   ldemul_before_allocation ();
diff --git a/ld/testsuite/ld-i386/pr27491-1a.d b/ld/testsuite/ld-i386/pr27491-1a.d
index 006c17695c1..39b25f6507f 100644
--- a/ld/testsuite/ld-i386/pr27491-1a.d
+++ b/ld/testsuite/ld-i386/pr27491-1a.d
@@ -9,6 +9,6 @@
 Disassembly of section .text:
 
 [a-f0-9]+ <foo>:
- +[a-f0-9]+:	8b 83 ([0-9a-f]{2} ){4}[ \t]+mov +-0x[a-f0-9]+\(%ebx\),%eax
- +[a-f0-9]+:	8b 83 ([0-9a-f]{2} ){4}[ \t]+mov +-0x[a-f0-9]+\(%ebx\),%eax
+ +[a-f0-9]+:	c7 c0 00 00 00 00    	mov    \$0x0,%eax
+ +[a-f0-9]+:	c7 c0 00 00 00 00    	mov    \$0x0,%eax
 #pass
diff --git a/ld/testsuite/ld-x86-64/pr27491-1a.d b/ld/testsuite/ld-x86-64/pr27491-1a.d
index ade5c6fa4f9..215124c6401 100644
--- a/ld/testsuite/ld-x86-64/pr27491-1a.d
+++ b/ld/testsuite/ld-x86-64/pr27491-1a.d
@@ -9,6 +9,6 @@
 Disassembly of section .text:
 
 [a-f0-9]+ <foo>:
- +[a-f0-9]+:	48 8b 05 ([0-9a-f]{2} ){4}[ \t]+mov +0x[a-f0-9]+\(%rip\),%rax[ \t]+# [a-f0-9]+ <_DYNAMIC\+0x[a-f0-9]+>
- +[a-f0-9]+:	48 8b 05 ([0-9a-f]{2} ){4}[ \t]+mov +0x[a-f0-9]+\(%rip\),%rax[ \t]+# [a-f0-9]+ <_DYNAMIC\+0x[a-f0-9]+>
+ +[a-f0-9]+:	48 c7 c0 00 00 00 00 	mov    \$0x0,%rax
+ +[a-f0-9]+:	48 c7 c0 00 00 00 00 	mov    \$0x0,%rax
 #pass
-- 
2.33.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] ld: Check ELF relocs before allocation
  2021-12-28  2:48 [PATCH] ld: Check ELF relocs before allocation H.J. Lu
@ 2021-12-29  7:46 ` Alan Modra
  2021-12-29 16:18   ` H.J. Lu
  0 siblings, 1 reply; 4+ messages in thread
From: Alan Modra @ 2021-12-29  7:46 UTC (permalink / raw)
  To: H.J. Lu; +Cc: binutils

On Mon, Dec 27, 2021 at 06:48:33PM -0800, H.J. Lu via Binutils wrote:
> To prepare for DT_RELR implemenation, delay checking ELF relocations
> before allocation so that all input sections have been mapped to output
> sections when relocations are checked.  This is only enabled for x86
> targets.

Please expand on why this is necessary.  "Prepare for DT_RELR
implementation" doesn't tell me much.

Also, it seems to me that you could have made the functional changes
in this patch by implementing an x86 before_allocation routine rather
than introducing a new BFD_JUMP_TABLE_LINK entry.  Doing it that way
would result in a much smaller patch with few if any changes to
generic ELF linker code.

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] ld: Check ELF relocs before allocation
  2021-12-29  7:46 ` Alan Modra
@ 2021-12-29 16:18   ` H.J. Lu
  2021-12-29 22:15     ` H.J. Lu
  0 siblings, 1 reply; 4+ messages in thread
From: H.J. Lu @ 2021-12-29 16:18 UTC (permalink / raw)
  To: Alan Modra; +Cc: Binutils

On Tue, Dec 28, 2021 at 11:46 PM Alan Modra <amodra@gmail.com> wrote:
>
> On Mon, Dec 27, 2021 at 06:48:33PM -0800, H.J. Lu via Binutils wrote:
> > To prepare for DT_RELR implemenation, delay checking ELF relocations
> > before allocation so that all input sections have been mapped to output
> > sections when relocations are checked.  This is only enabled for x86
> > targets.
>
> Please expand on why this is necessary.  "Prepare for DT_RELR
> implementation" doesn't tell me much.

DT_RELR encodes consecutive R_*_RELATIVE relocations in GOT (the global
offset table) in a compact format:

https://groups.google.com/g/generic-abi/c/bX460iggiKg

On some targets, R_*_RELATIVE relocations are counted and the GOT offsets
are allocated when setting the dynamic section sizes after seeing all
relocations.  R_*_RELATIVE relocations are generated while relocating
sections after section layout has been finalized.

To prepare for DT_RELR implementation on these targets, we need to delay
checking ELF relocations before allocation so that when relocations are
checked, all input sections have been mapped to output sections, dynamic
symbols are known and R_*_RELATIVE relocations can be counted.

DT_RELR encoding may be generated in the relax pass when R_*_RELATIVE
relocations and their GOT offsets are known.  A later fix up is needed if
the final GOT vma is different from when DT_RELR encoding was generated.

> Also, it seems to me that you could have made the functional changes
> in this patch by implementing an x86 before_allocation routine rather
> than introducing a new BFD_JUMP_TABLE_LINK entry.  Doing it that way
> would result in a much smaller patch with few if any changes to
> generic ELF linker code.

A new linker function, bfd_link_make_reloc_sections, is added to create
dynamic relocation sections after all input files have been opened if relocation
check is delayed.  Otherwise, the dynamic relocation sections are missing
when input sections are being mapped to output sections.   It is much
cleaner than checking relocations in a before_allocation routine.

-- 
H.J.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] ld: Check ELF relocs before allocation
  2021-12-29 16:18   ` H.J. Lu
@ 2021-12-29 22:15     ` H.J. Lu
  0 siblings, 0 replies; 4+ messages in thread
From: H.J. Lu @ 2021-12-29 22:15 UTC (permalink / raw)
  To: Alan Modra; +Cc: Binutils

On Wed, Dec 29, 2021 at 8:18 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Tue, Dec 28, 2021 at 11:46 PM Alan Modra <amodra@gmail.com> wrote:
> >
> > On Mon, Dec 27, 2021 at 06:48:33PM -0800, H.J. Lu via Binutils wrote:
> > > To prepare for DT_RELR implemenation, delay checking ELF relocations
> > > before allocation so that all input sections have been mapped to output
> > > sections when relocations are checked.  This is only enabled for x86
> > > targets.
> >
> > Please expand on why this is necessary.  "Prepare for DT_RELR
> > implementation" doesn't tell me much.
>
> DT_RELR encodes consecutive R_*_RELATIVE relocations in GOT (the global
> offset table) in a compact format:
>
> https://groups.google.com/g/generic-abi/c/bX460iggiKg
>
> On some targets, R_*_RELATIVE relocations are counted and the GOT offsets
> are allocated when setting the dynamic section sizes after seeing all
> relocations.  R_*_RELATIVE relocations are generated while relocating
> sections after section layout has been finalized.
>
> To prepare for DT_RELR implementation on these targets, we need to delay
> checking ELF relocations before allocation so that when relocations are
> checked, all input sections have been mapped to output sections, dynamic
> symbols are known and R_*_RELATIVE relocations can be counted.
>
> DT_RELR encoding may be generated in the relax pass when R_*_RELATIVE
> relocations and their GOT offsets are known.  A later fix up is needed if
> the final GOT vma is different from when DT_RELR encoding was generated.
>
> > Also, it seems to me that you could have made the functional changes
> > in this patch by implementing an x86 before_allocation routine rather
> > than introducing a new BFD_JUMP_TABLE_LINK entry.  Doing it that way
> > would result in a much smaller patch with few if any changes to
> > generic ELF linker code.
>
> A new linker function, bfd_link_make_reloc_sections, is added to create
> dynamic relocation sections after all input files have been opened if relocation
> check is delayed.  Otherwise, the dynamic relocation sections are missing
> when input sections are being mapped to output sections.   It is much
> cleaner than checking relocations in a before_allocation routine.

The v2 version without a new BFD_JUMP_TABLE_LINK entry is at

https://sourceware.org/pipermail/binutils/2021-December/119050.html

-- 
H.J.

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-12-29 22:16 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-28  2:48 [PATCH] ld: Check ELF relocs before allocation H.J. Lu
2021-12-29  7:46 ` Alan Modra
2021-12-29 16:18   ` H.J. Lu
2021-12-29 22:15     ` H.J. Lu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).