ELF: Support the section flag 'o' in .section directive

Message ID 20200203022059.2860106-1-hjl.tools@gmail.com
State New
Headers show
Series
  • ELF: Support the section flag 'o' in .section directive
Related show

Commit Message

H.J. Lu Feb. 3, 2020, 2:20 a.m.
As shown in

https://sourceware.org/bugzilla/show_bug.cgi?id=25490

--gc-sections will silently remove __patchable_function_entries section
and generate corrupt result.  This patch adds the section flag 'o' to
.section directive:

.section __patchable_function_entries,"awo",@progbits,foo
.section __patchable_function_entries,"awoG",@progbits,foo,foo,comdat
.section __patchable_function_entries,"awo",@progbits,bar,unique,4
.section __patchable_function_entries,"awoG",@progbits,foo,foo,comdat,unique,1

which specifies the symbol name which the section references.  Assmebler
will set its elf_linked_to_section to a local section where the symbol
is defined.

Linker is updated to set gc_mark if gc_mark of its linked-to section is
set after all sections, including backend specific ones, have been
garbage collected.  To make it to work, _bfd_elf_gc_mark_extra_sections
must be called after elf_backend_gc_mark_extra_sections.

bfd/

	PR gas/25381
	* bfd-in2.h: Regenerated.
	* elf32-arm.c (elf32_arm_gc_mark_extra_sections): Call
	_bfd_elf_gc_mark_extra_sections last.
	* elf32-csky.c (_bfd_elf_gc_mark_extra_sections): Likewise.
	* elf32-tic6x.c (elf32_tic6x_gc_mark_extra_sections): Likewise.
	* elfxx-mips.c (_bfd_mips_elf_gc_mark_extra_sections): Likewise.
	* elflink.c (_bfd_elf_gc_mark_extra_sections): Set gc_mark if
	gc_mark of its linked-to section is set.
	* section.c (asection): Add linked_to_symbol_name to map_head
	union.

gas/

	PR gas/25381
	* config/obj-elf.c (get_section): Also check
	linked_to_symbol_name.
	(obj_elf_change_section): Also set map_head.linked_to_symbol_name.
	(obj_elf_parse_section_letters): Handle the 'o' flag.
	(build_group_lists): Renamed to ...
	(build_additional_section_info): This.  Set elf_linked_to_section
	from map_head.linked_to_symbol_name.
	(elf_adjust_symtab): Updated.
	* config/obj-elf.h (elf_section_match): Add linked_to_symbol_name.
	* doc/as.texi: Document the 'o' flag.
	* testsuite/gas/elf/elf.exp: Run PR gas/25381 tests.
	* testsuite/gas/elf/section18.d: New file.
	* testsuite/gas/elf/section18.s: Likewise.
	* testsuite/gas/elf/section19.d: Likewise.
	* testsuite/gas/elf/section19.s: Likewise.
	* testsuite/gas/elf/section20.d: Likewise.
	* testsuite/gas/elf/section20.s: Likewise.
	* testsuite/gas/elf/section21.d: Likewise.
	* testsuite/gas/elf/section21.l: Likewise.
	* testsuite/gas/elf/section21.s: Likewise.

ld/

	PR gas/25381
	* testsuite/ld-elf/elf.exp: Run PR gas/25381 tests.
	* testsuite/ld-elf/pr25490-2-32.rd: New file.
	* testsuite/ld-elf/pr25490-2-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-2.s: Likewise.
	* testsuite/ld-elf/pr25490-3-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-3-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-3.s: Likewise.
	* testsuite/ld-elf/pr25490-4-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-4-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-4.s: Likewise.
	* testsuite/ld-elf/pr25490-5-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-5-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-5.s: Likewise.
	* testsuite/ld-elf/pr25490-6-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-6-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-6.s: Likewise.
---
 bfd/bfd-in2.h                       |  4 ++-
 bfd/elf32-arm.c                     |  4 +--
 bfd/elf32-csky.c                    |  4 +--
 bfd/elf32-tic6x.c                   |  4 +--
 bfd/elflink.c                       | 13 +++++++-
 bfd/elfxx-mips.c                    |  4 +--
 bfd/section.c                       |  4 ++-
 gas/config/obj-elf.c                | 52 ++++++++++++++++++++++++++---
 gas/config/obj-elf.h                |  1 +
 gas/doc/as.texi                     | 27 +++++++++++++++
 gas/testsuite/gas/elf/elf.exp       |  4 +++
 gas/testsuite/gas/elf/section18.d   |  8 +++++
 gas/testsuite/gas/elf/section18.s   | 13 ++++++++
 gas/testsuite/gas/elf/section19.d   |  8 +++++
 gas/testsuite/gas/elf/section19.s   | 13 ++++++++
 gas/testsuite/gas/elf/section20.d   | 17 ++++++++++
 gas/testsuite/gas/elf/section20.s   | 13 ++++++++
 gas/testsuite/gas/elf/section21.d   |  2 ++
 gas/testsuite/gas/elf/section21.l   |  4 +++
 gas/testsuite/gas/elf/section21.s   | 15 +++++++++
 ld/testsuite/ld-elf/elf.exp         | 50 +++++++++++++++++++++++++++
 ld/testsuite/ld-elf/pr25490-2-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-2-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-2.s     |  9 +++++
 ld/testsuite/ld-elf/pr25490-3-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-3-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-3.s     | 18 ++++++++++
 ld/testsuite/ld-elf/pr25490-4-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-4-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-4.s     | 18 ++++++++++
 ld/testsuite/ld-elf/pr25490-5-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-5-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-5.s     | 17 ++++++++++
 ld/testsuite/ld-elf/pr25490-6-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-6-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-6.s     | 28 ++++++++++++++++
 36 files changed, 408 insertions(+), 16 deletions(-)
 create mode 100644 gas/testsuite/gas/elf/section18.d
 create mode 100644 gas/testsuite/gas/elf/section18.s
 create mode 100644 gas/testsuite/gas/elf/section19.d
 create mode 100644 gas/testsuite/gas/elf/section19.s
 create mode 100644 gas/testsuite/gas/elf/section20.d
 create mode 100644 gas/testsuite/gas/elf/section20.s
 create mode 100644 gas/testsuite/gas/elf/section21.d
 create mode 100644 gas/testsuite/gas/elf/section21.l
 create mode 100644 gas/testsuite/gas/elf/section21.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-2-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-2-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-2.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-3-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-3-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-3.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-4-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-4-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-4.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-5-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-5-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-5.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-6-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-6-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-6.s

-- 
2.24.1

Comments

Fangrui Song Feb. 3, 2020, 6:26 a.m. | #1
On 2020-02-02, H.J. Lu wrote:
>As shown in

>

>https://sourceware.org/bugzilla/show_bug.cgi?id=25490

>

>--gc-sections will silently remove __patchable_function_entries section

>and generate corrupt result.  This patch adds the section flag 'o' to

>.section directive:

>

>.section __patchable_function_entries,"awo",@progbits,foo

>.section __patchable_function_entries,"awoG",@progbits,foo,foo,comdat

>.section __patchable_function_entries,"awo",@progbits,bar,unique,4

>.section __patchable_function_entries,"awoG",@progbits,foo,foo,comdat,unique,1

>

>which specifies the symbol name which the section references.  Assmebler

>will set its elf_linked_to_section to a local section where the symbol

>is defined.

>

>Linker is updated to set gc_mark if gc_mark of its linked-to section is

>set after all sections, including backend specific ones, have been

>garbage collected.  To make it to work, _bfd_elf_gc_mark_extra_sections

>must be called after elf_backend_gc_mark_extra_sections.

>

>bfd/

>

>	PR gas/25381

>	* bfd-in2.h: Regenerated.

>	* elf32-arm.c (elf32_arm_gc_mark_extra_sections): Call

>	_bfd_elf_gc_mark_extra_sections last.

>	* elf32-csky.c (_bfd_elf_gc_mark_extra_sections): Likewise.

>	* elf32-tic6x.c (elf32_tic6x_gc_mark_extra_sections): Likewise.

>	* elfxx-mips.c (_bfd_mips_elf_gc_mark_extra_sections): Likewise.

>	* elflink.c (_bfd_elf_gc_mark_extra_sections): Set gc_mark if

>	gc_mark of its linked-to section is set.

>	* section.c (asection): Add linked_to_symbol_name to map_head

>	union.

>

>gas/

>

>	PR gas/25381

>	* config/obj-elf.c (get_section): Also check

>	linked_to_symbol_name.

>	(obj_elf_change_section): Also set map_head.linked_to_symbol_name.

>	(obj_elf_parse_section_letters): Handle the 'o' flag.

>	(build_group_lists): Renamed to ...

>	(build_additional_section_info): This.  Set elf_linked_to_section

>	from map_head.linked_to_symbol_name.

>	(elf_adjust_symtab): Updated.

>	* config/obj-elf.h (elf_section_match): Add linked_to_symbol_name.

>	* doc/as.texi: Document the 'o' flag.

>	* testsuite/gas/elf/elf.exp: Run PR gas/25381 tests.

>	* testsuite/gas/elf/section18.d: New file.

>	* testsuite/gas/elf/section18.s: Likewise.

>	* testsuite/gas/elf/section19.d: Likewise.

>	* testsuite/gas/elf/section19.s: Likewise.

>	* testsuite/gas/elf/section20.d: Likewise.

>	* testsuite/gas/elf/section20.s: Likewise.

>	* testsuite/gas/elf/section21.d: Likewise.

>	* testsuite/gas/elf/section21.l: Likewise.

>	* testsuite/gas/elf/section21.s: Likewise.

>

>ld/

>

>	PR gas/25381

>	* testsuite/ld-elf/elf.exp: Run PR gas/25381 tests.

>	* testsuite/ld-elf/pr25490-2-32.rd: New file.

>	* testsuite/ld-elf/pr25490-2-64.rd: Likewise.

>	* testsuite/ld-elf/pr25490-2.s: Likewise.

>	* testsuite/ld-elf/pr25490-3-32.rd: Likewise.

>	* testsuite/ld-elf/pr25490-3-64.rd: Likewise.

>	* testsuite/ld-elf/pr25490-3.s: Likewise.

>	* testsuite/ld-elf/pr25490-4-32.rd: Likewise.

>	* testsuite/ld-elf/pr25490-4-64.rd: Likewise.

>	* testsuite/ld-elf/pr25490-4.s: Likewise.

>	* testsuite/ld-elf/pr25490-5-32.rd: Likewise.

>	* testsuite/ld-elf/pr25490-5-64.rd: Likewise.

>	* testsuite/ld-elf/pr25490-5.s: Likewise.

>	* testsuite/ld-elf/pr25490-6-32.rd: Likewise.

>	* testsuite/ld-elf/pr25490-6-64.rd: Likewise.

>	* testsuite/ld-elf/pr25490-6.s: Likewise.

>---

> bfd/bfd-in2.h                       |  4 ++-

> bfd/elf32-arm.c                     |  4 +--

> bfd/elf32-csky.c                    |  4 +--

> bfd/elf32-tic6x.c                   |  4 +--

> bfd/elflink.c                       | 13 +++++++-

> bfd/elfxx-mips.c                    |  4 +--

> bfd/section.c                       |  4 ++-

> gas/config/obj-elf.c                | 52 ++++++++++++++++++++++++++---

> gas/config/obj-elf.h                |  1 +

> gas/doc/as.texi                     | 27 +++++++++++++++

> gas/testsuite/gas/elf/elf.exp       |  4 +++

> gas/testsuite/gas/elf/section18.d   |  8 +++++

> gas/testsuite/gas/elf/section18.s   | 13 ++++++++

> gas/testsuite/gas/elf/section19.d   |  8 +++++

> gas/testsuite/gas/elf/section19.s   | 13 ++++++++

> gas/testsuite/gas/elf/section20.d   | 17 ++++++++++

> gas/testsuite/gas/elf/section20.s   | 13 ++++++++

> gas/testsuite/gas/elf/section21.d   |  2 ++

> gas/testsuite/gas/elf/section21.l   |  4 +++

> gas/testsuite/gas/elf/section21.s   | 15 +++++++++

> ld/testsuite/ld-elf/elf.exp         | 50 +++++++++++++++++++++++++++

> ld/testsuite/ld-elf/pr25490-2-32.rd |  7 ++++

> ld/testsuite/ld-elf/pr25490-2-64.rd |  7 ++++

> ld/testsuite/ld-elf/pr25490-2.s     |  9 +++++

> ld/testsuite/ld-elf/pr25490-3-32.rd |  7 ++++

> ld/testsuite/ld-elf/pr25490-3-64.rd |  7 ++++

> ld/testsuite/ld-elf/pr25490-3.s     | 18 ++++++++++

> ld/testsuite/ld-elf/pr25490-4-32.rd |  7 ++++

> ld/testsuite/ld-elf/pr25490-4-64.rd |  7 ++++

> ld/testsuite/ld-elf/pr25490-4.s     | 18 ++++++++++

> ld/testsuite/ld-elf/pr25490-5-32.rd |  7 ++++

> ld/testsuite/ld-elf/pr25490-5-64.rd |  7 ++++

> ld/testsuite/ld-elf/pr25490-5.s     | 17 ++++++++++

> ld/testsuite/ld-elf/pr25490-6-32.rd |  7 ++++

> ld/testsuite/ld-elf/pr25490-6-64.rd |  7 ++++

> ld/testsuite/ld-elf/pr25490-6.s     | 28 ++++++++++++++++

> 36 files changed, 408 insertions(+), 16 deletions(-)

> create mode 100644 gas/testsuite/gas/elf/section18.d

> create mode 100644 gas/testsuite/gas/elf/section18.s

> create mode 100644 gas/testsuite/gas/elf/section19.d

> create mode 100644 gas/testsuite/gas/elf/section19.s

> create mode 100644 gas/testsuite/gas/elf/section20.d

> create mode 100644 gas/testsuite/gas/elf/section20.s

> create mode 100644 gas/testsuite/gas/elf/section21.d

> create mode 100644 gas/testsuite/gas/elf/section21.l

> create mode 100644 gas/testsuite/gas/elf/section21.s

> create mode 100644 ld/testsuite/ld-elf/pr25490-2-32.rd

> create mode 100644 ld/testsuite/ld-elf/pr25490-2-64.rd

> create mode 100644 ld/testsuite/ld-elf/pr25490-2.s

> create mode 100644 ld/testsuite/ld-elf/pr25490-3-32.rd

> create mode 100644 ld/testsuite/ld-elf/pr25490-3-64.rd

> create mode 100644 ld/testsuite/ld-elf/pr25490-3.s

> create mode 100644 ld/testsuite/ld-elf/pr25490-4-32.rd

> create mode 100644 ld/testsuite/ld-elf/pr25490-4-64.rd

> create mode 100644 ld/testsuite/ld-elf/pr25490-4.s

> create mode 100644 ld/testsuite/ld-elf/pr25490-5-32.rd

> create mode 100644 ld/testsuite/ld-elf/pr25490-5-64.rd

> create mode 100644 ld/testsuite/ld-elf/pr25490-5.s

> create mode 100644 ld/testsuite/ld-elf/pr25490-6-32.rd

> create mode 100644 ld/testsuite/ld-elf/pr25490-6-64.rd

> create mode 100644 ld/testsuite/ld-elf/pr25490-6.s

>

>diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h

>index 09a5a39ff5e..2d26b81db38 100644

>--- a/bfd/bfd-in2.h

>+++ b/bfd/bfd-in2.h

>@@ -1185,10 +1185,12 @@ typedef struct bfd_section

>   /* 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

>-     structs.  */

>+     structs.  The linked_to_symbol_name field is for ELF assembler

>+     internal use.  */

>   union {

>     struct bfd_link_order *link_order;

>     struct bfd_section *s;

>+    const char *linked_to_symbol_name;

>   } map_head, map_tail;

> } asection;

>

>diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c

>index faf8376f200..a6df8f331ce 100644

>--- a/bfd/elf32-arm.c

>+++ b/bfd/elf32-arm.c

>@@ -15939,8 +15939,6 @@ elf32_arm_gc_mark_extra_sections (struct bfd_link_info *info,

>   bfd_boolean debug_sec_need_to_be_marked = FALSE;

>   asection *isec;

>

>-  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);

>-

>   out_attr = elf_known_obj_attributes_proc (info->output_bfd);

>   is_v8m = out_attr[Tag_CPU_arch].i >= TAG_CPU_ARCH_V8M_BASE

> 	   && out_attr[Tag_CPU_arch_profile].i == 'M';

>@@ -16024,6 +16022,8 @@ elf32_arm_gc_mark_extra_sections (struct bfd_link_info *info,

>       first_bfd_browse = FALSE;

>     }

>

>+  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);

>+

>   return TRUE;

> }

>

>diff --git a/bfd/elf32-csky.c b/bfd/elf32-csky.c

>index 04214f28ea5..3c5fcc82f5a 100644

>--- a/bfd/elf32-csky.c

>+++ b/bfd/elf32-csky.c

>@@ -3032,8 +3032,6 @@ elf32_csky_gc_mark_extra_sections (struct bfd_link_info *info,

> {

>   bfd *sub;

>

>-  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);

>-

>   for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)

>     {

>       asection *o;

>@@ -3043,6 +3041,8 @@ elf32_csky_gc_mark_extra_sections (struct bfd_link_info *info,

> 	  o->gc_mark = 1;

>     }

>

>+  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);

>+

>   return TRUE;

> }

>

>diff --git a/bfd/elf32-tic6x.c b/bfd/elf32-tic6x.c

>index 39ca6992007..6308eded0b4 100644

>--- a/bfd/elf32-tic6x.c

>+++ b/bfd/elf32-tic6x.c

>@@ -1935,8 +1935,6 @@ elf32_tic6x_gc_mark_extra_sections (struct bfd_link_info *info,

>   Elf_Internal_Shdr **elf_shdrp;

>   bfd_boolean again;

>

>-  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);

>-

>   /* Marking EH data may cause additional code sections to be marked,

>      requiring multiple passes.  */

>   again = TRUE;

>@@ -1970,6 +1968,8 @@ elf32_tic6x_gc_mark_extra_sections (struct bfd_link_info *info,

> 	}

>     }

>

>+  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);

>+

>   return TRUE;

> }

>

>diff --git a/bfd/elflink.c b/bfd/elflink.c

>index e4d92952aaf..8ad13b8b55f 100644

>--- a/bfd/elflink.c

>+++ b/bfd/elflink.c

>@@ -13312,7 +13312,9 @@ _bfd_elf_gc_mark_debug_special_section_group (asection *grp)

>     }

> }

>

>-/* Keep debug and special sections.  */

>+/* Keep debug and special sections.  NB: This function must be called

>+   after elf_backend_gc_mark_extra_sections so that linked-to section

>+   can be checkd properly.  */

>

> bfd_boolean

> _bfd_elf_gc_mark_extra_sections (struct bfd_link_info *info,

>@@ -13345,6 +13347,15 @@ _bfd_elf_gc_mark_extra_sections (struct bfd_link_info *info,

> 		   && (isec->flags & SEC_ALLOC) != 0

> 		   && elf_section_type (isec) != SHT_NOTE)

> 	    some_kept = TRUE;

>+	  else

>+	    {

>+	      /* Since all sections, including backend specific ones,

>+		 have been garbage collected, mark this section if its

>+		 linked-to section is marked.  */

>+	      asection *linked_to_sec = elf_linked_to_section (isec);

>+	      if (linked_to_sec && linked_to_sec->gc_mark)

>+		isec->gc_mark = 1;

>+	    }

>

> 	  if (!debug_frag_seen

> 	      && (isec->flags & SEC_DEBUGGING)

>diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c

>index d7e3aed3b67..a2d6ace3236 100644

>--- a/bfd/elfxx-mips.c

>+++ b/bfd/elfxx-mips.c

>@@ -12845,8 +12845,6 @@ _bfd_mips_elf_gc_mark_extra_sections (struct bfd_link_info *info,

> {

>   bfd *sub;

>

>-  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);

>-

>   for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)

>     {

>       asection *o;

>@@ -12863,6 +12861,8 @@ _bfd_mips_elf_gc_mark_extra_sections (struct bfd_link_info *info,

> 	  }

>     }

>

>+  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);

>+

>   return TRUE;

> }

> 

>diff --git a/bfd/section.c b/bfd/section.c

>index 0c15a0d646f..7de0a2d7a85 100644

>--- a/bfd/section.c

>+++ b/bfd/section.c

>@@ -544,10 +544,12 @@ CODE_FRAGMENT

> .  {* 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

>-.     structs.  *}

>+.     structs.  The linked_to_symbol_name field is for ELF assembler

>+.     internal use.  *}

> .  union {

> .    struct bfd_link_order *link_order;

> .    struct bfd_section *s;

>+.    const char *linked_to_symbol_name;

> .  } map_head, map_tail;

> .} asection;

> .

>diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c

>index 2958490c323..d7a07fec6bf 100644

>--- a/gas/config/obj-elf.c

>+++ b/gas/config/obj-elf.c

>@@ -524,6 +524,8 @@ get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)

>   struct elf_section_match *match = (struct elf_section_match *) inf;

>   const char *gname = match->group_name;

>   const char *group_name = elf_group_name (sec);

>+  const char *linked_to_symbol_name

>+    = sec->map_head.linked_to_symbol_name;

>   unsigned int info = elf_section_data (sec)->this_hdr.sh_info;

>

>   return (info == match->info

>@@ -533,7 +535,12 @@ get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)

> 	  && (group_name == gname

> 	      || (group_name != NULL

> 		  && gname != NULL

>-		  && strcmp (group_name, gname) == 0)));

>+		  && strcmp (group_name, gname) == 0))

>+	  && (linked_to_symbol_name == match->linked_to_symbol_name

>+	      || (linked_to_symbol_name != NULL

>+		  && match->linked_to_symbol_name != NULL

>+		  && strcmp (linked_to_symbol_name,

>+			     match->linked_to_symbol_name) == 0)));

> }

>

> /* Handle the .section pseudo-op.  This code supports two different

>@@ -740,6 +747,10 @@ obj_elf_change_section (const char *name,

>       sec->section_id = match_p->section_id;

>       flags |= match_p->flags;

>

>+      /* Set the linked-to symbol name.  */

>+      sec->map_head.linked_to_symbol_name

>+	= match_p->linked_to_symbol_name;

>+

>       bfd_set_section_flags (sec, flags);

>       if (flags & SEC_MERGE)

> 	sec->entsize = entsize;

