public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2] ld: Check ELF relocs before allocation
@ 2021-12-29 22:14 H.J. Lu
  2022-01-04 23:43 ` Alan Modra
  0 siblings, 1 reply; 9+ messages in thread
From: H.J. Lu @ 2021-12-29 22:14 UTC (permalink / raw)
  To: binutils

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.  This is
only enabled for x86 targets.

If relocation check is delayed, when the first time lang_check_relocs is
called, a new ELF backend function, make_reloc_section, is called to
to create dynamic relocation sections so that they will be mapped to
output sections.  ldelf_before_allocation calls lang_check_relocs again
to check relocations.

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.

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/

	* elf-bfd.h (elf_backend_data): Add make_reloc_section.
	* 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.
	* elfxx-target.h (elf_backend_make_reloc_section): New.
	(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.

include/

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

ld/

	* ldelf.c (ldelf_before_allocation): Call lang_check_relocs if
	relocation check should be done before allocation.
	* ldlang.c (lang_check_relocs): Remove static.  Don't check
	link_info.check_relocs_after_open_input.
	(lang_process): Set link_info.check_relocs_phase to
	check_relocs_after_open_input before calling lang_check_relocs.
	Set link_info.check_relocs_phase to
	check_relocs_before_allocation before calling
	ldemul_before_allocation.
	* ldlang.h (lang_check_relocs): New.
	* ldmain.c (main): Initialize link_info.check_relocs_phase
	to check_relocs_before_open_input.
	* emulparams/elf32_x86_64.sh (CHECK_RELOCS_CHOICE): New.
	* emulparams/elf_i386.sh (CHECK_RELOCS_CHOICE): Likewise.
	* emulparams/elf_i386_be.sh (CHECK_RELOCS_CHOICE): Likewise.
	* emulparams/elf_i386_ldso.sh (CHECK_RELOCS_CHOICE): Likewise.
	* emulparams/elf_i386_vxworks.sh (CHECK_RELOCS_CHOICE): Likewise.
	* emulparams/elf_x86_64.sh (CHECK_RELOCS_CHOICE): Likewise.
	* emultempl/aarch64elf.em (gld${EMULATION_NAME}_before_parse):
	Set link_info.check_relocs_choice 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_CHOICE): New.
	(gld${EMULATION_NAME}_before_parse): Set
	link_info.check_relocs_choice 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/elf-bfd.h                       |   6 ++
 bfd/elf32-i386.c                    | 117 +++++++++++++++++++++-----
 bfd/elf64-x86-64.c                  | 126 +++++++++++++++++++++++-----
 bfd/elflink.c                       |  58 ++++++++++++-
 bfd/elfxx-target.h                  |   4 +
 bfd/elfxx-x86.h                     |  33 --------
 include/bfdlink.h                   |  19 ++++-
 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/ldelf.c                          |   4 +
 ld/ldlang.c                         |  34 ++++----
 ld/ldlang.h                         |   3 +
 ld/ldmain.c                         |   1 +
 ld/testsuite/ld-i386/pr27491-1a.d   |   4 +-
 ld/testsuite/ld-x86-64/pr27491-1a.d |   4 +-
 24 files changed, 322 insertions(+), 111 deletions(-)

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index be2eb38ea6a..1f2c4b3c39c 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
diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c
index d4f8ec3b21d..3f3f1786d8d 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..433170eac93 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -4008,10 +4008,14 @@ _bfd_elf_notice_as_needed (bfd *ibfd,
   return (*info->callbacks->notice) (info, NULL, NULL, ibfd, NULL, act, 0);
 }
 
-/* Check relocations an ELF object file.  */
+/* Implementation of checking 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
+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 +4074,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 +4087,52 @@ _bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
   return true;
 }
 
+/* Check relocations or make reloc sections in an ELF object file,
+   depending on the current check relocs phase and the target choice.  */
+
+bool
+_bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
+{
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  switch (info->check_relocs_choice)
+    {
+    case check_relocs_before_open_input:
+      abort ();
+      break;
+    case check_relocs_after_open_input:
+      switch (info->check_relocs_phase)
+	{
+	case check_relocs_before_open_input:
+	  abort ();
+	  break;
+	case check_relocs_after_open_input:
+	  return elf_link_check_relocs_or_make_reloc_sections
+	    (abfd, info, bed->check_relocs);
+	  break;
+	case check_relocs_before_allocation:
+	  break;
+	}
+      break;
+    case check_relocs_before_allocation:
+      switch (info->check_relocs_phase)
+	{
+	case check_relocs_before_open_input:
+	  abort ();
+	  break;
+	case check_relocs_after_open_input:
+	  return elf_link_check_relocs_or_make_reloc_sections
+	    (abfd, info, bed->make_reloc_section);
+	  break;
+	case check_relocs_before_allocation:
+	  return elf_link_check_relocs_or_make_reloc_sections
+	    (abfd, info, bed->check_relocs);
+	  break;
+	}
+      break;
+    }
+  return true;
+}
+
 /* 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..9ed3d548668 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -474,6 +474,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 +840,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..8251f641a77 100644
--- a/bfd/elfxx-x86.h
+++ b/bfd/elfxx-x86.h
@@ -731,36 +731,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/include/bfdlink.h b/include/bfdlink.h
index 566529ee644..3040c23689d 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,11 @@ 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_choice: 2;
+
+  /* The current phase 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..26b615df944 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_CHOICE=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..994ecb0994d 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_CHOICE=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..65a931a36f3 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_CHOICE=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..0c57f1712c5 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_CHOICE=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..38198b3d9bd 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_CHOICE=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..72439fecfc4 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_CHOICE=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..35ab829b8da 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_choice = $CHECK_RELOCS_CHOICE;
 EOF
 if test -n "$COMMONPAGESIZE"; then
 fragment <<EOF
diff --git a/ld/emultempl/armelf.em b/ld/emultempl/armelf.em
index 7aec17e5ede..a23220af527 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_choice = $CHECK_RELOCS_CHOICE;
 EOF
 if test -n "$COMMONPAGESIZE"; then
 fragment <<EOF
diff --git a/ld/emultempl/elf.em b/ld/emultempl/elf.em
index bfaf8130a3e..1403fd1e3fa 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_CHOICE"; then
+  CHECK_RELOCS_CHOICE=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_choice = $CHECK_RELOCS_CHOICE;
 EOF
 if test -n "$COMMONPAGESIZE"; then
 fragment <<EOF
diff --git a/ld/emultempl/mmix-elfnmmo.em b/ld/emultempl/mmix-elfnmmo.em
index a88fc5a6596..4d94790fe95 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_choice = check_relocs_after_open_input;
   gld${EMULATION_NAME}_before_parse ();
 }
 
diff --git a/ld/emultempl/scoreelf.em b/ld/emultempl/scoreelf.em
index 9aea76a8f51..725976c5d03 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_choice = $CHECK_RELOCS_CHOICE;
 EOF
 if test -n "$COMMONPAGESIZE"; then
 fragment <<EOF
diff --git a/ld/ldelf.c b/ld/ldelf.c
index 529992b02ae..045f584339e 100644
--- a/ld/ldelf.c
+++ b/ld/ldelf.c
@@ -1617,6 +1617,10 @@ ldelf_before_allocation (char *audit, char *depaudit,
       lang_for_each_statement (ldelf_find_statement_assignment);
     }
 
+  /* Check relocations after __ehdr_start has been finalized.  */
+  if (link_info.check_relocs_choice == check_relocs_before_allocation)
+    lang_check_relocs ();
+
   /* Let the ELF backend work out the sizes of any sections required
      by dynamic linking.  */
   rpath = command_line.rpath;
diff --git a/ld/ldlang.c b/ld/ldlang.c
index a0ff1229344..d71e30c42aa 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -7914,24 +7914,21 @@ lang_add_gc_name (const char *name)
 
 /* Check relocations.  */
 
-static void
+void
 lang_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.  */
-	  }
-    }
+  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.  */
+      }
 }
 
 /* Look through all output sections looking for places where we can
@@ -8165,8 +8162,10 @@ lang_process (void)
 
   lang_mark_undefineds ();
 
-  /* Check relocations.  */
-  lang_check_relocs ();
+  /* Check relocations after all input files have been opened.  */
+  link_info.check_relocs_phase = check_relocs_after_open_input;
+  if (link_info.check_relocs_choice != check_relocs_before_open_input)
+    lang_check_relocs ();
 
   ldemul_after_check_relocs ();
 
@@ -8233,6 +8232,7 @@ lang_process (void)
 
   /* Do anything special before sizing sections.  This is where ELF
      and other back-ends size dynamic sections.  */
+  link_info.check_relocs_phase = check_relocs_before_allocation;
   ldemul_before_allocation ();
 
   /* We must record the program headers before we try to fix the
diff --git a/ld/ldlang.h b/ld/ldlang.h
index f68ae27b409..7189c726564 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -717,6 +717,9 @@ lang_print_memory_usage (void);
 extern void
 lang_add_gc_name (const char *);
 
+extern void
+lang_check_relocs (void);
+
 extern bool
 print_one_symbol (struct bfd_link_hash_entry *hash_entry, void *ptr);
 
diff --git a/ld/ldmain.c b/ld/ldmain.c
index 02029237e1a..aefef9029dc 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -359,6 +359,7 @@ main (int argc, char **argv)
 #endif
   link_info.start_stop_gc = false;
   link_info.start_stop_visibility = STV_PROTECTED;
+  link_info.check_relocs_phase = check_relocs_before_open_input;
 
   ldfile_add_arch ("");
   emulation = get_emulation (argc, argv);
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] 9+ messages in thread

* Re: [PATCH v2] ld: Check ELF relocs before allocation
  2021-12-29 22:14 [PATCH v2] ld: Check ELF relocs before allocation H.J. Lu
@ 2022-01-04 23:43 ` Alan Modra
  2022-01-06 15:05   ` [PATCH] ld: Add scan_relocs to ELF linker H.J. Lu
  0 siblings, 1 reply; 9+ messages in thread
From: Alan Modra @ 2022-01-04 23:43 UTC (permalink / raw)
  To: H.J. Lu; +Cc: binutils

On Wed, Dec 29, 2021 at 02:14:31PM -0800, H.J. Lu via Binutils wrote:
> bfd/
> 
> 	* elf-bfd.h (elf_backend_data): Add make_reloc_section.

No, none of the elf-bfd.h, elflink.c, elfxx-target.h, bfdlink.h,
ldlang.c or any other general linker files need changing to support an
x86 separation of check_relocs into two pieces.  You do not need new
linker infrastructure.  You already have that capability by defining
an x86 specific LDEMUL_AFTER_ALLOCATION in elf-x86.em.  Perhaps you
might need to duplicate elf_x86.em into two files, one for 32-bit and
one for 64-bit, or otherwise arrange to call different functions for
x86_64 and ix86.

Doing the x86 DT_RELR support that way will make it easier to port the
support to other architectures.  aarch64, arm, avr, csky, hppa,
m68hc1x, metag, nios2, ppc64, ppc32, all have existing
after_allocation functions that size stubs.  You need something
similar to size the DT_RELR section, after all linker inputs are
seen and symbols resolved, and if you want to minimize DT_RELR size,
a preliminary layout can be done.  It would also be possible to size
DT_RELR in before_allocation, but then you'd need to assume an
address/offset word is emitted whenever changing sections.  That would
be entirely reasonable too.  Either way, I don't see any need to
invent a new way of switching between target functions when you
already have that via LDEMUL_*.

-- 
Alan Modra
Australia Development Lab, IBM

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

* [PATCH] ld: Add scan_relocs to ELF linker
  2022-01-04 23:43 ` Alan Modra
