ELF: Add support for unique section ID to assembler

Message ID 20200202175054.1864906-1-hjl.tools@gmail.com
State New
Headers show
Series
  • ELF: Add support for unique section ID to assembler
Related show

Commit Message

H.J. Lu Feb. 2, 2020, 5:50 p.m.
Clang's integrated assembler supports multiple section with the same
name:

	.section .text,"ax",@progbits,unique,1
	nop
	.section .text,"ax",@progbits,unique,2
	nop

"unique,N" assigns the number, N, as the section ID, to a section.  The
valid values of the section ID are between 0 and 4294967295.  It can be
used to distinguish different sections with the same section name.

This is useful with -fno-unique-section-names -ffunction-sections.
-ffunction-sections by default generates .text.foo, .text.bar, etc.
Using the same string can save lots of space in .strtab.

This patch adds section_id to bfd_section and reuses the linker
internal bit in BFD section flags, SEC_LINKER_CREATED, for assmebler
internal use to mark valid section_id.  It also updates objdump to
compare section pointers if 2 sections comes from the same file since
2 different sections can have the same section name.

bfd/

	PR gas/25380
	* bfd-in2.h: Regenerated.
	* ecoff.c (bfd_debug_section): Add section_id.
	* section.c (bfd_section): Add section_id.
	(SEC_ASSEMBLER_SECTION_ID): New.
	(BFD_FAKE_SECTION): Add section_id.

binutils/

	PR gas/25380
	* objdump.c (sym_ok): Return FALSE if 2 sections are in the
	same file with different section pointers.

gas/

	PR gas/25380
	* config/obj-elf.c (section_match): Removed.
	(get_section): Also match SEC_ASSEMBLER_SECTION_ID and
	section_id.
	(obj_elf_change_section): Replace info and group_name arguments
	with match_p.  Also update the section ID and flags from match_p.
	(obj_elf_section): Handle "unique,N".  Update call to
	obj_elf_change_section.
	* config/obj-elf.h (elf_section_match): New.
	(obj_elf_change_section): Updated.
	* config/tc-arm.c (start_unwind_section): Update call to
	obj_elf_change_section.
	* config/tc-ia64.c (obj_elf_vms_common): Likewise.
	* config/tc-microblaze.c (microblaze_s_data): Likewise.
	(microblaze_s_sdata): Likewise.
	(microblaze_s_rdata): Likewise.
	(microblaze_s_bss): Likewise.
	* config/tc-mips.c (s_change_section): Likewise.
	* config/tc-msp430.c (msp430_profiler): Likewise.
	* config/tc-rx.c (parse_rx_section): Likewise.
	* config/tc-tic6x.c (tic6x_start_unwind_section): Likewise.
	* doc/as.texi: Document "unique,N" in .section directive.
	* testsuite/gas/elf/elf.exp: Run "unique,N" tests.
	* testsuite/gas/elf/section15.d: New file.
	* testsuite/gas/elf/section15.s: Likewise.
	* testsuite/gas/elf/section16.s: Likewise.
	* testsuite/gas/elf/section16a.d: Likewise.
	* testsuite/gas/elf/section16b.d: Likewise.
	* testsuite/gas/elf/section17.d: Likewise.
	* testsuite/gas/elf/section17.l: Likewise.
	* testsuite/gas/elf/section17.s: Likewise.
	* testsuite/gas/i386/unique.d: Likewise.
	* testsuite/gas/i386/unique.s: Likewise.
	* testsuite/gas/i386/x86-64-unique.d: Likewise.
	* testsuite/gas/i386/i386.exp: Run unique and x86-64-unique.

ld/

	PR gas/25380
	* testsuite/ld-i386/pr22001-1c.S: Use "unique,N" in .section
	directives.
	* testsuite/ld-i386/tls-gd1.S: Likewise.
	* testsuite/ld-x86-64/pr21481b.S: Likewise.
---
 bfd/bfd-in2.h                          |  12 ++-
 bfd/ecoff.c                            |   6 +-
 bfd/section.c                          |  12 ++-
 binutils/objdump.c                     |   7 ++
 gas/config/obj-elf.c                   | 121 +++++++++++++++++++------
 gas/config/obj-elf.h                   |  12 ++-
 gas/config/tc-arm.c                    |  10 +-
 gas/config/tc-ia64.c                   |   2 +-
 gas/config/tc-microblaze.c             |  12 +--
 gas/config/tc-mips.c                   |   2 +-
 gas/config/tc-msp430.c                 |   2 +-
 gas/config/tc-rx.c                     |   4 +-
 gas/config/tc-tic6x.c                  |  10 +-
 gas/doc/as.texi                        |   5 +
 gas/testsuite/gas/elf/elf.exp          |   4 +
 gas/testsuite/gas/elf/section15.d      |  24 +++++
 gas/testsuite/gas/elf/section15.s      |  38 ++++++++
 gas/testsuite/gas/elf/section16.s      |  33 +++++++
 gas/testsuite/gas/elf/section16a.d     |  36 ++++++++
 gas/testsuite/gas/elf/section16b.d     |  36 ++++++++
 gas/testsuite/gas/elf/section17.d      |   2 +
 gas/testsuite/gas/elf/section17.l      |   4 +
 gas/testsuite/gas/elf/section17.s      |   6 ++
 gas/testsuite/gas/i386/i386.exp        |   2 +
 gas/testsuite/gas/i386/unique.d        |  48 ++++++++++
 gas/testsuite/gas/i386/unique.s        |  36 ++++++++
 gas/testsuite/gas/i386/x86-64-unique.d |  48 ++++++++++
 ld/testsuite/ld-i386/pr22001-1c.S      |   4 +-
 ld/testsuite/ld-i386/tls-gd1.S         |   5 +-
 ld/testsuite/ld-x86-64/pr21481b.S      |   4 +-
 30 files changed, 483 insertions(+), 64 deletions(-)
 create mode 100644 gas/testsuite/gas/elf/section15.d
 create mode 100644 gas/testsuite/gas/elf/section15.s
 create mode 100644 gas/testsuite/gas/elf/section16.s
 create mode 100644 gas/testsuite/gas/elf/section16a.d
 create mode 100644 gas/testsuite/gas/elf/section16b.d
 create mode 100644 gas/testsuite/gas/elf/section17.d
 create mode 100644 gas/testsuite/gas/elf/section17.l
 create mode 100644 gas/testsuite/gas/elf/section17.s
 create mode 100644 gas/testsuite/gas/i386/unique.d
 create mode 100644 gas/testsuite/gas/i386/unique.s
 create mode 100644 gas/testsuite/gas/i386/x86-64-unique.d

-- 
2.24.1

Comments

Alan Modra Feb. 3, 2020, 12:21 a.m. | #1
On Sun, Feb 02, 2020 at 09:50:54AM -0800, H.J. Lu wrote:
> Clang's integrated assembler supports multiple section with the same

> name:

> 

> 	.section .text,"ax",@progbits,unique,1

> 	nop

> 	.section .text,"ax",@progbits,unique,2

> 	nop

> 

> "unique,N" assigns the number, N, as the section ID, to a section.  The

> valid values of the section ID are between 0 and 4294967295.  It can be

> used to distinguish different sections with the same section name.

> 

> This is useful with -fno-unique-section-names -ffunction-sections.

> -ffunction-sections by default generates .text.foo, .text.bar, etc.

> Using the same string can save lots of space in .strtab.

> 

> This patch adds section_id to bfd_section and reuses the linker

> internal bit in BFD section flags, SEC_LINKER_CREATED, for assmebler

> internal use to mark valid section_id.  It also updates objdump to

> compare section pointers if 2 sections comes from the same file since

> 2 different sections can have the same section name.


Looks OK to me.

-- 
Alan Modra
Australia Development Lab, IBM
H.J. Lu Feb. 3, 2020, 1:04 a.m. | #2
On Sun, Feb 2, 2020 at 4:22 PM Alan Modra <amodra@gmail.com> wrote:
>

> On Sun, Feb 02, 2020 at 09:50:54AM -0800, H.J. Lu wrote:

> > Clang's integrated assembler supports multiple section with the same

> > name:

> >

> >       .section .text,"ax",@progbits,unique,1

> >       nop

> >       .section .text,"ax",@progbits,unique,2

> >       nop

> >

> > "unique,N" assigns the number, N, as the section ID, to a section.  The

> > valid values of the section ID are between 0 and 4294967295.  It can be

> > used to distinguish different sections with the same section name.

> >

> > This is useful with -fno-unique-section-names -ffunction-sections.

> > -ffunction-sections by default generates .text.foo, .text.bar, etc.

> > Using the same string can save lots of space in .strtab.

> >

> > This patch adds section_id to bfd_section and reuses the linker

> > internal bit in BFD section flags, SEC_LINKER_CREATED, for assmebler

> > internal use to mark valid section_id.  It also updates objdump to

> > compare section pointers if 2 sections comes from the same file since

> > 2 different sections can have the same section name.

>

> Looks OK to me.

>


This is the patch I am checking in.  I updated documentation with

iff --git a/gas/doc/as.texi b/gas/doc/as.texi
index fa155096159..152bbfdd009 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -6709,6 +6709,18 @@ this directive.  If that section used @code{G}, then the
new section will use
 @code{G} with those same @var{GroupName} and @var{linkage} fields implicitly.
 If not, then the @code{?} symbol has no effect.

+The optional @var{unique,@code{<number>}} argument must come last.  It
+assigns @var{@code{<number>}} as a unique section ID to distinguish
+different sections with the same section name like these:
+
+@smallexample
+.section @var{name},"@var{flags}",@@@var{type},@var{unique,@code{<number>}}
+.section @var{name},"@var{flags}"G,@@@var{type},@var{GroupName},[@var{linkage}]
,@var{unique,@code{<number>}}
+.section @var{name},"@var{flags}"MG,@@@var{type},@var{entsize},@var{GroupName}[
,@var{linkage}],@var{unique,@code{<number>}}
+@end smallexample
+
+The valid values of @var{@code{<number>}} are between 0 and 4294967295.
+
 If no flags are specified, the default flags depend upon the section name.  If
 the section name is not recognized, the default will be for the section to have
 none of the above flags: it will not be allocated in memory, nor writable, nor


-- 
H.J.
From fd0834347c57605bac20ffd800700211e4bb3d38 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 1 Feb 2020 12:22:09 -0800
Subject: [PATCH] ELF: Add support for unique section ID to assembler

Clang's integrated assembler supports multiple section with the same
name:

	.section .text,"ax",@progbits,unique,1
	nop
	.section .text,"ax",@progbits,unique,2
	nop

"unique,N" assigns the number, N, as the section ID, to a section.  The
valid values of the section ID are between 0 and 4294967295.  It can be
used to distinguish different sections with the same section name.

This is useful with -fno-unique-section-names -ffunction-sections.
-ffunction-sections by default generates .text.foo, .text.bar, etc.
Using the same string can save lots of space in .strtab.

This patch adds section_id to bfd_section and reuses the linker
internal bit in BFD section flags, SEC_LINKER_CREATED, for assmebler
internal use to mark valid section_id.  It also updates objdump to
compare section pointers if 2 sections comes from the same file since
2 different sections can have the same section name.

bfd/

	PR gas/25380
	* bfd-in2.h: Regenerated.
	* ecoff.c (bfd_debug_section): Add section_id.
	* section.c (bfd_section): Add section_id.
	(SEC_ASSEMBLER_SECTION_ID): New.
	(BFD_FAKE_SECTION): Add section_id.

binutils/

	PR gas/25380
	* objdump.c (sym_ok): Return FALSE if 2 sections are in the
	same file with different section pointers.

gas/

	PR gas/25380
	* config/obj-elf.c (section_match): Removed.
	(get_section): Also match SEC_ASSEMBLER_SECTION_ID and
	section_id.
	(obj_elf_change_section): Replace info and group_name arguments
	with match_p.  Also update the section ID and flags from match_p.
	(obj_elf_section): Handle "unique,N".  Update call to
	obj_elf_change_section.
	* config/obj-elf.h (elf_section_match): New.
	(obj_elf_change_section): Updated.
	* config/tc-arm.c (start_unwind_section): Update call to
	obj_elf_change_section.
	* config/tc-ia64.c (obj_elf_vms_common): Likewise.
	* config/tc-microblaze.c (microblaze_s_data): Likewise.
	(microblaze_s_sdata): Likewise.
	(microblaze_s_rdata): Likewise.
	(microblaze_s_bss): Likewise.
	* config/tc-mips.c (s_change_section): Likewise.
	* config/tc-msp430.c (msp430_profiler): Likewise.
	* config/tc-rx.c (parse_rx_section): Likewise.
	* config/tc-tic6x.c (tic6x_start_unwind_section): Likewise.
	* doc/as.texi: Document "unique,N" in .section directive.
	* testsuite/gas/elf/elf.exp: Run "unique,N" tests.
	* testsuite/gas/elf/section15.d: New file.
	* testsuite/gas/elf/section15.s: Likewise.
	* testsuite/gas/elf/section16.s: Likewise.
	* testsuite/gas/elf/section16a.d: Likewise.
	* testsuite/gas/elf/section16b.d: Likewise.
	* testsuite/gas/elf/section17.d: Likewise.
	* testsuite/gas/elf/section17.l: Likewise.
	* testsuite/gas/elf/section17.s: Likewise.
	* testsuite/gas/i386/unique.d: Likewise.
	* testsuite/gas/i386/unique.s: Likewise.
	* testsuite/gas/i386/x86-64-unique.d: Likewise.
	* testsuite/gas/i386/i386.exp: Run unique and x86-64-unique.

