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.
+2020-02-02  H.J. Lu  <hongjiu.lu@intel.com>
+
+       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.
+
 2020-02-01  Nick Clifton  <nickc@redhat.com>
 
        * config.bfd: Move the c30-aout and tic30-aout targets onto the
 
   /* 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;
 
      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.  */
 }
 
 #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,                        \
 
 /* 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,                     */
 
 .  {* 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;
 .
 .     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.  *}
 .}
 .
 .#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,                       \
 
+2020-02-02  H.J. Lu  <hongjiu.lu@intel.com>
+
+       PR gas/25380
+       * objdump.c (sym_ok): Return FALSE if 2 sections are in the
+       same file with different section pointers.
+
 2020-02-01  Nick Clifton  <nickc@redhat.com>
 
        * README-how-to-make-a-release: Update with more details on the
 
 {
   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
 
+2020-02-02  H.J. Lu  <hongjiu.lu@intel.com>
+
+       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.
+
 2020-02-02  H.J. Lu  <hongjiu.lu@intel.com>
 
        * testsuite/gas/elf/section13.s: Replace @nobits with %nobits.
 
 
 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
 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)
 {
   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 ();
   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;
 #endif
          else
            {
-             if (group_name == NULL)
+             if (match_p->group_name == NULL)
                as_warn (_("setting incorrect section attributes for %s"),
                         name);
              override = TRUE;
        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);
 void
 obj_elf_section (int push)
 {
-  const char *name, *group_name;
+  const char *name;
   char *beg;
   int type, dummy;
   bfd_vma attr;
   int entsize;
   int linkonce;
   subsegT new_subsection = -1;
-  unsigned int info = 0;
+  struct elf_section_match match;
 
   if (flag_mri)
     {
   if (name == NULL)
     return;
 
+  memset (&match, 0, sizeof (match));
+
   symbolS * sym;
   if ((sym = symbol_find (name)) != NULL
       && ! symbol_section_p (sym)
   type = SHT_NULL;
   attr = 0;
   gnu_attr = 0;
-  group_name = NULL;
   entsize = 0;
   linkonce = 0;
 
          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 == ',')
                {
              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
 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)
     {
 
 #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
 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);
 
   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;
 
   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));
       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.  */
 
   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);
 
 
 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);
 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);
   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);
   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
 {
 #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);
 
   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)
 
   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);
 
       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.  */
     {
        | ((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);
 
   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;
 
   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));
       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.  */
 
 @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
 
     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
 
--- /dev/null
+#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
+#...
 
--- /dev/null
+       .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
 
--- /dev/null
+       .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
 
--- /dev/null
+#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
 
--- /dev/null
+#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
 
--- /dev/null
+#name: incorrect section ID
+#error_output: section17.l
 
--- /dev/null
+[^:]*: 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 `,'
 
--- /dev/null
+       .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
 
     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"
     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"
 
--- /dev/null
+#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
 
--- /dev/null
+       .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
 
--- /dev/null
+#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
 
+2020-02-02  H.J. Lu  <hongjiu.lu@intel.com>
+
+       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.
+
 2020-01-30  Jan Beulich  <jbeulich@suse.com>
 
        * ld.texi: Remove space between @option and brace.
 
        .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
        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
 
        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
 
-       .section        .rodata.str1.1,"aMS",@progbits,1
+       .section .rodata.foo,"aMS",@progbits,1,unique,1
 .LC0:
        .string "PASS"
        .text
        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