>@@ -801,6 +812,9 @@ obj_elf_parse_section_letters (char *str, size_t len,

> 	case 'e':

> 	  attr |= SHF_EXCLUDE;

> 	  break;

>+	case 'o':

>+	  attr |= SHF_LINK_ORDER;

>+	  break;

> 	case 'w':

> 	  attr |= SHF_WRITE;

> 	  break;

>@@ -841,7 +855,7 @@ obj_elf_parse_section_letters (char *str, size_t len,

> 	default:

> 	  {

> 	    const char *bad_msg = _("unrecognized .section attribute:"

>-				    " want a,e,w,x,M,S,G,T or number");

>+				    " want a,e,o,w,x,M,S,G,T or number");

> #ifdef md_elf_section_letter

> 	    bfd_vma md_attr = md_elf_section_letter (*str, &bad_msg);

> 	    if (md_attr != (bfd_vma) -1)

>@@ -1154,6 +1168,19 @@ obj_elf_section (int push)

> 	      attr &= ~SHF_MERGE;

> 	    }

>

>+	  if ((attr & SHF_LINK_ORDER) != 0 && *input_line_pointer == ',')

>+	    {

>+	      char c;

>+	      unsigned int length;

>+	      ++input_line_pointer;

>+	      SKIP_WHITESPACE ();

>+	      c = get_symbol_name (& beg);

>+	      (void) restore_line_pointer (c);

>+	      length = input_line_pointer - beg;

>+	      if (length)

>+		match.linked_to_symbol_name = xmemdup0 (beg, length);

>+	    }

>+

> 	  if ((attr & SHF_GROUP) != 0 && is_clone)

> 	    {

> 	      as_warn (_("? section flag ignored with G present"));

>@@ -2476,10 +2503,12 @@ static struct group_list groups;

> /* Called via bfd_map_over_sections.  If SEC is a member of a group,

>    add it to a list of sections belonging to the group.  INF is a

>    pointer to a struct group_list, which is where we store the head of

>-   each list.  */

>+   each list.  If its link_to_symbol_name isn't NULL, set up its

>+   linked-to section.  */

>

> static void

>-build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)

>+build_additional_section_info (bfd *abfd ATTRIBUTE_UNUSED,

>+				  asection *sec, void *inf)

> {

>   struct group_list *list = (struct group_list *) inf;

>   const char *group_name = elf_group_name (sec);

>@@ -2487,6 +2516,18 @@ build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)

>   unsigned int *elem_idx;

>   unsigned int *idx_ptr;

>

>+  if (sec->map_head.linked_to_symbol_name)

>+    {

>+      symbolS *linked_to_sym;

>+      linked_to_sym = symbol_find (sec->map_head.linked_to_symbol_name);

>+      if (!linked_to_sym || !S_IS_DEFINED (linked_to_sym))

>+	as_bad (_("undefined linked-to symbol `%s' on section `%s'"),

>+		sec->map_head.linked_to_symbol_name,

>+		bfd_section_name (sec));

>+      else

>+	elf_linked_to_section (sec) = S_GET_SEGMENT (linked_to_sym);

>+    }

>+

>   if (group_name == NULL)

>     return;

>

>@@ -2533,7 +2574,8 @@ elf_adjust_symtab (void)

>   groups.num_group = 0;

>   groups.head = NULL;

>   groups.indexes = hash_new ();

>-  bfd_map_over_sections (stdoutput, build_group_lists, &groups);

>+  bfd_map_over_sections (stdoutput, build_additional_section_info,

>+			 &groups);

>

>   /* Make the SHT_GROUP sections that describe each section group.  We

>      can't set up the section contents here yet, because elf section

>diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h

>index 7cfcc254823..54af9ebc0e2 100644

>--- a/gas/config/obj-elf.h

>+++ b/gas/config/obj-elf.h

>@@ -82,6 +82,7 @@ struct elf_obj_sy

> struct elf_section_match

> {

>   const char *group_name;

>+  const char *linked_to_symbol_name;

>   unsigned int info;

>   unsigned int section_id;

>   flagword flags;

>diff --git a/gas/doc/as.texi b/gas/doc/as.texi

>index 152bbfdd009..1554c51ad2f 100644

>--- a/gas/doc/as.texi

>+++ b/gas/doc/as.texi

>@@ -6599,6 +6599,9 @@ section is allocatable

> section is a GNU_MBIND section

> @item e

> section is excluded from executable and shared library.

>+@item o

>+section references a symbol defined in another section (the linked-to

>+section) in the same file.

> @item w

> section is writable

> @item x

>@@ -6678,6 +6681,23 @@ which is a suffix of a larger string is considered a duplicate.  Thus

> @code{"def"} will be merged with @code{"abcdef"};  A reference to the first

> @code{"def"} will be changed to a reference to @code{"abcdef"+3}.

>

>+If @var{flags} contains the @code{o} flag, then the @var{type} argument

>+must be present along with an additional field like this:

>+

>+@smallexample

>+.section @var{name},"@var{flags}"o,@@@var{type},@var{SymbolName}

>+@end smallexample

>+

>+The @var{SymbolName} field specifies the symbol name which the section

>+references.

>+

>+Note: If both the @var{M} and @var{o} flags are present, then the fields

>+for the Merge flag should come first, like this:

>+

>+@smallexample

>+.section @var{name},"@var{flags}"Mo,@@@var{type},@var{entsize},@var{SymbolName}

>+@end smallexample

>+

> If @var{flags} contains the @code{G} symbol then the @var{type} argument must

> be present along with an additional field like this:

>

>@@ -6702,6 +6722,13 @@ the Merge flag should come first, like this:

> .section @var{name} , "@var{flags}"MG, @@@var{type}, @var{entsize}, @var{GroupName}[, @var{linkage}]

> @end smallexample

>

>+If both @code{o} flag and @code{G} flag are present, then the

>+@var{SymbolName} field for @code{o} comes first, like this:

>+

>+@smallexample

>+.section @var{name},"@var{flags}"oG,@@@var{type},@var{SymbolName},@var{GroupName}[,@var{linkage}]

>+@end smallexample

>+

> If @var{flags} contains the @code{?} symbol then it may not also contain the

> @code{G} symbol and the @var{GroupName} or @var{linkage} fields should not be

> present.  Instead, @code{?} says to consider the section that's current before

>diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp

>index 08c6830e0db..0f9b2672c4c 100644

>--- a/gas/testsuite/gas/elf/elf.exp

>+++ b/gas/testsuite/gas/elf/elf.exp

>@@ -249,6 +249,10 @@ if { [is_elf_format] } then {

>     run_dump_test "section16a"

>     run_dump_test "section16b"

>     run_dump_test "section17"

>+    run_dump_test "section18"

>+    run_dump_test "section19"

>+    run_dump_test "section20"

>+    run_dump_test "section21"

>     run_dump_test "dwarf2-1" $dump_opts

>     run_dump_test "dwarf2-2" $dump_opts

>     run_dump_test "dwarf2-3" $dump_opts

>diff --git a/gas/testsuite/gas/elf/section18.d b/gas/testsuite/gas/elf/section18.d

>new file mode 100644

>index 00000000000..a00b4a65f95

>--- /dev/null

>+++ b/gas/testsuite/gas/elf/section18.d

>@@ -0,0 +1,8 @@

>+#readelf: -SW

>+#name: linked-to section 1

>+

>+#...

>+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WAL +.*

>+#...

>+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WAL +.*

>+#pass

>diff --git a/gas/testsuite/gas/elf/section18.s b/gas/testsuite/gas/elf/section18.s

>new file mode 100644

>index 00000000000..d51eb681318

>--- /dev/null

>+++ b/gas/testsuite/gas/elf/section18.s

>@@ -0,0 +1,13 @@

>+	.text

>+foo:

>+	.section __patchable_function_entries,"awo",%progbits,foo

>+	.dc.a	.LPFE1

>+	.text

>+.LPFE1:

>+	.byte 0

>+	.section __patchable_function_entries,"awo",%progbits,bar

>+	.dc.a	.LPFE2

>+	.text

>+bar:

>+.LPFE2:

>+	.byte 0

>diff --git a/gas/testsuite/gas/elf/section19.d b/gas/testsuite/gas/elf/section19.d

>new file mode 100644

>index 00000000000..4e500ad3bd2

>--- /dev/null

>+++ b/gas/testsuite/gas/elf/section19.d

>@@ -0,0 +1,8 @@

>+#readelf: -SW

>+#name: linked-to section 2

>+

>+#...

>+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WAL +.*

>+#...

>+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WAL +.*

>+#pass

>diff --git a/gas/testsuite/gas/elf/section19.s b/gas/testsuite/gas/elf/section19.s

>new file mode 100644

>index 00000000000..7d30ea1ff9c

>--- /dev/null

>+++ b/gas/testsuite/gas/elf/section19.s

>@@ -0,0 +1,13 @@

>+	.section .text,"ax",%progbits,unique,20

>+foo:

>+	.section __patchable_function_entries,"awo",%progbits,foo,unique,2

>+	.dc.a	.LPFE1

>+	.section .text,"ax",%progbits,unique,20

>+.LPFE1:

>+	.byte 0

>+	.section __patchable_function_entries,"awo",%progbits,bar,unique,102

>+	.dc.a	.LPFE2

>+	.section .text,"ax",%progbits,unique,2

>+bar:

>+.LPFE2:

>+	.byte 0

>diff --git a/gas/testsuite/gas/elf/section20.d b/gas/testsuite/gas/elf/section20.d

>new file mode 100644

>index 00000000000..3e0b023f7ac

>--- /dev/null

>+++ b/gas/testsuite/gas/elf/section20.d

>@@ -0,0 +1,17 @@

>+#readelf: -SWg

>+#name: linked-to section 3

>+

>+#...

>+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WALG +.*

>+#...

>+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WALG +.*

>+#...

>+COMDAT group section \[[ 0-9]+\] `.group' \[foo\] contains [0-9]+ sections:

>+   \[Index\]    Name

>+#...

>+   \[[ 0-9]+\]   __patchable_function_entries

>+#...

>+COMDAT group section \[[ 0-9]+\] `.group' \[bar\] contains [0-9]+ sections:

>+#...

>+   \[[ 0-9]+\]   __patchable_function_entries

>+#pass

>diff --git a/gas/testsuite/gas/elf/section20.s b/gas/testsuite/gas/elf/section20.s

>new file mode 100644

>index 00000000000..1e9639ef4a7

>--- /dev/null

>+++ b/gas/testsuite/gas/elf/section20.s

>@@ -0,0 +1,13 @@

>+	.section .text,"axG",%progbits,foo,comdat

>+foo:

>+	.section __patchable_function_entries,"awoG",%progbits,foo,foo,comdat

>+	.dc.a	.LPFE1

>+	.section .text,"axG",%progbits,foo,comdat

>+.LPFE1:

>+	.byte 0

>+	.section __patchable_function_entries,"awoG",%progbits,bar,bar,comdat,unique,4

>+	.dc.a	.LPFE2

>+	.section .text,"axG",%progbits,bar,comdat,unique,24

>+bar:

>+.LPFE2:

>+	.byte 0

>diff --git a/gas/testsuite/gas/elf/section21.d b/gas/testsuite/gas/elf/section21.d

>new file mode 100644

>index 00000000000..54fa9d419bc

>--- /dev/null

>+++ b/gas/testsuite/gas/elf/section21.d

>@@ -0,0 +1,2 @@

>+#name: incorrect linked-to symbols

>+#error_output: section21.l

>diff --git a/gas/testsuite/gas/elf/section21.l b/gas/testsuite/gas/elf/section21.l

>new file mode 100644

>index 00000000000..8b9af6118a9

>--- /dev/null

>+++ b/gas/testsuite/gas/elf/section21.l

>@@ -0,0 +1,4 @@

>+[^:]*: Assembler messages:

>+[^:]*:11: Error: junk@end of line, first unrecognized character is `1'

>+[^:]*: Error: undefined linked-to symbol `bar' on section `__patchable_function_entries'

>+[^:]*: Error: undefined linked-to symbol `foo' on section `__patchable_function_entries'

>diff --git a/gas/testsuite/gas/elf/section21.s b/gas/testsuite/gas/elf/section21.s

>new file mode 100644

>index 00000000000..ae5f848b299

>--- /dev/null

>+++ b/gas/testsuite/gas/elf/section21.s

>@@ -0,0 +1,15 @@

>+	.section __patchable_function_entries,"awo",%progbits,bar

>+	.dc.a	.LPFE1

>+	.text

>+.LPFE1:

>+	.byte 0

>+	.section __patchable_function_entries,"awo",%progbits,foo

>+	.dc.a	.LPFE2

>+	.text

>+.LPFE2:

>+	.dc.a foo

>+	.section __patchable_function_entries,"awo",%progbits,1foo

>+	.dc.a	.LPFE3

>+	.text

>+.LPFE3:

>+	.byte 0

>diff --git a/ld/testsuite/ld-elf/elf.exp b/ld/testsuite/ld-elf/elf.exp

>index 989fb50d4ae..ea8cd884056 100644

>--- a/ld/testsuite/ld-elf/elf.exp

>+++ b/ld/testsuite/ld-elf/elf.exp

>@@ -76,9 +76,19 @@ run_ld_link_tests [list \

> if [is_elf64 tmpdir/symbol3w.a] {

>     set ASFLAGS "$ASFLAGS --defsym ALIGN=3"

>     set pr23900_1_exp "pr23900-1-64.rd"

>+    set pr25490_2_exp "pr25490-2-64.rd"

>+    set pr25490_3_exp "pr25490-3-64.rd"

>+    set pr25490_4_exp "pr25490-4-64.rd"

>+    set pr25490_5_exp "pr25490-5-64.rd"

>+    set pr25490_6_exp "pr25490-6-64.rd"

> } else {

>     set ASFLAGS "$ASFLAGS --defsym ALIGN=2"

>     set pr23900_1_exp "pr23900-1-32.rd"

>+    set pr25490_2_exp "pr25490-2-32.rd"

>+    set pr25490_3_exp "pr25490-3-32.rd"

>+    set pr25490_4_exp "pr25490-4-32.rd"

>+    set pr25490_5_exp "pr25490-5-32.rd"

>+    set pr25490_6_exp "pr25490-6-32.rd"

> }

>

>

>@@ -172,6 +182,46 @@ if { [istarget *-*-*linux*]

> 	]

> }

>

>+if [check_gc_sections_available] {

>+    run_ld_link_tests [list \

>+	[list "__patchable_function_entries section 2" \

>+	    "--gc-sections -e _start" \

>+	    "" \

>+	    "" \

>+	    {pr25490-2.s} \

>+	    [list [list "readelf" {-SW} $pr25490_2_exp]] \

>+	    "pr25490-2.exe"] \

>+	[list "__patchable_function_entries section 3" \

>+	    "--gc-sections -e _start" \

>+	    "" \

>+	    "" \

>+	    {pr25490-3.s} \

>+	    [list [list "readelf" {-SW} $pr25490_3_exp]] \

>+	    "pr25490-3.exe"] \

>+	[list "__patchable_function_entries section 4" \

>+	    "--gc-sections -e _start" \

>+	    "" \

>+	    "" \

>+	    {pr25490-4.s} \

>+	    [list [list "readelf" {-SW} $pr25490_4_exp]] \

>+	    "pr25490-4.exe"] \

>+	[list "__patchable_function_entries section 5" \

>+	    "--gc-sections -e _start" \

>+	    "" \

>+	    "" \

>+	    {pr25490-5.s} \

>+	    [list [list "readelf" {-SW} $pr25490_5_exp]] \

>+	    "pr25490-5.exe"] \

>+	[list "__patchable_function_entries section 6" \

>+	    "--gc-sections -e _start" \

>+	    "" \

>+	    "" \

>+	    {pr25490-6.s} \

>+	    [list [list "readelf" {-SW} $pr25490_6_exp]] \

>+	    "pr25490-6.exe"] \

>+	]

>+}

>+

> set LDFLAGS $old_ldflags

> set ASFLAGS $old_asflags

>

>diff --git a/ld/testsuite/ld-elf/pr25490-2-32.rd b/ld/testsuite/ld-elf/pr25490-2-32.rd

>new file mode 100644

>index 00000000000..99ee4dbb5d7

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-2-32.rd

>@@ -0,0 +1,7 @@

>+#source: pr25490-2.s

>+#ld: --gc-sections

>+#readelf: -SW

>+

>+#...

>+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+4 +00 +WAL +[0-9] +0 +1

>+#pass

>diff --git a/ld/testsuite/ld-elf/pr25490-2-64.rd b/ld/testsuite/ld-elf/pr25490-2-64.rd

>new file mode 100644

>index 00000000000..db086d5aafc

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-2-64.rd

>@@ -0,0 +1,7 @@

>+#source: pr25490-2.s

>+#ld: --gc-sections

>+#readelf: -SW

>+

>+#...

>+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +1

>+#pass

>diff --git a/ld/testsuite/ld-elf/pr25490-2.s b/ld/testsuite/ld-elf/pr25490-2.s

>new file mode 100644

>index 00000000000..856bb5fe9d3

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-2.s

>@@ -0,0 +1,9 @@

>+	.text

>+	.globl	_start

>+	.type	_start, %function

>+_start:

>+	.section __patchable_function_entries,"awo",%progbits,_start

>+	.dc.a	.LPFE1

>+	.text

>+.LPFE1:

>+	.byte 0

>diff --git a/ld/testsuite/ld-elf/pr25490-3-32.rd b/ld/testsuite/ld-elf/pr25490-3-32.rd

>new file mode 100644

>index 00000000000..d2765f84c7f

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-3-32.rd

>@@ -0,0 +1,7 @@

>+#source: pr25490-3.s

>+#ld: --gc-sections

>+#readelf: -SW

>+

>+#...

>+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+4 +00 +WAL +[0-9] +0 +1

>+#pass

>diff --git a/ld/testsuite/ld-elf/pr25490-3-64.rd b/ld/testsuite/ld-elf/pr25490-3-64.rd

>new file mode 100644

>index 00000000000..5faeebe65d8

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-3-64.rd

>@@ -0,0 +1,7 @@

>+#source: pr25490-3.s

>+#ld: --gc-sections

>+#readelf: -SW

>+

>+#...

>+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +1

>+#pass

>diff --git a/ld/testsuite/ld-elf/pr25490-3.s b/ld/testsuite/ld-elf/pr25490-3.s

>new file mode 100644

>index 00000000000..427ad69b535

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-3.s

>@@ -0,0 +1,18 @@

>+	.section	.text.bar,"ax",%progbits

>+	.globl	bar

>+	.type	bar, %function

>+bar:

>+	.section __patchable_function_entries,"awo",%progbits,bar

>+	.dc.a	.LPFE1

>+	.section	.text.bar,"ax",%progbits

>+.LPFE1:

>+	.byte 0

>+	.section	.text._start,"ax",%progbits

>+	.globl	_start

>+	.type	_start, %function

>+_start:

>+	.section __patchable_function_entries,"awo",%progbits,_start

>+	.dc.a	.LPFE2

>+	.section	.text._start,"ax",%progbits

>+.LPFE2:

>+	.byte 0

>diff --git a/ld/testsuite/ld-elf/pr25490-4-32.rd b/ld/testsuite/ld-elf/pr25490-4-32.rd

>new file mode 100644

>index 00000000000..5667f40836b

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-4-32.rd

>@@ -0,0 +1,7 @@

>+#source: pr25490-4.s

>+#ld: --gc-sections

>+#readelf: -SW

>+

>+#...

>+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +1

>+#pass

>diff --git a/ld/testsuite/ld-elf/pr25490-4-64.rd b/ld/testsuite/ld-elf/pr25490-4-64.rd

>new file mode 100644

>index 00000000000..5a3b34dcd56

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-4-64.rd

>@@ -0,0 +1,7 @@

>+#source: pr25490-4.s

>+#ld: --gc-sections

>+#readelf: -SW

>+

>+#...

>+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+10 +00 +WAL +[0-9] +0 +1

>+#pass

>diff --git a/ld/testsuite/ld-elf/pr25490-4.s b/ld/testsuite/ld-elf/pr25490-4.s

>new file mode 100644

>index 00000000000..9b3ffcad04d

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-4.s

>@@ -0,0 +1,18 @@

>+	.section	.text.bar,"ax",%progbits

>+	.globl	bar

>+	.type	bar, %function

>+bar:

>+	.section __patchable_function_entries,"awo",%progbits,bar

>+	.dc.a	.LPFE1

>+	.section	.text.bar,"ax",%progbits

>+.LPFE1:

>+	.byte 0

>+	.section	.text._start,"ax",%progbits

>+	.globl	_start

>+	.type	_start, %function

>+_start:

>+	.section __patchable_function_entries,"awo",%progbits,_start

>+	.dc.a	.LPFE2

>+	.section	.text._start,"ax",%progbits

>+.LPFE2:

>+	.dc.a	bar

>diff --git a/ld/testsuite/ld-elf/pr25490-5-32.rd b/ld/testsuite/ld-elf/pr25490-5-32.rd

>new file mode 100644

>index 00000000000..32e19b8b5c8

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-5-32.rd

>@@ -0,0 +1,7 @@

>+#source: pr25490-5.s

>+#ld: --gc-sections

>+#readelf: -SW

>+

>+#...

>+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +1

>+#pass

>diff --git a/ld/testsuite/ld-elf/pr25490-5-64.rd b/ld/testsuite/ld-elf/pr25490-5-64.rd

>new file mode 100644

>index 00000000000..17358d5feda

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-5-64.rd

>@@ -0,0 +1,7 @@

>+#source: pr25490-5.s

>+#ld: --gc-sections

>+#readelf: -SW

>+

>+#...

>+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+10 +00 +WAL +[0-9] +0 +1

>+#pass

>diff --git a/ld/testsuite/ld-elf/pr25490-5.s b/ld/testsuite/ld-elf/pr25490-5.s

>new file mode 100644

>index 00000000000..f9d46b8e0b0

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-5.s

>@@ -0,0 +1,17 @@

>+	.text

>+	.type	bar, %function

>+bar:

>+	.section __patchable_function_entries,"awo",%progbits,bar

>+	.dc.a	.LPFE1

>+	.text

>+.LPFE1:

>+	.byte 0

>+	.text

>+	.globl	_start

>+	.type	_start, %function

>+_start:

>+	.section __patchable_function_entries,"awo",%progbits,_start

>+	.dc.a	.LPFE2

>+	.text

>+.LPFE2:

>+	.byte	0

>diff --git a/ld/testsuite/ld-elf/pr25490-6-32.rd b/ld/testsuite/ld-elf/pr25490-6-32.rd

>new file mode 100644

>index 00000000000..807e83159a8

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-6-32.rd

>@@ -0,0 +1,7 @@

>+#source: pr25490-6.s

>+#ld: --gc-sections

>+#readelf: -SW

>+

>+#...

>+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+c +00 +WAL +[0-9] +0 +1

>+#pass

>diff --git a/ld/testsuite/ld-elf/pr25490-6-64.rd b/ld/testsuite/ld-elf/pr25490-6-64.rd

>new file mode 100644

>index 00000000000..668eaa45601

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-6-64.rd

>@@ -0,0 +1,7 @@

>+#source: pr25490-6.s

>+#ld: --gc-sections

>+#readelf: -SW

>+

>+#...

>+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+18 +00 +WAL +[0-9] +0 +1

>+#pass

>diff --git a/ld/testsuite/ld-elf/pr25490-6.s b/ld/testsuite/ld-elf/pr25490-6.s

>new file mode 100644

>index 00000000000..5f5a348d4d0

>--- /dev/null

>+++ b/ld/testsuite/ld-elf/pr25490-6.s

>@@ -0,0 +1,28 @@

>+	.section .text,"axG",%progbits,bar,comdat

>+	.globl	bar

>+	.type	bar, %function

>+bar:

>+	.section __patchable_function_entries,"awo",%progbits,bar

>+	.dc.a	.LPFE1

>+	.section .text,"axG",%progbits,bar,comdat

>+.LPFE1:

>+	.byte 0

>+	.section .text,"axG",%progbits,foo,comdat

>+	.globl	foo

>+	.type	foo, %function

>+foo:

>+	.section __patchable_function_entries,"awo",%progbits,foo,unique,0

>+	.dc.a	.LPFE2

>+	.section .text,"axG",%progbits,foo,comdat

>+.LPFE2:

>+	.byte 0

>+	.section .text,"axG",%progbits,_start,comdat,unique,1

>+	.globl	_start

>+	.type	_start, %function

>+_start:

>+	.section __patchable_function_entries,"awoG",%progbits,_start,_start,comdat,unique,3

>+	.dc.a	.LPFE3

>+	.section .text,"axG",%progbits,_start,comdat,unique,1

>+.LPFE3:

>+	.dc.a	foo

>+	.dc.a	bar

>-- 

>2.24.1


Does .pushsection directive support the section flag "o" as well?

   .section __patchable_function_entries,"awoG",%progbits,_start,_start,comdat,unique,3
   .pushsection __patchable_function_entries,"awoG",%progbits,_start,_start,comdat,unique,3

How many feature requests from https://sourceware.org/ml/binutils/2019-11/msg00266.html are implemented in this patch?

   Generalize GC support for SHF_LINK_ORDER
   https://sourceware.org/bugzilla/show_bug.cgi?id=24526
   
   Garbage collecting non-alloc SHF_LINK_ORDER sections
   https://sourceware.org/bugzilla/show_bug.cgi?id=25021
   
   Make /DISCARD/ discard SHF_LINK_ORDER sections
   https://sourceware.org/bugzilla/show_bug.cgi?id=25022


If possible, separating the section flag "o" change from ld --gc-sections semantics may be clearer.

--gc-sections is a bit tricky. Its implementation may require more scrutiny.

The desired semantics:

For SHF_ALLOC sections, GC roots consist of
SHT_INIT_ARRAY / SHT_FINI_ARRAY / SHT_PREINIT_ARRAY / (SHT_NOTE not in a section group).
GNU ld may have rules for other GC roots. I haven't check that.

For a non-SHF_ALLOC section, it is a GC root only if all the following
conditions are satisfied:

* it is not a SHT_REL[A] (used by -r --gc-sections and --emit-relocs)
* it is not in a section group with at least one SHF_ALLOC section.
   If it belongs to such a section group, we expect a
   SHF_ALLOC section in the section group responsible for making the whole group
   alive. This semantic is at least expected by
   https://fedoraproject.org/wiki/Toolchain/Watermark

   If it belongs to a section group with no SHF_ALLOC sections => A real
   world example: clang/gcc -fdebug-type-sections places .debug_types and
   .rela.debug_types in a section group. We should consider .debug_types a GC root.
   (see https://reviews.llvm.org/D70146 + https://reviews.llvm.org/D71157)

   GNU ld's current implementation only special cases SEC_DEBUGGING. It'd
   be nice to generalize it.
* it does not have the SHF_LINK_ORDER flag. We expect the linked-to
   section responsible for making it alive.
H.J. Lu Feb. 3, 2020, 2:37 p.m. | #2
On Sun, Feb 02, 2020 at 10:26:42PM -0800, Fangrui Song wrote:
> Does .pushsection directive support the section flag "o" as well?

> 

>   .section __patchable_function_entries,"awoG",%progbits,_start,_start,comdat,unique,3

>   .pushsection __patchable_function_entries,"awoG",%progbits,_start,_start,comdat,unique,3


Yes, it does.

> 

> How many feature requests from https://sourceware.org/ml/binutils/2019-11/msg00266.html are implemented in this patch?

> 

>   Generalize GC support for SHF_LINK_ORDER

>   https://sourceware.org/bugzilla/show_bug.cgi?id=24526

>   Garbage collecting non-alloc SHF_LINK_ORDER sections

>   https://sourceware.org/bugzilla/show_bug.cgi?id=25021


I updated my patch to include testcases for these 2 bugs.

>   Make /DISCARD/ discard SHF_LINK_ORDER sections

>   https://sourceware.org/bugzilla/show_bug.cgi?id=25022

> 


I will submit a separate patch for it.

> 

> If possible, separating the section flag "o" change from ld --gc-sections semantics may be clearer.


Linker change is relatively small and I'd like to use linker tests to
verify assembler changes.

> 

> --gc-sections is a bit tricky. Its implementation may require more scrutiny.

> 

> The desired semantics:

> 

> For SHF_ALLOC sections, GC roots consist of

> SHT_INIT_ARRAY / SHT_FINI_ARRAY / SHT_PREINIT_ARRAY / (SHT_NOTE not in a section group).

> GNU ld may have rules for other GC roots. I haven't check that.

> 

> For a non-SHF_ALLOC section, it is a GC root only if all the following

> conditions are satisfied:

> 

> * it is not a SHT_REL[A] (used by -r --gc-sections and --emit-relocs)

> * it is not in a section group with at least one SHF_ALLOC section.

>   If it belongs to such a section group, we expect a

>   SHF_ALLOC section in the section group responsible for making the whole group

>   alive. This semantic is at least expected by

>   https://fedoraproject.org/wiki/Toolchain/Watermark

> 

>   If it belongs to a section group with no SHF_ALLOC sections => A real

>   world example: clang/gcc -fdebug-type-sections places .debug_types and

>   .rela.debug_types in a section group. We should consider .debug_types a GC root.

>   (see https://reviews.llvm.org/D70146 + https://reviews.llvm.org/D71157)

> 

>   GNU ld's current implementation only special cases SEC_DEBUGGING. It'd

>   be nice to generalize it.

> * it does not have the SHF_LINK_ORDER flag. We expect the linked-to

>   section responsible for making it alive.


Please file bug reports for any GC issues and CC me.

Thanks.

H.J.
---
As shown in

https://sourceware.org/bugzilla/show_bug.cgi?id=25490

--gc-sections will silently remove __patchable_function_entries section
and generate corrupt result.  This patch adds the section flag 'o' to
.section directive:

.section __patchable_function_entries,"awo",@progbits,foo
.section __patchable_function_entries,"awoG",@progbits,foo,foo,comdat
.section __patchable_function_entries,"awo",@progbits,bar,unique,4
.section __patchable_function_entries,"awoG",@progbits,foo,foo,comdat,unique,1

which specifies the symbol name which the section references.  Assmebler
will set its elf_linked_to_section to a local section where the symbol
is defined.

Linker is updated to set gc_mark only if gc_mark of any of its linked-to
section is set after all sections, including backend specific ones, have
been garbage collected.  _bfd_elf_gc_mark_extra_sections must be called
after elf_backend_gc_mark_extra_sections.

bfd/

	PR gas/25381
	* bfd-in2.h: Regenerated.
	* elf32-arm.c (elf32_arm_gc_mark_extra_sections): Call
	_bfd_elf_gc_mark_extra_sections last.
	* elf32-csky.c (_bfd_elf_gc_mark_extra_sections): Likewise.
	* elf32-tic6x.c (elf32_tic6x_gc_mark_extra_sections): Likewise.
	* elfxx-mips.c (_bfd_mips_elf_gc_mark_extra_sections): Likewise.
	* elflink.c (_bfd_elf_gc_mark_extra_sections): Set gc_mark only
	if gc_mark of any of its linked-to section is set and don't set
	gc_mark again.
	* section.c (asection): Add linked_to_symbol_name to map_head
	union.

gas/

	PR gas/25381
	* config/obj-elf.c (get_section): Also check
	linked_to_symbol_name.
	(obj_elf_change_section): Also set map_head.linked_to_symbol_name.
	(obj_elf_parse_section_letters): Handle the 'o' flag.
	(build_group_lists): Renamed to ...
	(build_additional_section_info): This.  Set elf_linked_to_section
	from map_head.linked_to_symbol_name.
	(elf_adjust_symtab): Updated.
	* config/obj-elf.h (elf_section_match): Add linked_to_symbol_name.
	* doc/as.texi: Document the 'o' flag.
	* testsuite/gas/elf/elf.exp: Run PR gas/25381 tests.
	* testsuite/gas/elf/section18.d: New file.
	* testsuite/gas/elf/section18.s: Likewise.
	* testsuite/gas/elf/section19.d: Likewise.
	* testsuite/gas/elf/section19.s: Likewise.
	* testsuite/gas/elf/section20.d: Likewise.
	* testsuite/gas/elf/section20.s: Likewise.
	* testsuite/gas/elf/section21.d: Likewise.
	* testsuite/gas/elf/section21.l: Likewise.
	* testsuite/gas/elf/section21.s: Likewise.

ld/

	PR ld/24526
	PR ld/25021
	PR ld/25490
	* testsuite/ld-elf/elf.exp: Run PR ld/25490 tests.
	* testsuite/ld-elf/pr24526.d: New file.
	* testsuite/ld-elf/pr24526.s: Likewise.
	* testsuite/ld-elf/pr25021.d: Likewise.
	* testsuite/ld-elf/pr25021.s: Likewise.
	* testsuite/ld-elf/pr25490-2-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-2-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-2.s: Likewise.
	* testsuite/ld-elf/pr25490-3-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-3-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-3.s: Likewise.
	* testsuite/ld-elf/pr25490-4-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-4-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-4.s: Likewise.
	* testsuite/ld-elf/pr25490-5-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-5-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-5.s: Likewise.
	* testsuite/ld-elf/pr25490-6-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-6-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-6.s: Likewise.
---
 bfd/bfd-in2.h                       |  4 ++-
 bfd/elf32-arm.c                     |  4 +--
 bfd/elf32-csky.c                    |  4 +--
 bfd/elf32-tic6x.c                   |  4 +--
 bfd/elflink.c                       | 24 +++++++++++--
 bfd/elfxx-mips.c                    |  4 +--
 bfd/section.c                       |  4 ++-
 gas/config/obj-elf.c                | 52 ++++++++++++++++++++++++++---
 gas/config/obj-elf.h                |  1 +
 gas/doc/as.texi                     | 27 +++++++++++++++
 gas/testsuite/gas/elf/elf.exp       |  4 +++
 gas/testsuite/gas/elf/section18.d   |  8 +++++
 gas/testsuite/gas/elf/section18.s   | 13 ++++++++
 gas/testsuite/gas/elf/section19.d   |  8 +++++
 gas/testsuite/gas/elf/section19.s   | 13 ++++++++
 gas/testsuite/gas/elf/section20.d   | 17 ++++++++++
 gas/testsuite/gas/elf/section20.s   | 13 ++++++++
 gas/testsuite/gas/elf/section21.d   |  2 ++
 gas/testsuite/gas/elf/section21.l   |  4 +++
 gas/testsuite/gas/elf/section21.s   | 15 +++++++++
 ld/testsuite/ld-elf/elf.exp         | 50 +++++++++++++++++++++++++++
 ld/testsuite/ld-elf/pr24526.d       |  8 +++++
 ld/testsuite/ld-elf/pr24526.s       | 11 ++++++
 ld/testsuite/ld-elf/pr25021.d       |  6 ++++
 ld/testsuite/ld-elf/pr25021.s       | 20 +++++++++++
 ld/testsuite/ld-elf/pr25490-2-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-2-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-2.s     |  9 +++++
 ld/testsuite/ld-elf/pr25490-3-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-3-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-3.s     | 18 ++++++++++
 ld/testsuite/ld-elf/pr25490-4-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-4-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-4.s     | 18 ++++++++++
 ld/testsuite/ld-elf/pr25490-5-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-5-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-5.s     | 17 ++++++++++
 ld/testsuite/ld-elf/pr25490-6-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-6-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-6.s     | 28 ++++++++++++++++
 40 files changed, 462 insertions(+), 18 deletions(-)
 create mode 100644 gas/testsuite/gas/elf/section18.d
 create mode 100644 gas/testsuite/gas/elf/section18.s
 create mode 100644 gas/testsuite/gas/elf/section19.d
 create mode 100644 gas/testsuite/gas/elf/section19.s
 create mode 100644 gas/testsuite/gas/elf/section20.d
 create mode 100644 gas/testsuite/gas/elf/section20.s
 create mode 100644 gas/testsuite/gas/elf/section21.d
 create mode 100644 gas/testsuite/gas/elf/section21.l
 create mode 100644 gas/testsuite/gas/elf/section21.s
 create mode 100644 ld/testsuite/ld-elf/pr24526.d
 create mode 100644 ld/testsuite/ld-elf/pr24526.s
 create mode 100644 ld/testsuite/ld-elf/pr25021.d
 create mode 100644 ld/testsuite/ld-elf/pr25021.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-2-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-2-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-2.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-3-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-3-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-3.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-4-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-4-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-4.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-5-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-5-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-5.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-6-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-6-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-6.s

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 09a5a39ff5e..2d26b81db38 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1185,10 +1185,12 @@ typedef struct bfd_section
   /* 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
-     structs.  */
+     structs.  The linked_to_symbol_name field is for ELF assembler
+     internal use.  */
   union {
     struct bfd_link_order *link_order;
     struct bfd_section *s;
+    const char *linked_to_symbol_name;
   } map_head, map_tail;
 } asection;
 
diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c
index faf8376f200..a6df8f331ce 100644
--- a/bfd/elf32-arm.c
+++ b/bfd/elf32-arm.c
@@ -15939,8 +15939,6 @@ elf32_arm_gc_mark_extra_sections (struct bfd_link_info *info,
   bfd_boolean debug_sec_need_to_be_marked = FALSE;
   asection *isec;
 
-  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
-
   out_attr = elf_known_obj_attributes_proc (info->output_bfd);
   is_v8m = out_attr[Tag_CPU_arch].i >= TAG_CPU_ARCH_V8M_BASE
 	   && out_attr[Tag_CPU_arch_profile].i == 'M';
@@ -16024,6 +16022,8 @@ elf32_arm_gc_mark_extra_sections (struct bfd_link_info *info,
       first_bfd_browse = FALSE;
     }
 
+  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
+
   return TRUE;
 }
 
diff --git a/bfd/elf32-csky.c b/bfd/elf32-csky.c
index 04214f28ea5..3c5fcc82f5a 100644
--- a/bfd/elf32-csky.c
+++ b/bfd/elf32-csky.c
@@ -3032,8 +3032,6 @@ elf32_csky_gc_mark_extra_sections (struct bfd_link_info *info,
 {
   bfd *sub;
 
-  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
-
   for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
     {
       asection *o;
@@ -3043,6 +3041,8 @@ elf32_csky_gc_mark_extra_sections (struct bfd_link_info *info,
 	  o->gc_mark = 1;
     }
 
+  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
+
   return TRUE;
 }
 
diff --git a/bfd/elf32-tic6x.c b/bfd/elf32-tic6x.c
index 39ca6992007..6308eded0b4 100644
--- a/bfd/elf32-tic6x.c
+++ b/bfd/elf32-tic6x.c
@@ -1935,8 +1935,6 @@ elf32_tic6x_gc_mark_extra_sections (struct bfd_link_info *info,
   Elf_Internal_Shdr **elf_shdrp;
   bfd_boolean again;
 
-  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
-
   /* Marking EH data may cause additional code sections to be marked,
      requiring multiple passes.  */
   again = TRUE;
@@ -1970,6 +1968,8 @@ elf32_tic6x_gc_mark_extra_sections (struct bfd_link_info *info,
 	}
     }
 
+  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
+
   return TRUE;
 }
 
diff --git a/bfd/elflink.c b/bfd/elflink.c
index e4d92952aaf..b69105fcd57 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -13312,7 +13312,9 @@ _bfd_elf_gc_mark_debug_special_section_group (asection *grp)
     }
 }
 
-/* Keep debug and special sections.  */
+/* Keep debug and special sections.  NB: This function must be called
+   after elf_backend_gc_mark_extra_sections so that linked-to section
+   can be checkd properly.  */
 
 bfd_boolean
 _bfd_elf_gc_mark_extra_sections (struct bfd_link_info *info,
@@ -13345,6 +13347,20 @@ _bfd_elf_gc_mark_extra_sections (struct bfd_link_info *info,
 		   && (isec->flags & SEC_ALLOC) != 0
 		   && elf_section_type (isec) != SHT_NOTE)
 	    some_kept = TRUE;
+	  else
+	    {
+	      /* Since all sections, including backend specific ones,
+		 have been garbage collected, mark this section if any
+		 of its linked-to section is marked.  */
+	      asection *linked_to_sec = elf_linked_to_section (isec);
+	      for (; linked_to_sec != NULL;
+		   linked_to_sec = elf_linked_to_section (linked_to_sec))
+		if (linked_to_sec->gc_mark)
+		  {
+		    isec->gc_mark = 1;
+		    break;
+		  }
+	    }
 
 	  if (!debug_frag_seen
 	      && (isec->flags & SEC_DEBUGGING)
@@ -13366,14 +13382,16 @@ _bfd_elf_gc_mark_extra_sections (struct bfd_link_info *info,
 
       /* Keep debug and special sections like .comment when they are
 	 not part of a group.  Also keep section groups that contain
-	 just debug sections or special sections.  */
+	 just debug sections or special sections.  NB: Sections with
+	 linked-to section has been handled above.  */
       for (isec = ibfd->sections; isec != NULL; isec = isec->next)
 	{
 	  if ((isec->flags & SEC_GROUP) != 0)
 	    _bfd_elf_gc_mark_debug_special_section_group (isec);
 	  else if (((isec->flags & SEC_DEBUGGING) != 0
 		    || (isec->flags & (SEC_ALLOC | SEC_LOAD | SEC_RELOC)) == 0)
-		   && elf_next_in_group (isec) == NULL)
+		   && elf_next_in_group (isec) == NULL
+		   && elf_linked_to_section (isec) == NULL)
 	    isec->gc_mark = 1;
 	  if (isec->gc_mark && (isec->flags & SEC_DEBUGGING) != 0)
 	    has_kept_debug_info = TRUE;
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index d7e3aed3b67..a2d6ace3236 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -12845,8 +12845,6 @@ _bfd_mips_elf_gc_mark_extra_sections (struct bfd_link_info *info,
 {
   bfd *sub;
 
-  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
-
   for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
     {
       asection *o;
@@ -12863,6 +12861,8 @@ _bfd_mips_elf_gc_mark_extra_sections (struct bfd_link_info *info,
 	  }
     }
 
+  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
+
   return TRUE;
 }
 
diff --git a/bfd/section.c b/bfd/section.c
index 0c15a0d646f..7de0a2d7a85 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -544,10 +544,12 @@ CODE_FRAGMENT
 .  {* 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
-.     structs.  *}
+.     structs.  The linked_to_symbol_name field is for ELF assembler
+.     internal use.  *}
 .  union {
 .    struct bfd_link_order *link_order;
 .    struct bfd_section *s;
+.    const char *linked_to_symbol_name;
 .  } map_head, map_tail;
 .} asection;
 .
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index 2958490c323..d7a07fec6bf 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -524,6 +524,8 @@ get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
   struct elf_section_match *match = (struct elf_section_match *) inf;
   const char *gname = match->group_name;
   const char *group_name = elf_group_name (sec);
+  const char *linked_to_symbol_name
+    = sec->map_head.linked_to_symbol_name;
   unsigned int info = elf_section_data (sec)->this_hdr.sh_info;
 
   return (info == match->info
@@ -533,7 +535,12 @@ get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 	  && (group_name == gname
 	      || (group_name != NULL
 		  && gname != NULL
-		  && strcmp (group_name, gname) == 0)));
+		  && strcmp (group_name, gname) == 0))
+	  && (linked_to_symbol_name == match->linked_to_symbol_name
+	      || (linked_to_symbol_name != NULL
+		  && match->linked_to_symbol_name != NULL
+		  && strcmp (linked_to_symbol_name,
+			     match->linked_to_symbol_name) == 0)));
 }
 
 /* Handle the .section pseudo-op.  This code supports two different
@@ -740,6 +747,10 @@ obj_elf_change_section (const char *name,
       sec->section_id = match_p->section_id;
       flags |= match_p->flags;
 
+      /* Set the linked-to symbol name.  */
+      sec->map_head.linked_to_symbol_name
+	= match_p->linked_to_symbol_name;
+
       bfd_set_section_flags (sec, flags);
       if (flags & SEC_MERGE)
 	sec->entsize = entsize;