@ 2022-01-06 15:05   ` H.J. Lu
  2022-01-06 22:59     ` Alan Modra
  0 siblings, 1 reply; 9+ messages in thread
From: H.J. Lu @ 2022-01-06 15:05 UTC (permalink / raw)
  To: Alan Modra; +Cc: binutils

On Wed, Jan 05, 2022 at 10:13:29AM +1030, Alan Modra wrote:
> On Wed, Dec 29, 2021 at 02:14:31PM -0800, H.J. Lu via Binutils wrote:
> > bfd/
> > 
> > 	* elf-bfd.h (elf_backend_data): Add make_reloc_section.
> 
> No, none of the elf-bfd.h, elflink.c, elfxx-target.h, bfdlink.h,
> ldlang.c or any other general linker files need changing to support an
> x86 separation of check_relocs into two pieces.  You do not need new
> linker infrastructure.  You already have that capability by defining
> an x86 specific LDEMUL_AFTER_ALLOCATION in elf-x86.em.  Perhaps you
> might need to duplicate elf_x86.em into two files, one for 32-bit and
> one for 64-bit, or otherwise arrange to call different functions for
> x86_64 and ix86.
> 
> Doing the x86 DT_RELR support that way will make it easier to port the
> support to other architectures.  aarch64, arm, avr, csky, hppa,
> m68hc1x, metag, nios2, ppc64, ppc32, all have existing
> after_allocation functions that size stubs.  You need something
> similar to size the DT_RELR section, after all linker inputs are
> seen and symbols resolved, and if you want to minimize DT_RELR size,
> a preliminary layout can be done.  It would also be possible to size
> DT_RELR in before_allocation, but then you'd need to assume an
> address/offset word is emitted whenever changing sections.  That would
> be entirely reasonable too.  Either way, I don't see any need to
> invent a new way of switching between target functions when you
> already have that via LDEMUL_*.
> 

Here is a patch to add scan_relocs to ELF linker.  It is called in
ldelf_before_allocation after rel_from_abs has been set on __ehdr_start.


H.J.
--
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, add scan_relocs
to ELF linker to scan 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.  ldelf_before_allocation is updated to call scan_relocs after
rel_from_abs has been set on __ehdr_start.

For x86 targets, the old check_relocs is renamed to scan_relocs and a
new check_relocs is added to chek input sections and create dynamic
relocation sections so that they will be mapped to output sections.

Since relocations are scanned 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/

	* elf-bfd.h (elf_backend_data): Add scan_relocs.
	* elf32-i386.c (elf_i386_convert_load_reloc): Don't call
	elf_x86_start_stop_gc_p.
	(elf_i386_check_relocs): Renamed to ...
	(elf_i386_scan_relocs): This.  Don't call
	_bfd_elf_make_dynamic_reloc_section.
	(elf_backend_check_relocs): Removed.
	(elf_backend_scan_relocs): New.
	* elf64-x86-64.c (elf_x86_64_convert_load_reloc): Don't call
	elf_x86_start_stop_gc_p.
	(elf_x86_64_check_relocs): Renamed to ...
	(elf_x86_64_scan_relocs): This.  Don't call
	_bfd_elf_make_dynamic_reloc_section.
	(elf_backend_check_relocs): Removed.
	(elf_backend_scan_relocs): New.
	* elflink.c (elf_link_check_or_scan_relocs):
	New.  Renamed and modified from _bfd_elf_link_check_relocs.
	(_bfd_elf_link_check_relocs): New.
	(_bfd_elf_link_scan_relocs): Likewise.
	* elfxx-target.h (elf_backend_scan_relocs): New.
	(elfNN_bed): Add elf_backend_scan_relocs.
	* elfxx-x86.c (_bfd_x86_elf_check_relocs): New.
	* elfxx-x86.h (X86_64_NEED_DYNAMIC_RELOC_TYPE_P): New.
	(I386_NEED_DYNAMIC_RELOC_TYPE_P): Likewise.
	(X86_NEED_DYNAMIC_RELOC_TYPE_P): Likewise.
	(_bfd_x86_elf_check_relocs): Likewise.
	(elf_backend_check_relocs): Likewise.
	(elf_x86_start_stop_gc_p): Removed.

ld/

	* ldelf.c (ldelf_before_allocation): If the backend has
	scan_relocs, call _bfd_elf_link_scan_relocs after rel_from_abs
	has been set on __ehdr_start.
	* testsuite/ld-i386/pr27491-1a.d: Updated.
	* testsuite/ld-x86-64/pr27491-1a.d: Likewise.
---
 bfd/elf-bfd.h                       |  9 +++
 bfd/elf32-i386.c                    | 40 ++++----------
 bfd/elf64-x86-64.c                  | 37 +++----------
 bfd/elflink.c                       | 36 ++++++++++--
 bfd/elfxx-target.h                  |  4 ++
 bfd/elfxx-x86.c                     | 85 +++++++++++++++++++++++++++++
 bfd/elfxx-x86.h                     | 58 +++++++++-----------
 ld/ldelf.c                          | 18 +++++-
 ld/testsuite/ld-i386/pr27491-1a.d   |  4 +-
 ld/testsuite/ld-x86-64/pr27491-1a.d |  4 +-
 10 files changed, 193 insertions(+), 102 deletions(-)

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 896aa08fd76..d42c3cf3ec9 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -1104,6 +1104,13 @@ struct elf_backend_data
     (bfd *abfd, struct bfd_link_info *info, asection *o,
      const Elf_Internal_Rela *relocs);
 
