SHF_LINK_ORDER fixup_link_order in ld

Alan Modra amodra@gmail.com
Wed Jan 13 11:46:10 GMT 2021


This moves the SHF_LINK_ORDER sorting from bfd_elf_final_link to
the linker which means generic ELF targets now support SHF_LINK_ORDER
and  we cope with odd cases that require resizing of output sections.
The patch also fixes two bugs in the current implementation,
introduced by commit cd6d537c48fa.  The pattern test used by that
commit meant that sections matching something like
"*(.IA_64.unwind* .gnu.linkonce.ia64unw.*)" would not properly sort a
mix of sections matching the two wildcards.  That commit also assumed
a stable qsort.

bfd/
	PR 27160
	* section.c (struct bfd_section): Remove pattern field.
	(BFD_FAKE_SECTION): Adjust to suit.
	* bfd-in2.h: Regenerate.
	* elflink.c (compare_link_order, elf_fixup_link_order): Delete.
	(bfd_elf_final_link): Don't call elf_fixup_link_order.
ld/
	PR 27160
	* ldlang.h (lang_output_section_statement_type): Add data field.
	(lang_input_section_type, lang_section_bst_type): Add pattern field.
	(statement_list): Declare.
	(lang_add_section): Adjust prototype.
	* emultempl/aarch64elf.em: Adjust lang_add_section calls.
	* emultempl/armelf.em: Likewise.
	* emultempl/beos.em: Likewise.
	* emultempl/cskyelf.em: Likewise.
	* emultempl/hppaelf.em: Likewise.
	* emultempl/m68hc1xelf.em: Likewise.
	* emultempl/metagelf.em: Likewise.
	* emultempl/mipself.em: Likewise.
	* emultempl/mmo.em: Likewise.
	* emultempl/msp430.em: Likewise.
	* emultempl/nios2elf.em: Likewise.
	* emultempl/pe.em: Likewise.
	* emultempl/pep.em: Likewise.
	* emultempl/ppc64elf.em: Likewise.
	* emultempl/spuelf.em: Likewise.
	* emultempl/vms.em: Likewise.
	* ldelf.c: Likewise.
	* ldelfgen.c: Include ldctor.h.
	(struct os_sections): New.
	(add_link_order_input_section, link_order_scan): New functions.
	(compare_link_order, fixup_link_order): New functions.
	(ldelf_map_segments): Call link_order_scan and fixup_link_order.
	* ldlang.c (statement_list): Make global.
	(output_section_callback_fast): Save pattern in tree node.
	(lang_add_section): Add pattern parameter, save in lang_input_section.
	(output_section_callback_tree_to_list): Adjust lang_add_section calls.
	(lang_insert_orphan, output_section_callback): Likewise.
	(ldlang_place_orphan): Likewise.
	(gc_section_callback): Don't set section->pattern
	* testsuite/ld-elf/pr26256-2a.d: Don't xfail generic.
	* testsuite/ld-elf/pr26256-3b.d: Likewise.
	* testsuite/ld-elf/pr26256-2b.d: Likewise.  notarget xgate.

diff --git a/bfd/elflink.c b/bfd/elflink.c
index acc959d526..d20857eb6b 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -11863,193 +11863,6 @@ elf_reloc_link_order (bfd *output_bfd,
   return TRUE;
 }
 