ld/

	PR gas/25380
	* testsuite/ld-i386/pr22001-1c.S: Use "unique,N" in .section
	directives.
	* testsuite/ld-i386/tls-gd1.S: Likewise.
	* testsuite/ld-x86-64/pr21481b.S: Likewise.
---
 bfd/bfd-in2.h                          |  12 ++-
 bfd/ecoff.c                            |   6 +-
 bfd/section.c                          |  12 ++-
 binutils/objdump.c                     |   7 ++
 gas/config/obj-elf.c                   | 121 +++++++++++++++++++------
 gas/config/obj-elf.h                   |  12 ++-
 gas/config/tc-arm.c                    |  10 +-
 gas/config/tc-ia64.c                   |   2 +-
 gas/config/tc-microblaze.c             |  12 +--
 gas/config/tc-mips.c                   |   2 +-
 gas/config/tc-msp430.c                 |   2 +-
 gas/config/tc-rx.c                     |   4 +-
 gas/config/tc-tic6x.c                  |  10 +-
 gas/doc/as.texi                        |  12 +++
 gas/testsuite/gas/elf/elf.exp          |   4 +
 gas/testsuite/gas/elf/section15.d      |  24 +++++
 gas/testsuite/gas/elf/section15.s      |  38 ++++++++
 gas/testsuite/gas/elf/section16.s      |  33 +++++++
 gas/testsuite/gas/elf/section16a.d     |  36 ++++++++
 gas/testsuite/gas/elf/section16b.d     |  36 ++++++++
 gas/testsuite/gas/elf/section17.d      |   2 +
 gas/testsuite/gas/elf/section17.l      |   4 +
 gas/testsuite/gas/elf/section17.s      |   6 ++
 gas/testsuite/gas/i386/i386.exp        |   2 +
 gas/testsuite/gas/i386/unique.d        |  48 ++++++++++
 gas/testsuite/gas/i386/unique.s        |  36 ++++++++
 gas/testsuite/gas/i386/x86-64-unique.d |  48 ++++++++++
 ld/testsuite/ld-i386/pr22001-1c.S      |   4 +-
 ld/testsuite/ld-i386/tls-gd1.S         |   5 +-
 ld/testsuite/ld-x86-64/pr21481b.S      |   4 +-
 30 files changed, 490 insertions(+), 64 deletions(-)
 create mode 100644 gas/testsuite/gas/elf/section15.d
 create mode 100644 gas/testsuite/gas/elf/section15.s
 create mode 100644 gas/testsuite/gas/elf/section16.s
 create mode 100644 gas/testsuite/gas/elf/section16a.d
 create mode 100644 gas/testsuite/gas/elf/section16b.d
 create mode 100644 gas/testsuite/gas/elf/section17.d
 create mode 100644 gas/testsuite/gas/elf/section17.l
 create mode 100644 gas/testsuite/gas/elf/section17.s
 create mode 100644 gas/testsuite/gas/i386/unique.d
 create mode 100644 gas/testsuite/gas/i386/unique.s
 create mode 100644 gas/testsuite/gas/i386/x86-64-unique.d

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 8144b167e04..c890520ccb1 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -795,6 +795,10 @@ typedef struct bfd_section
   /* A unique sequence number.  */
   unsigned int id;
 
+  /* A unique section number which can be used by assembler to
+     distinguish different sections with the same section name.  */
+  unsigned int section_id;
+
   /* Which section in the bfd; 0..n-1 as sections are created in a bfd.  */
   unsigned int index;
 
@@ -928,6 +932,10 @@ typedef struct bfd_section
      else up the line will take care of it later.  */
 #define SEC_LINKER_CREATED           0x100000
 
+  /* This section contains a section ID to distinguish different
+     sections withe the same section name.  */
+#define SEC_ASSEMBLER_SECTION_ID     0x100000
+
   /* This section should not be subject to garbage collection.
      Also set to inform the linker that this section should not be
      listed in the link map as discarded.  */
@@ -1329,8 +1337,8 @@ discarded_section (const asection *sec)
 }
 
 #define BFD_FAKE_SECTION(SEC, SYM, NAME, IDX, FLAGS)                   \