+  /* The SCAN_RELOCS function is similar to the CHECK_RELOCS function.
+     But it is called before allocation from linker, instead of after
+     input files have been opened.  */
+  bool (*scan_relocs)
+    (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
@@ -2632,6 +2639,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_scan_relocs
+  (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 97962072ff0..4b011f8f945 100644
--- a/bfd/elf32-i386.c
+++ b/bfd/elf32-i386.c
@@ -522,7 +522,7 @@ elf_i386_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
    Functions named elf_i386_* are called by external routines, other
    functions are only called locally.  elf_i386_* functions appear
    in this file more or less in the order in which they are called
-   from external routines.  eg. elf_i386_check_relocs is called
+   from external routines.  eg. elf_i386_scan_relocs is called
    early in the link process, elf_i386_finish_dynamic_sections is
    one of the last functions.  */
 
@@ -1106,7 +1106,7 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
 	    }
 
 	  /* We checked the transition before when we were called from
-	     elf_i386_check_relocs.  We only want to check the new
+	     elf_i386_scan_relocs.  We only want to check the new
 	     transition which hasn't been checked before.  */
 	  check = new_to_type != to_type && from_type == to_type;
 	  to_type = new_to_type;
@@ -1387,11 +1387,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)
 	    {
@@ -1452,21 +1447,20 @@ elf_i386_convert_load_reloc (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
 }
 
 /* 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,
-		       struct bfd_link_info *info,
-		       asection *sec,
-		       const Elf_Internal_Rela *relocs)
+elf_i386_scan_relocs (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;
   bfd_byte *contents;
   bool converted;
 
@@ -1496,8 +1490,6 @@ elf_i386_check_relocs (bfd *abfd,
 
   converted = false;
 
-  sreloc = NULL;
-
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
@@ -1818,18 +1810,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)
@@ -2000,7 +1980,7 @@ elf_i386_relocate_section (bfd *output_bfd,
   bool is_vxworks_tls;
   unsigned plt_entry_size;
 
-  /* Skip if check_relocs failed.  */
+  /* Skip if check_relocs or scan_relocs failed.  */
   if (input_section->check_relocs_failed)
     return false;
 
@@ -4390,7 +4370,7 @@ elf_i386_link_setup_gnu_properties (struct bfd_link_info *info)
 #define bfd_elf32_get_synthetic_symtab	      elf_i386_get_synthetic_symtab
 
 #define elf_backend_relocs_compatible	      _bfd_elf_relocs_compatible
-#define elf_backend_check_relocs	      elf_i386_check_relocs
+#define elf_backend_scan_relocs		      elf_i386_scan_relocs
 #define elf_backend_create_dynamic_sections   _bfd_elf_create_dynamic_sections
 #define elf_backend_fake_sections	      elf_i386_fake_sections
 #define elf_backend_finish_dynamic_sections   elf_i386_finish_dynamic_sections
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 22aa3ee3b68..3799f25ccd3 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1309,7 +1309,7 @@ elf_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd,
 	    }
 
 	  /* We checked the transition before when we were called from
-	     elf_x86_64_check_relocs.  We only want to check the new
+	     elf_x86_64_scan_relocs.  We only want to check the new
 	     transition which hasn't been checked before.  */
 	  check = new_to_type != to_type && from_type == to_type;
 	  to_type = new_to_type;