@@ -801,6 +812,9 @@ obj_elf_parse_section_letters (char *str, size_t len,
 	case 'e':
 	  attr |= SHF_EXCLUDE;
 	  break;
+	case 'o':
+	  attr |= SHF_LINK_ORDER;
+	  break;
 	case 'w':
 	  attr |= SHF_WRITE;
 	  break;
@@ -841,7 +855,7 @@ obj_elf_parse_section_letters (char *str, size_t len,
 	default:
 	  {
 	    const char *bad_msg = _("unrecognized .section attribute:"
-				    " want a,e,w,x,M,S,G,T or number");
+				    " want a,e,o,w,x,M,S,G,T or number");
 #ifdef md_elf_section_letter
 	    bfd_vma md_attr = md_elf_section_letter (*str, &bad_msg);
 	    if (md_attr != (bfd_vma) -1)
@@ -1154,6 +1168,19 @@ obj_elf_section (int push)
 	      attr &= ~SHF_MERGE;
 	    }
 
+	  if ((attr & SHF_LINK_ORDER) != 0 && *input_line_pointer == ',')
+	    {
+	      char c;
+	      unsigned int length;
+	      ++input_line_pointer;
+	      SKIP_WHITESPACE ();
+	      c = get_symbol_name (& beg);
+	      (void) restore_line_pointer (c);
+	      length = input_line_pointer - beg;
+	      if (length)
+		match.linked_to_symbol_name = xmemdup0 (beg, length);
+	    }
+
 	  if ((attr & SHF_GROUP) != 0 && is_clone)
 	    {
 	      as_warn (_("? section flag ignored with G present"));
@@ -2476,10 +2503,12 @@ static struct group_list groups;
 /* Called via bfd_map_over_sections.  If SEC is a member of a group,
    add it to a list of sections belonging to the group.  INF is a
    pointer to a struct group_list, which is where we store the head of
-   each list.  */
+   each list.  If its link_to_symbol_name isn't NULL, set up its
+   linked-to section.  */
 
 static void
-build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
+build_additional_section_info (bfd *abfd ATTRIBUTE_UNUSED,
+				  asection *sec, void *inf)
 {
   struct group_list *list = (struct group_list *) inf;
   const char *group_name = elf_group_name (sec);
@@ -2487,6 +2516,18 @@ build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
   unsigned int *elem_idx;
   unsigned int *idx_ptr;
 
+  if (sec->map_head.linked_to_symbol_name)
+    {
+      symbolS *linked_to_sym;
+      linked_to_sym = symbol_find (sec->map_head.linked_to_symbol_name);
+      if (!linked_to_sym || !S_IS_DEFINED (linked_to_sym))
+	as_bad (_("undefined linked-to symbol `%s' on section `%s'"),
+		sec->map_head.linked_to_symbol_name,
+		bfd_section_name (sec));
+      else
+	elf_linked_to_section (sec) = S_GET_SEGMENT (linked_to_sym);
+    }
+
   if (group_name == NULL)
     return;
 
@@ -2533,7 +2574,8 @@ elf_adjust_symtab (void)
   groups.num_group = 0;
   groups.head = NULL;
   groups.indexes = hash_new ();
-  bfd_map_over_sections (stdoutput, build_group_lists, &groups);
+  bfd_map_over_sections (stdoutput, build_additional_section_info,
+			 &groups);
 
   /* Make the SHT_GROUP sections that describe each section group.  We
      can't set up the section contents here yet, because elf section
diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h
index 7cfcc254823..54af9ebc0e2 100644
--- a/gas/config/obj-elf.h
+++ b/gas/config/obj-elf.h
@@ -82,6 +82,7 @@ struct elf_obj_sy
 struct elf_section_match
 {
   const char *group_name;
+  const char *linked_to_symbol_name;
   unsigned int info;
   unsigned int section_id;
   flagword flags;
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 152bbfdd009..1554c51ad2f 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -6599,6 +6599,9 @@ section is allocatable
 section is a GNU_MBIND section
 @item e
 section is excluded from executable and shared library.
+@item o
+section references a symbol defined in another section (the linked-to
+section) in the same file.
 @item w
 section is writable
 @item x
@@ -6678,6 +6681,23 @@ which is a suffix of a larger string is considered a duplicate.  Thus
 @code{"def"} will be merged with @code{"abcdef"};  A reference to the first
 @code{"def"} will be changed to a reference to @code{"abcdef"+3}.
 
+If @var{flags} contains the @code{o} flag, then the @var{type} argument
+must be present along with an additional field like this:
+
+@smallexample
+.section @var{name},"@var{flags}"o,@@@var{type},@var{SymbolName}
+@end smallexample
+
+The @var{SymbolName} field specifies the symbol name which the section
+references.
+
+Note: If both the @var{M} and @var{o} flags are present, then the fields
+for the Merge flag should come first, like this:
+
+@smallexample
+.section @var{name},"@var{flags}"Mo,@@@var{type},@var{entsize},@var{SymbolName}
+@end smallexample
+
 If @var{flags} contains the @code{G} symbol then the @var{type} argument must
 be present along with an additional field like this:
 
@@ -6702,6 +6722,13 @@ the Merge flag should come first, like this:
 .section @var{name} , "@var{flags}"MG, @@@var{type}, @var{entsize}, @var{GroupName}[, @var{linkage}]
 @end smallexample
 
+If both @code{o} flag and @code{G} flag are present, then the
+@var{SymbolName} field for @code{o} comes first, like this:
+
+@smallexample
+.section @var{name},"@var{flags}"oG,@@@var{type},@var{SymbolName},@var{GroupName}[,@var{linkage}]
+@end smallexample
+
 If @var{flags} contains the @code{?} symbol then it may not also contain the
 @code{G} symbol and the @var{GroupName} or @var{linkage} fields should not be
 present.  Instead, @code{?} says to consider the section that's current before
diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp
index 08c6830e0db..0f9b2672c4c 100644
--- a/gas/testsuite/gas/elf/elf.exp
+++ b/gas/testsuite/gas/elf/elf.exp
@@ -249,6 +249,10 @@ if { [is_elf_format] } then {
     run_dump_test "section16a"
     run_dump_test "section16b"
     run_dump_test "section17"
+    run_dump_test "section18"
+    run_dump_test "section19"
+    run_dump_test "section20"
+    run_dump_test "section21"
     run_dump_test "dwarf2-1" $dump_opts
     run_dump_test "dwarf2-2" $dump_opts
     run_dump_test "dwarf2-3" $dump_opts
diff --git a/gas/testsuite/gas/elf/section18.d b/gas/testsuite/gas/elf/section18.d
new file mode 100644
index 00000000000..a00b4a65f95
--- /dev/null
+++ b/gas/testsuite/gas/elf/section18.d
@@ -0,0 +1,8 @@
+#readelf: -SW
+#name: linked-to section 1
+
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WAL +.*
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WAL +.*
+#pass
diff --git a/gas/testsuite/gas/elf/section18.s b/gas/testsuite/gas/elf/section18.s
new file mode 100644
index 00000000000..d51eb681318
--- /dev/null
+++ b/gas/testsuite/gas/elf/section18.s
@@ -0,0 +1,13 @@
+	.text
+foo:
+	.section __patchable_function_entries,"awo",%progbits,foo
+	.dc.a	.LPFE1
+	.text
+.LPFE1:
+	.byte 0
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE2
+	.text
+bar:
+.LPFE2:
+	.byte 0
diff --git a/gas/testsuite/gas/elf/section19.d b/gas/testsuite/gas/elf/section19.d
new file mode 100644
index 00000000000..4e500ad3bd2
--- /dev/null
+++ b/gas/testsuite/gas/elf/section19.d
@@ -0,0 +1,8 @@
+#readelf: -SW
+#name: linked-to section 2
+
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WAL +.*
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WAL +.*
+#pass
diff --git a/gas/testsuite/gas/elf/section19.s b/gas/testsuite/gas/elf/section19.s
new file mode 100644
index 00000000000..7d30ea1ff9c
--- /dev/null
+++ b/gas/testsuite/gas/elf/section19.s
@@ -0,0 +1,13 @@
+	.section .text,"ax",%progbits,unique,20
+foo:
+	.section __patchable_function_entries,"awo",%progbits,foo,unique,2
+	.dc.a	.LPFE1
+	.section .text,"ax",%progbits,unique,20
+.LPFE1:
+	.byte 0
+	.section __patchable_function_entries,"awo",%progbits,bar,unique,102
+	.dc.a	.LPFE2
+	.section .text,"ax",%progbits,unique,2
+bar:
+.LPFE2:
+	.byte 0
diff --git a/gas/testsuite/gas/elf/section20.d b/gas/testsuite/gas/elf/section20.d
new file mode 100644
index 00000000000..3e0b023f7ac
--- /dev/null
+++ b/gas/testsuite/gas/elf/section20.d
@@ -0,0 +1,17 @@
+#readelf: -SWg
+#name: linked-to section 3
+
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WALG +.*
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WALG +.*
+#...
+COMDAT group section \[[ 0-9]+\] `.group' \[foo\] contains [0-9]+ sections:
+   \[Index\]    Name
+#...
+   \[[ 0-9]+\]   __patchable_function_entries
+#...
+COMDAT group section \[[ 0-9]+\] `.group' \[bar\] contains [0-9]+ sections:
+#...
+   \[[ 0-9]+\]   __patchable_function_entries
+#pass
diff --git a/gas/testsuite/gas/elf/section20.s b/gas/testsuite/gas/elf/section20.s
new file mode 100644
index 00000000000..1e9639ef4a7
--- /dev/null
+++ b/gas/testsuite/gas/elf/section20.s
@@ -0,0 +1,13 @@
+	.section .text,"axG",%progbits,foo,comdat
+foo:
+	.section __patchable_function_entries,"awoG",%progbits,foo,foo,comdat
+	.dc.a	.LPFE1
+	.section .text,"axG",%progbits,foo,comdat
+.LPFE1:
+	.byte 0
+	.section __patchable_function_entries,"awoG",%progbits,bar,bar,comdat,unique,4
+	.dc.a	.LPFE2
+	.section .text,"axG",%progbits,bar,comdat,unique,24
+bar:
+.LPFE2:
+	.byte 0
diff --git a/gas/testsuite/gas/elf/section21.d b/gas/testsuite/gas/elf/section21.d
new file mode 100644
index 00000000000..54fa9d419bc
--- /dev/null
+++ b/gas/testsuite/gas/elf/section21.d
@@ -0,0 +1,2 @@
+#name: incorrect linked-to symbols
+#error_output: section21.l
diff --git a/gas/testsuite/gas/elf/section21.l b/gas/testsuite/gas/elf/section21.l
new file mode 100644
index 00000000000..8b9af6118a9
--- /dev/null
+++ b/gas/testsuite/gas/elf/section21.l
@@ -0,0 +1,4 @@
+[^:]*: Assembler messages:
+[^:]*:11: Error: junk at end of line, first unrecognized character is `1'
+[^:]*: Error: undefined linked-to symbol `bar' on section `__patchable_function_entries'
+[^:]*: Error: undefined linked-to symbol `foo' on section `__patchable_function_entries'
diff --git a/gas/testsuite/gas/elf/section21.s b/gas/testsuite/gas/elf/section21.s
new file mode 100644
index 00000000000..ae5f848b299
--- /dev/null
+++ b/gas/testsuite/gas/elf/section21.s
@@ -0,0 +1,15 @@
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.text
+.LPFE1:
+	.byte 0
+	.section __patchable_function_entries,"awo",%progbits,foo
+	.dc.a	.LPFE2
+	.text
+.LPFE2:
+	.dc.a foo
+	.section __patchable_function_entries,"awo",%progbits,1foo
+	.dc.a	.LPFE3
+	.text
+.LPFE3:
+	.byte 0
diff --git a/ld/testsuite/ld-elf/elf.exp b/ld/testsuite/ld-elf/elf.exp
index 989fb50d4ae..ea8cd884056 100644
--- a/ld/testsuite/ld-elf/elf.exp
+++ b/ld/testsuite/ld-elf/elf.exp
@@ -76,9 +76,19 @@ run_ld_link_tests [list \
 if [is_elf64 tmpdir/symbol3w.a] {
     set ASFLAGS "$ASFLAGS --defsym ALIGN=3"
     set pr23900_1_exp "pr23900-1-64.rd"
+    set pr25490_2_exp "pr25490-2-64.rd"
+    set pr25490_3_exp "pr25490-3-64.rd"
+    set pr25490_4_exp "pr25490-4-64.rd"
+    set pr25490_5_exp "pr25490-5-64.rd"
+    set pr25490_6_exp "pr25490-6-64.rd"
 } else {
     set ASFLAGS "$ASFLAGS --defsym ALIGN=2"
     set pr23900_1_exp "pr23900-1-32.rd"
+    set pr25490_2_exp "pr25490-2-32.rd"
+    set pr25490_3_exp "pr25490-3-32.rd"
+    set pr25490_4_exp "pr25490-4-32.rd"
+    set pr25490_5_exp "pr25490-5-32.rd"
+    set pr25490_6_exp "pr25490-6-32.rd"
 }
 
 
@@ -172,6 +182,46 @@ if { [istarget *-*-*linux*]
 	]
 }
 
+if [check_gc_sections_available] {
+    run_ld_link_tests [list \
+	[list "__patchable_function_entries section 2" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-2.s} \
+	    [list [list "readelf" {-SW} $pr25490_2_exp]] \
+	    "pr25490-2.exe"] \
+	[list "__patchable_function_entries section 3" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-3.s} \
+	    [list [list "readelf" {-SW} $pr25490_3_exp]] \
+	    "pr25490-3.exe"] \
+	[list "__patchable_function_entries section 4" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-4.s} \
+	    [list [list "readelf" {-SW} $pr25490_4_exp]] \
+	    "pr25490-4.exe"] \
+	[list "__patchable_function_entries section 5" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-5.s} \
+	    [list [list "readelf" {-SW} $pr25490_5_exp]] \
+	    "pr25490-5.exe"] \
+	[list "__patchable_function_entries section 6" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-6.s} \
+	    [list [list "readelf" {-SW} $pr25490_6_exp]] \
+	    "pr25490-6.exe"] \
+	]
+}
+
 set LDFLAGS $old_ldflags
 set ASFLAGS $old_asflags
 
diff --git a/ld/testsuite/ld-elf/pr24526.d b/ld/testsuite/ld-elf/pr24526.d
new file mode 100644
index 00000000000..6c9a01aa707
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr24526.d
@@ -0,0 +1,8 @@
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] \.bar +PROGBITS +[0-9a-f]+ +[0-9a-f]+ [0-9a-f]+ +00 +AL +[0-9] .*
+#...
+ +\[ *[0-9]+\] \.zed +PROGBITS +[0-9a-f]+ +[0-9a-f]+ [0-9a-f]+ +00 +AL +[0-9] .*
+#pass
diff --git a/ld/testsuite/ld-elf/pr24526.s b/ld/testsuite/ld-elf/pr24526.s
new file mode 100644
index 00000000000..4be0a92913d
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr24526.s
@@ -0,0 +1,11 @@
+	.text
+	.globl _start
+_start:
+	.dc.a .foo
+
+	.section .foo,"a"
+	.dc.a	0
+	.section .bar,"ao",%progbits,.foo
+	.dc.a	0
+	.section .zed,"ao",%progbits,.foo
+	.dc.a	0
diff --git a/ld/testsuite/ld-elf/pr25021.d b/ld/testsuite/ld-elf/pr25021.d
new file mode 100644
index 00000000000..491518f2e04
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25021.d
@@ -0,0 +1,6 @@
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] \.stack_sizes +PROGBITS +0+ +[0-9a-f]+ 0+1 +00 +L +[0-9] .*
+#pass
diff --git a/ld/testsuite/ld-elf/pr25021.s b/ld/testsuite/ld-elf/pr25021.s
new file mode 100644
index 00000000000..17599c41db6
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25021.s
@@ -0,0 +1,20 @@
+	.section .text.live,"ax",%progbits
+	.globl live
+live:
+	.byte 0
+
+	.section .stack_sizes,"o",%progbits,.text.live,unique,0
+	.byte 1
+
+	.section .text.dead,"ax",%progbits
+	.globl dead
+dead:
+	.byte 1
+
+	.section .stack_sizes,"o",%progbits,.text.dead,unique,1
+	.byte 2
+
+	.section .text.main,"ax",%progbits
+	.globl _start
+_start:
+	.dc.a live
diff --git a/ld/testsuite/ld-elf/pr25490-2-32.rd b/ld/testsuite/ld-elf/pr25490-2-32.rd
new file mode 100644
index 00000000000..476ff4bf40f
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-2-32.rd
@@ -0,0 +1,7 @@
+#source: pr25490-2.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+4 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-2-64.rd b/ld/testsuite/ld-elf/pr25490-2-64.rd
new file mode 100644
index 00000000000..2107b750041
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-2-64.rd
@@ -0,0 +1,7 @@
+#source: pr25490-2.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-2.s b/ld/testsuite/ld-elf/pr25490-2.s
new file mode 100644
index 00000000000..856bb5fe9d3
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-2.s
@@ -0,0 +1,9 @@
+	.text
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awo",%progbits,_start
+	.dc.a	.LPFE1
+	.text
+.LPFE1:
+	.byte 0
diff --git a/ld/testsuite/ld-elf/pr25490-3-32.rd b/ld/testsuite/ld-elf/pr25490-3-32.rd
new file mode 100644
index 00000000000..7b2fba260ed
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-3-32.rd
@@ -0,0 +1,7 @@
+#source: pr25490-3.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+4 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-3-64.rd b/ld/testsuite/ld-elf/pr25490-3-64.rd
new file mode 100644
index 00000000000..b02393a3782
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-3-64.rd
@@ -0,0 +1,7 @@
+#source: pr25490-3.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-3.s b/ld/testsuite/ld-elf/pr25490-3.s
new file mode 100644
index 00000000000..427ad69b535
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-3.s
@@ -0,0 +1,18 @@
+	.section	.text.bar,"ax",%progbits
+	.globl	bar
+	.type	bar, %function
+bar:
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.section	.text.bar,"ax",%progbits
+.LPFE1:
+	.byte 0
+	.section	.text._start,"ax",%progbits
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awo",%progbits,_start
+	.dc.a	.LPFE2
+	.section	.text._start,"ax",%progbits
+.LPFE2:
+	.byte 0
diff --git a/ld/testsuite/ld-elf/pr25490-4-32.rd b/ld/testsuite/ld-elf/pr25490-4-32.rd
new file mode 100644
index 00000000000..26037b917a4
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-4-32.rd
@@ -0,0 +1,7 @@
+#source: pr25490-4.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-4-64.rd b/ld/testsuite/ld-elf/pr25490-4-64.rd
new file mode 100644
index 00000000000..5e7f8a763f3
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-4-64.rd
@@ -0,0 +1,7 @@
+#source: pr25490-4.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+10 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-4.s b/ld/testsuite/ld-elf/pr25490-4.s
new file mode 100644
index 00000000000..9b3ffcad04d
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-4.s
@@ -0,0 +1,18 @@
+	.section	.text.bar,"ax",%progbits
+	.globl	bar
+	.type	bar, %function
+bar:
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.section	.text.bar,"ax",%progbits
+.LPFE1:
+	.byte 0
+	.section	.text._start,"ax",%progbits
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awo",%progbits,_start
+	.dc.a	.LPFE2
+	.section	.text._start,"ax",%progbits
+.LPFE2:
+	.dc.a	bar
diff --git a/ld/testsuite/ld-elf/pr25490-5-32.rd b/ld/testsuite/ld-elf/pr25490-5-32.rd
new file mode 100644
index 00000000000..7bf986e6e1b
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-5-32.rd
@@ -0,0 +1,7 @@
+#source: pr25490-5.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-5-64.rd b/ld/testsuite/ld-elf/pr25490-5-64.rd
new file mode 100644
index 00000000000..93a731aea7b
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-5-64.rd
@@ -0,0 +1,7 @@
+#source: pr25490-5.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+10 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-5.s b/ld/testsuite/ld-elf/pr25490-5.s
new file mode 100644
index 00000000000..f9d46b8e0b0
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-5.s
@@ -0,0 +1,17 @@
+	.text
+	.type	bar, %function
+bar:
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.text
+.LPFE1:
+	.byte 0
+	.text
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awo",%progbits,_start
+	.dc.a	.LPFE2
+	.text
+.LPFE2:
+	.byte	0
diff --git a/ld/testsuite/ld-elf/pr25490-6-32.rd b/ld/testsuite/ld-elf/pr25490-6-32.rd
new file mode 100644
index 00000000000..3ab8fe34977
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-6-32.rd
@@ -0,0 +1,7 @@
+#source: pr25490-6.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+c +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-6-64.rd b/ld/testsuite/ld-elf/pr25490-6-64.rd
new file mode 100644
index 00000000000..18c7eda901a
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-6-64.rd
@@ -0,0 +1,7 @@
+#source: pr25490-6.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+18 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-6.s b/ld/testsuite/ld-elf/pr25490-6.s
new file mode 100644
index 00000000000..5f5a348d4d0
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-6.s
@@ -0,0 +1,28 @@
+	.section .text,"axG",%progbits,bar,comdat
+	.globl	bar
+	.type	bar, %function
+bar:
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.section .text,"axG",%progbits,bar,comdat
+.LPFE1:
+	.byte 0
+	.section .text,"axG",%progbits,foo,comdat
+	.globl	foo
+	.type	foo, %function
+foo:
+	.section __patchable_function_entries,"awo",%progbits,foo,unique,0
+	.dc.a	.LPFE2
+	.section .text,"axG",%progbits,foo,comdat
+.LPFE2:
+	.byte 0
+	.section .text,"axG",%progbits,_start,comdat,unique,1
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awoG",%progbits,_start,_start,comdat,unique,3
+	.dc.a	.LPFE3
+	.section .text,"axG",%progbits,_start,comdat,unique,1
+.LPFE3:
+	.dc.a	foo
+	.dc.a	bar
-- 
2.24.1
Alan Modra Feb. 6, 2020, 7:46 a.m. | #3
On Mon, Feb 03, 2020 at 06:37:17AM -0800, H.J. Lu wrote:
> bfd/

> 

> 	PR gas/25381

> 	* bfd-in2.h: Regenerated.

> 	* elf32-arm.c (elf32_arm_gc_mark_extra_sections): Call

> 	_bfd_elf_gc_mark_extra_sections last.

> 	* elf32-csky.c (_bfd_elf_gc_mark_extra_sections): Likewise.

> 	* elf32-tic6x.c (elf32_tic6x_gc_mark_extra_sections): Likewise.

> 	* elfxx-mips.c (_bfd_mips_elf_gc_mark_extra_sections): Likewise.

> 	* elflink.c (_bfd_elf_gc_mark_extra_sections): Set gc_mark only

> 	if gc_mark of any of its linked-to section is set and don't set

> 	gc_mark again.

> 	* section.c (asection): Add linked_to_symbol_name to map_head

> 	union.

> 

> gas/

> 

> 	PR gas/25381

> 	* config/obj-elf.c (get_section): Also check

> 	linked_to_symbol_name.

> 	(obj_elf_change_section): Also set map_head.linked_to_symbol_name.

> 	(obj_elf_parse_section_letters): Handle the 'o' flag.

> 	(build_group_lists): Renamed to ...

> 	(build_additional_section_info): This.  Set elf_linked_to_section

> 	from map_head.linked_to_symbol_name.

> 	(elf_adjust_symtab): Updated.

> 	* config/obj-elf.h (elf_section_match): Add linked_to_symbol_name.

> 	* doc/as.texi: Document the 'o' flag.

> 	* testsuite/gas/elf/elf.exp: Run PR gas/25381 tests.

> 	* testsuite/gas/elf/section18.d: New file.

> 	* testsuite/gas/elf/section18.s: Likewise.

> 	* testsuite/gas/elf/section19.d: Likewise.

> 	* testsuite/gas/elf/section19.s: Likewise.

> 	* testsuite/gas/elf/section20.d: Likewise.

> 	* testsuite/gas/elf/section20.s: Likewise.

> 	* testsuite/gas/elf/section21.d: Likewise.

> 	* testsuite/gas/elf/section21.l: Likewise.

> 	* testsuite/gas/elf/section21.s: Likewise.

> 

> ld/

> 

> 	PR ld/24526

> 	PR ld/25021

> 	PR ld/25490

> 	* testsuite/ld-elf/elf.exp: Run PR ld/25490 tests.

> 	* testsuite/ld-elf/pr24526.d: New file.

> 	* testsuite/ld-elf/pr24526.s: Likewise.

> 	* testsuite/ld-elf/pr25021.d: Likewise.

> 	* testsuite/ld-elf/pr25021.s: Likewise.

> 	* testsuite/ld-elf/pr25490-2-32.rd: Likewise.

> 	* testsuite/ld-elf/pr25490-2-64.rd: Likewise.

> 	* testsuite/ld-elf/pr25490-2.s: Likewise.

> 	* testsuite/ld-elf/pr25490-3-32.rd: Likewise.

> 	* testsuite/ld-elf/pr25490-3-64.rd: Likewise.

> 	* testsuite/ld-elf/pr25490-3.s: Likewise.

> 	* testsuite/ld-elf/pr25490-4-32.rd: Likewise.

> 	* testsuite/ld-elf/pr25490-4-64.rd: Likewise.

> 	* testsuite/ld-elf/pr25490-4.s: Likewise.

> 	* testsuite/ld-elf/pr25490-5-32.rd: Likewise.

> 	* testsuite/ld-elf/pr25490-5-64.rd: Likewise.

> 	* testsuite/ld-elf/pr25490-5.s: Likewise.

> 	* testsuite/ld-elf/pr25490-6-32.rd: Likewise.

> 	* testsuite/ld-elf/pr25490-6-64.rd: Likewise.

> 	* testsuite/ld-elf/pr25490-6.s: Likewise.


I see rather a lot of fails for the new testcases.  Some changes to
the testcases clearly are needed before this can go in.  A quick
glance shows two major problems:
1) You don't want to run a testcase requiring --gc-sections on a
   target that doesn't support --gc-sections.
2) You assume .dc.a sizes are only 4 or 8 but we have a number of
   targets that have 16-bit addresses.

Also, for sh-linux, I see this in pr25490-2.o
  [ 4] __patchable_function_entries PROGBITS        00000000 000038 000004 00 WAL  1   0  4
Linking to itself?  Please do look into all these fails.

avr-elf  +FAIL: linked-to section 1
avr-elf  +FAIL: linked-to section 2
avr-elf  +FAIL: linked-to section 3
avr-elf  +FAIL: __patchable_function_entries section 2
avr-elf  +FAIL: __patchable_function_entries section 3
avr-elf  +FAIL: __patchable_function_entries section 4
avr-elf  +FAIL: __patchable_function_entries section 5
avr-elf  +FAIL: __patchable_function_entries section 6
bfin-linux-uclibc  +FAIL: ld-elf/pr24526
bfin-linux-uclibc  +FAIL: ld-elf/pr25021
bfin-linux-uclibc  +FAIL: __patchable_function_entries section 4
bfin-linux-uclibc  +FAIL: __patchable_function_entries section 6
d30v-elf  +FAIL: ld-elf/pr24526
d30v-elf  +FAIL: ld-elf/pr25021
dlx-elf  +FAIL: ld-elf/pr24526
dlx-elf  +FAIL: ld-elf/pr25021
frv-linux  +FAIL: ld-elf/pr24526
frv-linux  +FAIL: ld-elf/pr25021
frv-linux  +FAIL: __patchable_function_entries section 4
frv-linux  +FAIL: __patchable_function_entries section 6
h8300-elf  +FAIL: linked-to section 1
h8300-elf  +FAIL: linked-to section 2
h8300-elf  +FAIL: linked-to section 3
h8300-elf  +FAIL: __patchable_function_entries section 2
h8300-elf  +FAIL: __patchable_function_entries section 3
h8300-elf  +FAIL: __patchable_function_entries section 4
h8300-elf  +FAIL: __patchable_function_entries section 5
h8300-elf  +FAIL: __patchable_function_entries section 6
h8300-linux  +FAIL: linked-to section 1
h8300-linux  +FAIL: linked-to section 2
h8300-linux  +FAIL: linked-to section 3
h8300-linux  +FAIL: __patchable_function_entries section 2
h8300-linux  +FAIL: __patchable_function_entries section 3
h8300-linux  +FAIL: __patchable_function_entries section 4
h8300-linux  +FAIL: __patchable_function_entries section 5
h8300-linux  +FAIL: __patchable_function_entries section 6
hppa64-hp-hpux11.23  +FAIL: ld-elf/pr24526
hppa64-hp-hpux11.23  +FAIL: ld-elf/pr25021
hppa64-linux  +FAIL: ld-elf/pr24526
hppa64-linux  +FAIL: ld-elf/pr25021
ip2k-elf  +FAIL: linked-to section 1
ip2k-elf  +FAIL: linked-to section 2
ip2k-elf  +FAIL: linked-to section 3
ip2k-elf  +FAIL: __patchable_function_entries section 2
ip2k-elf  +FAIL: __patchable_function_entries section 3
ip2k-elf  +FAIL: __patchable_function_entries section 4
ip2k-elf  +FAIL: __patchable_function_entries section 5
ip2k-elf  +FAIL: __patchable_function_entries section 6
lm32-linux  +FAIL: ld-elf/pr24526
lm32-linux  +FAIL: ld-elf/pr25021
lm32-linux  +FAIL: __patchable_function_entries section 4
lm32-linux  +FAIL: __patchable_function_entries section 6
m68hc11-elf  +FAIL: linked-to section 1
m68hc11-elf  +FAIL: linked-to section 2
m68hc11-elf  +FAIL: linked-to section 3
m68hc11-elf  +FAIL: __patchable_function_entries section 2
m68hc11-elf  +FAIL: __patchable_function_entries section 3
m68hc11-elf  +FAIL: __patchable_function_entries section 4
m68hc11-elf  +FAIL: __patchable_function_entries section 5
m68hc11-elf  +FAIL: __patchable_function_entries section 6
mep-elf  +FAIL: ld-elf/pr24526
mep-elf  +FAIL: ld-elf/pr25021
mips64el-openbsd  +FAIL: --gc-sections with relocations in debug section
mips64-openbsd  +FAIL: --gc-sections with relocations in debug section
mipstx39-elf  +FAIL: Build pr22649-2a.so
mipstx39-elf  +FAIL: Build pr22649-2b.so
mn10200-elf  +FAIL: ld-elf/pr24526
mn10200-elf  +FAIL: ld-elf/pr25021
nios2-linux  +FAIL: __patchable_function_entries section 2
nios2-linux  +FAIL: __patchable_function_entries section 3
nios2-linux  +FAIL: __patchable_function_entries section 4
nios2-linux  +FAIL: __patchable_function_entries section 5
nios2-linux  +FAIL: __patchable_function_entries section 6
pj-elf  +FAIL: ld-elf/pr24526
pj-elf  +FAIL: ld-elf/pr25021
pru-elf  +FAIL: ld-elf/pr24526
pru-elf  +FAIL: ld-elf/pr25021
s12z-elf  +FAIL: ld-elf/pr24526
s12z-elf  +FAIL: ld-elf/pr25021
shle-unknown-netbsdelf  +FAIL: incorrect linked-to symbols
shle-unknown-netbsdelf  +FAIL: __patchable_function_entries section 2
shle-unknown-netbsdelf  +FAIL: __patchable_function_entries section 3
shle-unknown-netbsdelf  +FAIL: __patchable_function_entries section 4
shle-unknown-netbsdelf  +FAIL: __patchable_function_entries section 5
shle-unknown-netbsdelf  +FAIL: __patchable_function_entries section 6
sh-linux  +FAIL: incorrect linked-to symbols
sh-linux  +FAIL: __patchable_function_entries section 2
sh-linux  +FAIL: __patchable_function_entries section 3
sh-linux  +FAIL: __patchable_function_entries section 4
sh-linux  +FAIL: __patchable_function_entries section 5
sh-linux  +FAIL: __patchable_function_entries section 6
sh-nto  +FAIL: incorrect linked-to symbols
sh-nto  +FAIL: __patchable_function_entries section 2
sh-nto  +FAIL: __patchable_function_entries section 3
sh-nto  +FAIL: __patchable_function_entries section 4
sh-nto  +FAIL: __patchable_function_entries section 5
sh-nto  +FAIL: __patchable_function_entries section 6
sh-rtems  +FAIL: incorrect linked-to symbols
sh-rtems  +FAIL: __patchable_function_entries section 2
sh-rtems  +FAIL: __patchable_function_entries section 3
sh-rtems  +FAIL: __patchable_function_entries section 4
sh-rtems  +FAIL: __patchable_function_entries section 5
sh-rtems  +FAIL: __patchable_function_entries section 6
xc16x-elf  +FAIL: linked-to section 1
xc16x-elf  +FAIL: linked-to section 2
xc16x-elf  +FAIL: linked-to section 3
xc16x-elf  +FAIL: __patchable_function_entries section 2
xc16x-elf  +FAIL: __patchable_function_entries section 3
xc16x-elf  +FAIL: __patchable_function_entries section 4
xc16x-elf  +FAIL: __patchable_function_entries section 5
xc16x-elf  +FAIL: __patchable_function_entries section 6
xgate-elf  +FAIL: ld-elf/pr24526
xgate-elf  +FAIL: ld-elf/pr25021
z80-elf  +FAIL: linked-to section 1
z80-elf  +FAIL: linked-to section 2
z80-elf  +FAIL: linked-to section 3
z80-elf  +FAIL: ld-elf/pr24526
z80-elf  +FAIL: ld-elf/pr25021
z80-elf  +FAIL: __patchable_function_entries section 2
z80-elf  +FAIL: __patchable_function_entries section 3
z80-elf  +FAIL: __patchable_function_entries section 4
z80-elf  +FAIL: __patchable_function_entries section 5
z80-elf  +FAIL: __patchable_function_entries section 6

-- 
Alan Modra
Australia Development Lab, IBM
H.J. Lu Feb. 6, 2020, 7:23 p.m. | #4
On Wed, Feb 5, 2020 at 11:46 PM Alan Modra <amodra@gmail.com> wrote:
>

> On Mon, Feb 03, 2020 at 06:37:17AM -0800, H.J. Lu wrote:

> > bfd/

> >

> >       PR gas/25381

> >       * bfd-in2.h: Regenerated.

> >       * elf32-arm.c (elf32_arm_gc_mark_extra_sections): Call

> >       _bfd_elf_gc_mark_extra_sections last.

> >       * elf32-csky.c (_bfd_elf_gc_mark_extra_sections): Likewise.

> >       * elf32-tic6x.c (elf32_tic6x_gc_mark_extra_sections): Likewise.

> >       * elfxx-mips.c (_bfd_mips_elf_gc_mark_extra_sections): Likewise.

> >       * elflink.c (_bfd_elf_gc_mark_extra_sections): Set gc_mark only

> >       if gc_mark of any of its linked-to section is set and don't set

> >       gc_mark again.

> >       * section.c (asection): Add linked_to_symbol_name to map_head

> >       union.

> >

> > gas/

> >

> >       PR gas/25381

> >       * config/obj-elf.c (get_section): Also check

> >       linked_to_symbol_name.

> >       (obj_elf_change_section): Also set map_head.linked_to_symbol_name.

> >       (obj_elf_parse_section_letters): Handle the 'o' flag.

> >       (build_group_lists): Renamed to ...

> >       (build_additional_section_info): This.  Set elf_linked_to_section

> >       from map_head.linked_to_symbol_name.

> >       (elf_adjust_symtab): Updated.

> >       * config/obj-elf.h (elf_section_match): Add linked_to_symbol_name.

> >       * doc/as.texi: Document the 'o' flag.

> >       * testsuite/gas/elf/elf.exp: Run PR gas/25381 tests.

> >       * testsuite/gas/elf/section18.d: New file.

> >       * testsuite/gas/elf/section18.s: Likewise.

> >       * testsuite/gas/elf/section19.d: Likewise.

> >       * testsuite/gas/elf/section19.s: Likewise.

> >       * testsuite/gas/elf/section20.d: Likewise.

> >       * testsuite/gas/elf/section20.s: Likewise.

> >       * testsuite/gas/elf/section21.d: Likewise.

> >       * testsuite/gas/elf/section21.l: Likewise.

> >       * testsuite/gas/elf/section21.s: Likewise.

> >

> > ld/

> >

> >       PR ld/24526

> >       PR ld/25021

> >       PR ld/25490

> >       * testsuite/ld-elf/elf.exp: Run PR ld/25490 tests.

> >       * testsuite/ld-elf/pr24526.d: New file.

> >       * testsuite/ld-elf/pr24526.s: Likewise.

> >       * testsuite/ld-elf/pr25021.d: Likewise.

> >       * testsuite/ld-elf/pr25021.s: Likewise.

> >       * testsuite/ld-elf/pr25490-2-32.rd: Likewise.

> >       * testsuite/ld-elf/pr25490-2-64.rd: Likewise.

> >       * testsuite/ld-elf/pr25490-2.s: Likewise.

> >       * testsuite/ld-elf/pr25490-3-32.rd: Likewise.

> >       * testsuite/ld-elf/pr25490-3-64.rd: Likewise.

> >       * testsuite/ld-elf/pr25490-3.s: Likewise.

> >       * testsuite/ld-elf/pr25490-4-32.rd: Likewise.

> >       * testsuite/ld-elf/pr25490-4-64.rd: Likewise.

> >       * testsuite/ld-elf/pr25490-4.s: Likewise.

> >       * testsuite/ld-elf/pr25490-5-32.rd: Likewise.

> >       * testsuite/ld-elf/pr25490-5-64.rd: Likewise.

> >       * testsuite/ld-elf/pr25490-5.s: Likewise.

> >       * testsuite/ld-elf/pr25490-6-32.rd: Likewise.

> >       * testsuite/ld-elf/pr25490-6-64.rd: Likewise.

> >       * testsuite/ld-elf/pr25490-6.s: Likewise.

>

> I see rather a lot of fails for the new testcases.  Some changes to

> the testcases clearly are needed before this can go in.  A quick

> glance shows two major problems:

> 1) You don't want to run a testcase requiring --gc-sections on a

>    target that doesn't support --gc-sections.


Fixed.

> 2) You assume .dc.a sizes are only 4 or 8 but we have a number of

>    targets that have 16-bit addresses.


Fixed.

> Also, for sh-linux, I see this in pr25490-2.o

>   [ 4] __patchable_function_entries PROGBITS        00000000 000038 000004 00 WAL  1   0  4


4 is sh_addralign.  sh_link is 1.

> Linking to itself?  Please do look into all these fails.

>

> avr-elf  +FAIL: linked-to section 1

> avr-elf  +FAIL: linked-to section 2

> avr-elf  +FAIL: linked-to section 3

> avr-elf  +FAIL: __patchable_function_entries section 2

> avr-elf  +FAIL: __patchable_function_entries section 3

> avr-elf  +FAIL: __patchable_function_entries section 4

> avr-elf  +FAIL: __patchable_function_entries section 5

> avr-elf  +FAIL: __patchable_function_entries section 6

> bfin-linux-uclibc  +FAIL: ld-elf/pr24526

> bfin-linux-uclibc  +FAIL: ld-elf/pr25021

> bfin-linux-uclibc  +FAIL: __patchable_function_entries section 4

> bfin-linux-uclibc  +FAIL: __patchable_function_entries section 6

> d30v-elf  +FAIL: ld-elf/pr24526

> d30v-elf  +FAIL: ld-elf/pr25021

> dlx-elf  +FAIL: ld-elf/pr24526

> dlx-elf  +FAIL: ld-elf/pr25021

> frv-linux  +FAIL: ld-elf/pr24526

> frv-linux  +FAIL: ld-elf/pr25021

> frv-linux  +FAIL: __patchable_function_entries section 4

> frv-linux  +FAIL: __patchable_function_entries section 6

> h8300-elf  +FAIL: linked-to section 1

> h8300-elf  +FAIL: linked-to section 2

> h8300-elf  +FAIL: linked-to section 3

> h8300-elf  +FAIL: __patchable_function_entries section 2

> h8300-elf  +FAIL: __patchable_function_entries section 3

> h8300-elf  +FAIL: __patchable_function_entries section 4

> h8300-elf  +FAIL: __patchable_function_entries section 5

> h8300-elf  +FAIL: __patchable_function_entries section 6

> h8300-linux  +FAIL: linked-to section 1

> h8300-linux  +FAIL: linked-to section 2

> h8300-linux  +FAIL: linked-to section 3

> h8300-linux  +FAIL: __patchable_function_entries section 2

> h8300-linux  +FAIL: __patchable_function_entries section 3

> h8300-linux  +FAIL: __patchable_function_entries section 4

> h8300-linux  +FAIL: __patchable_function_entries section 5

> h8300-linux  +FAIL: __patchable_function_entries section 6

> hppa64-hp-hpux11.23  +FAIL: ld-elf/pr24526

> hppa64-hp-hpux11.23  +FAIL: ld-elf/pr25021

> hppa64-linux  +FAIL: ld-elf/pr24526

> hppa64-linux  +FAIL: ld-elf/pr25021

> ip2k-elf  +FAIL: linked-to section 1

> ip2k-elf  +FAIL: linked-to section 2

> ip2k-elf  +FAIL: linked-to section 3

> ip2k-elf  +FAIL: __patchable_function_entries section 2

> ip2k-elf  +FAIL: __patchable_function_entries section 3

> ip2k-elf  +FAIL: __patchable_function_entries section 4

> ip2k-elf  +FAIL: __patchable_function_entries section 5

> ip2k-elf  +FAIL: __patchable_function_entries section 6

> lm32-linux  +FAIL: ld-elf/pr24526

> lm32-linux  +FAIL: ld-elf/pr25021

> lm32-linux  +FAIL: __patchable_function_entries section 4

> lm32-linux  +FAIL: __patchable_function_entries section 6

> m68hc11-elf  +FAIL: linked-to section 1

> m68hc11-elf  +FAIL: linked-to section 2

> m68hc11-elf  +FAIL: linked-to section 3

> m68hc11-elf  +FAIL: __patchable_function_entries section 2

> m68hc11-elf  +FAIL: __patchable_function_entries section 3

> m68hc11-elf  +FAIL: __patchable_function_entries section 4

> m68hc11-elf  +FAIL: __patchable_function_entries section 5

> m68hc11-elf  +FAIL: __patchable_function_entries section 6

> mep-elf  +FAIL: ld-elf/pr24526

> mep-elf  +FAIL: ld-elf/pr25021

> mips64el-openbsd  +FAIL: --gc-sections with relocations in debug section

> mips64-openbsd  +FAIL: --gc-sections with relocations in debug section

> mipstx39-elf  +FAIL: Build pr22649-2a.so

> mipstx39-elf  +FAIL: Build pr22649-2b.so

> mn10200-elf  +FAIL: ld-elf/pr24526

> mn10200-elf  +FAIL: ld-elf/pr25021

> nios2-linux  +FAIL: __patchable_function_entries section 2

> nios2-linux  +FAIL: __patchable_function_entries section 3

> nios2-linux  +FAIL: __patchable_function_entries section 4

> nios2-linux  +FAIL: __patchable_function_entries section 5

> nios2-linux  +FAIL: __patchable_function_entries section 6

> pj-elf  +FAIL: ld-elf/pr24526

> pj-elf  +FAIL: ld-elf/pr25021

> pru-elf  +FAIL: ld-elf/pr24526

> pru-elf  +FAIL: ld-elf/pr25021

> s12z-elf  +FAIL: ld-elf/pr24526

> s12z-elf  +FAIL: ld-elf/pr25021

> shle-unknown-netbsdelf  +FAIL: incorrect linked-to symbols

> shle-unknown-netbsdelf  +FAIL: __patchable_function_entries section 2

> shle-unknown-netbsdelf  +FAIL: __patchable_function_entries section 3

> shle-unknown-netbsdelf  +FAIL: __patchable_function_entries section 4

> shle-unknown-netbsdelf  +FAIL: __patchable_function_entries section 5

> shle-unknown-netbsdelf  +FAIL: __patchable_function_entries section 6

> sh-linux  +FAIL: incorrect linked-to symbols

> sh-linux  +FAIL: __patchable_function_entries section 2

> sh-linux  +FAIL: __patchable_function_entries section 3

> sh-linux  +FAIL: __patchable_function_entries section 4

> sh-linux  +FAIL: __patchable_function_entries section 5

> sh-linux  +FAIL: __patchable_function_entries section 6

> sh-nto  +FAIL: incorrect linked-to symbols

> sh-nto  +FAIL: __patchable_function_entries section 2

> sh-nto  +FAIL: __patchable_function_entries section 3

> sh-nto  +FAIL: __patchable_function_entries section 4

> sh-nto  +FAIL: __patchable_function_entries section 5

> sh-nto  +FAIL: __patchable_function_entries section 6

> sh-rtems  +FAIL: incorrect linked-to symbols

> sh-rtems  +FAIL: __patchable_function_entries section 2

> sh-rtems  +FAIL: __patchable_function_entries section 3

> sh-rtems  +FAIL: __patchable_function_entries section 4

> sh-rtems  +FAIL: __patchable_function_entries section 5

> sh-rtems  +FAIL: __patchable_function_entries section 6

> xc16x-elf  +FAIL: linked-to section 1

> xc16x-elf  +FAIL: linked-to section 2

> xc16x-elf  +FAIL: linked-to section 3

> xc16x-elf  +FAIL: __patchable_function_entries section 2

> xc16x-elf  +FAIL: __patchable_function_entries section 3

> xc16x-elf  +FAIL: __patchable_function_entries section 4

> xc16x-elf  +FAIL: __patchable_function_entries section 5

> xc16x-elf  +FAIL: __patchable_function_entries section 6

> xgate-elf  +FAIL: ld-elf/pr24526

> xgate-elf  +FAIL: ld-elf/pr25021

> z80-elf  +FAIL: linked-to section 1

> z80-elf  +FAIL: linked-to section 2

> z80-elf  +FAIL: linked-to section 3

> z80-elf  +FAIL: ld-elf/pr24526

> z80-elf  +FAIL: ld-elf/pr25021

> z80-elf  +FAIL: __patchable_function_entries section 2

> z80-elf  +FAIL: __patchable_function_entries section 3

> z80-elf  +FAIL: __patchable_function_entries section 4

> z80-elf  +FAIL: __patchable_function_entries section 5

> z80-elf  +FAIL: __patchable_function_entries section 6

>


All fixed.   Here is the updated patch.  The only bfd changes are

* bfd-in2.h: Regenerated.
* elflink.c (_bfd_elf_gc_mark_extra_sections): Call mark_hook
on section if gc_mark of any of its linked-to sections is set
and don't set gc_mark again.
* section.c (asection): Add linked_to_symbol_name to map_head
union.

I changed  _bfd_elf_gc_mark_extra_sections to call mark_hook
instead of setting gc_mark directly.

OK for master?

Thanks.

-- 
H.J.
From 7db6e5fbb4da66756583e9bdf15153fb315d14e6 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 1 Feb 2020 13:38:47 -0800
Subject: [PATCH] ELF: Support the section flag 'o' in .section directive

As shown in

https://sourceware.org/bugzilla/show_bug.cgi?id=25490

--gc-sections will silently remove __patchable_function_entries section
and generate corrupt result.  This patch adds the section flag 'o' to
.section directive:

.section __patchable_function_entries,"awo",@progbits,foo
.section __patchable_function_entries,"awoG",@progbits,foo,foo,comdat
.section __patchable_function_entries,"awo",@progbits,bar,unique,4
.section __patchable_function_entries,"awoG",@progbits,foo,foo,comdat,unique,1

which specifies the symbol name which the section references.  Assmebler
will set its elf_linked_to_section to a local section where the symbol
is defined.

Linker is updated to call mark_hook if gc_mark of any of its linked-to
sections is set after all sections, except for backend specific ones,
have been garbage collected.

bfd/

	PR gas/25381
	* bfd-in2.h: Regenerated.
	* elflink.c (_bfd_elf_gc_mark_extra_sections): Call mark_hook
	on section if gc_mark of any of its linked-to sections is set
	and don't set gc_mark again.
	* section.c (asection): Add linked_to_symbol_name to map_head
	union.

gas/

	PR gas/25381
	* config/obj-elf.c (get_section): Also check
	linked_to_symbol_name.
	(obj_elf_change_section): Also set map_head.linked_to_symbol_name.
	(obj_elf_parse_section_letters): Handle the 'o' flag.
	(build_group_lists): Renamed to ...
	(build_additional_section_info): This.  Set elf_linked_to_section
	from map_head.linked_to_symbol_name.
	(elf_adjust_symtab): Updated.
	* config/obj-elf.h (elf_section_match): Add linked_to_symbol_name.
	* doc/as.texi: Document the 'o' flag.
	* testsuite/gas/elf/elf.exp: Run PR gas/25381 tests.
	* testsuite/gas/elf/section18.d: New file.
	* testsuite/gas/elf/section18.s: Likewise.
	* testsuite/gas/elf/section19.d: Likewise.
	* testsuite/gas/elf/section19.s: Likewise.
	* testsuite/gas/elf/section20.d: Likewise.
	* testsuite/gas/elf/section20.s: Likewise.
	* testsuite/gas/elf/section21.d: Likewise.
	* testsuite/gas/elf/section21.l: Likewise.
	* testsuite/gas/elf/section21.s: Likewise.

ld/

	PR ld/24526
	PR ld/25021
	PR ld/25490
	* testsuite/ld-elf/elf.exp: Run PR ld/25490 tests.
	* testsuite/ld-elf/pr24526.d: New file.
	* testsuite/ld-elf/pr24526.s: Likewise.
	* testsuite/ld-elf/pr25021.d: Likewise.
	* testsuite/ld-elf/pr25021.s: Likewise.
	* testsuite/ld-elf/pr25490-2-16.rd: Likewise.
	* testsuite/ld-elf/pr25490-2-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-2-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-2.s: Likewise.
	* testsuite/ld-elf/pr25490-3-16.rd: Likewise.
	* testsuite/ld-elf/pr25490-3-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-3-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-3.s: Likewise.
	* testsuite/ld-elf/pr25490-4-16.rd: Likewise.
	* testsuite/ld-elf/pr25490-4-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-4-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-4.s: Likewise.
	* testsuite/ld-elf/pr25490-5-16.rd: Likewise.
	* testsuite/ld-elf/pr25490-5-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-5-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-5.s: Likewise.
	* testsuite/ld-elf/pr25490-6-16.rd: Likewise.
	* testsuite/ld-elf/pr25490-6-32.rd: Likewise.
	* testsuite/ld-elf/pr25490-6-64.rd: Likewise.
	* testsuite/ld-elf/pr25490-6.s: Likewise.
---
 bfd/bfd-in2.h                       |  4 +-
 bfd/elflink.c                       | 23 +++++++++--
 bfd/section.c                       |  4 +-
 gas/config/obj-elf.c                | 52 +++++++++++++++++++++---
 gas/config/obj-elf.h                |  1 +
 gas/doc/as.texi                     | 27 +++++++++++++
 gas/testsuite/gas/elf/elf.exp       |  4 ++
 gas/testsuite/gas/elf/section18.d   |  8 ++++
 gas/testsuite/gas/elf/section18.s   | 13 ++++++
 gas/testsuite/gas/elf/section19.d   |  8 ++++
 gas/testsuite/gas/elf/section19.s   | 13 ++++++
 gas/testsuite/gas/elf/section20.d   | 17 ++++++++
 gas/testsuite/gas/elf/section20.s   | 13 ++++++
 gas/testsuite/gas/elf/section21.d   |  2 +
 gas/testsuite/gas/elf/section21.l   |  5 +++
 gas/testsuite/gas/elf/section21.s   | 15 +++++++
 ld/testsuite/ld-elf/elf.exp         | 63 +++++++++++++++++++++++++++++
 ld/testsuite/ld-elf/pr24526.d       |  9 +++++
 ld/testsuite/ld-elf/pr24526.s       | 13 ++++++
 ld/testsuite/ld-elf/pr25021.d       |  7 ++++
 ld/testsuite/ld-elf/pr25021.s       | 22 ++++++++++
 ld/testsuite/ld-elf/pr25490-2-16.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-2-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-2-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-2.s     |  9 +++++
 ld/testsuite/ld-elf/pr25490-3-16.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-3-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-3-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-3.s     | 18 +++++++++
 ld/testsuite/ld-elf/pr25490-4-16.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-4-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-4-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-4.s     | 20 +++++++++
 ld/testsuite/ld-elf/pr25490-5-16.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-5-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-5-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-5.s     | 17 ++++++++
 ld/testsuite/ld-elf/pr25490-6-16.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-6-32.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-6-64.rd |  7 ++++
 ld/testsuite/ld-elf/pr25490-6.s     | 30 ++++++++++++++
 41 files changed, 512 insertions(+), 10 deletions(-)
 create mode 100644 gas/testsuite/gas/elf/section18.d
 create mode 100644 gas/testsuite/gas/elf/section18.s
 create mode 100644 gas/testsuite/gas/elf/section19.d
 create mode 100644 gas/testsuite/gas/elf/section19.s
 create mode 100644 gas/testsuite/gas/elf/section20.d
 create mode 100644 gas/testsuite/gas/elf/section20.s
 create mode 100644 gas/testsuite/gas/elf/section21.d
 create mode 100644 gas/testsuite/gas/elf/section21.l
 create mode 100644 gas/testsuite/gas/elf/section21.s
 create mode 100644 ld/testsuite/ld-elf/pr24526.d
 create mode 100644 ld/testsuite/ld-elf/pr24526.s
 create mode 100644 ld/testsuite/ld-elf/pr25021.d
 create mode 100644 ld/testsuite/ld-elf/pr25021.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-2-16.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-2-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-2-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-2.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-3-16.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-3-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-3-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-3.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-4-16.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-4-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-4-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-4.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-5-16.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-5-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-5-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-5.s
 create mode 100644 ld/testsuite/ld-elf/pr25490-6-16.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-6-32.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-6-64.rd
 create mode 100644 ld/testsuite/ld-elf/pr25490-6.s

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 09a5a39ff5..2d26b81db3 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1185,10 +1185,12 @@ typedef struct bfd_section
   /* 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
-     structs.  */
+     structs.  The linked_to_symbol_name field is for ELF assembler
+     internal use.  */
   union {
     struct bfd_link_order *link_order;
     struct bfd_section *s;
+    const char *linked_to_symbol_name;
   } map_head, map_tail;
 } asection;
 
diff --git a/bfd/elflink.c b/bfd/elflink.c
index e4d92952aa..3add9f18bd 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -13316,7 +13316,7 @@ _bfd_elf_gc_mark_debug_special_section_group (asection *grp)
 
 bfd_boolean
 _bfd_elf_gc_mark_extra_sections (struct bfd_link_info *info,
-				 elf_gc_mark_hook_fn mark_hook ATTRIBUTE_UNUSED)
+				 elf_gc_mark_hook_fn mark_hook)
 {
   bfd *ibfd;
 
@@ -13345,6 +13345,21 @@ _bfd_elf_gc_mark_extra_sections (struct bfd_link_info *info,
 		   && (isec->flags & SEC_ALLOC) != 0
 		   && elf_section_type (isec) != SHT_NOTE)
 	    some_kept = TRUE;
+	  else
+	    {
+	      /* Since all sections, except for backend specific ones,
+		 have been garbage collected, call mark_hook on this
+		 section if any of its linked-to sections is marked.  */
+	      asection *linked_to_sec = elf_linked_to_section (isec);
+	      for (; linked_to_sec != NULL;
+		   linked_to_sec = elf_linked_to_section (linked_to_sec))
+		if (linked_to_sec->gc_mark)
+		  {
+		    if (!_bfd_elf_gc_mark (info, isec, mark_hook))
+		      return FALSE;
+		    break;
+		  }
+	    }
 
 	  if (!debug_frag_seen
 	      && (isec->flags & SEC_DEBUGGING)
@@ -13366,14 +13381,16 @@ _bfd_elf_gc_mark_extra_sections (struct bfd_link_info *info,
 
       /* Keep debug and special sections like .comment when they are
 	 not part of a group.  Also keep section groups that contain
-	 just debug sections or special sections.  */
+	 just debug sections or special sections.  NB: Sections with
+	 linked-to section has been handled above.  */
       for (isec = ibfd->sections; isec != NULL; isec = isec->next)
 	{
 	  if ((isec->flags & SEC_GROUP) != 0)
 	    _bfd_elf_gc_mark_debug_special_section_group (isec);
 	  else if (((isec->flags & SEC_DEBUGGING) != 0
 		    || (isec->flags & (SEC_ALLOC | SEC_LOAD | SEC_RELOC)) == 0)
-		   && elf_next_in_group (isec) == NULL)
+		   && elf_next_in_group (isec) == NULL
+		   && elf_linked_to_section (isec) == NULL)
 	    isec->gc_mark = 1;
 	  if (isec->gc_mark && (isec->flags & SEC_DEBUGGING) != 0)
 	    has_kept_debug_info = TRUE;
diff --git a/bfd/section.c b/bfd/section.c
index 0c15a0d646..7de0a2d7a8 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -544,10 +544,12 @@ CODE_FRAGMENT
 .  {* 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
-.     structs.  *}
+.     structs.  The linked_to_symbol_name field is for ELF assembler
+.     internal use.  *}
 .  union {
 .    struct bfd_link_order *link_order;
 .    struct bfd_section *s;
+.    const char *linked_to_symbol_name;
 .  } map_head, map_tail;
 .} asection;
 .
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index 2958490c32..d7a07fec6b 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -524,6 +524,8 @@ get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
   struct elf_section_match *match = (struct elf_section_match *) inf;
   const char *gname = match->group_name;
   const char *group_name = elf_group_name (sec);
+  const char *linked_to_symbol_name
+    = sec->map_head.linked_to_symbol_name;
   unsigned int info = elf_section_data (sec)->this_hdr.sh_info;
 
   return (info == match->info
@@ -533,7 +535,12 @@ get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 	  && (group_name == gname
 	      || (group_name != NULL
 		  && gname != NULL
-		  && strcmp (group_name, gname) == 0)));
+		  && strcmp (group_name, gname) == 0))
+	  && (linked_to_symbol_name == match->linked_to_symbol_name
+	      || (linked_to_symbol_name != NULL
+		  && match->linked_to_symbol_name != NULL
+		  && strcmp (linked_to_symbol_name,
+			     match->linked_to_symbol_name) == 0)));
 }
 
 /* Handle the .section pseudo-op.  This code supports two different
@@ -740,6 +747,10 @@ obj_elf_change_section (const char *name,
       sec->section_id = match_p->section_id;
       flags |= match_p->flags;
 
+      /* Set the linked-to symbol name.  */
+      sec->map_head.linked_to_symbol_name
+	= match_p->linked_to_symbol_name;
+
       bfd_set_section_flags (sec, flags);
       if (flags & SEC_MERGE)
 	sec->entsize = entsize;