-
-/* Compare two sections based on the locations of the sections they are
-   linked to.  Used by elf_fixup_link_order.  */
-
-static int
-compare_link_order (const void *a, const void *b)
-{
-  const struct bfd_link_order *alo = *(const struct bfd_link_order **) a;
-  const struct bfd_link_order *blo = *(const struct bfd_link_order **) b;
-  asection *asec = elf_linked_to_section (alo->u.indirect.section);
-  asection *bsec = elf_linked_to_section (blo->u.indirect.section);
-  bfd_vma apos, bpos;
-
-  /* Check if any sections are unordered.  */
-  if (asec == NULL || bsec == NULL)
-    {
-      /* Place unordered sections before ordered sections.  */
-      if (bsec != NULL)
-	return -1;
-      else if (asec != NULL)
-	return 1;
-      return 0;
-    }
-
-  apos = asec->output_section->lma + asec->output_offset;
-  bpos = bsec->output_section->lma + bsec->output_offset;
-
-  if (apos < bpos)
-    return -1;
-  if (apos > bpos)
-    return 1;
-
-  /* The only way we should get matching LMAs is when the first of two
-     sections has zero size.  */
-  if (asec->size < bsec->size)
-    return -1;
-  if (asec->size > bsec->size)
-    return 1;
-
-  /* If they are both zero size then they almost certainly have the same
-     VMA and thus are not ordered with respect to each other.  Test VMA
-     anyway, and fall back to id to make the result reproducible across
-     qsort implementations.  */
-  apos = asec->output_section->vma + asec->output_offset;
-  bpos = bsec->output_section->vma + bsec->output_offset;
-  if (apos < bpos)
-    return -1;
-  if (apos > bpos)
-    return 1;
-
-  return asec->id - bsec->id;
-}
-
-
-/* Looks for sections with SHF_LINK_ORDER set.  Rearranges them into the same
-   order as their linked sections.  Returns false if this could not be done
-   because an output section includes both ordered and unordered
-   sections.  Ideally we'd do this in the linker proper.  */
-
-static bfd_boolean
-elf_fixup_link_order (struct bfd_link_info *info, bfd *abfd, asection *o)
-{
-  size_t seen_linkorder;
-  size_t seen_other;
-  size_t n;
-  struct bfd_link_order *p;
-  bfd *sub;
-  struct bfd_link_order **sections, **indirect_sections;
-  asection *other_sec, *linkorder_sec;
-  bfd_vma offset;  /* Octets.  */
-
-  other_sec = NULL;
-  linkorder_sec = NULL;
-  seen_other = 0;
-  seen_linkorder = 0;
-  for (p = o->map_head.link_order; p != NULL; p = p->next)
-    {
-      if (p->type == bfd_indirect_link_order)
-	{
-	  asection *s = p->u.indirect.section;
-	  sub = s->owner;
-	  if ((s->flags & SEC_LINKER_CREATED) == 0
-	      && bfd_get_flavour (sub) == bfd_target_elf_flavour
-	      && elf_section_data (s) != NULL
-	      && elf_linked_to_section (s) != NULL)
-	    {
-	      seen_linkorder++;
-	      linkorder_sec = s;
-	    }
-	  else
-	    {
-	      seen_other++;
-	      other_sec = s;
-	    }
-	}
-      else
-	seen_other++;
-
-      /* Allow mixed ordered and unordered input sections for
-         non-relocatable link.  */
-      if (bfd_link_relocatable (info) && seen_other && seen_linkorder)
-	{
-	  if (other_sec && linkorder_sec)
-	    _bfd_error_handler
-	      /* xgettext:c-format */
-	      (_("%pA has both ordered [`%pA' in %pB] "
-		 "and unordered [`%pA' in %pB] sections"),
-	       o, linkorder_sec, linkorder_sec->owner,
-	       other_sec, other_sec->owner);
-	  else
-	    _bfd_error_handler
-	      (_("%pA has both ordered and unordered sections"), o);
-	  bfd_set_error (bfd_error_bad_value);
-	  return FALSE;
-	}
-    }
-
-  if (!seen_linkorder)
-    return TRUE;
-
-  /* Non-relocatable output can have both ordered and unordered input
-     sections.  */
-  seen_linkorder += seen_other;
-
-  sections = bfd_malloc (seen_linkorder * sizeof (*sections));
-  if (sections == NULL)
-    return FALSE;
-
-  seen_linkorder = 0;
-  for (p = o->map_head.link_order; p != NULL; p = p->next)
-    sections[seen_linkorder++] = p;
-
-  for (indirect_sections = sections, n = 0;
-       n < seen_linkorder;
-       indirect_sections++, n++)
-    {
-      /* Find the first bfd_indirect_link_order section.  */
-      if (indirect_sections[0]->type == bfd_indirect_link_order)
-	{
-	  /* Count the consecutive bfd_indirect_link_order sections
-	     with the same pattern.  */
-	  size_t i, n_indirect;
-	  const char *pattern
-	    = indirect_sections[0]->u.indirect.section->pattern;
-	  for (i = n + 1; i < seen_linkorder; i++)
-	    if (sections[i]->type != bfd_indirect_link_order
-		|| sections[i]->u.indirect.section->pattern != pattern)
-	      break;
-	  n_indirect = i - n;
-	  /* Sort the bfd_indirect_link_order sections in the order of
-	     their linked section.  */
-	  qsort (indirect_sections, n_indirect, sizeof (*sections),
-		 compare_link_order);
-	  indirect_sections += n_indirect;
-	  n += n_indirect;
-	}
-    }
-
-  /* Change the offsets of the bfd_indirect_link_order sections.  */
-  offset = 0;
-  for (n = 0; n < seen_linkorder; n++)
-    if (sections[n]->type == bfd_indirect_link_order)
-      {
-	bfd_vma mask;
-	asection *s = sections[n]->u.indirect.section;
-	unsigned int opb = bfd_octets_per_byte (abfd, s);
-
-	mask = ~(bfd_vma) 0 << s->alignment_power * opb;
-	offset = (offset + ~mask) & mask;
-	sections[n]->offset = s->output_offset = offset / opb;
-	offset += sections[n]->size;
-      }
-    else
-      offset = sections[n]->offset + sections[n]->size;
-
-  free (sections);
-
-  /* Verify that fixing up SHF_LINK_ORDER doesn't increase the section
-     size.  */
-  if (offset > o->size)
-    info->callbacks->einfo
-      (_("%F%P: %pA has ordered sections with incompatible alignments\n"),
-       o);
-
-  return TRUE;
-}
-
 /* Generate an import library in INFO->implib_bfd from symbols in ABFD.
    Returns TRUE upon success, FALSE otherwise.  */
 
@@ -12683,13 +12496,6 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
       htab->tls_size = end - base;
     }
 
-  /* Reorder SHF_LINK_ORDER sections.  */
-  for (o = abfd->sections; o != NULL; o = o->next)
-    {
-      if (!elf_fixup_link_order (info, abfd, o))
-	return FALSE;
-    }
-
   if (!_bfd_elf_fixup_eh_frame_hdr (info))
     return FALSE;
 
diff --git a/bfd/section.c b/bfd/section.c
index 10efc3c4aa..3e6ba0c093 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -541,9 +541,6 @@ CODE_FRAGMENT
 .  struct bfd_symbol *symbol;
 .  struct bfd_symbol **symbol_ptr_ptr;
 .