@@ -1628,11 +1628,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;
@@ -1823,20 +1818,19 @@ 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,
-			 asection *sec,
-			 const Elf_Internal_Rela *relocs)
+elf_x86_64_scan_relocs (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;
   bfd_byte *contents;
   bool converted;
 
@@ -1866,8 +1860,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++)
     {
@@ -2263,19 +2255,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)
@@ -2413,7 +2392,7 @@ elf_x86_64_relocate_section (bfd *output_bfd,
   unsigned int plt_entry_size;
   bool status;
 
-  /* Skip if check_relocs failed.  */
+  /* Skip if check_relocs or scan_relocs failed.  */
   if (input_section->check_relocs_failed)
     return false;
 
@@ -5238,7 +5217,7 @@ elf_x86_64_special_sections[]=
   elf_x86_64_reloc_name_lookup
 
 #define elf_backend_relocs_compatible	    elf_x86_64_relocs_compatible
-#define elf_backend_check_relocs	    elf_x86_64_check_relocs
+#define elf_backend_scan_relocs		    elf_x86_64_scan_relocs
 #define elf_backend_create_dynamic_sections _bfd_elf_create_dynamic_sections
 #define elf_backend_finish_dynamic_sections elf_x86_64_finish_dynamic_sections
 #define elf_backend_finish_dynamic_symbol   elf_x86_64_finish_dynamic_symbol
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 553efa26232..1f0dc9f520a 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -4008,10 +4008,14 @@ _bfd_elf_notice_as_needed (bfd *ibfd,
   return (*info->callbacks->notice) (info, NULL, NULL, ibfd, NULL, act, 0);
 }
 
-/* Check relocations an ELF object file.  */
+/* Implementation of checking or scaning relocations in an ELF object
+   file.  */
 
-bool
-_bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
+static bool
+elf_link_check_or_scan_relocs
+  (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 +4074,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 +4087,30 @@ _bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
   return true;
 }
 
+/* Check relocations in an ELF object file.  This is called after
+   all input files have been opened.  */
+
+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 elf_link_check_or_scan_relocs (abfd, info, bed->check_relocs);
+}
+
+/* Scan relocations in an ELF object file.  This is called before
+   allocation.  */
+
+bool
+_bfd_elf_link_scan_relocs (bfd *abfd, struct bfd_link_info *info)
+{
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  if (bed != NULL
+      && bed->scan_relocs != NULL)
+    return elf_link_check_or_scan_relocs (abfd, info, bed->scan_relocs);
+  else
+    return true;
+}
+
 /* 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 360b056ff58..7b8ccd6814c 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -478,6 +478,9 @@
 #ifndef elf_backend_check_relocs
 #define elf_backend_check_relocs	0
 #endif
+#ifndef elf_backend_scan_relocs
+#define elf_backend_scan_relocs	0
+#endif
 #ifndef elf_backend_check_directives
 #define elf_backend_check_directives	0
 #endif
@@ -842,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_scan_relocs,
   elf_backend_check_directives,
   elf_backend_notice_as_needed,
   elf_backend_adjust_dynamic_symbol,
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index ca4b90e22cc..25f7717ea88 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -892,6 +892,91 @@ _bfd_x86_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
   return _bfd_elf_link_check_relocs (abfd, info);
 }
 
+/* Look through the relocs for a section before allocation to make the
+   dynamic reloc section.  */
+
+bool
+_bfd_x86_elf_check_relocs (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;
+  const struct elf_backend_data *bed;
+  bool is_x86_64;
+
+  if (bfd_link_relocatable (info))
+    return true;
+
+  bed = get_elf_backend_data (abfd);
+  htab = elf_x86_hash_table (info, bed->target_id);
+  if (htab == NULL)
+    {
+      sec->check_relocs_failed = 1;
+      return false;
+    }
+
+  is_x86_64 = bed->target_id == X86_64_ELF_DATA;
+
+  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;
+	}
+
+      if (X86_NEED_DYNAMIC_RELOC_TYPE_P (is_x86_64, r_type)
+	  && NEED_DYNAMIC_RELOCATION_P (is_x86_64, 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, sec->use_rela_p);
+
+	  if (sreloc != NULL)
+	    return true;
+
+  error_return:
+	  sec->check_relocs_failed = 1;
+	  return false;
+	}
+    }
+
+  return true;
+}
+
 bool
 _bfd_elf_x86_valid_reloc_p (asection *input_section,
 			    struct bfd_link_info *info,
diff --git a/bfd/elfxx-x86.h b/bfd/elfxx-x86.h
index 16565a5638b..927c5a59475 100644
--- a/bfd/elfxx-x86.h
+++ b/bfd/elfxx-x86.h
@@ -47,6 +47,25 @@
 #define X86_SIZE_TYPE_P(IS_X86_64, TYPE) \
   ((IS_X86_64) ? X86_64_SIZE_TYPE_P(TYPE) : I386_SIZE_TYPE_P (TYPE))
 
+#define X86_64_NEED_DYNAMIC_RELOC_TYPE_P(TYPE) \
+  (X86_64_SIZE_TYPE_P (TYPE) \
+   || X86_64_PCREL_TYPE_P (TYPE) \
+   || (TYPE) == R_X86_64_8 \
+   || (TYPE) == R_X86_64_16 \
+   || (TYPE) == R_X86_64_32 \
+   || (TYPE) == R_X86_64_32S \
+   || (TYPE) == R_X86_64_64)
+#define I386_NEED_DYNAMIC_RELOC_TYPE_P(TYPE) \
+  (I386_SIZE_TYPE_P (TYPE) \
+   || I386_PCREL_TYPE_P (TYPE) \
+   || (TYPE) == R_386_32 \
+   || (TYPE) == R_386_TLS_LE \
+   || (TYPE) == R_386_TLS_LE_32)
+#define X86_NEED_DYNAMIC_RELOC_TYPE_P(IS_X86_64, TYPE) \
+  ((IS_X86_64) \
+   ? X86_64_NEED_DYNAMIC_RELOC_TYPE_P (TYPE) \
+   : I386_NEED_DYNAMIC_RELOC_TYPE_P (TYPE))
+
 #define PLT_CIE_LENGTH		20
 #define PLT_FDE_LENGTH		36
 #define PLT_FDE_START_OFFSET	4 + PLT_CIE_LENGTH + 8
@@ -653,6 +672,10 @@ extern int _bfd_x86_elf_compare_relocs
 extern bool _bfd_x86_elf_link_check_relocs
   (bfd *, struct bfd_link_info *);
 
+extern bool _bfd_x86_elf_check_relocs
+  (bfd *, struct bfd_link_info *, asection *,
+   const Elf_Internal_Rela *);
+
 extern bool _bfd_elf_x86_valid_reloc_p
   (asection *, struct bfd_link_info *, struct elf_x86_link_hash_table *,
    const Elf_Internal_Rela *, struct elf_link_hash_entry *,
@@ -730,6 +753,8 @@ extern void _bfd_x86_elf_link_report_relative_reloc
 #define bfd_elf32_bfd_link_check_relocs \
   _bfd_x86_elf_link_check_relocs
 
+#define elf_backend_check_relocs \
+  _bfd_x86_elf_check_relocs
 #define elf_backend_size_dynamic_sections \
   _bfd_x86_elf_size_dynamic_sections
 #define elf_backend_always_size_sections \
@@ -757,39 +782,6 @@ extern void _bfd_x86_elf_link_report_relative_reloc
 
 #define ELF_P_ALIGN ELF_MINPAGESIZE
 
-/* 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;
-}
-
 /* Allocate x86 GOT info for local symbols.  */
 
 static inline bool
diff --git a/ld/ldelf.c b/ld/ldelf.c
index d15f027e91a..a1b3a2086e6 100644
--- a/ld/ldelf.c
+++ b/ld/ldelf.c
@@ -1566,6 +1566,7 @@ ldelf_before_allocation (char *audit, char *depaudit,
   const char *rpath;
   asection *sinterp;
   bfd *abfd;
+  const struct elf_backend_data *bed = NULL;
   struct bfd_link_hash_entry *ehdr_start = NULL;
   unsigned char ehdr_start_save_type = 0;
   char ehdr_start_save_u[sizeof ehdr_start->u
@@ -1573,6 +1574,8 @@ ldelf_before_allocation (char *audit, char *depaudit,
 
   if (is_elf_hash_table (link_info.hash))
     {
+      bed = get_elf_backend_data (link_info.output_bfd);
+
       _bfd_elf_tls_setup (link_info.output_bfd, &link_info);
 
       /* Make __ehdr_start hidden if it has been referenced, to
@@ -1591,8 +1594,6 @@ ldelf_before_allocation (char *audit, char *depaudit,
 		  || h->root.type == bfd_link_hash_undefweak
 		  || h->root.type == bfd_link_hash_common))
 	    {
-	      const struct elf_backend_data *bed;
-	      bed = get_elf_backend_data (link_info.output_bfd);
 	      (*bed->elf_backend_hide_symbol) (&link_info, h, true);
 	      if (ELF_ST_VISIBILITY (h->other) != STV_INTERNAL)
 		h->other = (h->other & ~ELF_ST_VISIBILITY (-1)) | STV_HIDDEN;
@@ -1620,6 +1621,19 @@ ldelf_before_allocation (char *audit, char *depaudit,
       lang_for_each_statement (ldelf_find_statement_assignment);
     }
 
+  /* Scan relocations after rel_from_abs has been set on __ehdr_start.  */
+  if (bed != NULL && bed->scan_relocs != 0)
+    for (abfd = link_info.input_bfds;
+	 abfd != (bfd *) NULL; abfd = abfd->link.next)
+      if (!_bfd_elf_link_scan_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.  */
+	}
+
   /* Let the ELF backend work out the sizes of any sections required
      by dynamic linking.  */
   rpath = command_line.rpath;
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] 9+ messages in thread

* Re: [PATCH] ld: Add scan_relocs to ELF linker
  2022-01-06 15:05   ` [PATCH] ld: Add scan_relocs to ELF linker H.J. Lu
@ 2022-01-06 22:59     ` Alan Modra
  2022-01-06 23:22       ` H.J. Lu
  0 siblings, 1 reply; 9+ messages in thread
From: Alan Modra @ 2022-01-06 22:59 UTC (permalink / raw)
  To: H.J. Lu; +Cc: binutils

On Thu, Jan 06, 2022 at 07:05:48AM -0800, H.J. Lu wrote:
> On Wed, Jan 05, 2022 at 10:13:29AM +1030, Alan Modra wrote:
> > On Wed, Dec 29, 2021 at 02:14:31PM -0800, H.J. Lu via Binutils wrote:
> > > bfd/
> > > 
> > > 	* elf-bfd.h (elf_backend_data): Add make_reloc_section.
> > 
> > No, none of the elf-bfd.h, elflink.c, elfxx-target.h, bfdlink.h,
> > ldlang.c or any other general linker files need changing to support an
> > x86 separation of check_relocs into two pieces.

Are you hoping that someone else will approve the patch, ignoring what
I said above?

> Here is a patch to add scan_relocs to ELF linker.  It is called in
> ldelf_before_allocation after rel_from_abs has been set on __ehdr_start.

Did you consider using elf_backend_always_size_sections?  We have lots
of hooks already, no need to create more if one of the existing ones
works.

-- 
Alan Modra
Australia Development Lab, IBM

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

* Re: [PATCH] ld: Add scan_relocs to ELF linker
  2022-01-06 22:59     ` Alan Modra
@ 2022-01-06 23:22       ` H.J. Lu
  2022-01-07  3:16         ` Alan Modra
  0 siblings, 1 reply; 9+ messages in thread
From: H.J. Lu @ 2022-01-06 23:22 UTC (permalink / raw)
  To: Alan Modra; +Cc: Binutils

On Thu, Jan 6, 2022 at 2:59 PM Alan Modra <amodra@gmail.com> wrote:
>
> On Thu, Jan 06, 2022 at 07:05:48AM -0800, H.J. Lu wrote:
> > On Wed, Jan 05, 2022 at 10:13:29AM +1030, Alan Modra wrote:
> > > On Wed, Dec 29, 2021 at 02:14:31PM -0800, H.J. Lu via Binutils wrote:
> > > > bfd/
> > > >
> > > >   * elf-bfd.h (elf_backend_data): Add make_reloc_section.
> > >
> > > No, none of the elf-bfd.h, elflink.c, elfxx-target.h, bfdlink.h,
> > > ldlang.c or any other general linker files need changing to support an
> > > x86 separation of check_relocs into two pieces.
>
> Are you hoping that someone else will approve the patch, ignoring what
> I said above?

I made the change based on what you said.

> > Here is a patch to add scan_relocs to ELF linker.  It is called in
> > ldelf_before_allocation after rel_from_abs has been set on __ehdr_start.
>
> Did you consider using elf_backend_always_size_sections?  We have lots
> of hooks already, no need to create more if one of the existing ones
> works.
>

elf_backend_always_size_sections is called too late to scan input
relocation.  elf_backend_always_size_sections works for me when
it is called at the beginning of bfd_elf_size_dynamic_sections, like

diff --git a/bfd/elflink.c b/bfd/elflink.c
index 4d545170d42..504b493fce1 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -6626,6 +6626,14 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
   if (!is_elf_hash_table (info->hash))
     return true;

+  bed = get_elf_backend_data (output_bfd);
+
+  /* The backend may have to create some sections regardless of whether
+     we're dynamic or not.  */
+  if (bed->elf_backend_always_size_sections
+      && ! (*bed->elf_backend_always_size_sections) (output_bfd, info))
+    return false;
+
   dynobj = elf_hash_table (info)->dynobj;

   if (dynobj != NULL && elf_hash_table (info)->dynamic_sections_created)
@@ -6984,8 +6992,6 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
  }
     }

-  bed = get_elf_backend_data (output_bfd);
-
   if (info->gc_sections && bed->can_gc_sections)
     {
       struct elf_gc_sweep_symbol_info sweep_info;
@@ -7125,12 +7131,6 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
       && !_bfd_elf_size_group_sections (info))
     return false;

-  /* The backend may have to create some sections regardless of whether
-     we're dynamic or not.  */
-  if (bed->elf_backend_always_size_sections
-      && ! (*bed->elf_backend_always_size_sections) (output_bfd, info))
-    return false;
-
   /* Determine any GNU_STACK segment requirements, after the backend
      has had a chance to set a default segment size.  */
   if (info->execstack)

If this patch is OK, I can use elf_backend_always_size_sections to
scan input relocation.

-- 
H.J.

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

* Re: [PATCH] ld: Add scan_relocs to ELF linker
  2022-01-06 23:22       ` H.J. Lu
@ 2022-01-07  3:16         ` Alan Modra
  2022-01-07  4:24           ` [PATCH] ld: Extract _bfd_elf_link_check_or_scan_relocs H.J. Lu
  0 siblings, 1 reply; 9+ messages in thread
From: Alan Modra @ 2022-01-07  3:16 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Binutils

On Thu, Jan 06, 2022 at 03:22:22PM -0800, H.J. Lu wrote:
> elf_backend_always_size_sections is called too late to scan input
> relocation.  elf_backend_always_size_sections works for me when
> it is called at the beginning of bfd_elf_size_dynamic_sections, like

I think that should be OK, with the init_{got,plt}_refcount moved too.

	* elflink.c (bfd_elf_size_dynamic_sections): Move plt/got init
	earlier and call elf_backend_always_size_sections at the start
	of this function.

diff --git a/bfd/elflink.c b/bfd/elflink.c
index 553efa26232..08c161713cc 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -6506,6 +6506,21 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
   if (!is_elf_hash_table (info->hash))
     return true;
 
+  /* Any syms created from now on start with -1 in
+     got.refcount/offset and plt.refcount/offset.  */
+  elf_hash_table (info)->init_got_refcount
+    = elf_hash_table (info)->init_got_offset;
+  elf_hash_table (info)->init_plt_refcount
+    = elf_hash_table (info)->init_plt_offset;
+
+  bed = get_elf_backend_data (output_bfd);
+
+  /* The backend may have to create some sections regardless of whether
+     we're dynamic or not.  */
+  if (bed->elf_backend_always_size_sections
+      && ! (*bed->elf_backend_always_size_sections) (output_bfd, info))
+    return false;
+
   dynobj = elf_hash_table (info)->dynobj;
 
   if (dynobj != NULL && elf_hash_table (info)->dynamic_sections_created)
@@ -6864,8 +6879,6 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
 	}
     }
 
-  bed = get_elf_backend_data (output_bfd);
-
   if (info->gc_sections && bed->can_gc_sections)
     {
       struct elf_gc_sweep_symbol_info sweep_info;
@@ -6987,23 +7000,10 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
 	}
     }
 
-  /* Any syms created from now on start with -1 in
-     got.refcount/offset and plt.refcount/offset.  */
-  elf_hash_table (info)->init_got_refcount
-    = elf_hash_table (info)->init_got_offset;
-  elf_hash_table (info)->init_plt_refcount
-    = elf_hash_table (info)->init_plt_offset;
-
   if (bfd_link_relocatable (info)
       && !_bfd_elf_size_group_sections (info))
     return false;
 
-  /* The backend may have to create some sections regardless of whether
-     we're dynamic or not.  */
-  if (bed->elf_backend_always_size_sections
-      && ! (*bed->elf_backend_always_size_sections) (output_bfd, info))
-    return false;
-
   /* Determine any GNU_STACK segment requirements, after the backend
      has had a chance to set a default segment size.  */
   if (info->execstack)

-- 
Alan Modra
Australia Development Lab, IBM

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

* [PATCH] ld: Extract _bfd_elf_link_check_or_scan_relocs
  2022-01-07  3:16         ` Alan Modra
@ 2022-01-07  4:24           ` H.J. Lu
  2022-01-08  1:14             ` Alan Modra
  0 siblings, 1 reply; 9+ messages in thread
From: H.J. Lu @ 2022-01-07  4:24 UTC (permalink / raw)
  To: Alan Modra; +Cc: Binutils

On Fri, Jan 07, 2022 at 01:46:33PM +1030, Alan Modra wrote:
> On Thu, Jan 06, 2022 at 03:22:22PM -0800, H.J. Lu wrote:
> > elf_backend_always_size_sections is called too late to scan input
> > relocation.  elf_backend_always_size_sections works for me when
> > it is called at the beginning of bfd_elf_size_dynamic_sections, like
> 
> I think that should be OK, with the init_{got,plt}_refcount moved too.
> 
> 	* elflink.c (bfd_elf_size_dynamic_sections): Move plt/got init
> 	earlier and call elf_backend_always_size_sections at the start
> 	of this function.
> 

Thanks.  Now I can use elf_backend_always_size_sections to scan input
relocations.  Here is a patch to extract _bfd_elf_link_check_or_scan_relocs
so that I can use it to scan relocations.

OK for master?

Thanks.

H.J.
---
DT_RELR encodes consecutive R_*_RELATIVE relocations in GOT (the global
offset table) and data sections 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, extract
_bfd_elf_link_check_or_scan_relocs from _bfd_elf_link_check_relocs so
that a backend can scan relocations in elf_backend_always_size_sections

For x86 targets, the old check_relocs is renamed to scan_relocs and a
new check_relocs is added to chek input sections and create dynamic
relocation sections so that they will be mapped to output sections.
scan_relocs is now called from elf_backend_always_size_sections.

Since relocations are scanned 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/

	* elf-bfd.h (_bfd_elf_link_check_or_scan_relocs): New.
	* elf32-i386.c (elf_i386_convert_load_reloc): Don't call
	elf_x86_start_stop_gc_p.
	(elf_i386_check_relocs): Renamed to ...
	(elf_i386_scan_relocs): This.  Don't call
	_bfd_elf_make_dynamic_reloc_section.
	(elf_i386_always_size_sections): New.
	(elf_backend_check_relocs): Removed.
	(elf_backend_always_size_sections): New.
	* elf64-x86-64.c (elf_x86_64_convert_load_reloc): Don't call
	elf_x86_start_stop_gc_p.
	(elf_x86_64_check_relocs): Renamed to ...
	(elf_x86_64_scan_relocs): This.  Don't call
	_bfd_elf_make_dynamic_reloc_section.
	(elf_x86_64_always_size_sections): New.
	(elf_backend_check_relocs): Removed.
	(elf_backend_always_size_sections): New.
	* elflink.c (elf_link_check_or_scan_relocs):
	New.  Extracted from _bfd_elf_link_check_relocs.
	(_bfd_elf_link_check_relocs): Call elf_link_check_or_scan_relocs.
	* elfxx-x86.c (_bfd_x86_elf_check_relocs): New.
	* elfxx-x86.h (X86_64_NEED_DYNAMIC_RELOC_TYPE_P): New.
	(I386_NEED_DYNAMIC_RELOC_TYPE_P): Likewise.
	(X86_NEED_DYNAMIC_RELOC_TYPE_P): Likewise.
	(_bfd_x86_elf_check_relocs): Likewise.
	(elf_backend_check_relocs): Likewise.
	(elf_backend_always_size_sections): Removed.
	(elf_x86_start_stop_gc_p): Likewise.

ld/

	* testsuite/ld-i386/pr27491-1a.d: Updated.
	* testsuite/ld-x86-64/pr27491-1a.d: Likewise.
---
 bfd/elf-bfd.h                       |  4 ++
 bfd/elf32-i386.c                    | 58 ++++++++++----------
 bfd/elf64-x86-64.c                  | 55 +++++++++----------
 bfd/elflink.c                       | 24 ++++++--
 bfd/elfxx-x86.c                     | 85 +++++++++++++++++++++++++++++
 bfd/elfxx-x86.h                     | 60 +++++++++-----------
 ld/testsuite/ld-i386/pr27491-1a.d   |  4 +-
 ld/testsuite/ld-x86-64/pr27491-1a.d |  4 +-
 8 files changed, 192 insertions(+), 102 deletions(-)

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 896aa08fd76..8442753bcb5 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -2632,6 +2632,10 @@ 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_check_or_scan_relocs
+ (bfd *, struct bfd_link_info *,
+  bool (*action) (bfd *, struct bfd_link_info *, asection *,
+		  const Elf_Internal_Rela *));
 
 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 97962072ff0..5df38be1335 100644
--- a/bfd/elf32-i386.c
+++ b/bfd/elf32-i386.c
@@ -522,7 +522,7 @@ elf_i386_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
    Functions named elf_i386_* are called by external routines, other
    functions are only called locally.  elf_i386_* functions appear
    in this file more or less in the order in which they are called
-   from external routines.  eg. elf_i386_check_relocs is called
+   from external routines.  eg. elf_i386_scan_relocs is called
    early in the link process, elf_i386_finish_dynamic_sections is
    one of the last functions.  */
 
@@ -1106,7 +1106,7 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
 	    }
 
 	  /* We checked the transition before when we were called from
-	     elf_i386_check_relocs.  We only want to check the new
+	     elf_i386_scan_relocs.  We only want to check the new
 	     transition which hasn't been checked before.  */
 	  check = new_to_type != to_type && from_type == to_type;
 	  to_type = new_to_type;
@@ -1387,11 +1387,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)
 	    {
@@ -1452,21 +1447,20 @@ elf_i386_convert_load_reloc (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
 }
 
 /* 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,
-		       struct bfd_link_info *info,
-		       asection *sec,
-		       const Elf_Internal_Rela *relocs)
+elf_i386_scan_relocs (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;
   bfd_byte *contents;
   bool converted;
 
@@ -1496,8 +1490,6 @@ elf_i386_check_relocs (bfd *abfd,
 
   converted = false;
 
-  sreloc = NULL;
-
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
@@ -1818,18 +1810,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)
@@ -1924,6 +1904,24 @@ elf_i386_check_relocs (bfd *abfd,
   return false;
 }
 
+static bool
+elf_i386_always_size_sections (bfd *output_bfd,
+			       struct bfd_link_info *info)
+{
+  bfd *abfd;
+
+  /* Scan relocations after rel_from_abs has been set on __ehdr_start.  */
+  for (abfd = info->input_bfds;
+       abfd != (bfd *) NULL;
+       abfd = abfd->link.next)
+    if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+	&& !_bfd_elf_link_check_or_scan_relocs (abfd, info,
+						elf_i386_scan_relocs))
+      return false;
+
+  return _bfd_x86_elf_always_size_sections (output_bfd, info);
+}
+
 /* Set the correct type for an x86 ELF section.  We do this by the
    section name, which is a hack, but ought to work.  */
 