@@ -801,6 +812,9 @@ obj_elf_parse_section_letters (char *str, size_t len,
 	case 'e':
 	  attr |= SHF_EXCLUDE;
 	  break;
+	case 'o':
+	  attr |= SHF_LINK_ORDER;
+	  break;
 	case 'w':
 	  attr |= SHF_WRITE;
 	  break;
@@ -841,7 +855,7 @@ obj_elf_parse_section_letters (char *str, size_t len,
 	default:
 	  {
 	    const char *bad_msg = _("unrecognized .section attribute:"
-				    " want a,e,w,x,M,S,G,T or number");
+				    " want a,e,o,w,x,M,S,G,T or number");
 #ifdef md_elf_section_letter
 	    bfd_vma md_attr = md_elf_section_letter (*str, &bad_msg);
 	    if (md_attr != (bfd_vma) -1)
@@ -1154,6 +1168,19 @@ obj_elf_section (int push)
 	      attr &= ~SHF_MERGE;
 	    }
 
+	  if ((attr & SHF_LINK_ORDER) != 0 && *input_line_pointer == ',')
+	    {
+	      char c;
+	      unsigned int length;
+	      ++input_line_pointer;
+	      SKIP_WHITESPACE ();
+	      c = get_symbol_name (& beg);
+	      (void) restore_line_pointer (c);
+	      length = input_line_pointer - beg;
+	      if (length)
+		match.linked_to_symbol_name = xmemdup0 (beg, length);
+	    }
+
 	  if ((attr & SHF_GROUP) != 0 && is_clone)
 	    {
 	      as_warn (_("? section flag ignored with G present"));
@@ -2476,10 +2503,12 @@ static struct group_list groups;
 /* Called via bfd_map_over_sections.  If SEC is a member of a group,
    add it to a list of sections belonging to the group.  INF is a
    pointer to a struct group_list, which is where we store the head of
-   each list.  */
+   each list.  If its link_to_symbol_name isn't NULL, set up its
+   linked-to section.  */
 
 static void
-build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
+build_additional_section_info (bfd *abfd ATTRIBUTE_UNUSED,
+				  asection *sec, void *inf)
 {
   struct group_list *list = (struct group_list *) inf;
   const char *group_name = elf_group_name (sec);
@@ -2487,6 +2516,18 @@ build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
   unsigned int *elem_idx;
   unsigned int *idx_ptr;
 
+  if (sec->map_head.linked_to_symbol_name)
+    {
+      symbolS *linked_to_sym;
+      linked_to_sym = symbol_find (sec->map_head.linked_to_symbol_name);
+      if (!linked_to_sym || !S_IS_DEFINED (linked_to_sym))
+	as_bad (_("undefined linked-to symbol `%s' on section `%s'"),
+		sec->map_head.linked_to_symbol_name,
+		bfd_section_name (sec));
+      else
+	elf_linked_to_section (sec) = S_GET_SEGMENT (linked_to_sym);
+    }
+
   if (group_name == NULL)
     return;
 
@@ -2533,7 +2574,8 @@ elf_adjust_symtab (void)
   groups.num_group = 0;
   groups.head = NULL;
   groups.indexes = hash_new ();
-  bfd_map_over_sections (stdoutput, build_group_lists, &groups);
+  bfd_map_over_sections (stdoutput, build_additional_section_info,
+			 &groups);
 
   /* Make the SHT_GROUP sections that describe each section group.  We
      can't set up the section contents here yet, because elf section
diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h
index 7cfcc25482..54af9ebc0e 100644
--- a/gas/config/obj-elf.h
+++ b/gas/config/obj-elf.h
@@ -82,6 +82,7 @@ struct elf_obj_sy
 struct elf_section_match
 {
   const char *group_name;
+  const char *linked_to_symbol_name;
   unsigned int info;
   unsigned int section_id;
   flagword flags;
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 152bbfdd00..1554c51ad2 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -6599,6 +6599,9 @@ section is allocatable
 section is a GNU_MBIND section
 @item e
 section is excluded from executable and shared library.
+@item o
+section references a symbol defined in another section (the linked-to
+section) in the same file.
 @item w
 section is writable
 @item x
@@ -6678,6 +6681,23 @@ which is a suffix of a larger string is considered a duplicate.  Thus
 @code{"def"} will be merged with @code{"abcdef"};  A reference to the first
 @code{"def"} will be changed to a reference to @code{"abcdef"+3}.
 
+If @var{flags} contains the @code{o} flag, then the @var{type} argument
+must be present along with an additional field like this:
+
+@smallexample
+.section @var{name},"@var{flags}"o,@@@var{type},@var{SymbolName}
+@end smallexample
+
+The @var{SymbolName} field specifies the symbol name which the section
+references.
+
+Note: If both the @var{M} and @var{o} flags are present, then the fields
+for the Merge flag should come first, like this:
+
+@smallexample
+.section @var{name},"@var{flags}"Mo,@@@var{type},@var{entsize},@var{SymbolName}
+@end smallexample
+
 If @var{flags} contains the @code{G} symbol then the @var{type} argument must
 be present along with an additional field like this:
 
@@ -6702,6 +6722,13 @@ the Merge flag should come first, like this:
 .section @var{name} , "@var{flags}"MG, @@@var{type}, @var{entsize}, @var{GroupName}[, @var{linkage}]
 @end smallexample
 
+If both @code{o} flag and @code{G} flag are present, then the
+@var{SymbolName} field for @code{o} comes first, like this:
+
+@smallexample
+.section @var{name},"@var{flags}"oG,@@@var{type},@var{SymbolName},@var{GroupName}[,@var{linkage}]
+@end smallexample
+
 If @var{flags} contains the @code{?} symbol then it may not also contain the
 @code{G} symbol and the @var{GroupName} or @var{linkage} fields should not be
 present.  Instead, @code{?} says to consider the section that's current before
diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp
index 08c6830e0d..0f9b2672c4 100644
--- a/gas/testsuite/gas/elf/elf.exp
+++ b/gas/testsuite/gas/elf/elf.exp
@@ -249,6 +249,10 @@ if { [is_elf_format] } then {
     run_dump_test "section16a"
     run_dump_test "section16b"
     run_dump_test "section17"
+    run_dump_test "section18"
+    run_dump_test "section19"
+    run_dump_test "section20"
+    run_dump_test "section21"
     run_dump_test "dwarf2-1" $dump_opts
     run_dump_test "dwarf2-2" $dump_opts
     run_dump_test "dwarf2-3" $dump_opts
diff --git a/gas/testsuite/gas/elf/section18.d b/gas/testsuite/gas/elf/section18.d
new file mode 100644
index 0000000000..f4f2930b00
--- /dev/null
+++ b/gas/testsuite/gas/elf/section18.d
@@ -0,0 +1,8 @@
+#readelf: -SW
+#name: linked-to section 1
+
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[248] +00 +WAL +.*
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[248] +00 +WAL +.*
+#pass
diff --git a/gas/testsuite/gas/elf/section18.s b/gas/testsuite/gas/elf/section18.s
new file mode 100644
index 0000000000..d51eb68131
--- /dev/null
+++ b/gas/testsuite/gas/elf/section18.s
@@ -0,0 +1,13 @@
+	.text
+foo:
+	.section __patchable_function_entries,"awo",%progbits,foo
+	.dc.a	.LPFE1
+	.text
+.LPFE1:
+	.byte 0
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE2
+	.text
+bar:
+.LPFE2:
+	.byte 0
diff --git a/gas/testsuite/gas/elf/section19.d b/gas/testsuite/gas/elf/section19.d
new file mode 100644
index 0000000000..edf2b9480b
--- /dev/null
+++ b/gas/testsuite/gas/elf/section19.d
@@ -0,0 +1,8 @@
+#readelf: -SW
+#name: linked-to section 2
+
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[248] +00 +WAL +.*
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[248] +00 +WAL +.*
+#pass
diff --git a/gas/testsuite/gas/elf/section19.s b/gas/testsuite/gas/elf/section19.s
new file mode 100644
index 0000000000..7d30ea1ff9
--- /dev/null
+++ b/gas/testsuite/gas/elf/section19.s
@@ -0,0 +1,13 @@
+	.section .text,"ax",%progbits,unique,20
+foo:
+	.section __patchable_function_entries,"awo",%progbits,foo,unique,2
+	.dc.a	.LPFE1
+	.section .text,"ax",%progbits,unique,20
+.LPFE1:
+	.byte 0
+	.section __patchable_function_entries,"awo",%progbits,bar,unique,102
+	.dc.a	.LPFE2
+	.section .text,"ax",%progbits,unique,2
+bar:
+.LPFE2:
+	.byte 0
diff --git a/gas/testsuite/gas/elf/section20.d b/gas/testsuite/gas/elf/section20.d
new file mode 100644
index 0000000000..cd7ab5e07c
--- /dev/null
+++ b/gas/testsuite/gas/elf/section20.d
@@ -0,0 +1,17 @@
+#readelf: -SWg
+#name: linked-to section 3
+
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[248] +00 +WALG +.*
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[248] +00 +WALG +.*
+#...
+COMDAT group section \[[ 0-9]+\] `.group' \[foo\] contains [0-9]+ sections:
+   \[Index\]    Name
+#...
+   \[[ 0-9]+\]   __patchable_function_entries
+#...
+COMDAT group section \[[ 0-9]+\] `.group' \[bar\] contains [0-9]+ sections:
+#...
+   \[[ 0-9]+\]   __patchable_function_entries
+#pass
diff --git a/gas/testsuite/gas/elf/section20.s b/gas/testsuite/gas/elf/section20.s
new file mode 100644
index 0000000000..1e9639ef4a
--- /dev/null
+++ b/gas/testsuite/gas/elf/section20.s
@@ -0,0 +1,13 @@
+	.section .text,"axG",%progbits,foo,comdat
+foo:
+	.section __patchable_function_entries,"awoG",%progbits,foo,foo,comdat
+	.dc.a	.LPFE1
+	.section .text,"axG",%progbits,foo,comdat
+.LPFE1:
+	.byte 0
+	.section __patchable_function_entries,"awoG",%progbits,bar,bar,comdat,unique,4
+	.dc.a	.LPFE2
+	.section .text,"axG",%progbits,bar,comdat,unique,24
+bar:
+.LPFE2:
+	.byte 0
diff --git a/gas/testsuite/gas/elf/section21.d b/gas/testsuite/gas/elf/section21.d
new file mode 100644
index 0000000000..54fa9d419b
--- /dev/null
+++ b/gas/testsuite/gas/elf/section21.d
@@ -0,0 +1,2 @@
+#name: incorrect linked-to symbols
+#error_output: section21.l
diff --git a/gas/testsuite/gas/elf/section21.l b/gas/testsuite/gas/elf/section21.l
new file mode 100644
index 0000000000..50342ec659
--- /dev/null
+++ b/gas/testsuite/gas/elf/section21.l
@@ -0,0 +1,5 @@
+[^:]*: Assembler messages:
+[^:]*:11: Error: junk at end of line, first unrecognized character is `1'
+#...
+[^:]*: Error: undefined linked-to symbol `bar' on section `__patchable_function_entries'
+[^:]*: Error: undefined linked-to symbol `foo' on section `__patchable_function_entries'
diff --git a/gas/testsuite/gas/elf/section21.s b/gas/testsuite/gas/elf/section21.s
new file mode 100644
index 0000000000..ae5f848b29
--- /dev/null
+++ b/gas/testsuite/gas/elf/section21.s
@@ -0,0 +1,15 @@
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.text
+.LPFE1:
+	.byte 0
+	.section __patchable_function_entries,"awo",%progbits,foo
+	.dc.a	.LPFE2
+	.text
+.LPFE2:
+	.dc.a foo
+	.section __patchable_function_entries,"awo",%progbits,1foo
+	.dc.a	.LPFE3
+	.text
+.LPFE3:
+	.byte 0
diff --git a/ld/testsuite/ld-elf/elf.exp b/ld/testsuite/ld-elf/elf.exp
index 989fb50d4a..3c74a55518 100644
--- a/ld/testsuite/ld-elf/elf.exp
+++ b/ld/testsuite/ld-elf/elf.exp
@@ -76,9 +76,32 @@ run_ld_link_tests [list \
 if [is_elf64 tmpdir/symbol3w.a] {
     set ASFLAGS "$ASFLAGS --defsym ALIGN=3"
     set pr23900_1_exp "pr23900-1-64.rd"
+    set pr25490_2_exp "pr25490-2-64.rd"
+    set pr25490_3_exp "pr25490-3-64.rd"
+    set pr25490_4_exp "pr25490-4-64.rd"
+    set pr25490_5_exp "pr25490-5-64.rd"
+    set pr25490_6_exp "pr25490-6-64.rd"
 } else {
     set ASFLAGS "$ASFLAGS --defsym ALIGN=2"
     set pr23900_1_exp "pr23900-1-32.rd"
+    if {    [istarget avr-*-*]
+	 || [istarget h8300-*-*]
+	 || [istarget ip2k-*-*]
+	 || [istarget "m68hc1*-*"]
+	 || [istarget "xc16x-*"]
+	 || [istarget "z80-*-*"] } {
+	set pr25490_2_exp "pr25490-2-16.rd"
+	set pr25490_3_exp "pr25490-3-16.rd"
+	set pr25490_4_exp "pr25490-4-16.rd"
+	set pr25490_5_exp "pr25490-5-16.rd"
+	set pr25490_6_exp "pr25490-6-16.rd"
+    } else {
+	set pr25490_2_exp "pr25490-2-32.rd"
+	set pr25490_3_exp "pr25490-3-32.rd"
+	set pr25490_4_exp "pr25490-4-32.rd"
+	set pr25490_5_exp "pr25490-5-32.rd"
+	set pr25490_6_exp "pr25490-6-32.rd"
+    }
 }
 
 
@@ -172,6 +195,46 @@ if { [istarget *-*-*linux*]
 	]
 }
 
+if [check_gc_sections_available] {
+    run_ld_link_tests [list \
+	[list "__patchable_function_entries section 2" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-2.s} \
+	    [list [list "readelf" {-SW} $pr25490_2_exp]] \
+	    "pr25490-2.exe"] \
+	[list "__patchable_function_entries section 3" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-3.s} \
+	    [list [list "readelf" {-SW} $pr25490_3_exp]] \
+	    "pr25490-3.exe"] \
+	[list "__patchable_function_entries section 4" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-4.s} \
+	    [list [list "readelf" {-SW} $pr25490_4_exp]] \
+	    "pr25490-4.exe"] \
+	[list "__patchable_function_entries section 5" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-5.s} \
+	    [list [list "readelf" {-SW} $pr25490_5_exp]] \
+	    "pr25490-5.exe"] \
+	[list "__patchable_function_entries section 6" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-6.s} \
+	    [list [list "readelf" {-SW} $pr25490_6_exp]] \
+	    "pr25490-6.exe"] \
+	]
+}
+
 set LDFLAGS $old_ldflags
 set ASFLAGS $old_asflags
 
diff --git a/ld/testsuite/ld-elf/pr24526.d b/ld/testsuite/ld-elf/pr24526.d
new file mode 100644
index 0000000000..2094b17e3e
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr24526.d
@@ -0,0 +1,9 @@
+#ld: --gc-sections -e _start
+#target: [check_gc_sections_available]
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] \.bar +PROGBITS +[0-9a-f]+ +[0-9a-f]+ [0-9a-f]+ +00 +AL +[0-9] .*
+#...
+ +\[ *[0-9]+\] \.zed +PROGBITS +[0-9a-f]+ +[0-9a-f]+ [0-9a-f]+ +00 +AL +[0-9] .*
+#pass
diff --git a/ld/testsuite/ld-elf/pr24526.s b/ld/testsuite/ld-elf/pr24526.s
new file mode 100644
index 0000000000..7d773387aa
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr24526.s
@@ -0,0 +1,13 @@
+	.text
+	.globl _start
+_start:
+	.byte 0
+	.section .note,"",%note
+	.dc.a .foo
+
+	.section .foo,"a"
+	.dc.a	0
+	.section .bar,"ao",%progbits,.foo
+	.dc.a	0
+	.section .zed,"ao",%progbits,.foo
+	.dc.a	0
diff --git a/ld/testsuite/ld-elf/pr25021.d b/ld/testsuite/ld-elf/pr25021.d
new file mode 100644
index 0000000000..1cb6a87184
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25021.d
@@ -0,0 +1,7 @@
+#ld: --gc-sections -e _start
+#target: [check_gc_sections_available]
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] \.stack_sizes +PROGBITS +0+ +[0-9a-f]+ 0+1 +00 +L +[0-9] .*
+#pass
diff --git a/ld/testsuite/ld-elf/pr25021.s b/ld/testsuite/ld-elf/pr25021.s
new file mode 100644
index 0000000000..dd71202eb6
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25021.s
@@ -0,0 +1,22 @@
+	.section .text.live,"ax",%progbits
+	.globl live
+live:
+	.byte 0
+
+	.section .stack_sizes,"o",%progbits,.text.live,unique,0
+	.byte 1
+
+	.section .text.dead,"ax",%progbits
+	.globl dead
+dead:
+	.byte 1
+
+	.section .stack_sizes,"o",%progbits,.text.dead,unique,1
+	.byte 2
+
+	.section .text.main,"ax",%progbits
+	.globl _start
+_start:
+	.byte 0
+	.section .note,"",%note
+	.dc.a live
diff --git a/ld/testsuite/ld-elf/pr25490-2-16.rd b/ld/testsuite/ld-elf/pr25490-2-16.rd
new file mode 100644
index 0000000000..2e521cf2ef
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-2-16.rd
@@ -0,0 +1,7 @@
+#source: pr25490-2.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+2 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-2-32.rd b/ld/testsuite/ld-elf/pr25490-2-32.rd
new file mode 100644
index 0000000000..8b13348225
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-2-32.rd
@@ -0,0 +1,7 @@
+#source: pr25490-2.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+4 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-2-64.rd b/ld/testsuite/ld-elf/pr25490-2-64.rd
new file mode 100644
index 0000000000..6533042c58
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-2-64.rd
@@ -0,0 +1,7 @@
+#source: pr25490-2.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-2.s b/ld/testsuite/ld-elf/pr25490-2.s
new file mode 100644
index 0000000000..856bb5fe9d
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-2.s
@@ -0,0 +1,9 @@
+	.text
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awo",%progbits,_start
+	.dc.a	.LPFE1
+	.text
+.LPFE1:
+	.byte 0
diff --git a/ld/testsuite/ld-elf/pr25490-3-16.rd b/ld/testsuite/ld-elf/pr25490-3-16.rd
new file mode 100644
index 0000000000..1c2f3d1bab
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-3-16.rd
@@ -0,0 +1,7 @@
+#source: pr25490-3.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+2 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-3-32.rd b/ld/testsuite/ld-elf/pr25490-3-32.rd
new file mode 100644
index 0000000000..eb5587f5f3
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-3-32.rd
@@ -0,0 +1,7 @@
+#source: pr25490-3.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+4 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-3-64.rd b/ld/testsuite/ld-elf/pr25490-3-64.rd
new file mode 100644
index 0000000000..9fe794bc80
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-3-64.rd
@@ -0,0 +1,7 @@
+#source: pr25490-3.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-3.s b/ld/testsuite/ld-elf/pr25490-3.s
new file mode 100644
index 0000000000..427ad69b53
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-3.s
@@ -0,0 +1,18 @@
+	.section	.text.bar,"ax",%progbits
+	.globl	bar
+	.type	bar, %function
+bar:
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.section	.text.bar,"ax",%progbits
+.LPFE1:
+	.byte 0
+	.section	.text._start,"ax",%progbits
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awo",%progbits,_start
+	.dc.a	.LPFE2
+	.section	.text._start,"ax",%progbits
+.LPFE2:
+	.byte 0
diff --git a/ld/testsuite/ld-elf/pr25490-4-16.rd b/ld/testsuite/ld-elf/pr25490-4-16.rd
new file mode 100644
index 0000000000..1f89d504e8
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-4-16.rd
@@ -0,0 +1,7 @@
+#source: pr25490-4.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+4 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-4-32.rd b/ld/testsuite/ld-elf/pr25490-4-32.rd
new file mode 100644
index 0000000000..4562a6eb1e
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-4-32.rd
@@ -0,0 +1,7 @@
+#source: pr25490-4.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-4-64.rd b/ld/testsuite/ld-elf/pr25490-4-64.rd
new file mode 100644
index 0000000000..da295f3018
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-4-64.rd
@@ -0,0 +1,7 @@
+#source: pr25490-4.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+10 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-4.s b/ld/testsuite/ld-elf/pr25490-4.s
new file mode 100644
index 0000000000..5a31f8e086
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-4.s
@@ -0,0 +1,20 @@
+	.section	.text.bar,"ax",%progbits
+	.globl	bar
+	.type	bar, %function
+bar:
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.section	.text.bar,"ax",%progbits
+.LPFE1:
+	.byte 0
+	.section	.text._start,"ax",%progbits
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awo",%progbits,_start
+	.dc.a	.LPFE2
+	.section	.text._start,"ax",%progbits
+.LPFE2:
+	.byte 0
+	.section .note,"",%note
+	.dc.a	bar
diff --git a/ld/testsuite/ld-elf/pr25490-5-16.rd b/ld/testsuite/ld-elf/pr25490-5-16.rd
new file mode 100644
index 0000000000..b51eb533a6
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-5-16.rd
@@ -0,0 +1,7 @@
+#source: pr25490-5.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+4 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-5-32.rd b/ld/testsuite/ld-elf/pr25490-5-32.rd
new file mode 100644
index 0000000000..8d6efb7582
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-5-32.rd
@@ -0,0 +1,7 @@
+#source: pr25490-5.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-5-64.rd b/ld/testsuite/ld-elf/pr25490-5-64.rd
new file mode 100644
index 0000000000..1375f9abce
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-5-64.rd
@@ -0,0 +1,7 @@
+#source: pr25490-5.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+10 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-5.s b/ld/testsuite/ld-elf/pr25490-5.s
new file mode 100644
index 0000000000..f9d46b8e0b
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-5.s
@@ -0,0 +1,17 @@
+	.text
+	.type	bar, %function
+bar:
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.text
+.LPFE1:
+	.byte 0
+	.text
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awo",%progbits,_start
+	.dc.a	.LPFE2
+	.text
+.LPFE2:
+	.byte	0
diff --git a/ld/testsuite/ld-elf/pr25490-6-16.rd b/ld/testsuite/ld-elf/pr25490-6-16.rd
new file mode 100644
index 0000000000..80eda13e65
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-6-16.rd
@@ -0,0 +1,7 @@
+#source: pr25490-6.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+6 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-6-32.rd b/ld/testsuite/ld-elf/pr25490-6-32.rd
new file mode 100644
index 0000000000..37928429c0
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-6-32.rd
@@ -0,0 +1,7 @@
+#source: pr25490-6.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+c +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-6-64.rd b/ld/testsuite/ld-elf/pr25490-6-64.rd
new file mode 100644
index 0000000000..a95ca48e02
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-6-64.rd
@@ -0,0 +1,7 @@
+#source: pr25490-6.s
+#ld: --gc-sections -e _start
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+18 +00 +WAL +[0-9] +0 +[1248]
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-6.s b/ld/testsuite/ld-elf/pr25490-6.s
new file mode 100644
index 0000000000..43fbc17989
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-6.s
@@ -0,0 +1,30 @@
+	.section .text,"axG",%progbits,bar,comdat
+	.globl	bar
+	.type	bar, %function
+bar:
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.section .text,"axG",%progbits,bar,comdat
+.LPFE1:
+	.byte 0
+	.section .text,"axG",%progbits,foo,comdat
+	.globl	foo
+	.type	foo, %function
+foo:
+	.section __patchable_function_entries,"awo",%progbits,foo,unique,0
+	.dc.a	.LPFE2
+	.section .text,"axG",%progbits,foo,comdat
+.LPFE2:
+	.byte 0
+	.section .text,"axG",%progbits,_start,comdat,unique,1
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awoG",%progbits,_start,_start,comdat,unique,3
+	.dc.a	.LPFE3
+	.section .text,"axG",%progbits,_start,comdat,unique,1
+.LPFE3:
+	.byte 0
+	.section .note,"",%note
+	.dc.a	foo
+	.dc.a	bar
Alan Modra Feb. 7, 2020, 1:41 a.m. | #5
On Thu, Feb 06, 2020 at 11:23:10AM -0800, H.J. Lu wrote:
> On Wed, Feb 5, 2020 at 11:46 PM Alan Modra <amodra@gmail.com> wrote:

> > Also, for sh-linux, I see this in pr25490-2.o

> >   [ 4] __patchable_function_entries PROGBITS        00000000 000038 000004 00 WAL  1   0  4

> 

> 4 is sh_addralign.  sh_link is 1.


Oops.

> All fixed.   Here is the updated patch.  The only bfd changes are

> 

> * bfd-in2.h: Regenerated.

> * elflink.c (_bfd_elf_gc_mark_extra_sections): Call mark_hook

> on section if gc_mark of any of its linked-to sections is set

> and don't set gc_mark again.

> * section.c (asection): Add linked_to_symbol_name to map_head

> union.

> 

> I changed  _bfd_elf_gc_mark_extra_sections to call mark_hook

> instead of setting gc_mark directly.

> 

> OK for master?


Yes, with these extra tweaks.  m68hc12 is 32 bit, and msp430 generates
enough extra sections that the link to .foo is 10.

diff --git a/ld/testsuite/ld-elf/elf.exp b/ld/testsuite/ld-elf/elf.exp
index 3c74a55518..7b8e8f6f3c 100644
--- a/ld/testsuite/ld-elf/elf.exp
+++ b/ld/testsuite/ld-elf/elf.exp
@@ -87,7 +87,7 @@ if [is_elf64 tmpdir/symbol3w.a] {
     if {    [istarget avr-*-*]
 	 || [istarget h8300-*-*]
 	 || [istarget ip2k-*-*]
-	 || [istarget "m68hc1*-*"]
+	 || [istarget m68hc11-*]
 	 || [istarget "xc16x-*"]
 	 || [istarget "z80-*-*"] } {
 	set pr25490_2_exp "pr25490-2-16.rd"
diff --git a/ld/testsuite/ld-elf/pr24526.d b/ld/testsuite/ld-elf/pr24526.d
index 2094b17e3e..3faaa1e6f6 100644
--- a/ld/testsuite/ld-elf/pr24526.d
+++ b/ld/testsuite/ld-elf/pr24526.d
@@ -3,7 +3,7 @@
 #readelf: -SW
 
 #...
- +\[ *[0-9]+\] \.bar +PROGBITS +[0-9a-f]+ +[0-9a-f]+ [0-9a-f]+ +00 +AL +[0-9] .*
+ +\[ *[0-9]+\] \.bar +PROGBITS +[0-9a-f]+ +[0-9a-f]+ [0-9a-f]+ +00 +AL .*
 #...
- +\[ *[0-9]+\] \.zed +PROGBITS +[0-9a-f]+ +[0-9a-f]+ [0-9a-f]+ +00 +AL +[0-9] .*
+ +\[ *[0-9]+\] \.zed +PROGBITS +[0-9a-f]+ +[0-9a-f]+ [0-9a-f]+ +00 +AL .*
 #pass

-- 
Alan Modra
Australia Development Lab, IBM

Patch

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 09a5a39ff5e..2d26b81db38 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1185,10 +1185,12 @@  typedef struct bfd_section
   /* 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
-     structs.  */
+     structs.  The linked_to_symbol_name field is for ELF assembler
+     internal use.  */
   union {
     struct bfd_link_order *link_order;
     struct bfd_section *s;
+    const char *linked_to_symbol_name;
   } map_head, map_tail;
 } asection;
 
diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c
index faf8376f200..a6df8f331ce 100644
--- a/bfd/elf32-arm.c
+++ b/bfd/elf32-arm.c
@@ -15939,8 +15939,6 @@  elf32_arm_gc_mark_extra_sections (struct bfd_link_info *info,
   bfd_boolean debug_sec_need_to_be_marked = FALSE;
   asection *isec;
 
-  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
-
   out_attr = elf_known_obj_attributes_proc (info->output_bfd);
   is_v8m = out_attr[Tag_CPU_arch].i >= TAG_CPU_ARCH_V8M_BASE
 	   && out_attr[Tag_CPU_arch_profile].i == 'M';
@@ -16024,6 +16022,8 @@  elf32_arm_gc_mark_extra_sections (struct bfd_link_info *info,
       first_bfd_browse = FALSE;
     }
 
+  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
+
   return TRUE;
 }
 
diff --git a/bfd/elf32-csky.c b/bfd/elf32-csky.c
index 04214f28ea5..3c5fcc82f5a 100644
--- a/bfd/elf32-csky.c
+++ b/bfd/elf32-csky.c
@@ -3032,8 +3032,6 @@  elf32_csky_gc_mark_extra_sections (struct bfd_link_info *info,
 {
   bfd *sub;
 
-  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
-
   for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
     {
       asection *o;
@@ -3043,6 +3041,8 @@  elf32_csky_gc_mark_extra_sections (struct bfd_link_info *info,
 	  o->gc_mark = 1;
     }
 
+  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
+
   return TRUE;
 }
 
diff --git a/bfd/elf32-tic6x.c b/bfd/elf32-tic6x.c
index 39ca6992007..6308eded0b4 100644
--- a/bfd/elf32-tic6x.c
+++ b/bfd/elf32-tic6x.c
@@ -1935,8 +1935,6 @@  elf32_tic6x_gc_mark_extra_sections (struct bfd_link_info *info,
   Elf_Internal_Shdr **elf_shdrp;
   bfd_boolean again;
 
-  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
-
   /* Marking EH data may cause additional code sections to be marked,
      requiring multiple passes.  */
   again = TRUE;
@@ -1970,6 +1968,8 @@  elf32_tic6x_gc_mark_extra_sections (struct bfd_link_info *info,
 	}
     }
 
+  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
+
   return TRUE;
 }
 
diff --git a/bfd/elflink.c b/bfd/elflink.c
index e4d92952aaf..8ad13b8b55f 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -13312,7 +13312,9 @@  _bfd_elf_gc_mark_debug_special_section_group (asection *grp)
     }
 }
 
-/* Keep debug and special sections.  */
+/* Keep debug and special sections.  NB: This function must be called
+   after elf_backend_gc_mark_extra_sections so that linked-to section
+   can be checkd properly.  */
 
 bfd_boolean
 _bfd_elf_gc_mark_extra_sections (struct bfd_link_info *info,
@@ -13345,6 +13347,15 @@  _bfd_elf_gc_mark_extra_sections (struct bfd_link_info *info,
 		   && (isec->flags & SEC_ALLOC) != 0
 		   && elf_section_type (isec) != SHT_NOTE)
 	    some_kept = TRUE;
+	  else
+	    {
+	      /* Since all sections, including backend specific ones,
+		 have been garbage collected, mark this section if its
+		 linked-to section is marked.  */
+	      asection *linked_to_sec = elf_linked_to_section (isec);
+	      if (linked_to_sec && linked_to_sec->gc_mark)
+		isec->gc_mark = 1;
+	    }
 
 	  if (!debug_frag_seen
 	      && (isec->flags & SEC_DEBUGGING)
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index d7e3aed3b67..a2d6ace3236 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -12845,8 +12845,6 @@  _bfd_mips_elf_gc_mark_extra_sections (struct bfd_link_info *info,
 {
   bfd *sub;
 
-  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
-
   for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
     {
       asection *o;
@@ -12863,6 +12861,8 @@  _bfd_mips_elf_gc_mark_extra_sections (struct bfd_link_info *info,
 	  }
     }
 
+  _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
+
   return TRUE;
 }
 
diff --git a/bfd/section.c b/bfd/section.c
index 0c15a0d646f..7de0a2d7a85 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -544,10 +544,12 @@  CODE_FRAGMENT
 .  {* 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
-.     structs.  *}
+.     structs.  The linked_to_symbol_name field is for ELF assembler
+.     internal use.  *}
 .  union {
 .    struct bfd_link_order *link_order;
 .    struct bfd_section *s;
+.    const char *linked_to_symbol_name;
 .  } map_head, map_tail;
 .} asection;
 .
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index 2958490c323..d7a07fec6bf 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -524,6 +524,8 @@  get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
   struct elf_section_match *match = (struct elf_section_match *) inf;
   const char *gname = match->group_name;
   const char *group_name = elf_group_name (sec);
+  const char *linked_to_symbol_name
+    = sec->map_head.linked_to_symbol_name;
   unsigned int info = elf_section_data (sec)->this_hdr.sh_info;
 
   return (info == match->info
@@ -533,7 +535,12 @@  get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 	  && (group_name == gname
 	      || (group_name != NULL
 		  && gname != NULL
-		  && strcmp (group_name, gname) == 0)));
+		  && strcmp (group_name, gname) == 0))
+	  && (linked_to_symbol_name == match->linked_to_symbol_name
+	      || (linked_to_symbol_name != NULL
+		  && match->linked_to_symbol_name != NULL
+		  && strcmp (linked_to_symbol_name,
+			     match->linked_to_symbol_name) == 0)));
 }
 
 /* Handle the .section pseudo-op.  This code supports two different
@@ -740,6 +747,10 @@  obj_elf_change_section (const char *name,
       sec->section_id = match_p->section_id;
       flags |= match_p->flags;
 
+      /* Set the linked-to symbol name.  */
+      sec->map_head.linked_to_symbol_name
+	= match_p->linked_to_symbol_name;
+
       bfd_set_section_flags (sec, flags);
       if (flags & SEC_MERGE)
 	sec->entsize = entsize;