-  /* name, id,  index, next, prev, flags, user_set_vma,            */  \
-  {  NAME, IDX, 0,     NULL, NULL, FLAGS, 0,                           \
+  /* name, id,  section_id, index, next, prev, flags, user_set_vma, */ \
+  {  NAME, IDX, 0,          0,     NULL, NULL, FLAGS, 0,               \
                                                                        \
   /* linker_mark, linker_has_input, gc_mark, decompress_status,    */  \
      0,           0,                1,       0,                        \
diff --git a/bfd/ecoff.c b/bfd/ecoff.c
index f2713626d60..050fd7b5081 100644
--- a/bfd/ecoff.c
+++ b/bfd/ecoff.c
@@ -52,8 +52,10 @@
 /* This stuff is somewhat copied from coffcode.h.  */
 static asection bfd_debug_section =
 {
-  /* name,	id,  index, next, prev, flags, user_set_vma,	   */
-     "*DEBUG*", 0,   0,	    NULL, NULL, 0,     0,
+  /* name,	id,  section_id, index, next, prev, flags,	  */
+     "*DEBUG*", 0,   0,		 0,	NULL, NULL, 0,
+  /* user_set_vma,	   */
+     0,
   /* linker_mark, linker_has_input, gc_mark, compress_status,	   */
      0,		  0,		    1,	     0,
   /* segment_mark, sec_info_type, use_rela_p,			   */
diff --git a/bfd/section.c b/bfd/section.c
index d42c2b4287e..161ed33edc4 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -154,6 +154,10 @@ CODE_FRAGMENT
 .  {* A unique sequence number.  *}
 .  unsigned int id;
 .
+.  {* A unique section number which can be used by assembler to
+.     distinguish different sections with the same section name.  *}
+.  unsigned int section_id;
+.
 .  {* Which section in the bfd; 0..n-1 as sections are created in a bfd.  *}
 .  unsigned int index;
 .
@@ -287,6 +291,10 @@ CODE_FRAGMENT
 .     else up the line will take care of it later.  *}
 .#define SEC_LINKER_CREATED           0x100000
 .
+.  {* This section contains a section ID to distinguish different
+.     sections withe the same section name.  *}
+.#define SEC_ASSEMBLER_SECTION_ID     0x100000
+.
 .  {* This section should not be subject to garbage collection.
 .     Also set to inform the linker that this section should not be
 .     listed in the link map as discarded.  *}
@@ -688,8 +696,8 @@ CODE_FRAGMENT
 .}
 .
 .#define BFD_FAKE_SECTION(SEC, SYM, NAME, IDX, FLAGS)			\
-.  {* name, id,  index, next, prev, flags, user_set_vma,            *}	\
-.  {  NAME, IDX, 0,     NULL, NULL, FLAGS, 0,				\
+.  {* name, id,  section_id, index, next, prev, flags, user_set_vma, *}	\
+.  {  NAME, IDX, 0,          0,     NULL, NULL, FLAGS, 0,		\
 .									\
 .  {* linker_mark, linker_has_input, gc_mark, decompress_status,    *}	\
 .     0,           0,                1,       0,			\
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 60a39671292..17c0637b350 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -1065,6 +1065,13 @@ sym_ok (bfd_boolean               want_section,
 {
   if (want_section)
     {
+      /* NB: An object file can have different sections with the same
+         section name.  Compare compare section pointers if they have
+	 the same owner.  */
+      if (sorted_syms[place]->section->owner == sec->owner
+	  && sorted_syms[place]->section != sec)
+	return FALSE;
+
       /* Note - we cannot just compare section pointers because they could
 	 be different, but the same...  Ie the symbol that we are trying to
 	 find could have come from a separate debug info file.  Under such
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index 7cf921c051f..2958490c323 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -518,22 +518,18 @@ struct section_stack
 
 static struct section_stack *section_stack;
 
-/* Match both section group name and the sh_info field.  */
-struct section_match
-{
-  const char *group_name;
-  unsigned int info;
-};
-
 static bfd_boolean
 get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 {
-  struct section_match *match = (struct section_match *) 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);
   unsigned int info = elf_section_data (sec)->this_hdr.sh_info;
 
   return (info == match->info
+	  && ((bfd_section_flags (sec) & SEC_ASSEMBLER_SECTION_ID)
+	       == (match->flags & SEC_ASSEMBLER_SECTION_ID))
+	  && sec->section_id == match->section_id
 	  && (group_name == gname
 	      || (group_name != NULL
 		  && gname != NULL
@@ -561,10 +557,9 @@ get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 void
 obj_elf_change_section (const char *name,
 			unsigned int type,
-			unsigned int info,
 			bfd_vma attr,
 			int entsize,
-			const char *group_name,
+			struct elf_section_match *match_p,
 			int linkonce,
 			int push)
 {
@@ -573,7 +568,12 @@ obj_elf_change_section (const char *name,
   flagword flags;
   const struct elf_backend_data *bed;
   const struct bfd_elf_special_section *ssect;
-  struct section_match match;
+
+  if (match_p == NULL)
+    {
+      static struct elf_section_match unused_match;
+      match_p = &unused_match;
+    }
 
 #ifdef md_flush_pending_output
   md_flush_pending_output ();
@@ -594,10 +594,8 @@ obj_elf_change_section (const char *name,
   previous_section = now_seg;
   previous_subsection = now_subseg;
 
-  match.group_name = group_name;
-  match.info = info;
   old_sec = bfd_get_section_by_name_if (stdoutput, name, get_section,
-					(void *) &match);
+					(void *) match_p);
   if (old_sec)
     {
       sec = old_sec;
@@ -696,7 +694,7 @@ obj_elf_change_section (const char *name,
 #endif
 	  else
 	    {
-	      if (group_name == NULL)
+	      if (match_p->group_name == NULL)
 		as_warn (_("setting incorrect section attributes for %s"),
 			 name);
 	      override = TRUE;
@@ -732,16 +730,20 @@ obj_elf_change_section (const char *name,
 	type = bfd_elf_get_default_section_type (flags);
       elf_section_type (sec) = type;
       elf_section_flags (sec) = attr;
-      elf_section_data (sec)->this_hdr.sh_info = info;
+      elf_section_data (sec)->this_hdr.sh_info = match_p->info;
 
       /* Prevent SEC_HAS_CONTENTS from being inadvertently set.  */
       if (type == SHT_NOBITS)
 	seg_info (sec)->bss = 1;
 
+      /* Set the section ID and flags.  */
+      sec->section_id = match_p->section_id;
+      flags |= match_p->flags;
+
       bfd_set_section_flags (sec, flags);
       if (flags & SEC_MERGE)
 	sec->entsize = entsize;
-      elf_group_name (sec) = group_name;
+      elf_group_name (sec) = match_p->group_name;
 
       /* Add a symbol for this section to the symbol table.  */
       secsym = symbol_find (name);
@@ -1006,7 +1008,7 @@ obj_elf_section_name (void)
 void
 obj_elf_section (int push)
 {
-  const char *name, *group_name;
+  const char *name;
   char *beg;
   int type, dummy;
   bfd_vma attr;
@@ -1014,7 +1016,7 @@ obj_elf_section (int push)
   int entsize;
   int linkonce;
   subsegT new_subsection = -1;
-  unsigned int info = 0;
+  struct elf_section_match match;
 
   if (flag_mri)
     {
@@ -1040,6 +1042,8 @@ obj_elf_section (int push)
   if (name == NULL)
     return;
 
+  memset (&match, 0, sizeof (match));
+
   symbolS * sym;
   if ((sym = symbol_find (name)) != NULL
       && ! symbol_section_p (sym)
@@ -1054,7 +1058,6 @@ obj_elf_section (int push)
   type = SHT_NULL;
   attr = 0;
   gnu_attr = 0;
-  group_name = NULL;
   entsize = 0;
   linkonce = 0;
 
@@ -1159,8 +1162,8 @@ obj_elf_section (int push)
 	  if ((attr & SHF_GROUP) != 0 && *input_line_pointer == ',')
 	    {
 	      ++input_line_pointer;
-	      group_name = obj_elf_section_name ();
-	      if (group_name == NULL)
+	      match.group_name = obj_elf_section_name ();
+	      if (match.group_name == NULL)
 		attr &= ~SHF_GROUP;
 	      else if (*input_line_pointer == ',')
 		{
@@ -1186,26 +1189,86 @@ obj_elf_section (int push)
 	      const char *now_group = elf_group_name (now_seg);
 	      if (now_group != NULL)
 		{
-		  group_name = xstrdup (now_group);
+		  match.group_name = xstrdup (now_group);
 		  linkonce = (now_seg->flags & SEC_LINK_ONCE) != 0;
 		}
 	    }
 
 	  if ((gnu_attr & SHF_GNU_MBIND) != 0 && *input_line_pointer == ',')
 	    {
+	      char *save = input_line_pointer;
 	      ++input_line_pointer;
 	      SKIP_WHITESPACE ();
 	      if (ISDIGIT (* input_line_pointer))
 		{
 		  char *t = input_line_pointer;
-		  info = strtoul (input_line_pointer,
-				  &input_line_pointer, 0);
-		  if (info == (unsigned int) -1)
+		  match.info = strtoul (input_line_pointer,
+					&input_line_pointer, 0);
+		  if (match.info == (unsigned int) -1)
 		    {
 		      as_warn (_("unsupported mbind section info: %s"), t);
-		      info = 0;
+		      match.info = 0;
 		    }
 		}
+	      else
+		input_line_pointer = save;
+	    }
+
+	  if (*input_line_pointer == ',')
+	    {
+	      char *save = input_line_pointer;
+	      ++input_line_pointer;
+	      SKIP_WHITESPACE ();
+	      if (strncmp (input_line_pointer, "unique", 6) == 0)
+		{
+		  input_line_pointer += 6;
+		  SKIP_WHITESPACE ();
+		  if (*input_line_pointer == ',')
+		    {
+		      ++input_line_pointer;
+		      SKIP_WHITESPACE ();
+		      if (ISDIGIT (* input_line_pointer))
+			{
+			  bfd_vma id;
+			  bfd_boolean overflow;
+			  char *t = input_line_pointer;
+			  if (sizeof (bfd_vma) <= sizeof (unsigned long))
+			    {
+			      errno = 0;
+			      id = strtoul (input_line_pointer,
+					    &input_line_pointer, 0);
+			      overflow = (id == (unsigned long) -1
+					  && errno == ERANGE);
+			    }
+			  else
+			    {
+			      id = bfd_scan_vma
+				(input_line_pointer,
+				 (const char **) &input_line_pointer, 0);
+			      overflow = id == ~(bfd_vma) 0;
+			    }
+			  if (overflow || id > (unsigned int) -1)
+			    {
+			      char *linefeed, saved_char = 0;
+			      if ((linefeed = strchr (t, '\n')) != NULL)
+				{
+				  saved_char = *linefeed;
+				  *linefeed = '\0';
+				}
+			      as_bad (_("unsupported section id: %s"), t);
+			      if (saved_char)
+				*linefeed = saved_char;
+			    }
+			  else
+			    {
+			      match.section_id = id;
+			      match.flags |= SEC_ASSEMBLER_SECTION_ID;
+			    }
+			}
+		    }
+		}
+	      else
+		input_line_pointer = save;
 	    }
 	}
       else
@@ -1238,8 +1301,8 @@ obj_elf_section (int push)
 done:
   demand_empty_rest_of_line ();
 
-  obj_elf_change_section (name, type, info, attr, entsize, group_name,
-			  linkonce, push);
+  obj_elf_change_section (name, type, attr, entsize, &match, linkonce,
+			  push);
 
   if ((gnu_attr & SHF_GNU_MBIND) != 0)
     {
diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h
index b0a5a2d7b82..7cfcc254823 100644
--- a/gas/config/obj-elf.h
+++ b/gas/config/obj-elf.h
@@ -77,6 +77,16 @@ struct elf_obj_sy
 #endif
 };
 
+/* Match section group name, the sh_info field and the section_id
+   field.  */
+struct elf_section_match
+{
+  const char *group_name;
+  unsigned int info;
+  unsigned int section_id;
+  flagword flags;
+};
+
 #define OBJ_SYMFIELD_TYPE struct elf_obj_sy
 
 #ifndef FALSE
@@ -162,7 +172,7 @@ extern void obj_elf_common (int);
 extern void obj_elf_data (int);
 extern void obj_elf_text (int);
 extern void obj_elf_change_section
-  (const char *, unsigned int, unsigned int, bfd_vma, int, const char *,
+  (const char *, unsigned int, bfd_vma, int, struct elf_section_match *,
    int, int);
 extern void obj_elf_vtable_inherit (int);
 extern void obj_elf_vtable_entry (int);
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c
index bb0b03e35ea..26a76f3fe12 100644
--- a/gas/config/tc-arm.c
+++ b/gas/config/tc-arm.c
@@ -27011,7 +27011,7 @@ start_unwind_section (const segT text_seg, int idx)
   const char * text_name;
   const char * prefix;
   const char * prefix_once;
-  const char * group_name;
+  struct elf_section_match match;
   char * sec_name;
   int type;
   int flags;
@@ -27045,13 +27045,13 @@ start_unwind_section (const segT text_seg, int idx)
 
   flags = SHF_ALLOC;
   linkonce = 0;
-  group_name = 0;
+  memset (&match, 0, sizeof (match));
 
   /* Handle COMDAT group.  */
   if (prefix != prefix_once && (text_seg->flags & SEC_LINK_ONCE) != 0)
     {
-      group_name = elf_group_name (text_seg);
-      if (group_name == NULL)
+      match.group_name = elf_group_name (text_seg);
+      if (match.group_name == NULL)
 	{
 	  as_bad (_("Group section `%s' has no group signature"),
 		  segment_name (text_seg));
@@ -27062,7 +27062,7 @@ start_unwind_section (const segT text_seg, int idx)
       linkonce = 1;
     }
 
-  obj_elf_change_section (sec_name, type, 0, flags, 0, group_name,
+  obj_elf_change_section (sec_name, type, flags, 0, &match,
 			  linkonce, 0);
 
   /* Set the section link for index tables.  */
diff --git a/gas/config/tc-ia64.c b/gas/config/tc-ia64.c
index fbddd54120a..f908ba51fa3 100644
--- a/gas/config/tc-ia64.c
+++ b/gas/config/tc-ia64.c
@@ -1139,7 +1139,7 @@ obj_elf_vms_common (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 
   obj_elf_change_section
-    (sec_name, SHT_NOBITS, 0,
+    (sec_name, SHT_NOBITS,
      SHF_ALLOC | SHF_WRITE | SHF_IA_64_VMS_OVERLAID | SHF_IA_64_VMS_GLOBAL,
      0, NULL, 1, 0);
 
diff --git a/gas/config/tc-microblaze.c b/gas/config/tc-microblaze.c
index f4ae298b5b8..24ea3582447 100644
--- a/gas/config/tc-microblaze.c
+++ b/gas/config/tc-microblaze.c
@@ -149,7 +149,7 @@ static void
 microblaze_s_data (int ignore ATTRIBUTE_UNUSED)
 {
 #ifdef OBJ_ELF
-  obj_elf_change_section (".data", SHT_PROGBITS, 0, SHF_ALLOC+SHF_WRITE,
+  obj_elf_change_section (".data", SHT_PROGBITS, SHF_ALLOC+SHF_WRITE,
 			  0, 0, 0, 0);
 #else
   s_data (ignore);
@@ -162,7 +162,7 @@ static void
 microblaze_s_sdata (int ignore ATTRIBUTE_UNUSED)
 {
 #ifdef OBJ_ELF
-  obj_elf_change_section (".sdata", SHT_PROGBITS, 0, SHF_ALLOC+SHF_WRITE,
+  obj_elf_change_section (".sdata", SHT_PROGBITS, SHF_ALLOC+SHF_WRITE,
 			  0, 0, 0, 0);
 #else
   s_data (ignore);
@@ -281,7 +281,7 @@ microblaze_s_rdata (int localvar)
   if (localvar == 0)
     {
       /* rodata.  */
-      obj_elf_change_section (".rodata", SHT_PROGBITS, 0, SHF_ALLOC,
+      obj_elf_change_section (".rodata", SHT_PROGBITS, SHF_ALLOC,
 			      0, 0, 0, 0);
       if (rodata_segment == 0)
 	rodata_segment = subseg_new (".rodata", 0);
@@ -289,7 +289,7 @@ microblaze_s_rdata (int localvar)
   else
     {
       /* 1 .sdata2.  */
-      obj_elf_change_section (".sdata2", SHT_PROGBITS, 0, SHF_ALLOC,
+      obj_elf_change_section (".sdata2", SHT_PROGBITS, SHF_ALLOC,
 			      0, 0, 0, 0);
     }
 #else
@@ -302,12 +302,12 @@ microblaze_s_bss (int localvar)
 {
 #ifdef OBJ_ELF
   if (localvar == 0) /* bss.  */
-    obj_elf_change_section (".bss", SHT_NOBITS, 0, SHF_ALLOC+SHF_WRITE,
+    obj_elf_change_section (".bss", SHT_NOBITS, SHF_ALLOC+SHF_WRITE,
 			    0, 0, 0, 0);
   else if (localvar == 1)
     {
       /* sbss.  */
-      obj_elf_change_section (".sbss", SHT_NOBITS, 0, SHF_ALLOC+SHF_WRITE,
+      obj_elf_change_section (".sbss", SHT_NOBITS, SHF_ALLOC+SHF_WRITE,
 			      0, 0, 0, 0);
       if (sbss_segment == 0)
 	sbss_segment = subseg_new (".sbss", 0);
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index fc6898834e7..6244d8ac091 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -16418,7 +16418,7 @@ s_change_section (int ignore ATTRIBUTE_UNUSED)
   if (section_type == SHT_MIPS_DWARF)
     section_type = SHT_PROGBITS;
 
-  obj_elf_change_section (section_name, section_type, 0, section_flag,
+  obj_elf_change_section (section_name, section_type, section_flag,
 			  section_entry_size, 0, 0, 0);
 
   if (now_seg->name != section_name)
diff --git a/gas/config/tc-msp430.c b/gas/config/tc-msp430.c
index 57538824e92..840e5137ae7 100644
--- a/gas/config/tc-msp430.c
+++ b/gas/config/tc-msp430.c
@@ -620,7 +620,7 @@ msp430_profiler (int dummy ATTRIBUTE_UNUSED)
   subseg = now_subseg;
 
   /* Now go to .profiler section.  */
-  obj_elf_change_section (".profiler", SHT_PROGBITS, 0, 0, 0, 0, 0, 0);
+  obj_elf_change_section (".profiler", SHT_PROGBITS, 0, 0, 0, 0, 0);
 
   /* Save flags.  */
   emit_expr (& exp, 2);
diff --git a/gas/config/tc-rx.c b/gas/config/tc-rx.c
index b406e03b079..febdb5ab956 100644
--- a/gas/config/tc-rx.c
+++ b/gas/config/tc-rx.c
@@ -491,7 +491,7 @@ parse_rx_section (char * name)
       else
 	type = SHT_NOBITS;
 
-      obj_elf_change_section (name, type, 0, attr, 0, NULL, FALSE, FALSE);
+      obj_elf_change_section (name, type, attr, 0, NULL, FALSE, FALSE);
     }
   else /* Try not to redefine a section, especially B_1.  */
     {
@@ -506,7 +506,7 @@ parse_rx_section (char * name)
 	| ((flags & SEC_STRINGS) ? SHF_STRINGS : 0)
 	| ((flags & SEC_THREAD_LOCAL) ? SHF_TLS : 0);
 
-      obj_elf_change_section (name, type, 0, attr, 0, NULL, FALSE, FALSE);
+      obj_elf_change_section (name, type, attr, 0, NULL, FALSE, FALSE);
     }
 
   bfd_set_section_alignment (now_seg, align);
diff --git a/gas/config/tc-tic6x.c b/gas/config/tc-tic6x.c
index 9a506ed1e55..9f5b24e648d 100644
--- a/gas/config/tc-tic6x.c
+++ b/gas/config/tc-tic6x.c
@@ -4606,7 +4606,7 @@ tic6x_start_unwind_section (const segT text_seg, int idx)
   const char * text_name;
   const char * prefix;
   const char * prefix_once;
-  const char * group_name;
+  struct elf_section_match match;
   size_t prefix_len;
   size_t text_len;
   char * sec_name;
@@ -4649,13 +4649,13 @@ tic6x_start_unwind_section (const segT text_seg, int idx)
 
   flags = SHF_ALLOC;
   linkonce = 0;
-  group_name = 0;
+  memset (&match, 0, sizeof (match));
 
   /* Handle COMDAT group.  */
   if (prefix != prefix_once && (text_seg->flags & SEC_LINK_ONCE) != 0)
     {
-      group_name = elf_group_name (text_seg);
-      if (group_name == NULL)
+      match.group_name = elf_group_name (text_seg);
+      if (match.group_name == NULL)
 	{
 	  as_bad (_("group section `%s' has no group signature"),
 		  segment_name (text_seg));
@@ -4666,7 +4666,7 @@ tic6x_start_unwind_section (const segT text_seg, int idx)
       linkonce = 1;
     }
 
-  obj_elf_change_section (sec_name, type, 0, flags, 0, group_name,
+  obj_elf_change_section (sec_name, type, flags, 0, &match,
 			  linkonce, 0);
 
   /* Set the section link for index tables.  */
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index fa155096159..152bbfdd009 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -6709,6 +6709,18 @@ this directive.  If that section used @code{G}, then the new section will use
 @code{G} with those same @var{GroupName} and @var{linkage} fields implicitly.
 If not, then the @code{?} symbol has no effect.
 
+The optional @var{unique,@code{<number>}} argument must come last.  It
+assigns @var{@code{<number>}} as a unique section ID to distinguish
+different sections with the same section name like these:
+
+@smallexample
+.section @var{name},"@var{flags}",@@@var{type},@var{unique,@code{<number>}}
+.section @var{name},"@var{flags}"G,@@@var{type},@var{GroupName},[@var{linkage}],@var{unique,@code{<number>}}
+.section @var{name},"@var{flags}"MG,@@@var{type},@var{entsize},@var{GroupName}[,@var{linkage}],@var{unique,@code{<number>}}
+@end smallexample
+
+The valid values of @var{@code{<number>}} are between 0 and 4294967295.
+
 If no flags are specified, the default flags depend upon the section name.  If
 the section name is not recognized, the default will be for the section to have
 none of the above flags: it will not be allocated in memory, nor writable, nor
diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp
index 5a298fe5832..08c6830e0db 100644
--- a/gas/testsuite/gas/elf/elf.exp
+++ b/gas/testsuite/gas/elf/elf.exp
@@ -245,6 +245,10 @@ if { [is_elf_format] } then {
     run_dump_test "section12b"
     run_dump_test "section13"
     run_dump_test "section14"
+    run_dump_test "section15"
+    run_dump_test "section16a"
+    run_dump_test "section16b"
+    run_dump_test "section17"
     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/section15.d b/gas/testsuite/gas/elf/section15.d
new file mode 100644
index 00000000000..a7cda1394f4
--- /dev/null
+++ b/gas/testsuite/gas/elf/section15.d
@@ -0,0 +1,24 @@
+#objdump: -s
+#name: elf section15
+# .pushsection always creates the named section, but the
+# test harness translates ".text" into "P" for the RX...
+#notarget: rx-*
+
+.*: +file format .*
+
+# The MIPS includes a 'section .reginfo' and such here.
+#...
+Contents of section .bar:
+ 0000 00000000 00000000 0000 .*
+Contents of section .bar:
+ 0000 0102 .*
+Contents of section .bar:
+ 0000 0102 .*
+Contents of section .bar:
+ 0000 0103 .*
+Contents of section .bar:
+ 0000 04 .*
+Contents of section .text:
+ 0000 feff .*
+# Arm includes a .ARM.attributes section here
+#...
diff --git a/gas/testsuite/gas/elf/section15.s b/gas/testsuite/gas/elf/section15.s
new file mode 100644
index 00000000000..949cd84f89d
--- /dev/null
+++ b/gas/testsuite/gas/elf/section15.s
@@ -0,0 +1,38 @@
+	.section .bar,"a",unique,0
+	.byte 0
+ .pushsection .bar,2,"a",unique,1
+	.byte 2
+ .popsection
+	.byte 0
+ .pushsection .bar,3,"a",unique,2
+	.byte 2
+ .popsection
+	.byte 0
+ .pushsection .bar,2,"a", %progbits,unique,3
+	.byte 3
+ .popsection
+	.byte 0
+ .pushsection .bar,"",unique,4
+	.byte 4
+ .popsection
+	.byte 0
+ .pushsection .text,1,"axG",%progbits,foo,comdat,unique,0xffffffff
+	.byte -1
+ .popsection
+	.byte 0
+ .pushsection .text,"axG",%progbits,foo,comdat,unique,0xffffffff
+	.byte -2
+ .popsection
+	.byte 0
+ .pushsection .bar,"a",unique,1
+	.byte 1
+ .popsection
+	.byte 0
+ .pushsection .bar,"a", %progbits,unique,3
+	.byte 1
+ .popsection
+	.byte 0
+ .pushsection .bar,"a",unique,2
+	.byte 1
+ .popsection
+	.byte 0
diff --git a/gas/testsuite/gas/elf/section16.s b/gas/testsuite/gas/elf/section16.s
new file mode 100644
index 00000000000..77e20a368f7
--- /dev/null
+++ b/gas/testsuite/gas/elf/section16.s
@@ -0,0 +1,33 @@
+	.section .mbind.data,"adw",%progbits,unique,0
+	.byte 1
+
+	.section .mbind.data,"adw",%progbits,0x3,unique,1
+	.byte 2
+
+	.section .mbind.text,"adx",%progbits,unique,2
+	.byte 3
+
+	.section .mbind.text,"adx",%progbits,0x3,unique,3
+	.byte 4
+
+	.section .mbind.bss,"adw",%nobits,unique,4
+	.zero 5
+
+	.section .mbind.bss,"adw",%nobits,0x3,unique,5
+	.zero 6
+
+	.section .mbind.rodata,"adG",%progbits,.foo_group,comdat,0x2,unique,6
+	.byte 7
+
+	.section .mbind.data,"adGw",%progbits,.foo_group,comdat,unique,7
+	.byte 8
+
+	.section .mbind.data,"adGw",%progbits,.foo_group,comdat,0x3,unique,8
+	.byte 9
+
+	# Check that .pushsection works as well.
+	.pushsection .mbind.text,"adGx",%progbits,.foo_group,comdat,0x3,unique,9
+	.byte 10
+
+	.popsection
+	.byte 11
diff --git a/gas/testsuite/gas/elf/section16a.d b/gas/testsuite/gas/elf/section16a.d
new file mode 100644
index 00000000000..d1abf570c61
--- /dev/null
+++ b/gas/testsuite/gas/elf/section16a.d
@@ -0,0 +1,36 @@
+#source: section16.s
+#as: --no-pad-sections
+#readelf: -Sg --wide
+#name: mbind sections
+# A number of targets do not support SHF_GNU_MBIND
+#xfail: arm*-*-netbsdelf* arm*-*-nto* msp430-*-* visium-*-*
+#xfail: *-*-hpux* *-*-cloudabi
+
+#...
+  \[[ 0-9]+\] \.mbind\.data[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 WAD  0   0  1
+#...
+  \[[ 0-9]+\] \.mbind\.data[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 WAD  0   3  1
+#...
+  \[[ 0-9]+\] \.mbind\.text[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 AXD  0   0  .
+#...
+  \[[ 0-9]+\] \.mbind\.text[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 AXD  0   3  .
+#...
+  \[[ 0-9]+\] \.mbind\.bss[ 	]+NOBITS[ 	]+0+0 0+[0-9a-f]+ 0+5 00 WAD  0   0  1
+#...
+  \[[ 0-9]+\] \.mbind\.bss[ 	]+NOBITS[ 	]+0+0 0+[0-9a-f]+ 0+6 00 WAD  0   3  1
+#...
+  \[[ 0-9]+\] \.mbind\.rodata[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 AGD  0   2  1
+#...
+  \[[ 0-9]+\] \.mbind\.data[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 WAGD  0   0  1
+#...
+  \[[ 0-9]+\] \.mbind\.data[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+2 00 WAGD  0   3  1
+#...
+  \[[ 0-9]+\] \.mbind\.text[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 AXGD  0   3  1
+#...
+COMDAT group section \[    1\] `\.group' \[\.foo_group\] contains . sections:
+[ 	]+\[Index\][ 	]+Name
+[ 	]+\[[ 0-9]+][ 	]+\.mbind\.rodata
+[ 	]+\[[ 0-9]+][ 	]+\.mbind\.data
+[ 	]+\[[ 0-9]+][ 	]+\.mbind\.data
+[ 	]+\[[ 0-9]+][ 	]+\.mbind\.text
+#pass
diff --git a/gas/testsuite/gas/elf/section16b.d b/gas/testsuite/gas/elf/section16b.d
new file mode 100644
index 00000000000..a146c0de7e1
--- /dev/null
+++ b/gas/testsuite/gas/elf/section16b.d
@@ -0,0 +1,36 @@
+#source: section16.s
+#as: --no-pad-sections
+#objdump: -s
+#name: mbind section contents
+# RX annoyingly reorders the sections so that they do not match the sequence
+# expected below.
+#xfail: rx-*-*
+# A number of targets do not support SHF_GNU_MBIND
+#xfail: arm*-*-netbsdelf* arm*-*-nto* msp430-*-* visium-*-*
+#xfail: *-*-hpux* *-*-cloudabi
+
+#...
+Contents of section .mbind.data:
+ 0000 01                                   .               
+#...
+Contents of section .mbind.data:
+ 0000 02                                   .               
+#...
+Contents of section .mbind.text:
+ 0000 03                                   .               
+#...
+Contents of section .mbind.text:
+ 0000 04                                   .               
+#...
+Contents of section .mbind.rodata:
+ 0000 07                                   .               
+#...
+Contents of section .mbind.data:
+ 0000 08                                   .               
+#...
+Contents of section .mbind.data:
+ 0000 090b                                 ..              
+#...
+Contents of section .mbind.text:
+ 0000 0a                                   .               
+#pass
diff --git a/gas/testsuite/gas/elf/section17.d b/gas/testsuite/gas/elf/section17.d
new file mode 100644
index 00000000000..3efdc22abfe
--- /dev/null
+++ b/gas/testsuite/gas/elf/section17.d
@@ -0,0 +1,2 @@
+#name: incorrect section ID
+#error_output: section17.l
diff --git a/gas/testsuite/gas/elf/section17.l b/gas/testsuite/gas/elf/section17.l
new file mode 100644
index 00000000000..5b3e2b6d340
--- /dev/null
+++ b/gas/testsuite/gas/elf/section17.l
@@ -0,0 +1,4 @@
+[^:]*: Assembler messages:
+[^:]*:1: Error: unsupported section id: 0x100000000
+[^:]*:3: Error: junk at end of line, first unrecognized character is `f'
+[^:]*:5: Error: junk at end of line, first unrecognized character is `,'
diff --git a/gas/testsuite/gas/elf/section17.s b/gas/testsuite/gas/elf/section17.s
new file mode 100644
index 00000000000..43de1d7f2b6
--- /dev/null
+++ b/gas/testsuite/gas/elf/section17.s
@@ -0,0 +1,6 @@
+	.section .data,"aw",%progbits,unique,0x100000000
+	.byte 0
+	.section .bss,"aw",%nobits,unique,foo
+	.byte 0
+	.section .text,"ax",%progbits,unique,1,foo
+	.byte 0
diff --git a/gas/testsuite/gas/i386/i386.exp b/gas/testsuite/gas/i386/i386.exp
index feab8c2be95..ffeff0c101a 100644
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -491,6 +491,7 @@ if [expr ([istarget "i*86-*-*"] ||  [istarget "x86_64-*-*"]) && [gas_32_check]]
     run_list_test "inval-pseudo" "-al"
     run_dump_test "nop-1"
     run_dump_test "nop-2"
+    run_dump_test "unique"
     run_dump_test "optimize-1"
     run_dump_test "optimize-1a"
     run_dump_test "optimize-2"
@@ -1050,6 +1051,7 @@ if [expr ([istarget "i*86-*-*"] || [istarget "x86_64-*-*"]) && [gas_64_check]] t
     run_dump_test "x86-64-movd-intel"
     run_dump_test "x86-64-nop-1"
     run_dump_test "x86-64-nop-2"
+    run_dump_test "x86-64-unique"
     run_dump_test "x86-64-movsxd"
     run_dump_test "x86-64-movsxd-intel"
     run_list_test "x86-64-movsxd-inval" "-al"
diff --git a/gas/testsuite/gas/i386/unique.d b/gas/testsuite/gas/i386/unique.d
new file mode 100644
index 00000000000..0337733d992
--- /dev/null
+++ b/gas/testsuite/gas/i386/unique.d
@@ -0,0 +1,48 @@
+#objdump: -dw
+#name: i386 unique sections
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <foo>:
+ +[a-f0-9]+:	89 c3                	mov    %eax,%ebx
+ +[a-f0-9]+:	c3                   	ret    
+
+Disassembly of section .text:
+
+0+ <bar>:
+ +[a-f0-9]+:	31 c3                	xor    %eax,%ebx
+ +[a-f0-9]+:	c3                   	ret    
+
+Disassembly of section .text:
+
+0+ <foo1>:
+ +[a-f0-9]+:	89 c3                	mov    %eax,%ebx
+ +[a-f0-9]+:	c3                   	ret    
+
+Disassembly of section .text:
+
+0+ <bar1>:
+ +[a-f0-9]+:	01 c3                	add    %eax,%ebx
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	c3                   	ret    
+
+Disassembly of section .text:
+
+0+ <bar2>:
+ +[a-f0-9]+:	29 c3                	sub    %eax,%ebx
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	c3                   	ret    
+
+Disassembly of section .text:
+
+0+ <foo2>:
+ +[a-f0-9]+:	31 c3                	xor    %eax,%ebx
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	c3                   	ret    
+#pass
diff --git a/gas/testsuite/gas/i386/unique.s b/gas/testsuite/gas/i386/unique.s
new file mode 100644
index 00000000000..89b23b0fc66
--- /dev/null
+++ b/gas/testsuite/gas/i386/unique.s
@@ -0,0 +1,36 @@
+	.section .text,"ax",@progbits,unique,1
+foo:
+	mov %eax, %ebx
+	.section .text,"ax",@progbits,unique,2
+bar:
+	xor %eax, %ebx
+	.section .text,"ax",@progbits,unique,1
+	ret
+	.section .text,"ax",@progbits,unique,2
+	ret
+	.section .text,"axG",@progbits,foo,comdat,unique,1
+foo1:
+	mov	%eax, %ebx
+	.section .text,"axG",@progbits,bar,comdat,unique,1
+bar1:
+	add	%eax, %ebx
+	.section .text,"axG",@progbits,bar,comdat,unique,2
+bar2:
+	sub	%eax, %ebx
+	.section .text,"axG",@progbits,foo,comdat,unique,2
+foo2:
+	xor	%eax, %ebx
+	.section .text,"axG",@progbits,bar,comdat,unique,1
+	nop
+	ret
+	.section .text,"axG",@progbits,foo,comdat,unique,1
+	ret
+	.section .text,"axG",@progbits,bar,comdat,unique,2
+	nop
+	nop
+	nop
+	ret
+	.section .text,"axG",@progbits,foo,comdat,unique,2
+	nop
+	nop
+	ret
diff --git a/gas/testsuite/gas/i386/x86-64-unique.d b/gas/testsuite/gas/i386/x86-64-unique.d
new file mode 100644
index 00000000000..4cfd30d5e9d
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-unique.d
@@ -0,0 +1,48 @@
+#source: unique.s
+#objdump: -dw
+#name: 64bit unique sections
+
+.*: +file format .*
+
+Disassembly of section .text:
+
+0+ <foo>:
+ +[a-f0-9]+:	89 c3                	mov    %eax,%ebx
+ +[a-f0-9]+:	c3                   	retq   
+
+Disassembly of section .text:
+
+0+ <bar>:
+ +[a-f0-9]+:	31 c3                	xor    %eax,%ebx
+ +[a-f0-9]+:	c3                   	retq   
+
+Disassembly of section .text:
+
+0+ <foo1>:
+ +[a-f0-9]+:	89 c3                	mov    %eax,%ebx
+ +[a-f0-9]+:	c3                   	retq   
+
+Disassembly of section .text:
+
+0+ <bar1>:
+ +[a-f0-9]+:	01 c3                	add    %eax,%ebx
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	c3                   	retq   
+
+Disassembly of section .text:
+
+0+ <bar2>:
+ +[a-f0-9]+:	29 c3                	sub    %eax,%ebx
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	c3                   	retq   
+
+Disassembly of section .text:
+
+0+ <foo2>:
+ +[a-f0-9]+:	31 c3                	xor    %eax,%ebx
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	c3                   	retq   
+#pass
diff --git a/ld/testsuite/ld-i386/pr22001-1c.S b/ld/testsuite/ld-i386/pr22001-1c.S
index 2c1041dba77..51094efce35 100644
--- a/ld/testsuite/ld-i386/pr22001-1c.S
+++ b/ld/testsuite/ld-i386/pr22001-1c.S
@@ -1,7 +1,7 @@
 	.section	.rodata.str1.1,"aMS",@progbits,1
 .LC0:
 	.string	"PASS"
-	.section	.text.startup,"ax",@progbits
+	.section	.text,"ax",@progbits,unique,1
 	.p2align 4,,15
 	.globl	main
 	.type	main, @function
@@ -41,7 +41,7 @@ main:
 	addl	$16, %esp
 	jmp	.L3
 	.size	main, .-main
-	.section	.text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
+	.section .text,"axG",@progbits,__x86.get_pc_thunk.bx,comdat,unique,2
 	.globl	__x86.get_pc_thunk.bx
 	.hidden	__x86.get_pc_thunk.bx
 	.type	__x86.get_pc_thunk.bx, @function
diff --git a/ld/testsuite/ld-i386/tls-gd1.S b/ld/testsuite/ld-i386/tls-gd1.S
index 3b16eab6aa2..cf5c913560c 100644
--- a/ld/testsuite/ld-i386/tls-gd1.S
+++ b/ld/testsuite/ld-i386/tls-gd1.S
@@ -47,15 +47,14 @@ test_gd:
 	movzbl	%al, %eax
 	ret
 	.size	test_gd, .-test_gd
-	.section	.text.unlikely
-	.section	.text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
+	.section .text,"axG",@progbits,__x86.get_pc_thunk.bx,comdat,unique,1
 	.globl	__x86.get_pc_thunk.bx
 	.hidden	__x86.get_pc_thunk.bx
 	.type	__x86.get_pc_thunk.bx, @function
 __x86.get_pc_thunk.bx:
 	movl	(%esp), %ebx
 	ret
-	.section	.text.__x86.get_pc_thunk.cx,"axG",@progbits,__x86.get_pc_thunk.cx,comdat
+	.section .text,"axG",@progbits,__x86.get_pc_thunk.cx,comdat,unique,2
 	.globl	__x86.get_pc_thunk.cx
 	.hidden	__x86.get_pc_thunk.cx
 	.type	__x86.get_pc_thunk.cx, @function
diff --git a/ld/testsuite/ld-x86-64/pr21481b.S b/ld/testsuite/ld-x86-64/pr21481b.S
index 583ec77d121..d66c86f2286 100644
--- a/ld/testsuite/ld-x86-64/pr21481b.S
+++ b/ld/testsuite/ld-x86-64/pr21481b.S
@@ -1,4 +1,4 @@
-	.section	.rodata.str1.1,"aMS",@progbits,1
+	.section .rodata.foo,"aMS",@progbits,1,unique,1
 .LC0:
 	.string	"PASS"
 	.text
@@ -40,7 +40,7 @@ call_func1:
 	jmp	*func1@GOTPCREL(%rip)
 	.size	call_func1, .-call_func1
 	.globl	func1_p
-	.section	.rodata,"a",@progbits
+	.section .rodata.foo,"a",@progbits,unique,2
 	.align 8
 	.size	func1_p, 8
 	.type	func1_p, @object
Fangrui Song Feb. 3, 2020, 1:10 a.m. | #3
On 2020-02-02, H.J. Lu wrote:
>On Sun, Feb 2, 2020@4:22 PM Alan Modra <amodra@gmail.com> wrote:

>>

>> On Sun, Feb 02, 2020@09:50:54AM -0800, H.J. Lu wrote:

>> > Clang's integrated assembler supports multiple section with the same

>> > name:

>> >

>> >       .section .text,"ax",@progbits,unique,1

>> >       nop

>> >       .section .text,"ax",@progbits,unique,2

>> >       nop

>> >

>> > "unique,N" assigns the number, N, as the section ID, to a section.  The

>> > valid values of the section ID are between 0 and 4294967295.  It can be

>> > used to distinguish different sections with the same section name.

>> >

>> > This is useful with -fno-unique-section-names -ffunction-sections.

>> > -ffunction-sections by default generates .text.foo, .text.bar, etc.

>> > Using the same string can save lots of space in .strtab.

>> >

>> > This patch adds section_id to bfd_section and reuses the linker

>> > internal bit in BFD section flags, SEC_LINKER_CREATED, for assmebler

>> > internal use to mark valid section_id.  It also updates objdump to

>> > compare section pointers if 2 sections comes from the same file since

>> > 2 different sections can have the same section name.

>>

>> Looks OK to me.

>>

>

>This is the patch I am checking in.  I updated documentation with

>

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

>index fa155096159..152bbfdd009 100644

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

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

>@@ -6709,6 +6709,18 @@ this directive.  If that section used @code{G}, then the

>new section will use

> @code{G} with those same @var{GroupName} and @var{linkage} fields implicitly.

> If not, then the @code{?} symbol has no effect.

>

>+The optional @var{unique,@code{<number>}} argument must come last.  It

>+assigns @var{@code{<number>}} as a unique section ID to distinguish

>+different sections with the same section name like these:

>+

>+@smallexample

>+.section @var{name},"@var{flags}",@@@var{type},@var{unique,@code{<number>}}

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

>,@var{unique,@code{<number>}}

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

>,@var{linkage}],@var{unique,@code{<number>}}

>+@end smallexample

>+

>+The valid values of @var{@code{<number>}} are between 0 and 4294967295.

>+

> If no flags are specified, the default flags depend upon the section name.  If

> the section name is not recognized, the default will be for the section to have

> none of the above flags: it will not be allocated in memory, nor writable, nor

>

>

>-- 

>H.J.


Thanks for the patch! Looks good.

+     sections withe the same section name.  */

Typo. withe -> with
H.J. Lu Feb. 3, 2020, 1:16 a.m. | #4
On Sun, Feb 2, 2020 at 5:10 PM Fangrui Song <i@maskray.me> wrote:
>

>

> On 2020-02-02, H.J. Lu wrote:

> >On Sun, Feb 2, 2020@4:22 PM Alan Modra <amodra@gmail.com> wrote:

> >>

> >> On Sun, Feb 02, 2020@09:50:54AM -0800, H.J. Lu wrote:

> >> > Clang's integrated assembler supports multiple section with the same

> >> > name:

> >> >

> >> >       .section .text,"ax",@progbits,unique,1

> >> >       nop

> >> >       .section .text,"ax",@progbits,unique,2

> >> >       nop

> >> >

> >> > "unique,N" assigns the number, N, as the section ID, to a section.  The

> >> > valid values of the section ID are between 0 and 4294967295.  It can be

> >> > used to distinguish different sections with the same section name.

> >> >

> >> > This is useful with -fno-unique-section-names -ffunction-sections.

> >> > -ffunction-sections by default generates .text.foo, .text.bar, etc.

> >> > Using the same string can save lots of space in .strtab.

> >> >

> >> > This patch adds section_id to bfd_section and reuses the linker

> >> > internal bit in BFD section flags, SEC_LINKER_CREATED, for assmebler

> >> > internal use to mark valid section_id.  It also updates objdump to

> >> > compare section pointers if 2 sections comes from the same file since

> >> > 2 different sections can have the same section name.

> >>

> >> Looks OK to me.

> >>

> >

> >This is the patch I am checking in.  I updated documentation with

> >

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

> >index fa155096159..152bbfdd009 100644

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

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

> >@@ -6709,6 +6709,18 @@ this directive.  If that section used @code{G}, then the

> >new section will use

> > @code{G} with those same @var{GroupName} and @var{linkage} fields implicitly.

> > If not, then the @code{?} symbol has no effect.

> >

> >+The optional @var{unique,@code{<number>}} argument must come last.  It

> >+assigns @var{@code{<number>}} as a unique section ID to distinguish

> >+different sections with the same section name like these:

> >+

> >+@smallexample

> >+.section @var{name},"@var{flags}",@@@var{type},@var{unique,@code{<number>}}

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

> >,@var{unique,@code{<number>}}

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

> >,@var{linkage}],@var{unique,@code{<number>}}

> >+@end smallexample

> >+

> >+The valid values of @var{@code{<number>}} are between 0 and 4294967295.

> >+

> > If no flags are specified, the default flags depend upon the section name.  If

> > the section name is not recognized, the default will be for the section to have

> > none of the above flags: it will not be allocated in memory, nor writable, nor

> >

> >

> >--

> >H.J.

>

> Thanks for the patch! Looks good.

>

> +     sections withe the same section name.  */

>

> Typo. withe -> with


Oops.  Fixed with this.

Thanks.

-- 
H.J.
From ef4627fabaebd4f4a2bc2c5e92c95d747f388d78 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sun, 2 Feb 2020 17:14:12 -0800
Subject: [PATCH] section.c: Fix typo in comments (withe -> with)

	* bfd-in2.h: Regenerated.
	* section.c (SEC_ASSEMBLER_SECTION_ID): Fix a typo in comments.
---
 bfd/ChangeLog | 5 +++++
 bfd/bfd-in2.h | 2 +-
 bfd/section.c | 2 +-
 3 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index f8e7dfaf94..f758f65034 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,8 @@
+2020-02-02  H.J. Lu  <hongjiu.lu@intel.com>
+
+	* bfd-in2.h: Regenerated.
+	* section.c (SEC_ASSEMBLER_SECTION_ID): Fix a typo in comments.
+
 2020-02-02  H.J. Lu  <hongjiu.lu@intel.com>
 
 	PR gas/25380
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index c890520ccb..09a5a39ff5 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -933,7 +933,7 @@ typedef struct bfd_section
 #define SEC_LINKER_CREATED           0x100000
 
   /* This section contains a section ID to distinguish different
-     sections withe the same section name.  */
+     sections with the same section name.  */
 #define SEC_ASSEMBLER_SECTION_ID     0x100000
 
   /* This section should not be subject to garbage collection.
diff --git a/bfd/section.c b/bfd/section.c
index 161ed33edc..0c15a0d646 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -292,7 +292,7 @@ CODE_FRAGMENT
 .#define SEC_LINKER_CREATED           0x100000
 .
 .  {* This section contains a section ID to distinguish different
-.     sections withe the same section name.  *}
+.     sections with the same section name.  *}
 .#define SEC_ASSEMBLER_SECTION_ID     0x100000
 .
 .  {* This section should not be subject to garbage collection.

Patch

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 8144b167e04..c890520ccb1 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -795,6 +795,10 @@  typedef struct bfd_section
   /* A unique sequence number.  */
   unsigned int id;
 
+  /* A unique section number which can be used by assembler to
+     distinguish different sections with the same section name.  */
+  unsigned int section_id;
+
   /* Which section in the bfd; 0..n-1 as sections are created in a bfd.  */
   unsigned int index;
 
@@ -928,6 +932,10 @@  typedef struct bfd_section
      else up the line will take care of it later.  */
 #define SEC_LINKER_CREATED           0x100000
 
+  /* This section contains a section ID to distinguish different
+     sections withe the same section name.  */
+#define SEC_ASSEMBLER_SECTION_ID     0x100000
+
   /* This section should not be subject to garbage collection.
      Also set to inform the linker that this section should not be
      listed in the link map as discarded.  */
@@ -1329,8 +1337,8 @@  discarded_section (const asection *sec)
 }
 
 #define BFD_FAKE_SECTION(SEC, SYM, NAME, IDX, FLAGS)                   \
-  /* name, id,  index, next, prev, flags, user_set_vma,            */  \
-  {  NAME, IDX, 0,     NULL, NULL, FLAGS, 0,                           \
+  /* name, id,  section_id, index, next, prev, flags, user_set_vma, */ \
+  {  NAME, IDX, 0,          0,     NULL, NULL, FLAGS, 0,               \
                                                                        \
   /* linker_mark, linker_has_input, gc_mark, decompress_status,    */  \
      0,           0,                1,       0,                        \
diff --git a/bfd/ecoff.c b/bfd/ecoff.c
index f2713626d60..050fd7b5081 100644
--- a/bfd/ecoff.c
+++ b/bfd/ecoff.c
@@ -52,8 +52,10 @@ 
 /* This stuff is somewhat copied from coffcode.h.  */
 static asection bfd_debug_section =
 {
-  /* name,	id,  index, next, prev, flags, user_set_vma,	   */
-     "*DEBUG*", 0,   0,	    NULL, NULL, 0,     0,
+  /* name,	id,  section_id, index, next, prev, flags,	  */
+     "*DEBUG*", 0,   0,		 0,	NULL, NULL, 0,
+  /* user_set_vma,	   */
+     0,
   /* linker_mark, linker_has_input, gc_mark, compress_status,	   */
      0,		  0,		    1,	     0,
   /* segment_mark, sec_info_type, use_rela_p,			   */
diff --git a/bfd/section.c b/bfd/section.c
index d42c2b4287e..161ed33edc4 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -154,6 +154,10 @@  CODE_FRAGMENT
 .  {* A unique sequence number.  *}
 .  unsigned int id;
 .
+.  {* A unique section number which can be used by assembler to
+.     distinguish different sections with the same section name.  *}
+.  unsigned int section_id;
+.
 .  {* Which section in the bfd; 0..n-1 as sections are created in a bfd.  *}
 .  unsigned int index;
 .
@@ -287,6 +291,10 @@  CODE_FRAGMENT
 .     else up the line will take care of it later.  *}
 .#define SEC_LINKER_CREATED           0x100000
 .
+.  {* This section contains a section ID to distinguish different
+.     sections withe the same section name.  *}
+.#define SEC_ASSEMBLER_SECTION_ID     0x100000
+.
 .  {* This section should not be subject to garbage collection.
 .     Also set to inform the linker that this section should not be
 .     listed in the link map as discarded.  *}
@@ -688,8 +696,8 @@  CODE_FRAGMENT
 .}
 .
 .#define BFD_FAKE_SECTION(SEC, SYM, NAME, IDX, FLAGS)			\
-.  {* name, id,  index, next, prev, flags, user_set_vma,            *}	\
-.  {  NAME, IDX, 0,     NULL, NULL, FLAGS, 0,				\
+.  {* name, id,  section_id, index, next, prev, flags, user_set_vma, *}	\
+.  {  NAME, IDX, 0,          0,     NULL, NULL, FLAGS, 0,		\
 .									\
 .  {* linker_mark, linker_has_input, gc_mark, decompress_status,    *}	\
 .     0,           0,                1,       0,			\
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 60a39671292..17c0637b350 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -1065,6 +1065,13 @@  sym_ok (bfd_boolean               want_section,
 {
   if (want_section)
     {
+      /* NB: An object file can have different sections with the same
+         section name.  Compare compare section pointers if they have
+	 the same owner.  */
+      if (sorted_syms[place]->section->owner == sec->owner
+	  && sorted_syms[place]->section != sec)
+	return FALSE;
+
       /* Note - we cannot just compare section pointers because they could
 	 be different, but the same...  Ie the symbol that we are trying to
 	 find could have come from a separate debug info file.  Under such
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index 7cf921c051f..2958490c323 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -518,22 +518,18 @@  struct section_stack
 
 static struct section_stack *section_stack;
 
-/* Match both section group name and the sh_info field.  */
-struct section_match
-{
-  const char *group_name;
-  unsigned int info;
-};
-
 static bfd_boolean
 get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 {
-  struct section_match *match = (struct section_match *) 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);
   unsigned int info = elf_section_data (sec)->this_hdr.sh_info;
 
   return (info == match->info
+	  && ((bfd_section_flags (sec) & SEC_ASSEMBLER_SECTION_ID)
+	       == (match->flags & SEC_ASSEMBLER_SECTION_ID))
+	  && sec->section_id == match->section_id
 	  && (group_name == gname
 	      || (group_name != NULL
 		  && gname != NULL
@@ -561,10 +557,9 @@  get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 void
 obj_elf_change_section (const char *name,
 			unsigned int type,
-			unsigned int info,
 			bfd_vma attr,
 			int entsize,
-			const char *group_name,
+			struct elf_section_match *match_p,
 			int linkonce,
 			int push)
 {
@@ -573,7 +568,12 @@  obj_elf_change_section (const char *name,
   flagword flags;
   const struct elf_backend_data *bed;
   const struct bfd_elf_special_section *ssect;
-  struct section_match match;
+
+  if (match_p == NULL)
+    {
+      static struct elf_section_match unused_match;
+      match_p = &unused_match;
+    }
 
 #ifdef md_flush_pending_output
   md_flush_pending_output ();
@@ -594,10 +594,8 @@  obj_elf_change_section (const char *name,
   previous_section = now_seg;
   previous_subsection = now_subseg;
 
-  match.group_name = group_name;
-  match.info = info;
   old_sec = bfd_get_section_by_name_if (stdoutput, name, get_section,
-					(void *) &match);
+					(void *) match_p);
   if (old_sec)
     {
       sec = old_sec;
@@ -696,7 +694,7 @@  obj_elf_change_section (const char *name,
 #endif
 	  else
 	    {
-	      if (group_name == NULL)
+	      if (match_p->group_name == NULL)
 		as_warn (_("setting incorrect section attributes for %s"),
 			 name);
 	      override = TRUE;
@@ -732,16 +730,20 @@  obj_elf_change_section (const char *name,
 	type = bfd_elf_get_default_section_type (flags);
       elf_section_type (sec) = type;
       elf_section_flags (sec) = attr;
-      elf_section_data (sec)->this_hdr.sh_info = info;
+      elf_section_data (sec)->this_hdr.sh_info = match_p->info;
 
       /* Prevent SEC_HAS_CONTENTS from being inadvertently set.  */
       if (type == SHT_NOBITS)
 	seg_info (sec)->bss = 1;
 
+      /* Set the section ID and flags.  */
+      sec->section_id = match_p->section_id;
+      flags |= match_p->flags;
+
       bfd_set_section_flags (sec, flags);
       if (flags & SEC_MERGE)
 	sec->entsize = entsize;
-      elf_group_name (sec) = group_name;
+      elf_group_name (sec) = match_p->group_name;
 
       /* Add a symbol for this section to the symbol table.  */
       secsym = symbol_find (name);
@@ -1006,7 +1008,7 @@  obj_elf_section_name (void)
 void
 obj_elf_section (int push)
 {
-  const char *name, *group_name;
+  const char *name;
   char *beg;
   int type, dummy;
   bfd_vma attr;
@@ -1014,7 +1016,7 @@  obj_elf_section (int push)
   int entsize;
   int linkonce;
   subsegT new_subsection = -1;
-  unsigned int info = 0;
+  struct elf_section_match match;
 
   if (flag_mri)
     {
@@ -1040,6 +1042,8 @@  obj_elf_section (int push)
   if (name == NULL)
     return;
 
+  memset (&match, 0, sizeof (match));
+
   symbolS * sym;
   if ((sym = symbol_find (name)) != NULL
       && ! symbol_section_p (sym)
@@ -1054,7 +1058,6 @@  obj_elf_section (int push)
   type = SHT_NULL;
   attr = 0;
   gnu_attr = 0;
-  group_name = NULL;
   entsize = 0;
   linkonce = 0;
 
@@ -1159,8 +1162,8 @@  obj_elf_section (int push)
 	  if ((attr & SHF_GROUP) != 0 && *input_line_pointer == ',')
 	    {
 	      ++input_line_pointer;
-	      group_name = obj_elf_section_name ();
-	      if (group_name == NULL)
+	      match.group_name = obj_elf_section_name ();
+	      if (match.group_name == NULL)
 		attr &= ~SHF_GROUP;
 	      else if (*input_line_pointer == ',')
 		{
@@ -1186,26 +1189,86 @@  obj_elf_section (int push)
 	      const char *now_group = elf_group_name (now_seg);
 	      if (now_group != NULL)
 		{
-		  group_name = xstrdup (now_group);
+		  match.group_name = xstrdup (now_group);
 		  linkonce = (now_seg->flags & SEC_LINK_ONCE) != 0;
 		}
 	    }
 
 	  if ((gnu_attr & SHF_GNU_MBIND) != 0 && *input_line_pointer == ',')
 	    {
+	      char *save = input_line_pointer;
 	      ++input_line_pointer;
 	      SKIP_WHITESPACE ();
 	      if (ISDIGIT (* input_line_pointer))
 		{
 		  char *t = input_line_pointer;
-		  info = strtoul (input_line_pointer,
-				  &input_line_pointer, 0);
-		  if (info == (unsigned int) -1)
+		  match.info = strtoul (input_line_pointer,
+					&input_line_pointer, 0);
+		  if (match.info == (unsigned int) -1)
 		    {
 		      as_warn (_("unsupported mbind section info: %s"), t);
-		      info = 0;
+		      match.info = 0;
 		    }
 		}
+	      else
+		input_line_pointer = save;
+	    }
+
+	  if (*input_line_pointer == ',')
+	    {
+	      char *save = input_line_pointer;
+	      ++input_line_pointer;
+	      SKIP_WHITESPACE ();
+	      if (strncmp (input_line_pointer, "unique", 6) == 0)
+		{
+		  input_line_pointer += 6;
+		  SKIP_WHITESPACE ();
+		  if (*input_line_pointer == ',')
+		    {
+		      ++input_line_pointer;
+		      SKIP_WHITESPACE ();
+		      if (ISDIGIT (* input_line_pointer))
+			{
+			  bfd_vma id;
+			  bfd_boolean overflow;
+			  char *t = input_line_pointer;
+			  if (sizeof (bfd_vma) <= sizeof (unsigned long))
+			    {
+			      errno = 0;
+			      id = strtoul (input_line_pointer,
+					    &input_line_pointer, 0);
+			      overflow = (id == (unsigned long) -1
+					  && errno == ERANGE);
+			    }
+			  else
+			    {
+			      id = bfd_scan_vma
+				(input_line_pointer,
+				 (const char **) &input_line_pointer, 0);
+			      overflow = id == ~(bfd_vma) 0;
+			    }
+			  if (overflow || id > (unsigned int) -1)
+			    {
+			      char *linefeed, saved_char = 0;
+			      if ((linefeed = strchr (t, '\n')) != NULL)
+				{
+				  saved_char = *linefeed;
+				  *linefeed = '\0';
+				}
+			      as_bad (_("unsupported section id: %s"), t);
+			      if (saved_char)
+				*linefeed = saved_char;
+			    }
+			  else
+			    {
+			      match.section_id = id;
+			      match.flags |= SEC_ASSEMBLER_SECTION_ID;
+			    }
+			}
+		    }
+		}
+	      else
+		input_line_pointer = save;
 	    }
 	}
       else
@@ -1238,8 +1301,8 @@  obj_elf_section (int push)
 done:
   demand_empty_rest_of_line ();
 
-  obj_elf_change_section (name, type, info, attr, entsize, group_name,
-			  linkonce, push);
+  obj_elf_change_section (name, type, attr, entsize, &match, linkonce,
+			  push);
 
   if ((gnu_attr & SHF_GNU_MBIND) != 0)
     {
diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h
index b0a5a2d7b82..7cfcc254823 100644
--- a/gas/config/obj-elf.h
+++ b/gas/config/obj-elf.h
@@ -77,6 +77,16 @@  struct elf_obj_sy
 #endif
 };
 
+/* Match section group name, the sh_info field and the section_id
+   field.  */
+struct elf_section_match
+{
+  const char *group_name;
+  unsigned int info;
+  unsigned int section_id;
+  flagword flags;
+};
+
 #define OBJ_SYMFIELD_TYPE struct elf_obj_sy
 
 #ifndef FALSE
@@ -162,7 +172,7 @@  extern void obj_elf_common (int);
 extern void obj_elf_data (int);
 extern void obj_elf_text (int);
 extern void obj_elf_change_section
-  (const char *, unsigned int, unsigned int, bfd_vma, int, const char *,
+  (const char *, unsigned int, bfd_vma, int, struct elf_section_match *,
    int, int);
 extern void obj_elf_vtable_inherit (int);
 extern void obj_elf_vtable_entry (int);
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c
index bb0b03e35ea..26a76f3fe12 100644
--- a/gas/config/tc-arm.c
+++ b/gas/config/tc-arm.c
@@ -27011,7 +27011,7 @@  start_unwind_section (const segT text_seg, int idx)
   const char * text_name;
   const char * prefix;
   const char * prefix_once;
-  const char * group_name;
+  struct elf_section_match match;
   char * sec_name;
   int type;
   int flags;
@@ -27045,13 +27045,13 @@  start_unwind_section (const segT text_seg, int idx)
 
   flags = SHF_ALLOC;
   linkonce = 0;
-  group_name = 0;
+  memset (&match, 0, sizeof (match));
 
   /* Handle COMDAT group.  */
   if (prefix != prefix_once && (text_seg->flags & SEC_LINK_ONCE) != 0)
     {
-      group_name = elf_group_name (text_seg);
-      if (group_name == NULL)
+      match.group_name = elf_group_name (text_seg);
+      if (match.group_name == NULL)
 	{
 	  as_bad (_("Group section `%s' has no group signature"),
 		  segment_name (text_seg));
@@ -27062,7 +27062,7 @@  start_unwind_section (const segT text_seg, int idx)
       linkonce = 1;
     }
 
-  obj_elf_change_section (sec_name, type, 0, flags, 0, group_name,
+  obj_elf_change_section (sec_name, type, flags, 0, &match,
 			  linkonce, 0);
 
   /* Set the section link for index tables.  */
diff --git a/gas/config/tc-ia64.c b/gas/config/tc-ia64.c
index fbddd54120a..f908ba51fa3 100644
--- a/gas/config/tc-ia64.c
+++ b/gas/config/tc-ia64.c
@@ -1139,7 +1139,7 @@  obj_elf_vms_common (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 
   obj_elf_change_section
-    (sec_name, SHT_NOBITS, 0,
+    (sec_name, SHT_NOBITS,
      SHF_ALLOC | SHF_WRITE | SHF_IA_64_VMS_OVERLAID | SHF_IA_64_VMS_GLOBAL,
      0, NULL, 1, 0);
 
diff --git a/gas/config/tc-microblaze.c b/gas/config/tc-microblaze.c
index f4ae298b5b8..24ea3582447 100644
--- a/gas/config/tc-microblaze.c
+++ b/gas/config/tc-microblaze.c
@@ -149,7 +149,7 @@  static void
 microblaze_s_data (int ignore ATTRIBUTE_UNUSED)
 {
 #ifdef OBJ_ELF
-  obj_elf_change_section (".data", SHT_PROGBITS, 0, SHF_ALLOC+SHF_WRITE,
+  obj_elf_change_section (".data", SHT_PROGBITS, SHF_ALLOC+SHF_WRITE,
 			  0, 0, 0, 0);
 #else
   s_data (ignore);
@@ -162,7 +162,7 @@  static void
 microblaze_s_sdata (int ignore ATTRIBUTE_UNUSED)
 {
 #ifdef OBJ_ELF
-  obj_elf_change_section (".sdata", SHT_PROGBITS, 0, SHF_ALLOC+SHF_WRITE,
+  obj_elf_change_section (".sdata", SHT_PROGBITS, SHF_ALLOC+SHF_WRITE,
 			  0, 0, 0, 0);
 #else
   s_data (ignore);
@@ -281,7 +281,7 @@  microblaze_s_rdata (int localvar)
   if (localvar == 0)
     {
       /* rodata.  */
-      obj_elf_change_section (".rodata", SHT_PROGBITS, 0, SHF_ALLOC,
+      obj_elf_change_section (".rodata", SHT_PROGBITS, SHF_ALLOC,
 			      0, 0, 0, 0);
       if (rodata_segment == 0)
 	rodata_segment = subseg_new (".rodata", 0);
@@ -289,7 +289,7 @@  microblaze_s_rdata (int localvar)
   else
     {
       /* 1 .sdata2.  */
-      obj_elf_change_section (".sdata2", SHT_PROGBITS, 0, SHF_ALLOC,
+      obj_elf_change_section (".sdata2", SHT_PROGBITS, SHF_ALLOC,
 			      0, 0, 0, 0);
     }
 #else
@@ -302,12 +302,12 @@  microblaze_s_bss (int localvar)
 {
 #ifdef OBJ_ELF
   if (localvar == 0) /* bss.  */
-    obj_elf_change_section (".bss", SHT_NOBITS, 0, SHF_ALLOC+SHF_WRITE,
+    obj_elf_change_section (".bss", SHT_NOBITS, SHF_ALLOC+SHF_WRITE,
 			    0, 0, 0, 0);
   else if (localvar == 1)
     {
       /* sbss.  */
-      obj_elf_change_section (".sbss", SHT_NOBITS, 0, SHF_ALLOC+SHF_WRITE,
+      obj_elf_change_section (".sbss", SHT_NOBITS, SHF_ALLOC+SHF_WRITE,
 			      0, 0, 0, 0);
       if (sbss_segment == 0)
 	sbss_segment = subseg_new (".sbss", 0);
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index fc6898834e7..6244d8ac091 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -16418,7 +16418,7 @@  s_change_section (int ignore ATTRIBUTE_UNUSED)
   if (section_type == SHT_MIPS_DWARF)
     section_type = SHT_PROGBITS;
 
-  obj_elf_change_section (section_name, section_type, 0, section_flag,
+  obj_elf_change_section (section_name, section_type, section_flag,
 			  section_entry_size, 0, 0, 0);
 
   if (now_seg->name != section_name)
diff --git a/gas/config/tc-msp430.c b/gas/config/tc-msp430.c
index 57538824e92..840e5137ae7 100644
--- a/gas/config/tc-msp430.c
+++ b/gas/config/tc-msp430.c
@@ -620,7 +620,7 @@  msp430_profiler (int dummy ATTRIBUTE_UNUSED)
   subseg = now_subseg;
 
   /* Now go to .profiler section.  */
-  obj_elf_change_section (".profiler", SHT_PROGBITS, 0, 0, 0, 0, 0, 0);
+  obj_elf_change_section (".profiler", SHT_PROGBITS, 0, 0, 0, 0, 0);
 
   /* Save flags.  */
   emit_expr (& exp, 2);
diff --git a/gas/config/tc-rx.c b/gas/config/tc-rx.c
index b406e03b079..febdb5ab956 100644
--- a/gas/config/tc-rx.c
+++ b/gas/config/tc-rx.c
@@ -491,7 +491,7 @@  parse_rx_section (char * name)
       else
 	type = SHT_NOBITS;
 
-      obj_elf_change_section (name, type, 0, attr, 0, NULL, FALSE, FALSE);
+      obj_elf_change_section (name, type, attr, 0, NULL, FALSE, FALSE);
     }
   else /* Try not to redefine a section, especially B_1.  */
     {
@@ -506,7 +506,7 @@  parse_rx_section (char * name)
 	| ((flags & SEC_STRINGS) ? SHF_STRINGS : 0)
 	| ((flags & SEC_THREAD_LOCAL) ? SHF_TLS : 0);
 
-      obj_elf_change_section (name, type, 0, attr, 0, NULL, FALSE, FALSE);
+      obj_elf_change_section (name, type, attr, 0, NULL, FALSE, FALSE);
     }
 
   bfd_set_section_alignment (now_seg, align);
diff --git a/gas/config/tc-tic6x.c b/gas/config/tc-tic6x.c
index 9a506ed1e55..9f5b24e648d 100644
--- a/gas/config/tc-tic6x.c
+++ b/gas/config/tc-tic6x.c
@@ -4606,7 +4606,7 @@  tic6x_start_unwind_section (const segT text_seg, int idx)
   const char * text_name;
   const char * prefix;
   const char * prefix_once;
-  const char * group_name;
+  struct elf_section_match match;
   size_t prefix_len;
   size_t text_len;
   char * sec_name;
@@ -4649,13 +4649,13 @@  tic6x_start_unwind_section (const segT text_seg, int idx)
 
   flags = SHF_ALLOC;
   linkonce = 0;
-  group_name = 0;
+  memset (&match, 0, sizeof (match));
 
   /* Handle COMDAT group.  */
   if (prefix != prefix_once && (text_seg->flags & SEC_LINK_ONCE) != 0)
     {
-      group_name = elf_group_name (text_seg);
-      if (group_name == NULL)
+      match.group_name = elf_group_name (text_seg);
+      if (match.group_name == NULL)
 	{
 	  as_bad (_("group section `%s' has no group signature"),
 		  segment_name (text_seg));
@@ -4666,7 +4666,7 @@  tic6x_start_unwind_section (const segT text_seg, int idx)
       linkonce = 1;
     }
 
-  obj_elf_change_section (sec_name, type, 0, flags, 0, group_name,
+  obj_elf_change_section (sec_name, type, flags, 0, &match,
 			  linkonce, 0);
 
   /* Set the section link for index tables.  */
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index fa155096159..c86c38c1f03 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -6693,6 +6693,11 @@  particular section belongs.  The optional linkage field can contain:
 indicates that only one copy of this section should be retained
 @item .gnu.linkonce
 an alias for comdat
+
+@item unique,@code{<number>}
+assigns @code{<number>} as a unique section ID to distinguish different
+sections with the same section name.  The valid values of @code{<number>}
+are between 0 and 4294967295.
 @end table
 
 Note: if both the @var{M} and @var{G} flags are present then the fields for
diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp
index 5a298fe5832..08c6830e0db 100644
--- a/gas/testsuite/gas/elf/elf.exp
+++ b/gas/testsuite/gas/elf/elf.exp
@@ -245,6 +245,10 @@  if { [is_elf_format] } then {
     run_dump_test "section12b"
     run_dump_test "section13"
     run_dump_test "section14"
+    run_dump_test "section15"
+    run_dump_test "section16a"
+    run_dump_test "section16b"
+    run_dump_test "section17"
     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/section15.d b/gas/testsuite/gas/elf/section15.d
new file mode 100644
index 00000000000..a7cda1394f4
--- /dev/null
+++ b/gas/testsuite/gas/elf/section15.d
@@ -0,0 +1,24 @@ 
+#objdump: -s
+#name: elf section15
+# .pushsection always creates the named section, but the
+# test harness translates ".text" into "P" for the RX...
+#notarget: rx-*
+
+.*: +file format .*
+
+# The MIPS includes a 'section .reginfo' and such here.
+#...
+Contents of section .bar:
+ 0000 00000000 00000000 0000 .*
+Contents of section .bar:
+ 0000 0102 .*
+Contents of section .bar:
+ 0000 0102 .*
+Contents of section .bar:
+ 0000 0103 .*
+Contents of section .bar:
+ 0000 04 .*
+Contents of section .text:
+ 0000 feff .*
+# Arm includes a .ARM.attributes section here
+#...
diff --git a/gas/testsuite/gas/elf/section15.s b/gas/testsuite/gas/elf/section15.s
new file mode 100644
index 00000000000..949cd84f89d
--- /dev/null
+++ b/gas/testsuite/gas/elf/section15.s
@@ -0,0 +1,38 @@ 
+	.section .bar,"a",unique,0
+	.byte 0
+ .pushsection .bar,2,"a",unique,1
+	.byte 2
+ .popsection
+	.byte 0
+ .pushsection .bar,3,"a",unique,2
+	.byte 2
+ .popsection
+	.byte 0
+ .pushsection .bar,2,"a", %progbits,unique,3
+	.byte 3
+ .popsection
+	.byte 0
+ .pushsection .bar,"",unique,4
+	.byte 4
+ .popsection
+	.byte 0
+ .pushsection .text,1,"axG",%progbits,foo,comdat,unique,0xffffffff
+	.byte -1
+ .popsection
+	.byte 0
+ .pushsection .text,"axG",%progbits,foo,comdat,unique,0xffffffff
+	.byte -2
+ .popsection
+	.byte 0
+ .pushsection .bar,"a",unique,1
+	.byte 1
+ .popsection
+	.byte 0
+ .pushsection .bar,"a", %progbits,unique,3
+	.byte 1
+ .popsection
+	.byte 0
+ .pushsection .bar,"a",unique,2
+	.byte 1
+ .popsection
+	.byte 0
diff --git a/gas/testsuite/gas/elf/section16.s b/gas/testsuite/gas/elf/section16.s
new file mode 100644
index 00000000000..77e20a368f7
--- /dev/null
+++ b/gas/testsuite/gas/elf/section16.s
@@ -0,0 +1,33 @@ 
+	.section .mbind.data,"adw",%progbits,unique,0
+	.byte 1
+
+	.section .mbind.data,"adw",%progbits,0x3,unique,1
+	.byte 2
+
+	.section .mbind.text,"adx",%progbits,unique,2
+	.byte 3
+
+	.section .mbind.text,"adx",%progbits,0x3,unique,3
+	.byte 4
+
+	.section .mbind.bss,"adw",%nobits,unique,4
+	.zero 5
+
+	.section .mbind.bss,"adw",%nobits,0x3,unique,5
+	.zero 6
+
+	.section .mbind.rodata,"adG",%progbits,.foo_group,comdat,0x2,unique,6
+	.byte 7
+
+	.section .mbind.data,"adGw",%progbits,.foo_group,comdat,unique,7
+	.byte 8
+
+	.section .mbind.data,"adGw",%progbits,.foo_group,comdat,0x3,unique,8
+	.byte 9
+
+	# Check that .pushsection works as well.
+	.pushsection .mbind.text,"adGx",%progbits,.foo_group,comdat,0x3,unique,9
+	.byte 10
+
+	.popsection
+	.byte 11
diff --git a/gas/testsuite/gas/elf/section16a.d b/gas/testsuite/gas/elf/section16a.d
new file mode 100644
index 00000000000..d1abf570c61
--- /dev/null
+++ b/gas/testsuite/gas/elf/section16a.d
@@ -0,0 +1,36 @@ 
+#source: section16.s
+#as: --no-pad-sections
+#readelf: -Sg --wide
+#name: mbind sections
+# A number of targets do not support SHF_GNU_MBIND
+#xfail: arm*-*-netbsdelf* arm*-*-nto* msp430-*-* visium-*-*
+#xfail: *-*-hpux* *-*-cloudabi
+
+#...
+  \[[ 0-9]+\] \.mbind\.data[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 WAD  0   0  1
+#...
+  \[[ 0-9]+\] \.mbind\.data[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 WAD  0   3  1
+#...
+  \[[ 0-9]+\] \.mbind\.text[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 AXD  0   0  .
+#...
+  \[[ 0-9]+\] \.mbind\.text[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 AXD  0   3  .
+#...
+  \[[ 0-9]+\] \.mbind\.bss[ 	]+NOBITS[ 	]+0+0 0+[0-9a-f]+ 0+5 00 WAD  0   0  1
+#...
+  \[[ 0-9]+\] \.mbind\.bss[ 	]+NOBITS[ 	]+0+0 0+[0-9a-f]+ 0+6 00 WAD  0   3  1
+#...
+  \[[ 0-9]+\] \.mbind\.rodata[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 AGD  0   2  1
+#...
+  \[[ 0-9]+\] \.mbind\.data[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 WAGD  0   0  1
+#...
+  \[[ 0-9]+\] \.mbind\.data[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+2 00 WAGD  0   3  1
+#...
+  \[[ 0-9]+\] \.mbind\.text[ 	]+PROGBITS[ 	]+0+0 0+[0-9a-f]+ 0+1 00 AXGD  0   3  1
+#...
+COMDAT group section \[    1\] `\.group' \[\.foo_group\] contains . sections:
+[ 	]+\[Index\][ 	]+Name
+[ 	]+\[[ 0-9]+][ 	]+\.mbind\.rodata
+[ 	]+\[[ 0-9]+][ 	]+\.mbind\.data
+[ 	]+\[[ 0-9]+][ 	]+\.mbind\.data
+[ 	]+\[[ 0-9]+][ 	]+\.mbind\.text
+#pass
diff --git a/gas/testsuite/gas/elf/section16b.d b/gas/testsuite/gas/elf/section16b.d
new file mode 100644
index 00000000000..a146c0de7e1
--- /dev/null
+++ b/gas/testsuite/gas/elf/section16b.d
@@ -0,0 +1,36 @@ 
+#source: section16.s
+#as: --no-pad-sections
+#objdump: -s
+#name: mbind section contents
+# RX annoyingly reorders the sections so that they do not match the sequence
+# expected below.
+#xfail: rx-*-*
+# A number of targets do not support SHF_GNU_MBIND
+#xfail: arm*-*-netbsdelf* arm*-*-nto* msp430-*-* visium-*-*
+#xfail: *-*-hpux* *-*-cloudabi
+
+#...
+Contents of section .mbind.data:
+ 0000 01                                   .               
+#...
+Contents of section .mbind.data:
+ 0000 02                                   .               
+#...
+Contents of section .mbind.text:
+ 0000 03                                   .               
+#...
+Contents of section .mbind.text:
+ 0000 04                                   .               
+#...
+Contents of section .mbind.rodata:
+ 0000 07                                   .               
+#...
+Contents of section .mbind.data:
+ 0000 08                                   .               
+#...
+Contents of section .mbind.data:
+ 0000 090b                                 ..              
+#...
+Contents of section .mbind.text:
+ 0000 0a                                   .               
+#pass
diff --git a/gas/testsuite/gas/elf/section17.d b/gas/testsuite/gas/elf/section17.d
new file mode 100644
index 00000000000..3efdc22abfe
--- /dev/null
+++ b/gas/testsuite/gas/elf/section17.d
@@ -0,0 +1,2 @@ 
+#name: incorrect section ID
+#error_output: section17.l
diff --git a/gas/testsuite/gas/elf/section17.l b/gas/testsuite/gas/elf/section17.l
new file mode 100644
index 00000000000..5b3e2b6d340
--- /dev/null
+++ b/gas/testsuite/gas/elf/section17.l
@@ -0,0 +1,4 @@ 
+[^:]*: Assembler messages:
+[^:]*:1: Error: unsupported section id: 0x100000000
+[^:]*:3: Error: junk at end of line, first unrecognized character is `f'
+[^:]*:5: Error: junk at end of line, first unrecognized character is `,'
diff --git a/gas/testsuite/gas/elf/section17.s b/gas/testsuite/gas/elf/section17.s
new file mode 100644
index 00000000000..43de1d7f2b6
--- /dev/null
+++ b/gas/testsuite/gas/elf/section17.s
@@ -0,0 +1,6 @@ 
+	.section .data,"aw",%progbits,unique,0x100000000
+	.byte 0
+	.section .bss,"aw",%nobits,unique,foo
+	.byte 0
+	.section .text,"ax",%progbits,unique,1,foo
+	.byte 0
diff --git a/gas/testsuite/gas/i386/i386.exp b/gas/testsuite/gas/i386/i386.exp
index feab8c2be95..ffeff0c101a 100644
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -491,6 +491,7 @@  if [expr ([istarget "i*86-*-*"] ||  [istarget "x86_64-*-*"]) && [gas_32_check]]
     run_list_test "inval-pseudo" "-al"
     run_dump_test "nop-1"
     run_dump_test "nop-2"
+    run_dump_test "unique"
     run_dump_test "optimize-1"
     run_dump_test "optimize-1a"
     run_dump_test "optimize-2"
@@ -1050,6 +1051,7 @@  if [expr ([istarget "i*86-*-*"] || [istarget "x86_64-*-*"]) && [gas_64_check]] t
     run_dump_test "x86-64-movd-intel"
     run_dump_test "x86-64-nop-1"
     run_dump_test "x86-64-nop-2"
+    run_dump_test "x86-64-unique"
     run_dump_test "x86-64-movsxd"
     run_dump_test "x86-64-movsxd-intel"
     run_list_test "x86-64-movsxd-inval" "-al"
diff --git a/gas/testsuite/gas/i386/unique.d b/gas/testsuite/gas/i386/unique.d
new file mode 100644
index 00000000000..0337733d992
--- /dev/null
+++ b/gas/testsuite/gas/i386/unique.d
@@ -0,0 +1,48 @@ 
+#objdump: -dw
+#name: i386 unique sections
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+ <foo>:
+ +[a-f0-9]+:	89 c3                	mov    %eax,%ebx
+ +[a-f0-9]+:	c3                   	ret    
+
+Disassembly of section .text:
+
+0+ <bar>:
+ +[a-f0-9]+:	31 c3                	xor    %eax,%ebx
+ +[a-f0-9]+:	c3                   	ret    
+
+Disassembly of section .text:
+
+0+ <foo1>:
+ +[a-f0-9]+:	89 c3                	mov    %eax,%ebx
+ +[a-f0-9]+:	c3                   	ret    
+
+Disassembly of section .text:
+
+0+ <bar1>:
+ +[a-f0-9]+:	01 c3                	add    %eax,%ebx
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	c3                   	ret    
+
+Disassembly of section .text:
+
+0+ <bar2>:
+ +[a-f0-9]+:	29 c3                	sub    %eax,%ebx
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	c3                   	ret    
+
+Disassembly of section .text:
+
+0+ <foo2>:
+ +[a-f0-9]+:	31 c3                	xor    %eax,%ebx
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	c3                   	ret    
+#pass
diff --git a/gas/testsuite/gas/i386/unique.s b/gas/testsuite/gas/i386/unique.s
new file mode 100644
index 00000000000..89b23b0fc66
--- /dev/null
+++ b/gas/testsuite/gas/i386/unique.s
@@ -0,0 +1,36 @@ 
+	.section .text,"ax",@progbits,unique,1
+foo:
+	mov %eax, %ebx
+	.section .text,"ax",@progbits,unique,2
+bar:
+	xor %eax, %ebx
+	.section .text,"ax",@progbits,unique,1
+	ret
+	.section .text,"ax",@progbits,unique,2
+	ret
+	.section .text,"axG",@progbits,foo,comdat,unique,1
+foo1:
+	mov	%eax, %ebx
+	.section .text,"axG",@progbits,bar,comdat,unique,1
+bar1:
+	add	%eax, %ebx
+	.section .text,"axG",@progbits,bar,comdat,unique,2
+bar2:
+	sub	%eax, %ebx
+	.section .text,"axG",@progbits,foo,comdat,unique,2
+foo2:
+	xor	%eax, %ebx
+	.section .text,"axG",@progbits,bar,comdat,unique,1
+	nop
+	ret
+	.section .text,"axG",@progbits,foo,comdat,unique,1
+	ret
+	.section .text,"axG",@progbits,bar,comdat,unique,2
+	nop
+	nop
+	nop
+	ret
+	.section .text,"axG",@progbits,foo,comdat,unique,2
+	nop
+	nop
+	ret
diff --git a/gas/testsuite/gas/i386/x86-64-unique.d b/gas/testsuite/gas/i386/x86-64-unique.d
new file mode 100644
index 00000000000..4cfd30d5e9d
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-unique.d
@@ -0,0 +1,48 @@ 
+#source: unique.s
+#objdump: -dw
+#name: 64bit unique sections
+
+.*: +file format .*
+
+Disassembly of section .text:
+
+0+ <foo>:
+ +[a-f0-9]+:	89 c3                	mov    %eax,%ebx
+ +[a-f0-9]+:	c3                   	retq   
+
+Disassembly of section .text:
+
+0+ <bar>:
+ +[a-f0-9]+:	31 c3                	xor    %eax,%ebx
+ +[a-f0-9]+:	c3                   	retq   
+
+Disassembly of section .text:
+
+0+ <foo1>:
+ +[a-f0-9]+:	89 c3                	mov    %eax,%ebx
+ +[a-f0-9]+:	c3                   	retq   
+
+Disassembly of section .text:
+
+0+ <bar1>:
+ +[a-f0-9]+:	01 c3                	add    %eax,%ebx
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	c3                   	retq   
+
+Disassembly of section .text:
+
+0+ <bar2>:
+ +[a-f0-9]+:	29 c3                	sub    %eax,%ebx
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	c3                   	retq   
+
+Disassembly of section .text:
+
+0+ <foo2>:
+ +[a-f0-9]+:	31 c3                	xor    %eax,%ebx
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	90                   	nop
+ +[a-f0-9]+:	c3                   	retq   
+#pass
diff --git a/ld/testsuite/ld-i386/pr22001-1c.S b/ld/testsuite/ld-i386/pr22001-1c.S
index 2c1041dba77..51094efce35 100644
--- a/ld/testsuite/ld-i386/pr22001-1c.S
+++ b/ld/testsuite/ld-i386/pr22001-1c.S
@@ -1,7 +1,7 @@ 
 	.section	.rodata.str1.1,"aMS",@progbits,1
 .LC0:
 	.string	"PASS"
-	.section	.text.startup,"ax",@progbits
+	.section	.text,"ax",@progbits,unique,1
 	.p2align 4,,15
 	.globl	main
 	.type	main, @function
@@ -41,7 +41,7 @@  main:
 	addl	$16, %esp
 	jmp	.L3
 	.size	main, .-main
-	.section	.text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
+	.section .text,"axG",@progbits,__x86.get_pc_thunk.bx,comdat,unique,2
 	.globl	__x86.get_pc_thunk.bx
 	.hidden	__x86.get_pc_thunk.bx
 	.type	__x86.get_pc_thunk.bx, @function
diff --git a/ld/testsuite/ld-i386/tls-gd1.S b/ld/testsuite/ld-i386/tls-gd1.S
index 3b16eab6aa2..cf5c913560c 100644
--- a/ld/testsuite/ld-i386/tls-gd1.S
+++ b/ld/testsuite/ld-i386/tls-gd1.S
@@ -47,15 +47,14 @@  test_gd:
 	movzbl	%al, %eax
 	ret
 	.size	test_gd, .-test_gd
-	.section	.text.unlikely
-	.section	.text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
+	.section .text,"axG",@progbits,__x86.get_pc_thunk.bx,comdat,unique,1
 	.globl	__x86.get_pc_thunk.bx
 	.hidden	__x86.get_pc_thunk.bx
 	.type	__x86.get_pc_thunk.bx, @function
 __x86.get_pc_thunk.bx:
 	movl	(%esp), %ebx
 	ret
-	.section	.text.__x86.get_pc_thunk.cx,"axG",@progbits,__x86.get_pc_thunk.cx,comdat
+	.section .text,"axG",@progbits,__x86.get_pc_thunk.cx,comdat,unique,2
 	.globl	__x86.get_pc_thunk.cx
 	.hidden	__x86.get_pc_thunk.cx
 	.type	__x86.get_pc_thunk.cx, @function
diff --git a/ld/testsuite/ld-x86-64/pr21481b.S b/ld/testsuite/ld-x86-64/pr21481b.S
index 583ec77d121..d66c86f2286 100644
--- a/ld/testsuite/ld-x86-64/pr21481b.S
+++ b/ld/testsuite/ld-x86-64/pr21481b.S
@@ -1,4 +1,4 @@ 
-	.section	.rodata.str1.1,"aMS",@progbits,1
+	.section .rodata.foo,"aMS",@progbits,1,unique,1
 .LC0:
 	.string	"PASS"
 	.text
@@ -40,7 +40,7 @@  call_func1:
 	jmp	*func1@GOTPCREL(%rip)
 	.size	call_func1, .-call_func1
 	.globl	func1_p
-	.section	.rodata,"a",@progbits
+	.section .rodata.foo,"a",@progbits,unique,2
 	.align 8
 	.size	func1_p, 8
 	.type	func1_p, @object