@@ -2000,7 +1998,7 @@ elf_i386_relocate_section (bfd *output_bfd,
   bool is_vxworks_tls;
   unsigned plt_entry_size;
 
-  /* Skip if check_relocs failed.  */
+  /* Skip if check_relocs or scan_relocs failed.  */
   if (input_section->check_relocs_failed)
     return false;
 
@@ -4390,7 +4388,7 @@ elf_i386_link_setup_gnu_properties (struct bfd_link_info *info)
 #define bfd_elf32_get_synthetic_symtab	      elf_i386_get_synthetic_symtab
 
 #define elf_backend_relocs_compatible	      _bfd_elf_relocs_compatible
-#define elf_backend_check_relocs	      elf_i386_check_relocs
+#define elf_backend_always_size_sections      elf_i386_always_size_sections
 #define elf_backend_create_dynamic_sections   _bfd_elf_create_dynamic_sections
 #define elf_backend_fake_sections	      elf_i386_fake_sections
 #define elf_backend_finish_dynamic_sections   elf_i386_finish_dynamic_sections
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 22aa3ee3b68..6c5601d4fc2 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1309,7 +1309,7 @@ elf_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd,
 	    }
 
 	  /* We checked the transition before when we were called from
-	     elf_x86_64_check_relocs.  We only want to check the new
+	     elf_x86_64_scan_relocs.  We only want to check the new
 	     transition which hasn't been checked before.  */
 	  check = new_to_type != to_type && from_type == to_type;
 	  to_type = new_to_type;