@@ -801,6 +812,9 @@  obj_elf_parse_section_letters (char *str, size_t len,
 	case 'e':
 	  attr |= SHF_EXCLUDE;
 	  break;
+	case 'o':
+	  attr |= SHF_LINK_ORDER;
+	  break;
 	case 'w':
 	  attr |= SHF_WRITE;
 	  break;
@@ -841,7 +855,7 @@  obj_elf_parse_section_letters (char *str, size_t len,
 	default:
 	  {
 	    const char *bad_msg = _("unrecognized .section attribute:"
-				    " want a,e,w,x,M,S,G,T or number");
+				    " want a,e,o,w,x,M,S,G,T or number");
 #ifdef md_elf_section_letter
 	    bfd_vma md_attr = md_elf_section_letter (*str, &bad_msg);
 	    if (md_attr != (bfd_vma) -1)
@@ -1154,6 +1168,19 @@  obj_elf_section (int push)
 	      attr &= ~SHF_MERGE;
 	    }
 
+	  if ((attr & SHF_LINK_ORDER) != 0 && *input_line_pointer == ',')
+	    {
+	      char c;
+	      unsigned int length;
+	      ++input_line_pointer;
+	      SKIP_WHITESPACE ();
+	      c = get_symbol_name (& beg);
+	      (void) restore_line_pointer (c);
+	      length = input_line_pointer - beg;
+	      if (length)
+		match.linked_to_symbol_name = xmemdup0 (beg, length);
+	    }
+
 	  if ((attr & SHF_GROUP) != 0 && is_clone)
 	    {
 	      as_warn (_("? section flag ignored with G present"));
@@ -2476,10 +2503,12 @@  static struct group_list groups;
 /* Called via bfd_map_over_sections.  If SEC is a member of a group,
    add it to a list of sections belonging to the group.  INF is a
    pointer to a struct group_list, which is where we store the head of
-   each list.  */
+   each list.  If its link_to_symbol_name isn't NULL, set up its
+   linked-to section.  */
 
 static void
-build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
+build_additional_section_info (bfd *abfd ATTRIBUTE_UNUSED,
+				  asection *sec, void *inf)
 {
   struct group_list *list = (struct group_list *) inf;
   const char *group_name = elf_group_name (sec);
@@ -2487,6 +2516,18 @@  build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
   unsigned int *elem_idx;
   unsigned int *idx_ptr;
 
+  if (sec->map_head.linked_to_symbol_name)
+    {
+      symbolS *linked_to_sym;
+      linked_to_sym = symbol_find (sec->map_head.linked_to_symbol_name);
+      if (!linked_to_sym || !S_IS_DEFINED (linked_to_sym))
+	as_bad (_("undefined linked-to symbol `%s' on section `%s'"),
+		sec->map_head.linked_to_symbol_name,
+		bfd_section_name (sec));
+      else
+	elf_linked_to_section (sec) = S_GET_SEGMENT (linked_to_sym);
+    }
+
   if (group_name == NULL)
     return;
 
@@ -2533,7 +2574,8 @@  elf_adjust_symtab (void)
   groups.num_group = 0;
   groups.head = NULL;
   groups.indexes = hash_new ();
-  bfd_map_over_sections (stdoutput, build_group_lists, &groups);
+  bfd_map_over_sections (stdoutput, build_additional_section_info,
+			 &groups);
 
   /* Make the SHT_GROUP sections that describe each section group.  We
      can't set up the section contents here yet, because elf section
diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h
index 7cfcc254823..54af9ebc0e2 100644
--- a/gas/config/obj-elf.h
+++ b/gas/config/obj-elf.h
@@ -82,6 +82,7 @@  struct elf_obj_sy
 struct elf_section_match
 {
   const char *group_name;
+  const char *linked_to_symbol_name;
   unsigned int info;
   unsigned int section_id;
   flagword flags;
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 152bbfdd009..1554c51ad2f 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -6599,6 +6599,9 @@  section is allocatable
 section is a GNU_MBIND section
 @item e
 section is excluded from executable and shared library.
+@item o
+section references a symbol defined in another section (the linked-to
+section) in the same file.
 @item w
 section is writable
 @item x
@@ -6678,6 +6681,23 @@  which is a suffix of a larger string is considered a duplicate.  Thus
 @code{"def"} will be merged with @code{"abcdef"};  A reference to the first
 @code{"def"} will be changed to a reference to @code{"abcdef"+3}.
 
+If @var{flags} contains the @code{o} flag, then the @var{type} argument
+must be present along with an additional field like this:
+
+@smallexample
+.section @var{name},"@var{flags}"o,@@@var{type},@var{SymbolName}
+@end smallexample
+
+The @var{SymbolName} field specifies the symbol name which the section
+references.
+
+Note: If both the @var{M} and @var{o} flags are present, then the fields
+for the Merge flag should come first, like this:
+
+@smallexample
+.section @var{name},"@var{flags}"Mo,@@@var{type},@var{entsize},@var{SymbolName}
+@end smallexample
+
 If @var{flags} contains the @code{G} symbol then the @var{type} argument must
 be present along with an additional field like this:
 
@@ -6702,6 +6722,13 @@  the Merge flag should come first, like this:
 .section @var{name} , "@var{flags}"MG, @@@var{type}, @var{entsize}, @var{GroupName}[, @var{linkage}]
 @end smallexample
 
+If both @code{o} flag and @code{G} flag are present, then the
+@var{SymbolName} field for @code{o} comes first, like this:
+
+@smallexample
+.section @var{name},"@var{flags}"oG,@@@var{type},@var{SymbolName},@var{GroupName}[,@var{linkage}]
+@end smallexample
+
 If @var{flags} contains the @code{?} symbol then it may not also contain the
 @code{G} symbol and the @var{GroupName} or @var{linkage} fields should not be
 present.  Instead, @code{?} says to consider the section that's current before
diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp
index 08c6830e0db..0f9b2672c4c 100644
--- a/gas/testsuite/gas/elf/elf.exp
+++ b/gas/testsuite/gas/elf/elf.exp
@@ -249,6 +249,10 @@  if { [is_elf_format] } then {
     run_dump_test "section16a"
     run_dump_test "section16b"
     run_dump_test "section17"
+    run_dump_test "section18"
+    run_dump_test "section19"
+    run_dump_test "section20"
+    run_dump_test "section21"
     run_dump_test "dwarf2-1" $dump_opts
     run_dump_test "dwarf2-2" $dump_opts
     run_dump_test "dwarf2-3" $dump_opts
diff --git a/gas/testsuite/gas/elf/section18.d b/gas/testsuite/gas/elf/section18.d
new file mode 100644
index 00000000000..a00b4a65f95
--- /dev/null
+++ b/gas/testsuite/gas/elf/section18.d
@@ -0,0 +1,8 @@ 
+#readelf: -SW
+#name: linked-to section 1
+
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WAL +.*
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WAL +.*
+#pass
diff --git a/gas/testsuite/gas/elf/section18.s b/gas/testsuite/gas/elf/section18.s
new file mode 100644
index 00000000000..d51eb681318
--- /dev/null
+++ b/gas/testsuite/gas/elf/section18.s
@@ -0,0 +1,13 @@ 
+	.text
+foo:
+	.section __patchable_function_entries,"awo",%progbits,foo
+	.dc.a	.LPFE1
+	.text
+.LPFE1:
+	.byte 0
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE2
+	.text
+bar:
+.LPFE2:
+	.byte 0
diff --git a/gas/testsuite/gas/elf/section19.d b/gas/testsuite/gas/elf/section19.d
new file mode 100644
index 00000000000..4e500ad3bd2
--- /dev/null
+++ b/gas/testsuite/gas/elf/section19.d
@@ -0,0 +1,8 @@ 
+#readelf: -SW
+#name: linked-to section 2
+
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WAL +.*
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WAL +.*
+#pass
diff --git a/gas/testsuite/gas/elf/section19.s b/gas/testsuite/gas/elf/section19.s
new file mode 100644
index 00000000000..7d30ea1ff9c
--- /dev/null
+++ b/gas/testsuite/gas/elf/section19.s
@@ -0,0 +1,13 @@ 
+	.section .text,"ax",%progbits,unique,20
+foo:
+	.section __patchable_function_entries,"awo",%progbits,foo,unique,2
+	.dc.a	.LPFE1
+	.section .text,"ax",%progbits,unique,20
+.LPFE1:
+	.byte 0
+	.section __patchable_function_entries,"awo",%progbits,bar,unique,102
+	.dc.a	.LPFE2
+	.section .text,"ax",%progbits,unique,2
+bar:
+.LPFE2:
+	.byte 0
diff --git a/gas/testsuite/gas/elf/section20.d b/gas/testsuite/gas/elf/section20.d
new file mode 100644
index 00000000000..3e0b023f7ac
--- /dev/null
+++ b/gas/testsuite/gas/elf/section20.d
@@ -0,0 +1,17 @@ 
+#readelf: -SWg
+#name: linked-to section 3
+
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WALG +.*
+#...
+ +\[ *[0-9]+\] +__patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+[48] +00 +WALG +.*
+#...
+COMDAT group section \[[ 0-9]+\] `.group' \[foo\] contains [0-9]+ sections:
+   \[Index\]    Name
+#...
+   \[[ 0-9]+\]   __patchable_function_entries
+#...
+COMDAT group section \[[ 0-9]+\] `.group' \[bar\] contains [0-9]+ sections:
+#...
+   \[[ 0-9]+\]   __patchable_function_entries
+#pass
diff --git a/gas/testsuite/gas/elf/section20.s b/gas/testsuite/gas/elf/section20.s
new file mode 100644
index 00000000000..1e9639ef4a7
--- /dev/null
+++ b/gas/testsuite/gas/elf/section20.s
@@ -0,0 +1,13 @@ 
+	.section .text,"axG",%progbits,foo,comdat
+foo:
+	.section __patchable_function_entries,"awoG",%progbits,foo,foo,comdat
+	.dc.a	.LPFE1
+	.section .text,"axG",%progbits,foo,comdat
+.LPFE1:
+	.byte 0
+	.section __patchable_function_entries,"awoG",%progbits,bar,bar,comdat,unique,4
+	.dc.a	.LPFE2
+	.section .text,"axG",%progbits,bar,comdat,unique,24
+bar:
+.LPFE2:
+	.byte 0
diff --git a/gas/testsuite/gas/elf/section21.d b/gas/testsuite/gas/elf/section21.d
new file mode 100644
index 00000000000..54fa9d419bc
--- /dev/null
+++ b/gas/testsuite/gas/elf/section21.d
@@ -0,0 +1,2 @@ 
+#name: incorrect linked-to symbols
+#error_output: section21.l
diff --git a/gas/testsuite/gas/elf/section21.l b/gas/testsuite/gas/elf/section21.l
new file mode 100644
index 00000000000..8b9af6118a9
--- /dev/null
+++ b/gas/testsuite/gas/elf/section21.l
@@ -0,0 +1,4 @@ 
+[^:]*: Assembler messages:
+[^:]*:11: Error: junk at end of line, first unrecognized character is `1'
+[^:]*: Error: undefined linked-to symbol `bar' on section `__patchable_function_entries'
+[^:]*: Error: undefined linked-to symbol `foo' on section `__patchable_function_entries'
diff --git a/gas/testsuite/gas/elf/section21.s b/gas/testsuite/gas/elf/section21.s
new file mode 100644
index 00000000000..ae5f848b299
--- /dev/null
+++ b/gas/testsuite/gas/elf/section21.s
@@ -0,0 +1,15 @@ 
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.text
+.LPFE1:
+	.byte 0
+	.section __patchable_function_entries,"awo",%progbits,foo
+	.dc.a	.LPFE2
+	.text
+.LPFE2:
+	.dc.a foo
+	.section __patchable_function_entries,"awo",%progbits,1foo
+	.dc.a	.LPFE3
+	.text
+.LPFE3:
+	.byte 0
diff --git a/ld/testsuite/ld-elf/elf.exp b/ld/testsuite/ld-elf/elf.exp
index 989fb50d4ae..ea8cd884056 100644
--- a/ld/testsuite/ld-elf/elf.exp
+++ b/ld/testsuite/ld-elf/elf.exp
@@ -76,9 +76,19 @@  run_ld_link_tests [list \
 if [is_elf64 tmpdir/symbol3w.a] {
     set ASFLAGS "$ASFLAGS --defsym ALIGN=3"
     set pr23900_1_exp "pr23900-1-64.rd"
+    set pr25490_2_exp "pr25490-2-64.rd"
+    set pr25490_3_exp "pr25490-3-64.rd"
+    set pr25490_4_exp "pr25490-4-64.rd"
+    set pr25490_5_exp "pr25490-5-64.rd"
+    set pr25490_6_exp "pr25490-6-64.rd"
 } else {
     set ASFLAGS "$ASFLAGS --defsym ALIGN=2"
     set pr23900_1_exp "pr23900-1-32.rd"
+    set pr25490_2_exp "pr25490-2-32.rd"
+    set pr25490_3_exp "pr25490-3-32.rd"
+    set pr25490_4_exp "pr25490-4-32.rd"
+    set pr25490_5_exp "pr25490-5-32.rd"
+    set pr25490_6_exp "pr25490-6-32.rd"
 }
 
 
@@ -172,6 +182,46 @@  if { [istarget *-*-*linux*]
 	]
 }
 
+if [check_gc_sections_available] {
+    run_ld_link_tests [list \
+	[list "__patchable_function_entries section 2" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-2.s} \
+	    [list [list "readelf" {-SW} $pr25490_2_exp]] \
+	    "pr25490-2.exe"] \
+	[list "__patchable_function_entries section 3" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-3.s} \
+	    [list [list "readelf" {-SW} $pr25490_3_exp]] \
+	    "pr25490-3.exe"] \
+	[list "__patchable_function_entries section 4" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-4.s} \
+	    [list [list "readelf" {-SW} $pr25490_4_exp]] \
+	    "pr25490-4.exe"] \
+	[list "__patchable_function_entries section 5" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-5.s} \
+	    [list [list "readelf" {-SW} $pr25490_5_exp]] \
+	    "pr25490-5.exe"] \
+	[list "__patchable_function_entries section 6" \
+	    "--gc-sections -e _start" \
+	    "" \
+	    "" \
+	    {pr25490-6.s} \
+	    [list [list "readelf" {-SW} $pr25490_6_exp]] \
+	    "pr25490-6.exe"] \
+	]
+}
+
 set LDFLAGS $old_ldflags
 set ASFLAGS $old_asflags
 
diff --git a/ld/testsuite/ld-elf/pr25490-2-32.rd b/ld/testsuite/ld-elf/pr25490-2-32.rd
new file mode 100644
index 00000000000..99ee4dbb5d7
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-2-32.rd
@@ -0,0 +1,7 @@ 
+#source: pr25490-2.s
+#ld: --gc-sections
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+4 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-2-64.rd b/ld/testsuite/ld-elf/pr25490-2-64.rd
new file mode 100644
index 00000000000..db086d5aafc
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-2-64.rd
@@ -0,0 +1,7 @@ 
+#source: pr25490-2.s
+#ld: --gc-sections
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-2.s b/ld/testsuite/ld-elf/pr25490-2.s
new file mode 100644
index 00000000000..856bb5fe9d3
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-2.s
@@ -0,0 +1,9 @@ 
+	.text
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awo",%progbits,_start
+	.dc.a	.LPFE1
+	.text
+.LPFE1:
+	.byte 0
diff --git a/ld/testsuite/ld-elf/pr25490-3-32.rd b/ld/testsuite/ld-elf/pr25490-3-32.rd
new file mode 100644
index 00000000000..d2765f84c7f
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-3-32.rd
@@ -0,0 +1,7 @@ 
+#source: pr25490-3.s
+#ld: --gc-sections
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+4 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-3-64.rd b/ld/testsuite/ld-elf/pr25490-3-64.rd
new file mode 100644
index 00000000000..5faeebe65d8
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-3-64.rd
@@ -0,0 +1,7 @@ 
+#source: pr25490-3.s
+#ld: --gc-sections
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-3.s b/ld/testsuite/ld-elf/pr25490-3.s
new file mode 100644
index 00000000000..427ad69b535
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-3.s
@@ -0,0 +1,18 @@ 
+	.section	.text.bar,"ax",%progbits
+	.globl	bar
+	.type	bar, %function
+bar:
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.section	.text.bar,"ax",%progbits
+.LPFE1:
+	.byte 0
+	.section	.text._start,"ax",%progbits
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awo",%progbits,_start
+	.dc.a	.LPFE2
+	.section	.text._start,"ax",%progbits
+.LPFE2:
+	.byte 0
diff --git a/ld/testsuite/ld-elf/pr25490-4-32.rd b/ld/testsuite/ld-elf/pr25490-4-32.rd
new file mode 100644
index 00000000000..5667f40836b
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-4-32.rd
@@ -0,0 +1,7 @@ 
+#source: pr25490-4.s
+#ld: --gc-sections
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-4-64.rd b/ld/testsuite/ld-elf/pr25490-4-64.rd
new file mode 100644
index 00000000000..5a3b34dcd56
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-4-64.rd
@@ -0,0 +1,7 @@ 
+#source: pr25490-4.s
+#ld: --gc-sections
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+10 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-4.s b/ld/testsuite/ld-elf/pr25490-4.s
new file mode 100644
index 00000000000..9b3ffcad04d
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-4.s
@@ -0,0 +1,18 @@ 
+	.section	.text.bar,"ax",%progbits
+	.globl	bar
+	.type	bar, %function
+bar:
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.section	.text.bar,"ax",%progbits
+.LPFE1:
+	.byte 0
+	.section	.text._start,"ax",%progbits
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awo",%progbits,_start
+	.dc.a	.LPFE2
+	.section	.text._start,"ax",%progbits
+.LPFE2:
+	.dc.a	bar
diff --git a/ld/testsuite/ld-elf/pr25490-5-32.rd b/ld/testsuite/ld-elf/pr25490-5-32.rd
new file mode 100644
index 00000000000..32e19b8b5c8
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-5-32.rd
@@ -0,0 +1,7 @@ 
+#source: pr25490-5.s
+#ld: --gc-sections
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+8 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-5-64.rd b/ld/testsuite/ld-elf/pr25490-5-64.rd
new file mode 100644
index 00000000000..17358d5feda
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-5-64.rd
@@ -0,0 +1,7 @@ 
+#source: pr25490-5.s
+#ld: --gc-sections
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+10 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-5.s b/ld/testsuite/ld-elf/pr25490-5.s
new file mode 100644
index 00000000000..f9d46b8e0b0
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-5.s
@@ -0,0 +1,17 @@ 
+	.text
+	.type	bar, %function
+bar:
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.text
+.LPFE1:
+	.byte 0
+	.text
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awo",%progbits,_start
+	.dc.a	.LPFE2
+	.text
+.LPFE2:
+	.byte	0
diff --git a/ld/testsuite/ld-elf/pr25490-6-32.rd b/ld/testsuite/ld-elf/pr25490-6-32.rd
new file mode 100644
index 00000000000..807e83159a8
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-6-32.rd
@@ -0,0 +1,7 @@ 
+#source: pr25490-6.s
+#ld: --gc-sections
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+c +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-6-64.rd b/ld/testsuite/ld-elf/pr25490-6-64.rd
new file mode 100644
index 00000000000..668eaa45601
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-6-64.rd
@@ -0,0 +1,7 @@ 
+#source: pr25490-6.s
+#ld: --gc-sections
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] __patchable_function_entries +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0+18 +00 +WAL +[0-9] +0 +1
+#pass
diff --git a/ld/testsuite/ld-elf/pr25490-6.s b/ld/testsuite/ld-elf/pr25490-6.s
new file mode 100644
index 00000000000..5f5a348d4d0
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr25490-6.s
@@ -0,0 +1,28 @@ 
+	.section .text,"axG",%progbits,bar,comdat
+	.globl	bar
+	.type	bar, %function
+bar:
+	.section __patchable_function_entries,"awo",%progbits,bar
+	.dc.a	.LPFE1
+	.section .text,"axG",%progbits,bar,comdat
+.LPFE1:
+	.byte 0
+	.section .text,"axG",%progbits,foo,comdat
+	.globl	foo
+	.type	foo, %function
+foo:
+	.section __patchable_function_entries,"awo",%progbits,foo,unique,0
+	.dc.a	.LPFE2
+	.section .text,"axG",%progbits,foo,comdat
+.LPFE2:
+	.byte 0
+	.section .text,"axG",%progbits,_start,comdat,unique,1
+	.globl	_start
+	.type	_start, %function
+_start:
+	.section __patchable_function_entries,"awoG",%progbits,_start,_start,comdat,unique,3
+	.dc.a	.LPFE3
+	.section .text,"axG",%progbits,_start,comdat,unique,1
+.LPFE3:
+	.dc.a	foo
+	.dc.a	bar