-.  {* The matching section name pattern in linker script.  *}
-.  const char *pattern;
-.
 .  {* Early in the link process, map_head and map_tail are used to build
 .     a list of input sections attached to an output section.  Later,
 .     output sections use these fields for a list of bfd_link_order
@@ -737,8 +734,8 @@ CODE_FRAGMENT
 .  {* target_index, used_by_bfd, constructor_chain, owner,           *}	\
 .     0,            NULL,        NULL,              NULL,		\
 .									\
-.  {* symbol,                    symbol_ptr_ptr, pattern,            *}	\
-.     (struct bfd_symbol *) SYM, &SEC.symbol,    NULL,			\
+.  {* symbol,                    symbol_ptr_ptr,                     *}	\
+.     (struct bfd_symbol *) SYM, &SEC.symbol,				\
 .									\
 .  {* map_head, map_tail, already_assigned                           *}	\
 .     { NULL }, { NULL }, NULL						\
diff --git a/ld/emultempl/aarch64elf.em b/ld/emultempl/aarch64elf.em
index bda5afcb1e..a185560732 100644
--- a/ld/emultempl/aarch64elf.em
+++ b/ld/emultempl/aarch64elf.em
@@ -192,7 +192,7 @@ elf${ELFSIZE}_aarch64_add_stub_section (const char *stub_sec_name,
 
   info.input_section = input_section;
   lang_list_init (&info.add);
-  lang_add_section (&info.add, stub_sec, NULL, os);
+  lang_add_section (&info.add, stub_sec, NULL, NULL, os);
 
   if (info.add.head == NULL)
     goto err_ret;
diff --git a/ld/emultempl/armelf.em b/ld/emultempl/armelf.em
index 0c031080bd..a4cf93b493 100644
--- a/ld/emultempl/armelf.em
+++ b/ld/emultempl/armelf.em
@@ -246,7 +246,7 @@ elf32_arm_add_stub_section (const char * stub_sec_name,
 
   info.input_section = after_input_section;
   lang_list_init (&info.add);
-  lang_add_section (&info.add, stub_sec, NULL, os);
+  lang_add_section (&info.add, stub_sec, NULL, NULL, os);
 
   if (info.add.head == NULL)
     goto err_ret;
diff --git a/ld/emultempl/beos.em b/ld/emultempl/beos.em
index 64c0e1102e..bb4395f63a 100644
--- a/ld/emultempl/beos.em
+++ b/ld/emultempl/beos.em
@@ -704,7 +704,7 @@ gld${EMULATION_NAME}_place_orphan (asection *s,
      The sections still have to be sorted, but that has to wait until
      all such sections have been processed by us.  The sorting is done by
      sort_sections.  */
-  lang_add_section (&l->wild_statement.children, s, NULL, os);
+  lang_add_section (&l->wild_statement.children, s, NULL, NULL, os);
 
   return os;
 }
diff --git a/ld/emultempl/cskyelf.em b/ld/emultempl/cskyelf.em
index ce4047b49b..ca38cf62b9 100644
--- a/ld/emultempl/cskyelf.em
+++ b/ld/emultempl/cskyelf.em
@@ -189,7 +189,7 @@ elf32_csky_add_stub_section (const char *stub_sec_name,
 
   info.input_section = input_section;
   lang_list_init (&info.add);
-  lang_add_section (&info.add, stub_sec, NULL, os);
+  lang_add_section (&info.add, stub_sec, NULL, NULL, os);
 
   if (info.add.head == NULL)
     goto err_ret;
diff --git a/ld/emultempl/hppaelf.em b/ld/emultempl/hppaelf.em
index bbb8b7f4d7..f195a177ab 100644
--- a/ld/emultempl/hppaelf.em
+++ b/ld/emultempl/hppaelf.em
@@ -193,7 +193,7 @@ hppaelf_add_stub_section (const char *stub_sec_name, asection *input_section)
 
   info.input_section = input_section;
   lang_list_init (&info.add);
-  lang_add_section (&info.add, stub_sec, NULL, os);
+  lang_add_section (&info.add, stub_sec, NULL, NULL, os);
 
   if (info.add.head == NULL)
     goto err_ret;
diff --git a/ld/emultempl/m68hc1xelf.em b/ld/emultempl/m68hc1xelf.em
index c4546c6277..212db7c4e1 100644
--- a/ld/emultempl/m68hc1xelf.em
+++ b/ld/emultempl/m68hc1xelf.em
@@ -275,7 +275,7 @@ m68hc11elf_add_stub_section (const char *stub_sec_name,
      at the correct place.  */
   info.input_section = tramp_section;
   lang_list_init (&info.add);
-  lang_add_section (&info.add, stub_sec, NULL, os);
+  lang_add_section (&info.add, stub_sec, NULL, NULL, os);
 
   if (info.add.head == NULL)
     goto err_ret;
diff --git a/ld/emultempl/metagelf.em b/ld/emultempl/metagelf.em
index 51bec07f3c..41ada3fbeb 100644
--- a/ld/emultempl/metagelf.em
+++ b/ld/emultempl/metagelf.em
@@ -169,7 +169,7 @@ metagelf_add_stub_section (const char *stub_sec_name, asection *input_section)
 
   info.input_section = input_section;
   lang_list_init (&info.add);
-  lang_add_section (&info.add, stub_sec, NULL, os);
+  lang_add_section (&info.add, stub_sec, NULL, NULL, os);
 
   if (info.add.head == NULL)
     goto err_ret;
diff --git a/ld/emultempl/mipself.em b/ld/emultempl/mipself.em
index d27aa769f8..e27e53cf55 100644
--- a/ld/emultempl/mipself.em
+++ b/ld/emultempl/mipself.em
@@ -175,7 +175,7 @@ mips_add_stub_section (const char *stub_sec_name, asection *input_section,
 
   /* Initialize a statement list that contains only the new statement.  */
   lang_list_init (&info.add);
-  lang_add_section (&info.add, stub_sec, NULL, os);
+  lang_add_section (&info.add, stub_sec, NULL, NULL, os);
   if (info.add.head == NULL)
     goto err_ret;
 
diff --git a/ld/emultempl/mmo.em b/ld/emultempl/mmo.em
index 4289e779bc..fa0b19ae64 100644
--- a/ld/emultempl/mmo.em
+++ b/ld/emultempl/mmo.em
@@ -102,7 +102,7 @@ mmo_place_orphan (asection *s,
      (regardless of whether the linker script lists it as input).  */
   if (os != NULL)
     {
-      lang_add_section (&os->children, s, NULL, os);
+      lang_add_section (&os->children, s, NULL, NULL, os);
       return os;
     }
 
diff --git a/ld/emultempl/msp430.em b/ld/emultempl/msp430.em
index e3ea3c69a9..7e364afda8 100644
--- a/ld/emultempl/msp430.em
+++ b/ld/emultempl/msp430.em
@@ -325,7 +325,7 @@ gld${EMULATION_NAME}_place_orphan (asection * s,
 
   /* Always place orphaned sections in lower.  Optimal placement of either
      sections is performed later, once section sizes have been finalized.  */
-  lang_add_section (& lower->children, s, NULL, lower);
+  lang_add_section (& lower->children, s, NULL, NULL, lower);
  end:
   free (upper_name);
   free (lower_name);
@@ -358,7 +358,8 @@ change_output_section (lang_statement_union_type **head,
 	      lang_statement_list_type *old_list
 		= (lang_statement_list_type *) &old_os->children;
 	      s->output_section = NULL;
-	      lang_add_section (&new_os->children, s, NULL, new_os);
+	      lang_add_section (&new_os->children, s,
+				curr->input_section.pattern, NULL, new_os);
 
 	      /* Remove the section from the old output section.  */
 	      if (prev == NULL)
diff --git a/ld/emultempl/nios2elf.em b/ld/emultempl/nios2elf.em
index 29059809f1..fcc2756114 100644
--- a/ld/emultempl/nios2elf.em
+++ b/ld/emultempl/nios2elf.em
@@ -186,7 +186,7 @@ nios2elf_add_stub_section (const char *stub_sec_name, asection *input_section,
 
   info.input_section = input_section;
   lang_list_init (&info.add);
-  lang_add_section (&info.add, stub_sec, NULL, os);
+  lang_add_section (&info.add, stub_sec, NULL, NULL, os);
 
   if (info.add.head == NULL)
     goto err_ret;
diff --git a/ld/emultempl/pe.em b/ld/emultempl/pe.em
index ab7d4c485d..f9060be8c6 100644
--- a/ld/emultempl/pe.em
+++ b/ld/emultempl/pe.em
@@ -2085,7 +2085,7 @@ gld_${EMULATION_NAME}_place_orphan (asection *s,
 	       If the section already exists but does not have any flags set,
 	       then it has been created by the linker, probably as a result of
 	       a --section-start command line switch.  */
-	    lang_add_section (&add_child, s, NULL, os);
+	    lang_add_section (&add_child, s, NULL, NULL, os);
 	    break;
 	  }
 
@@ -2099,7 +2099,7 @@ gld_${EMULATION_NAME}_place_orphan (asection *s,
      unused one and use that.  */
   if (os == NULL && match_by_name)
     {
-      lang_add_section (&match_by_name->children, s, NULL, match_by_name);
+      lang_add_section (&match_by_name->children, s, NULL, NULL, match_by_name);
       return match_by_name;
     }
 
diff --git a/ld/emultempl/pep.em b/ld/emultempl/pep.em
index 3fdd605baf..ca335b5aa6 100644
--- a/ld/emultempl/pep.em
+++ b/ld/emultempl/pep.em
@@ -1905,7 +1905,7 @@ gld_${EMULATION_NAME}_place_orphan (asection *s,
 	       If the section already exists but does not have any flags set,
 	       then it has been created by the linker, probably as a result of
 	       a --section-start command line switch.  */
-	    lang_add_section (&add_child, s, NULL, os);
+	    lang_add_section (&add_child, s, NULL, NULL, os);
 	    break;
 	  }
 
@@ -1919,7 +1919,7 @@ gld_${EMULATION_NAME}_place_orphan (asection *s,
      unused one and use that.  */
   if (os == NULL && match_by_name)
     {
-      lang_add_section (&match_by_name->children, s, NULL, match_by_name);
+      lang_add_section (&match_by_name->children, s, NULL, NULL, match_by_name);
       return match_by_name;
     }
 
diff --git a/ld/emultempl/ppc64elf.em b/ld/emultempl/ppc64elf.em
index 403fd094c9..8253604539 100644
--- a/ld/emultempl/ppc64elf.em
+++ b/ld/emultempl/ppc64elf.em
@@ -445,7 +445,7 @@ ppc_add_stub_section (const char *stub_sec_name, asection *input_section)
 
   info.input_section = input_section;
   lang_list_init (&info.add);
-  lang_add_section (&info.add, stub_sec, NULL, os);
+  lang_add_section (&info.add, stub_sec, NULL, NULL, os);
 
   if (info.add.head == NULL)
     goto err_ret;
diff --git a/ld/emultempl/spuelf.em b/ld/emultempl/spuelf.em
index 2afad3e4dc..0c51b8e98f 100644
--- a/ld/emultempl/spuelf.em
+++ b/ld/emultempl/spuelf.em
@@ -151,7 +151,7 @@ spu_place_special_section (asection *s, asection *o, const char *output_name)
       lang_statement_list_type add;
 
       lang_list_init (&add);
-      lang_add_section (&add, s, NULL, os);
+      lang_add_section (&add, s, NULL, NULL, os);
       *add.tail = os->children.head;
       os->children.head = add.head;
     }
@@ -168,7 +168,7 @@ spu_place_special_section (asection *s, asection *o, const char *output_name)
 	  lang_add_assignment (exp_assign (".", e_size, FALSE));
 	  pop_stat_ptr ();
 	}
-      lang_add_section (&os->children, s, NULL, os);
+      lang_add_section (&os->children, s, NULL, NULL, os);
     }
 
   s->output_section->size += s->size;
diff --git a/ld/emultempl/vms.em b/ld/emultempl/vms.em
index 3aa00e3ab9..4c869626b9 100644
--- a/ld/emultempl/vms.em
+++ b/ld/emultempl/vms.em
@@ -116,7 +116,7 @@ vms_place_orphan (asection *s,
 
   if (hold_data.os != NULL)
     {
-      lang_add_section (&hold_data.os->children, s, NULL, hold_data.os);
+      lang_add_section (&hold_data.os->children, s, NULL, NULL, hold_data.os);
       return hold_data.os;
     }
   else
diff --git a/ld/ldelf.c b/ld/ldelf.c
index f7407ab55a..9887e53c17 100644
--- a/ld/ldelf.c
+++ b/ld/ldelf.c
@@ -2006,7 +2006,7 @@ ldelf_place_orphan (asection *s, const char *secname, int constraint)
 	    && (elf_section_data (os->bfd_section)->this_hdr.sh_info
 		== elf_section_data (s)->this_hdr.sh_info))
 	    {
-	      lang_add_section (&os->children, s, NULL, os);
+	      lang_add_section (&os->children, s, NULL, NULL, os);
 	      return os;
 	    }
 
@@ -2049,7 +2049,7 @@ ldelf_place_orphan (asection *s, const char *secname, int constraint)
 			|| !elfoutput
 			|| elf_orphan_compatible (s, os->bfd_section)))))
 	  {
-	    lang_add_section (&os->children, s, NULL, os);
+	    lang_add_section (&os->children, s, NULL, NULL, os);
 	    return os;
 	  }
 
@@ -2063,7 +2063,7 @@ ldelf_place_orphan (asection *s, const char *secname, int constraint)
      unused one and use that.  */
   if (match_by_name)
     {
-      lang_add_section (&match_by_name->children, s, NULL, match_by_name);
+      lang_add_section (&match_by_name->children, s, NULL, NULL, match_by_name);
       return match_by_name;
     }
 
@@ -2088,7 +2088,7 @@ ldelf_place_orphan (asection *s, const char *secname, int constraint)
       && hold[orphan_text].os != NULL)
     {
       os = hold[orphan_text].os;
-      lang_add_section (&os->children, s, NULL, os);
+      lang_add_section (&os->children, s, NULL, NULL, os);
       return os;
     }
 
diff --git a/ld/ldelfgen.c b/ld/ldelfgen.c
index f0502efaa2..8014e2229b 100644
--- a/ld/ldelfgen.c
+++ b/ld/ldelfgen.c
@@ -27,20 +27,263 @@
 #include "ldmisc.h"
 #include "ldexp.h"
 #include "ldlang.h"
+#include "ldctor.h"
 #include "elf-bfd.h"
 #include "elf/internal.h"
 #include "ldelfgen.h"
 
+/* Info attached to an output_section_statement about input sections,
+   used when sorting SHF_LINK_ORDER sections.  */
+
+struct os_sections
+{
+  /* Size allocated for isec.  */
+  unsigned int alloc;
+  /* Used entries in isec.  */
+  unsigned int count;
+  /* How many are SHF_LINK_ORDER.  */
+  unsigned int ordered;
+  /* Input sections attached to this output section.  */
+  struct os_sections_input {
+    lang_input_section_type *is;
+    unsigned int idx;
+  } isec[1];
+};
+
+/* Add IS to data kept for OS.  */
+
+static bfd_boolean
+add_link_order_input_section (lang_input_section_type *is,
+			      lang_output_section_statement_type *os)
+{
+  struct os_sections *os_info = os->data;
+  asection *s;
+
+  if (os_info == NULL)
+    {
+      os_info = xmalloc (sizeof (*os_info) + 63 * sizeof (*os_info->isec));
+      os_info->alloc = 64;
+      os_info->count = 0;
+      os_info->ordered = 0;
+      os->data = os_info;
+    }
+  if (os_info->count == os_info->alloc)
+    {
+      size_t want;
+      os_info->alloc *= 2;
+      want = sizeof (*os_info) + (os_info->alloc - 1) * sizeof (*os_info->isec);
+      os_info = xrealloc (os_info, want);
+      os->data = os_info;
+    }
+  os_info->isec[os_info->count].is = is;
+  os_info->isec[os_info->count].idx = os_info->count;
+  os_info->count++;
+  s = is->section;
+  if ((s->flags & SEC_LINKER_CREATED) == 0
+      && elf_section_data (s) != NULL
+      && elf_linked_to_section (s) != NULL)
+    os_info->ordered++;
+  return FALSE;
+}
+
+/* Run over the linker's statement list, extracting info about input
+   sections attached to each output section.  */
+
+static bfd_boolean
+link_order_scan (lang_statement_union_type *u,
+		 lang_output_section_statement_type *os)
+{
+  asection *s;
+  bfd_boolean ret = FALSE;
+
+  for (; u != NULL; u = u->header.next)
+    {
+      switch (u->header.type)
+	{
+	case lang_wild_statement_enum:
+	  if (link_order_scan (u->wild_statement.children.head, os))
+	    ret = TRUE;
+	  break;
+	case lang_constructors_statement_enum:
+	  if (link_order_scan (constructor_list.head, os))
+	    ret = TRUE;
+	  break;
+	case lang_output_section_statement_enum:
+	  if (u->output_section_statement.constraint != -1
+	      && link_order_scan (u->output_section_statement.children.head,
+				  &u->output_section_statement))
+	    ret = TRUE;
+	  break;
+	case lang_group_statement_enum:
+	  if (link_order_scan (u->group_statement.children.head, os))
+	    ret = TRUE;
+	  break;
+	case lang_input_section_enum:
+	  s = u->input_section.section;
+	  if (s->output_section != NULL
+	      && s->output_section->owner == link_info.output_bfd
+	      && (s->output_section->flags & SEC_EXCLUDE) == 0
+	      && ((s->output_section->flags & SEC_HAS_CONTENTS) != 0
+		  || ((s->output_section->flags & (SEC_LOAD | SEC_THREAD_LOCAL))
+		      == (SEC_LOAD | SEC_THREAD_LOCAL))))
+	    if (add_link_order_input_section (&u->input_section, os))
+	      ret = TRUE;
+	  break;
+	default:
+	  break;
+	}
+    }
+  return ret;
+}
+
+/* Compare two sections based on the locations of the sections they are
+   linked to.  Used by fixup_link_order.  */
+
+static int
+compare_link_order (const void *a, const void *b)
+{
+  const struct os_sections_input *ai = a;
+  const struct os_sections_input *bi = b;
+  asection *asec = elf_linked_to_section (ai->is->section);
+  asection *bsec = elf_linked_to_section (bi->is->section);
+  bfd_vma apos, bpos;
+
+  /* Place unordered sections before ordered sections.  */
+  if (asec == NULL || bsec == NULL)
+    {
+      if (bsec != NULL)
+	return -1;
+      else if (asec != NULL)
+	return 1;
+      return ai->idx - bi->idx;
+    }
+
+  apos = asec->output_section->lma + asec->output_offset;
+  bpos = bsec->output_section->lma + bsec->output_offset;
+
+  if (apos < bpos)
+    return -1;
+  else if (apos > bpos)
+    return 1;
+
+  /* The only way we should get matching LMAs is when the first of two
+     sections has zero size.  */
+  if (asec->size < bsec->size)
+    return -1;
+  else if (asec->size > bsec->size)
+    return 1;
+
+  /* If they are both zero size then they almost certainly have the same
+     VMA and thus are not ordered with respect to each other.  Test VMA
+     anyway, and fall back to id to make the result reproducible across
+     qsort implementations.  */
+  apos = asec->output_section->vma + asec->output_offset;
+  bpos = bsec->output_section->vma + bsec->output_offset;
+  if (apos < bpos)
+    return -1;
+  else if (apos > bpos)
+    return 1;
+
+  return asec->id - bsec->id;
+}
+
+/* Rearrange sections with SHF_LINK_ORDER into the same order as their
+   linked sections.  */
+
+static bfd_boolean
+fixup_link_order (lang_output_section_statement_type *os)
+{
+  struct os_sections *os_info = os->data;
+  unsigned int i, j;
+  lang_input_section_type **orig_is;
+  asection **save_s;
+
+  for (i = 0; i < os_info->count; i = j)
+    {
+      /* Normally a linker script will select SHF_LINK_ORDER sections
+	 with an input section wildcard something like the following:
+	 *(.IA_64.unwind* .gnu.linkonce.ia64unw.*)
+	 However if some other random sections are smashed into an
+	 output section, or if SHF_LINK_ORDER are split up by the
+	 linker script, then we only want to sort sections matching a
+	 given wildcard.  That's the purpose of the pattern test.  */
+      for (j = i + 1; j < os_info->count; j++)
+	if (os_info->isec[j].is->pattern != os_info->isec[i].is->pattern)
+	  break;
+      if (j - i > 1)
+	qsort (&os_info->isec[i], j - i, sizeof (*os_info->isec),
+	       compare_link_order);
+    }
+  for (i = 0; i < os_info->count; i++)
+    if (os_info->isec[i].idx != i)
+      break;
+  if (i == os_info->count)
+    return FALSE;
+
+  /* Now reorder the linker input section statements to reflect the
+     proper sorting.  The is done by rewriting the existing statements
+     rather than fiddling with lists, since the only thing we need to
+     change is the bfd section pointer.  */
+  orig_is = xmalloc (os_info->count * sizeof (*orig_is));
+  save_s = xmalloc (os_info->count * sizeof (*save_s));
+  for (i = 0; i < os_info->count; i++)
+    {
+      orig_is[os_info->isec[i].idx] = os_info->isec[i].is;
+      save_s[i] = os_info->isec[i].is->section;
+    }
+  for (i = 0; i < os_info->count; i++)
+    if (os_info->isec[i].idx != i)
+      {
+	orig_is[i]->section = save_s[i];
+	/* Restore os_info to pristine state before the qsort, for the
+	   next pass over sections.  */
+	os_info->isec[i].is = orig_is[i];
+	os_info->isec[i].idx = i;
+      }
+  free (save_s);
+  free (orig_is);
+  return TRUE;
+}
+
 void
 ldelf_map_segments (bfd_boolean need_layout)
 {
   int tries = 10;
+  static bfd_boolean done_link_order_scan = FALSE;
 
   do
     {
       lang_relax_sections (need_layout);
       need_layout = FALSE;
 
+      if (link_info.output_bfd->xvec->flavour == bfd_target_elf_flavour)
+	{
+	  lang_output_section_statement_type *os;
+	  if (!done_link_order_scan)
+	    {
+	      link_order_scan (statement_list.head, NULL);
+	      done_link_order_scan = TRUE;
+	    }
+	  for (os = (void *) lang_os_list.head; os != NULL; os = os->next)
+	    {
+	      struct os_sections *os_info = os->data;
+	      if (os_info != NULL && os_info->ordered != 0)
+		{
+		  if (os_info->ordered != os_info->count
+		      && bfd_link_relocatable (&link_info))
+		    {
+		      einfo (_("%F%P: "
+			       "%pA has both ordered and unordered sections"),
+			     os->bfd_section);
+		      return;
+		    }
+		  if (os_info->count > 1
+		      && fixup_link_order (os))
+		    need_layout = TRUE;
+		}
+	    }
+	}
+
       if (link_info.output_bfd->xvec->flavour == bfd_target_elf_flavour
 	  && !bfd_link_relocatable (&link_info))
 	{
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 4d3560fb1d..4ae9cec885 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -69,13 +69,6 @@ static bfd_boolean map_option_f;
 static bfd_vma print_dot;
 static lang_input_statement_type *first_file;
 static const char *current_target;
-/* Header for list of statements corresponding to any files involved in the
-   link, either specified from the command-line or added implicitely (eg.
-   archive member used to resolved undefined symbol, wildcard statement from
-   linker script, etc.).  Next pointer is in next field of a
-   lang_statement_header_type (reached via header field in a
-   lang_statement_union).  */
-static lang_statement_list_type statement_list;
 static lang_statement_list_type *stat_save[10];
 static lang_statement_list_type **stat_save_ptr = &stat_save[0];
 static struct unique_sections *unique_section_list;
@@ -103,6 +96,13 @@ static void lang_do_memory_regions (bfd_boolean);
 /* Exported variables.  */
 const char *output_target;
 lang_output_section_statement_type *abs_output_section;
+/* Header for list of statements corresponding to any files involved in the
+   link, either specified from the command-line or added implicitely (eg.
+   archive member used to resolved undefined symbol, wildcard statement from
+   linker script, etc.).  Next pointer is in next field of a
+   lang_statement_header_type (reached via header field in a
+   lang_statement_union).  */
+lang_statement_list_type statement_list;
 lang_statement_list_type lang_os_list;
 lang_statement_list_type *stat_ptr = &statement_list;
 /* Header for list of statements corresponding to files used in the final
@@ -582,6 +582,7 @@ output_section_callback_fast (lang_wild_statement_type *ptr,
   node->left = 0;
   node->right = 0;
   node->section = section;
+  node->pattern = ptr->section_list;
 
   tree = wild_sort_fast (ptr, sec, file, section);
   if (tree != NULL)
@@ -598,7 +599,7 @@ output_section_callback_tree_to_list (lang_wild_statement_type *ptr,
   if (tree->left)
     output_section_callback_tree_to_list (ptr, tree->left, output);
 
-  lang_add_section (&ptr->children, tree->section, NULL,
+  lang_add_section (&ptr->children, tree->section, tree->pattern, NULL,
 		    (lang_output_section_statement_type *) output);
 
   if (tree->right)
@@ -1896,7 +1897,7 @@ lang_insert_orphan (asection *s,
 
   if (add_child == NULL)
     add_child = &os->children;
-  lang_add_section (add_child, s, NULL, os);
+  lang_add_section (add_child, s, NULL, NULL, os);
 
   if (after && (s->flags & (SEC_LOAD | SEC_ALLOC)) != 0)
     {
@@ -2537,6 +2538,7 @@ lang_discard_section_p (asection *section)
 void
 lang_add_section (lang_statement_list_type *ptr,
 		  asection *section,
+		  struct wildcard_list *pattern,
 		  struct flag_info *sflag_info,
 		  lang_output_section_statement_type *output)
 {
@@ -2717,6 +2719,7 @@ lang_add_section (lang_statement_list_type *ptr,
   /* Add a section reference to the list.  */
   new_section = new_stat (lang_input_section, ptr);
   new_section->section = section;
+  new_section->pattern = pattern;
 }
 
 /* Handle wildcard sorting.  This returns the lang_input_section which
@@ -2842,14 +2845,16 @@ output_section_callback (lang_wild_statement_type *ptr,
      of the current list.  */
 
   if (before == NULL)
-    lang_add_section (&ptr->children, section, ptr->section_flag_list, os);
+    lang_add_section (&ptr->children, section, ptr->section_list,
+		      ptr->section_flag_list, os);
   else
     {
       lang_statement_list_type list;
       lang_statement_union_type **pp;
 
       lang_list_init (&list);
-      lang_add_section (&list, section, ptr->section_flag_list, os);
+      lang_add_section (&list, section, ptr->section_list,
+			ptr->section_flag_list, os);
 
       /* If we are discarding the section, LIST.HEAD will
 	 be NULL.  */
@@ -7204,7 +7209,7 @@ ldlang_place_orphan (asection *s)
 	  && (bfd_link_relocatable (&link_info)
 	      || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0))
 	os->addr_tree = exp_intop (0);
-      lang_add_section (&os->children, s, NULL, os);
+      lang_add_section (&os->children, s, NULL, NULL, os);
     }
   else
     {
@@ -7227,7 +7232,7 @@ ldlang_place_orphan (asection *s)
 	      && (bfd_link_relocatable (&link_info)
 		  || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0))
 	    os->addr_tree = exp_intop (0);
-	  lang_add_section (&os->children, s, NULL, os);
+	  lang_add_section (&os->children, s, NULL, NULL, os);
 	}
 
       if (config.orphan_handling == orphan_handling_warn)
@@ -7271,7 +7276,7 @@ lang_place_orphans (void)
 			default_common_section
 			  = lang_output_section_statement_lookup (".bss", 0, 1);
 		      lang_add_section (&default_common_section->children, s,
-					NULL, default_common_section);
+					NULL, NULL, default_common_section);
 		    }
 		}
 	      else
@@ -7485,7 +7490,7 @@ lang_reset_memory_regions (void)
 
 static void
 gc_section_callback (lang_wild_statement_type *ptr,
-		     struct wildcard_list *sec,
+		     struct wildcard_list *sec ATTRIBUTE_UNUSED,
 		     asection *section,
 		     lang_input_statement_type *file ATTRIBUTE_UNUSED,
 		     void *data ATTRIBUTE_UNUSED)
@@ -7494,8 +7499,6 @@ gc_section_callback (lang_wild_statement_type *ptr,
      should be as well.  */
   if (ptr->keep_sections)
     section->flags |= SEC_KEEP;
-  if (sec)
-    section->pattern = sec->spec.name;
 }
 
 /* Iterate over sections marking them against GC.  */
diff --git a/ld/ldlang.h b/ld/ldlang.h
index 205c305cce..3463d4ca0c 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -158,6 +158,9 @@ typedef struct lang_output_section_statement_struct
 
   lang_output_section_phdr_list *phdrs;
 
+  /* Used by ELF SHF_LINK_ORDER sorting.  */
+  void *data;
+
   unsigned int block_value;
   int constraint;
   flagword flags;
@@ -323,6 +326,7 @@ typedef struct
 {
   lang_statement_header_type header;
   asection *section;
+  void *pattern;
 } lang_input_section_type;
 
 struct map_symbol_def {
@@ -364,6 +368,7 @@ typedef bfd_boolean (*lang_match_sec_type_func) (bfd *, const asection *,
 typedef struct lang_section_bst
 {
   asection *section;
+  void *pattern;
   struct lang_section_bst *left;
   struct lang_section_bst *right;
 } lang_section_bst_type;
@@ -506,6 +511,7 @@ extern lang_output_section_statement_type *abs_output_section;
 extern lang_statement_list_type lang_os_list;
 extern struct lang_input_statement_flags input_flags;
 extern bfd_boolean lang_has_input_file;
+extern lang_statement_list_type statement_list;
 extern lang_statement_list_type *stat_ptr;
 extern bfd_boolean delete_output_file_on_failure;
 
@@ -650,7 +656,7 @@ extern void lang_enter_group
 extern void lang_leave_group
   (void);
 extern void lang_add_section
-  (lang_statement_list_type *, asection *,
+  (lang_statement_list_type *, asection *, struct wildcard_list *,
    struct flag_info *, lang_output_section_statement_type *);
 extern void lang_new_phdr
   (const char *, etree_type *, bfd_boolean, bfd_boolean, etree_type *,
diff --git a/ld/testsuite/ld-elf/pr26256-2a.d b/ld/testsuite/ld-elf/pr26256-2a.d
index 03804d844d..24e8e67ec3 100644
--- a/ld/testsuite/ld-elf/pr26256-2a.d
+++ b/ld/testsuite/ld-elf/pr26256-2a.d
@@ -1,7 +1,6 @@
 #source: pr26256-2.s
 #ld: -e _start -T pr26256-2.t
 #nm: -n
-#xfail: [is_generic]
 
 #...
 [0-9a-f]+ R linkorder2
diff --git a/ld/testsuite/ld-elf/pr26256-2b.d b/ld/testsuite/ld-elf/pr26256-2b.d
index 60c3bff7cd..3f8c37e267 100644
--- a/ld/testsuite/ld-elf/pr26256-2b.d
+++ b/ld/testsuite/ld-elf/pr26256-2b.d
@@ -1,8 +1,7 @@
 #source: pr26256-2.s
 #ld: -e _start
 #nm: -n
-#xfail: [is_generic]
-#notarget: fr30-*-* iq2000-*-* ip2k-*-* xstormy16-*-*
+#notarget: fr30-*-* iq2000-*-* ip2k-*-* xgate-*-* xstormy16-*-*
 # These targets place .linkorder sections before .text sections.
 
 #...
diff --git a/ld/testsuite/ld-elf/pr26256-3b.d b/ld/testsuite/ld-elf/pr26256-3b.d
index 7d6dff24fb..8a5e6dd59e 100644
--- a/ld/testsuite/ld-elf/pr26256-3b.d
+++ b/ld/testsuite/ld-elf/pr26256-3b.d
@@ -1,7 +1,6 @@
 #source: pr26256-3.s
 #ld: -e _start -T pr26256-3b.t
 #readelf: -x .rodata -x .text
-#xfail: [is_generic]
 
 Hex dump of section \'.rodata\':
   0x[a-f0-9]+ +00020301 +040907 +.+

-- 
Alan Modra
Australia Development Lab, IBM


More information about the Binutils mailing list