@@ -1628,11 +1628,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;
@@ -1823,20 +1818,19 @@ 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,
-			 asection *sec,
-			 const Elf_Internal_Rela *relocs)
+elf_x86_64_scan_relocs (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;
   bfd_byte *contents;
   bool converted;
 
@@ -1866,8 +1860,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++)
     {
@@ -2263,19 +2255,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)
@@ -2371,6 +2350,24 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
   return false;
 }
 
+static bool
+elf_x86_64_always_size_sections (bfd *output_bfd,
+				 struct bfd_link_info *info)
+{
+  bfd *abfd;
+
+  /* Scan relocations after rel_from_abs has been set on __ehdr_start.  */
+  for (abfd = info->input_bfds;
+       abfd != (bfd *) NULL;
+       abfd = abfd->link.next)
+    if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+	&& !_bfd_elf_link_check_or_scan_relocs (abfd, info,
+						elf_x86_64_scan_relocs))
+      return false;
+
+  return _bfd_x86_elf_always_size_sections (output_bfd, info);
+}
+
 /* Return the relocation value for @tpoff relocation
    if STT_TLS virtual address is ADDRESS.  */
 
@@ -2413,7 +2410,7 @@ elf_x86_64_relocate_section (bfd *output_bfd,
   unsigned int plt_entry_size;
   bool status;
 
-  /* Skip if check_relocs failed.  */
+  /* Skip if check_relocs or scan_relocs failed.  */
   if (input_section->check_relocs_failed)
     return false;
 
@@ -5238,7 +5235,7 @@ elf_x86_64_special_sections[]=
   elf_x86_64_reloc_name_lookup
 
 #define elf_backend_relocs_compatible	    elf_x86_64_relocs_compatible
-#define elf_backend_check_relocs	    elf_x86_64_check_relocs
+#define elf_backend_always_size_sections    elf_x86_64_always_size_sections
 #define elf_backend_create_dynamic_sections _bfd_elf_create_dynamic_sections
 #define elf_backend_finish_dynamic_sections elf_x86_64_finish_dynamic_sections
 #define elf_backend_finish_dynamic_symbol   elf_x86_64_finish_dynamic_symbol
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 08c161713cc..b166d47051a 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -4008,10 +4008,14 @@ _bfd_elf_notice_as_needed (bfd *ibfd,
   return (*info->callbacks->notice) (info, NULL, NULL, ibfd, NULL, act, 0);
 }
 
-/* Check relocations an ELF object file.  */
+/* Implementation of checking or scanning relocations in an ELF object
+   file.  */
 
 bool
-_bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
+_bfd_elf_link_check_or_scan_relocs
+  (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);
@@ -4035,7 +4039,6 @@ _bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
      different format.  It probably can't be done.  */
   if ((abfd->flags & DYNAMIC) == 0
       && is_elf_hash_table (&htab->root)
-      && bed->check_relocs != NULL
       && elf_object_id (abfd) == elf_hash_table_id (htab)
       && (*bed->relocs_compatible) (abfd->xvec, info->output_bfd->xvec))
     {
@@ -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,19 @@ _bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
   return true;
 }
 
+/* Check relocations in an ELF object file.  This is called after
+   all input files have been opened.  */
+
+bool
+_bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
+{
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  if (bed->check_relocs != NULL)
+    return _bfd_elf_link_check_or_scan_relocs (abfd, info,
+					       bed->check_relocs);
+  return true;
+}
+
 /* Add symbols from an ELF object file to the linker hash table.  */
 
 static bool
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index ca4b90e22cc..25f7717ea88 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -892,6 +892,91 @@ _bfd_x86_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
   return _bfd_elf_link_check_relocs (abfd, info);
 }
 
+/* Look through the relocs for a section before allocation to make the
+   dynamic reloc section.  */
+
+bool
+_bfd_x86_elf_check_relocs (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;
+  const struct elf_backend_data *bed;
+  bool is_x86_64;
+
+  if (bfd_link_relocatable (info))
+    return true;
+
+  bed = get_elf_backend_data (abfd);
+  htab = elf_x86_hash_table (info, bed->target_id);
+  if (htab == NULL)
+    {
+      sec->check_relocs_failed = 1;
+      return false;
+    }
+
+  is_x86_64 = bed->target_id == X86_64_ELF_DATA;
+
+  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;
+	}
+
+      if (X86_NEED_DYNAMIC_RELOC_TYPE_P (is_x86_64, r_type)
+	  && NEED_DYNAMIC_RELOCATION_P (is_x86_64, 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, sec->use_rela_p);
+
+	  if (sreloc != NULL)
+	    return true;
+
+  error_return:
+	  sec->check_relocs_failed = 1;
+	  return false;
+	}
+    }
+
+  return true;
+}
+
 bool
 _bfd_elf_x86_valid_reloc_p (asection *input_section,
 			    struct bfd_link_info *info,
diff --git a/bfd/elfxx-x86.h b/bfd/elfxx-x86.h
index 16565a5638b..1bb80280918 100644
--- a/bfd/elfxx-x86.h
+++ b/bfd/elfxx-x86.h
@@ -47,6 +47,25 @@
 #define X86_SIZE_TYPE_P(IS_X86_64, TYPE) \
   ((IS_X86_64) ? X86_64_SIZE_TYPE_P(TYPE) : I386_SIZE_TYPE_P (TYPE))
 
+#define X86_64_NEED_DYNAMIC_RELOC_TYPE_P(TYPE) \
+  (X86_64_SIZE_TYPE_P (TYPE) \
+   || X86_64_PCREL_TYPE_P (TYPE) \
+   || (TYPE) == R_X86_64_8 \
+   || (TYPE) == R_X86_64_16 \
+   || (TYPE) == R_X86_64_32 \
+   || (TYPE) == R_X86_64_32S \
+   || (TYPE) == R_X86_64_64)
+#define I386_NEED_DYNAMIC_RELOC_TYPE_P(TYPE) \
+  (I386_SIZE_TYPE_P (TYPE) \
+   || I386_PCREL_TYPE_P (TYPE) \
+   || (TYPE) == R_386_32 \
+   || (TYPE) == R_386_TLS_LE \
+   || (TYPE) == R_386_TLS_LE_32)
+#define X86_NEED_DYNAMIC_RELOC_TYPE_P(IS_X86_64, TYPE) \
+  ((IS_X86_64) \
+   ? X86_64_NEED_DYNAMIC_RELOC_TYPE_P (TYPE) \
+   : I386_NEED_DYNAMIC_RELOC_TYPE_P (TYPE))
+
 #define PLT_CIE_LENGTH		20
 #define PLT_FDE_LENGTH		36
 #define PLT_FDE_START_OFFSET	4 + PLT_CIE_LENGTH + 8
@@ -653,6 +672,10 @@ extern int _bfd_x86_elf_compare_relocs
 extern bool _bfd_x86_elf_link_check_relocs
   (bfd *, struct bfd_link_info *);
 
+extern bool _bfd_x86_elf_check_relocs
+  (bfd *, struct bfd_link_info *, asection *,
+   const Elf_Internal_Rela *);
+
 extern bool _bfd_elf_x86_valid_reloc_p
   (asection *, struct bfd_link_info *, struct elf_x86_link_hash_table *,
    const Elf_Internal_Rela *, struct elf_link_hash_entry *,
@@ -730,10 +753,10 @@ extern void _bfd_x86_elf_link_report_relative_reloc
 #define bfd_elf32_bfd_link_check_relocs \
   _bfd_x86_elf_link_check_relocs
 
+#define elf_backend_check_relocs \
+  _bfd_x86_elf_check_relocs
 #define elf_backend_size_dynamic_sections \
   _bfd_x86_elf_size_dynamic_sections
-#define elf_backend_always_size_sections \
-  _bfd_x86_elf_always_size_sections
 #define elf_backend_merge_symbol_attribute \
   _bfd_x86_elf_merge_symbol_attribute
 #define elf_backend_copy_indirect_symbol \
@@ -757,39 +780,6 @@ extern void _bfd_x86_elf_link_report_relative_reloc
 
 #define ELF_P_ALIGN ELF_MINPAGESIZE
 
-/* 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;
-}
-
 /* Allocate x86 GOT info for local symbols.  */
 
 static inline bool
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] 9+ messages in thread

* Re: [PATCH] ld: Extract _bfd_elf_link_check_or_scan_relocs
  2022-01-07  4:24           ` [PATCH] ld: Extract _bfd_elf_link_check_or_scan_relocs H.J. Lu
@ 2022-01-08  1:14             ` Alan Modra
  2022-01-08  1:57               ` H.J. Lu
  0 siblings, 1 reply; 9+ messages in thread
From: Alan Modra @ 2022-01-08  1:14 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Binutils

On Thu, Jan 06, 2022 at 08:24:43PM -0800, H.J. Lu wrote:
> On Fri, Jan 07, 2022 at 01:46:33PM +1030, Alan Modra wrote:
> > On Thu, Jan 06, 2022 at 03:22:22PM -0800, H.J. Lu wrote:
> > > elf_backend_always_size_sections is called too late to scan input
> > > relocation.  elf_backend_always_size_sections works for me when
> > > it is called at the beginning of bfd_elf_size_dynamic_sections, like
> > 
> > I think that should be OK, with the init_{got,plt}_refcount moved too.
> > 
> > 	* elflink.c (bfd_elf_size_dynamic_sections): Move plt/got init
> > 	earlier and call elf_backend_always_size_sections at the start
> > 	of this function.
> > 
> 
> Thanks.  Now I can use elf_backend_always_size_sections to scan input
> relocations.  Here is a patch to extract _bfd_elf_link_check_or_scan_relocs
> so that I can use it to scan relocations.
> 
> OK for master?

OK, thanks.

-- 
Alan Modra
Australia Development Lab, IBM

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

* Re: [PATCH] ld: Extract _bfd_elf_link_check_or_scan_relocs
  2022-01-08  1:14             ` Alan Modra
@ 2022-01-08  1:57               ` H.J. Lu
  0 siblings, 0 replies; 9+ messages in thread
From: H.J. Lu @ 2022-01-08  1:57 UTC (permalink / raw)
  To: Alan Modra; +Cc: Binutils

[-- Attachment #1: Type: text/plain, Size: 1182 bytes --]

On Fri, Jan 7, 2022 at 5:14 PM Alan Modra <amodra@gmail.com> wrote:
>
> On Thu, Jan 06, 2022 at 08:24:43PM -0800, H.J. Lu wrote:
> > On Fri, Jan 07, 2022 at 01:46:33PM +1030, Alan Modra wrote:
> > > On Thu, Jan 06, 2022 at 03:22:22PM -0800, H.J. Lu wrote:
> > > > elf_backend_always_size_sections is called too late to scan input
> > > > relocation.  elf_backend_always_size_sections works for me when
> > > > it is called at the beginning of bfd_elf_size_dynamic_sections, like
> > >
> > > I think that should be OK, with the init_{got,plt}_refcount moved too.
> > >
> > >     * elflink.c (bfd_elf_size_dynamic_sections): Move plt/got init
> > >     earlier and call elf_backend_always_size_sections at the start
> > >     of this function.
> > >
> >
> > Thanks.  Now I can use elf_backend_always_size_sections to scan input
> > relocations.  Here is a patch to extract _bfd_elf_link_check_or_scan_relocs
> > so that I can use it to scan relocations.
> >
> > OK for master?
>
> OK, thanks.
>
> --
> Alan Modra
> Australia Development Lab, IBM

This is the patch I am checking in.  I renamed
_bfd_elf_link_check_or_scan_relocs
to _bfd_elf_link_iterate_on_relocs.

Thanks.

-- 
H.J.

[-- Attachment #2: 0001-ld-Extract-_bfd_elf_link_iterate_on_relocs.patch --]
[-- Type: text/x-patch, Size: 21881 bytes --]

From 27ef4963702dbdc9cc40fe9f4079db25be1623f3 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Mon, 27 Dec 2021 10:13:06 -0800
Subject: [PATCH] ld: Extract _bfd_elf_link_iterate_on_relocs

DT_RELR encodes consecutive R_*_RELATIVE relocations in GOT (the global
offset table) and data sections 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, extract
_bfd_elf_link_iterate_on_relocs from _bfd_elf_link_check_relocs so
that a backend can scan relocations in elf_backend_always_size_sections

For x86 targets, the old check_relocs is renamed to scan_relocs and a
new check_relocs is added to chek input sections and create dynamic
relocation sections so that they will be mapped to output sections.
scan_relocs is now called from elf_backend_always_size_sections.

Since relocations are scanned 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/

	* elf-bfd.h (_bfd_elf_link_iterate_on_relocs): New.
	* elf32-i386.c (elf_i386_convert_load_reloc): Don't call
	elf_x86_start_stop_gc_p.
	(elf_i386_check_relocs): Renamed to ...
	(elf_i386_scan_relocs): This.  Don't call
	_bfd_elf_make_dynamic_reloc_section.
	(elf_i386_always_size_sections): New.
	(elf_backend_check_relocs): Removed.
	(elf_backend_always_size_sections): New.
	* elf64-x86-64.c (elf_x86_64_convert_load_reloc): Don't call
	elf_x86_start_stop_gc_p.
	(elf_x86_64_check_relocs): Renamed to ...
	(elf_x86_64_scan_relocs): This.  Don't call
	_bfd_elf_make_dynamic_reloc_section.
	(elf_x86_64_always_size_sections): New.
	(elf_backend_check_relocs): Removed.
	(elf_backend_always_size_sections): New.
	* elflink.c (elf_link_check_or_scan_relocs):
	New.  Extracted from _bfd_elf_link_check_relocs.
	(_bfd_elf_link_check_relocs): Call elf_link_check_or_scan_relocs.
	* elfxx-x86.c (_bfd_x86_elf_check_relocs): New.
	* elfxx-x86.h (X86_64_NEED_DYNAMIC_RELOC_TYPE_P): New.
	(I386_NEED_DYNAMIC_RELOC_TYPE_P): Likewise.
	(X86_NEED_DYNAMIC_RELOC_TYPE_P): Likewise.
	(_bfd_x86_elf_check_relocs): Likewise.
	(elf_backend_check_relocs): Likewise.
	(elf_backend_always_size_sections): Removed.
	(elf_x86_start_stop_gc_p): Likewise.

ld/

	* testsuite/ld-i386/pr27491-1a.d: Updated.
	* testsuite/ld-x86-64/pr27491-1a.d: Likewise.
---
 bfd/elf-bfd.h                       |  4 ++
 bfd/elf32-i386.c                    | 58 ++++++++++----------
 bfd/elf64-x86-64.c                  | 55 +++++++++----------
 bfd/elflink.c                       | 23 ++++++--
 bfd/elfxx-x86.c                     | 85 +++++++++++++++++++++++++++++
 bfd/elfxx-x86.h                     | 60 +++++++++-----------
 ld/testsuite/ld-i386/pr27491-1a.d   |  4 +-
 ld/testsuite/ld-x86-64/pr27491-1a.d |  4 +-
 8 files changed, 191 insertions(+), 102 deletions(-)

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 896aa08fd76..81f8fd47db7 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -2632,6 +2632,10 @@ 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_iterate_on_relocs
+ (bfd *, struct bfd_link_info *,
+  bool (*) (bfd *, struct bfd_link_info *, asection *,
+	    const Elf_Internal_Rela *));
 
 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 97962072ff0..d1f61be5044 100644
--- a/bfd/elf32-i386.c
+++ b/bfd/elf32-i386.c
@@ -522,7 +522,7 @@ elf_i386_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
    Functions named elf_i386_* are called by external routines, other
    functions are only called locally.  elf_i386_* functions appear
    in this file more or less in the order in which they are called
-   from external routines.  eg. elf_i386_check_relocs is called
+   from external routines.  eg. elf_i386_scan_relocs is called
    early in the link process, elf_i386_finish_dynamic_sections is
    one of the last functions.  */
 
@@ -1106,7 +1106,7 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
 	    }
 
 	  /* We checked the transition before when we were called from
-	     elf_i386_check_relocs.  We only want to check the new
+	     elf_i386_scan_relocs.  We only want to check the new
 	     transition which hasn't been checked before.  */
 	  check = new_to_type != to_type && from_type == to_type;
 	  to_type = new_to_type;
@@ -1387,11 +1387,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)
 	    {
@@ -1452,21 +1447,20 @@ elf_i386_convert_load_reloc (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
 }
 
 /* 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,
-		       struct bfd_link_info *info,
-		       asection *sec,
-		       const Elf_Internal_Rela *relocs)
+elf_i386_scan_relocs (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;
   bfd_byte *contents;
   bool converted;
 
@@ -1496,8 +1490,6 @@ elf_i386_check_relocs (bfd *abfd,
 
   converted = false;
 
-  sreloc = NULL;
-
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
@@ -1818,18 +1810,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)
@@ -1924,6 +1904,24 @@ elf_i386_check_relocs (bfd *abfd,
   return false;
 }
 
+static bool
+elf_i386_always_size_sections (bfd *output_bfd,
+			       struct bfd_link_info *info)
+{
+  bfd *abfd;
+
+  /* Scan relocations after rel_from_abs has been set on __ehdr_start.  */
+  for (abfd = info->input_bfds;
+       abfd != (bfd *) NULL;
+       abfd = abfd->link.next)
+    if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+	&& !_bfd_elf_link_iterate_on_relocs (abfd, info,
+					     elf_i386_scan_relocs))
+      return false;
+
+  return _bfd_x86_elf_always_size_sections (output_bfd, info);
+}
+
 /* Set the correct type for an x86 ELF section.  We do this by the
    section name, which is a hack, but ought to work.  */
 
@@ -2000,7 +1998,7 @@ elf_i386_relocate_section (bfd *output_bfd,
   bool is_vxworks_tls;
   unsigned plt_entry_size;
 
-  /* Skip if check_relocs failed.  */
+  /* Skip if check_relocs or scan_relocs failed.  */
   if (input_section->check_relocs_failed)
     return false;
 
@@ -4390,7 +4388,7 @@ elf_i386_link_setup_gnu_properties (struct bfd_link_info *info)
 #define bfd_elf32_get_synthetic_symtab	      elf_i386_get_synthetic_symtab
 
 #define elf_backend_relocs_compatible	      _bfd_elf_relocs_compatible
-#define elf_backend_check_relocs	      elf_i386_check_relocs
+#define elf_backend_always_size_sections      elf_i386_always_size_sections
 #define elf_backend_create_dynamic_sections   _bfd_elf_create_dynamic_sections
 #define elf_backend_fake_sections	      elf_i386_fake_sections
 #define elf_backend_finish_dynamic_sections   elf_i386_finish_dynamic_sections
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 22aa3ee3b68..ad885f89e11 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1309,7 +1309,7 @@ elf_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd,
 	    }
 
 	  /* We checked the transition before when we were called from
-	     elf_x86_64_check_relocs.  We only want to check the new
+	     elf_x86_64_scan_relocs.  We only want to check the new
 	     transition which hasn't been checked before.  */
 	  check = new_to_type != to_type && from_type == to_type;
 	  to_type = new_to_type;
@@ -1628,11 +1628,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;
@@ -1823,20 +1818,19 @@ 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,
-			 asection *sec,
-			 const Elf_Internal_Rela *relocs)
+elf_x86_64_scan_relocs (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;
   bfd_byte *contents;
   bool converted;
 
@@ -1866,8 +1860,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++)
     {
@@ -2263,19 +2255,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)
@@ -2371,6 +2350,24 @@ elf_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
   return false;
 }
 
+static bool
+elf_x86_64_always_size_sections (bfd *output_bfd,
+				 struct bfd_link_info *info)
+{
+  bfd *abfd;
+
+  /* Scan relocations after rel_from_abs has been set on __ehdr_start.  */
+  for (abfd = info->input_bfds;
+       abfd != (bfd *) NULL;
+       abfd = abfd->link.next)
+    if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+	&& !_bfd_elf_link_iterate_on_relocs (abfd, info,
+					     elf_x86_64_scan_relocs))
+      return false;
+
+  return _bfd_x86_elf_always_size_sections (output_bfd, info);
+}
+
 /* Return the relocation value for @tpoff relocation
    if STT_TLS virtual address is ADDRESS.  */
 
@@ -2413,7 +2410,7 @@ elf_x86_64_relocate_section (bfd *output_bfd,
   unsigned int plt_entry_size;
   bool status;
 
-  /* Skip if check_relocs failed.  */
+  /* Skip if check_relocs or scan_relocs failed.  */
   if (input_section->check_relocs_failed)
     return false;
 
@@ -5238,7 +5235,7 @@ elf_x86_64_special_sections[]=
   elf_x86_64_reloc_name_lookup
 
 #define elf_backend_relocs_compatible	    elf_x86_64_relocs_compatible
-#define elf_backend_check_relocs	    elf_x86_64_check_relocs
+#define elf_backend_always_size_sections    elf_x86_64_always_size_sections
 #define elf_backend_create_dynamic_sections _bfd_elf_create_dynamic_sections
 #define elf_backend_finish_dynamic_sections elf_x86_64_finish_dynamic_sections
 #define elf_backend_finish_dynamic_symbol   elf_x86_64_finish_dynamic_symbol
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 08c161713cc..059461b5725 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.  */
+/* Call ACTION on each relocation in an ELF object file.  */
 
 bool
-_bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
+_bfd_elf_link_iterate_on_relocs
+  (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);
@@ -4035,7 +4038,6 @@ _bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
      different format.  It probably can't be done.  */
   if ((abfd->flags & DYNAMIC) == 0
       && is_elf_hash_table (&htab->root)
-      && bed->check_relocs != NULL
       && elf_object_id (abfd) == elf_hash_table_id (htab)
       && (*bed->relocs_compatible) (abfd->xvec, info->output_bfd->xvec))
     {
@@ -4070,7 +4072,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 +4085,19 @@ _bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
   return true;
 }
 
+/* Check relocations in an ELF object file.  This is called after
+   all input files have been opened.  */
+
+bool
+_bfd_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
+{
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  if (bed->check_relocs != NULL)
+    return _bfd_elf_link_iterate_on_relocs (abfd, info,
+					    bed->check_relocs);
+  return true;
+}
+
 /* Add symbols from an ELF object file to the linker hash table.  */
 
 static bool
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index ca4b90e22cc..25f7717ea88 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -892,6 +892,91 @@ _bfd_x86_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
   return _bfd_elf_link_check_relocs (abfd, info);
 }
 
+/* Look through the relocs for a section before allocation to make the
+   dynamic reloc section.  */
+
+bool
+_bfd_x86_elf_check_relocs (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;
+  const struct elf_backend_data *bed;
+  bool is_x86_64;
+
+  if (bfd_link_relocatable (info))
+    return true;
+
+  bed = get_elf_backend_data (abfd);
+  htab = elf_x86_hash_table (info, bed->target_id);
+  if (htab == NULL)
+    {
+      sec->check_relocs_failed = 1;
+      return false;
+    }
+
+  is_x86_64 = bed->target_id == X86_64_ELF_DATA;
+
+  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;
+	}
+
+      if (X86_NEED_DYNAMIC_RELOC_TYPE_P (is_x86_64, r_type)
+	  && NEED_DYNAMIC_RELOCATION_P (is_x86_64, 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, sec->use_rela_p);
+
+	  if (sreloc != NULL)
+	    return true;
+
+  error_return:
+	  sec->check_relocs_failed = 1;
+	  return false;
+	}
+    }
+
+  return true;
+}
+
 bool
 _bfd_elf_x86_valid_reloc_p (asection *input_section,
 			    struct bfd_link_info *info,
diff --git a/bfd/elfxx-x86.h b/bfd/elfxx-x86.h
index 16565a5638b..1bb80280918 100644
--- a/bfd/elfxx-x86.h
+++ b/bfd/elfxx-x86.h
@@ -47,6 +47,25 @@
 #define X86_SIZE_TYPE_P(IS_X86_64, TYPE) \
   ((IS_X86_64) ? X86_64_SIZE_TYPE_P(TYPE) : I386_SIZE_TYPE_P (TYPE))
 
+#define X86_64_NEED_DYNAMIC_RELOC_TYPE_P(TYPE) \
+  (X86_64_SIZE_TYPE_P (TYPE) \
+   || X86_64_PCREL_TYPE_P (TYPE) \
+   || (TYPE) == R_X86_64_8 \
+   || (TYPE) == R_X86_64_16 \
+   || (TYPE) == R_X86_64_32 \
+   || (TYPE) == R_X86_64_32S \
+   || (TYPE) == R_X86_64_64)
+#define I386_NEED_DYNAMIC_RELOC_TYPE_P(TYPE) \
+  (I386_SIZE_TYPE_P (TYPE) \
+   || I386_PCREL_TYPE_P (TYPE) \
+   || (TYPE) == R_386_32 \
+   || (TYPE) == R_386_TLS_LE \
+   || (TYPE) == R_386_TLS_LE_32)
+#define X86_NEED_DYNAMIC_RELOC_TYPE_P(IS_X86_64, TYPE) \
+  ((IS_X86_64) \
+   ? X86_64_NEED_DYNAMIC_RELOC_TYPE_P (TYPE) \
+   : I386_NEED_DYNAMIC_RELOC_TYPE_P (TYPE))
+
 #define PLT_CIE_LENGTH		20
 #define PLT_FDE_LENGTH		36
 #define PLT_FDE_START_OFFSET	4 + PLT_CIE_LENGTH + 8
@@ -653,6 +672,10 @@ extern int _bfd_x86_elf_compare_relocs
 extern bool _bfd_x86_elf_link_check_relocs
   (bfd *, struct bfd_link_info *);
 
+extern bool _bfd_x86_elf_check_relocs
+  (bfd *, struct bfd_link_info *, asection *,
+   const Elf_Internal_Rela *);
+
 extern bool _bfd_elf_x86_valid_reloc_p
   (asection *, struct bfd_link_info *, struct elf_x86_link_hash_table *,
    const Elf_Internal_Rela *, struct elf_link_hash_entry *,
@@ -730,10 +753,10 @@ extern void _bfd_x86_elf_link_report_relative_reloc
 #define bfd_elf32_bfd_link_check_relocs \
   _bfd_x86_elf_link_check_relocs
 
+#define elf_backend_check_relocs \
+  _bfd_x86_elf_check_relocs
 #define elf_backend_size_dynamic_sections \
   _bfd_x86_elf_size_dynamic_sections
-#define elf_backend_always_size_sections \
-  _bfd_x86_elf_always_size_sections
 #define elf_backend_merge_symbol_attribute \
   _bfd_x86_elf_merge_symbol_attribute
 #define elf_backend_copy_indirect_symbol \
@@ -757,39 +780,6 @@ extern void _bfd_x86_elf_link_report_relative_reloc
 
 #define ELF_P_ALIGN ELF_MINPAGESIZE
 
-/* 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;
-}
-
 /* Allocate x86 GOT info for local symbols.  */
 
 static inline bool
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] 9+ messages in thread

end of thread, other threads:[~2022-01-08  1:58 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-29 22:14 [PATCH v2] ld: Check ELF relocs before allocation H.J. Lu
2022-01-04 23:43 ` Alan Modra
2022-01-06 15:05   ` [PATCH] ld: Add scan_relocs to ELF linker H.J. Lu
2022-01-06 22:59     ` Alan Modra
2022-01-06 23:22       ` H.J. Lu
2022-01-07  3:16         ` Alan Modra
2022-01-07  4:24           ` [PATCH] ld: Extract _bfd_elf_link_check_or_scan_relocs H.J. Lu
2022-01-08  1:14             ` Alan Modra
2022-01-08  1:57               ` 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).