V3 [PATCH] gas: Extend .symver directive

Message ID CAMe9rOqwWb+0tDLz+wwkHf=TOzaog50ByiU1JX0S4AONsFhxGA@mail.gmail.com
State New
Headers show
Series
  • V3 [PATCH] gas: Extend .symver directive
Related show

Commit Message

Peter Bergner via Binutils April 8, 2020, 1:21 p.m.
On Wed, Apr 8, 2020 at 5:53 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>

> On Tue, Apr 7, 2020 at 4:58 PM Fangrui Song <i@maskray.me> wrote:

> >

> > On 2020-04-07, H.J. Lu wrote:

> > >On Tue, Apr 7, 2020 at 2:23 PM Fangrui Song <i@maskray.me> wrote:

> > >>

> > >> On 2020-04-07, H.J. Lu via Binutils wrote:

> > >> >On Tue, Apr 7, 2020 at 5:55 AM Andreas Schwab <schwab@linux-m68k.org> wrote:

> > >> >>

> > >> >> On Apr 07 2020, H.J. Lu wrote:

> > >> >>

> > >> >> > The optional argument VISIBILITY updates the visibility of

> > >> >> > the original symbol.  The valid visibilities are 'local', 'hidden', and

> > >> >> > 'remove'.  The 'local' visibility makes the original symbol a local

> > >> >> > symbol (*note Local::).  The 'hidden' visibility sets the visibility of

> > >> >> > the original symbol to 'hidden' (*note Hidden::).  The 'remove'

> > >> >> > visibility removes the original symbol from the symbol table.  If

> > >> >> > visibility isn't specified, the original symbol is unchanged.

> > >> >>

> > >> >> Looks good.

> > >> >

> > >> >Here is the updated patch.

> > >> >

> > >> >--

> > >> >H.J.

> > >>

> > >> Let me summarize the current status:

> > >>

> > >> @@@ has the renaming semantics. (invented in 2000)

> > >> @ and @@ has the copying semantics. The original symbol remains which is usually cumbersome.

> > >>

> > >> We have received some requests:

> > >>

> > >> * Have a way to not retain the original symbol

> > >> * Have a way to define multiple versions `.symver something,foo@v1; .symver something,foo@v2. symver something,foo@@v3`

> > >>

> > >>

> > >> We have many choices.

> > >>

> > >> A. make @ @@ similar to @@@

> > >>    For @@, because of the linking rule (a @@ definition can satisfy a

> > >>    referenced without a version), there should be no difference.

> > >>

> > >>    For @, this will be a semantic break. Personally I don't think this

> > >>    matters. I believe 99% projects don't need the original symbol and

> > >>    will not notice anything. I also checked with FreeBSD developers.

> > >

> > >The original symbol name is used in glibc to bypass PLT within

> > >libc.so.

> >

> > This does not seem correct. By convention, the hidden aliases are those prefixed with __

> > They are called to bypass PLT (STV_HIDDEN implies the non-preemptible property).

> > The original symbol does not have the prefix.

> >

> > When a linker sees memcpy@@GLIBC_2.14 , basically it inserts both "memcpy" and

> > "memcpy@GLIBC_2.14" into the symbol table.  Both a reference without a version

> > "memcpy" and a reference with a version "memcpy@GLIBC_2.14" can be satisfied.

>

> As I said before, the original purpose of .symver is for glibc to

> implement symbol

> versioning.  Given:

>

> ---

> const int _sys_nerr_internal = 200;

> __asm__ (".symver _sys_nerr_internal, sys_nerr@@GLIBC_2.12");

> ---

>

> _sys_nerr_internal is used internally in glibc:

>

> File: libc_pic.a(_strerror.os)

>

> Relocation section '.rela.text' at offset 0x14e0 contains 17 entries:

>     Offset             Info             Type               Symbol's

> Value  Symbol's Name + Addend

> 000000000000001c  0000001500000002 R_X86_64_PC32

> 0000000000000000 _sys_nerr_internal - 4

> 000000000000002c  0000001600000002 R_X86_64_PC32

> 0000000000000000 _sys_errlist_internal - 4

>

> ---

> char *

> __strerror_r (int errnum, char *buf, size_t buflen)

> {

>   ...

>   return (char *) _(_sys_errlist_internal[errnum]);

> }

> ---

>

> Also with -g, _sys_nerr_internal is also referenced in debug info.  We

> just can't change .symver to rename.

>

> > If the definition of "memcpy" also exists, I think it is somewhat special cased

> > in GNU ld and/or gold. In GNU ld, the actual implementation may be more complex

> > with indirect symbol involved. I believe the whole thing can be simplified a

> > lot by using renaming semantic. I debugged this area last year and may

> > misremember something.

>

> It is OK to extend .symver directive.  Change it to rename semantic isn't an

> option.

>


Updated patch to call symbol_remove to remove the symbol
if it isn't used in relocation.

-- 
H.J.

Comments

Fangrui Song April 9, 2020, 5:16 p.m. | #1
On 2020-04-08, H.J. Lu wrote:
>On Wed, Apr 8, 2020 at 5:53 AM H.J. Lu <hjl.tools@gmail.com> wrote:

>>

>> On Tue, Apr 7, 2020 at 4:58 PM Fangrui Song <i@maskray.me> wrote:

>> >

>> > On 2020-04-07, H.J. Lu wrote:

>> > >On Tue, Apr 7, 2020 at 2:23 PM Fangrui Song <i@maskray.me> wrote:

>> > >>

>> > >> On 2020-04-07, H.J. Lu via Binutils wrote:

>> > >> >On Tue, Apr 7, 2020 at 5:55 AM Andreas Schwab <schwab@linux-m68k.org> wrote:

>> > >> >>

>> > >> >> On Apr 07 2020, H.J. Lu wrote:

>> > >> >>

>> > >> >> > The optional argument VISIBILITY updates the visibility of

>> > >> >> > the original symbol.  The valid visibilities are 'local', 'hidden', and

>> > >> >> > 'remove'.  The 'local' visibility makes the original symbol a local

>> > >> >> > symbol (*note Local::).  The 'hidden' visibility sets the visibility of

>> > >> >> > the original symbol to 'hidden' (*note Hidden::).  The 'remove'

>> > >> >> > visibility removes the original symbol from the symbol table.  If

>> > >> >> > visibility isn't specified, the original symbol is unchanged.

>> > >> >>

>> > >> >> Looks good.

>> > >> >

>> > >> >Here is the updated patch.

>> > >> >

>> > >> >--

>> > >> >H.J.

>> > >>

>> > >> Let me summarize the current status:

>> > >>

>> > >> @@@ has the renaming semantics. (invented in 2000)

>> > >> @ and @@ has the copying semantics. The original symbol remains which is usually cumbersome.

>> > >>

>> > >> We have received some requests:

>> > >>

>> > >> * Have a way to not retain the original symbol

>> > >> * Have a way to define multiple versions `.symver something,foo@v1; .symver something,foo@v2. symver something,foo@@v3`

>> > >>

>> > >>

>> > >> We have many choices.

>> > >>

>> > >> A. make @ @@ similar to @@@

>> > >>    For @@, because of the linking rule (a @@ definition can satisfy a

>> > >>    referenced without a version), there should be no difference.

>> > >>

>> > >>    For @, this will be a semantic break. Personally I don't think this

>> > >>    matters. I believe 99% projects don't need the original symbol and

>> > >>    will not notice anything. I also checked with FreeBSD developers.

>> > >

>> > >The original symbol name is used in glibc to bypass PLT within

>> > >libc.so.

>> >

>> > This does not seem correct. By convention, the hidden aliases are those prefixed with __

>> > They are called to bypass PLT (STV_HIDDEN implies the non-preemptible property).

>> > The original symbol does not have the prefix.

>> >

>> > When a linker sees memcpy@@GLIBC_2.14 , basically it inserts both "memcpy" and

>> > "memcpy@GLIBC_2.14" into the symbol table.  Both a reference without a version

>> > "memcpy" and a reference with a version "memcpy@GLIBC_2.14" can be satisfied.

>>

>> As I said before, the original purpose of .symver is for glibc to

>> implement symbol

>> versioning.  Given:

>>

>> ---

>> const int _sys_nerr_internal = 200;

>> __asm__ (".symver _sys_nerr_internal, sys_nerr@@GLIBC_2.12");

>> ---

>>

>> _sys_nerr_internal is used internally in glibc:

>>

>> File: libc_pic.a(_strerror.os)

>>

>> Relocation section '.rela.text' at offset 0x14e0 contains 17 entries:

>>     Offset             Info             Type               Symbol's

>> Value  Symbol's Name + Addend

>> 000000000000001c  0000001500000002 R_X86_64_PC32

>> 0000000000000000 _sys_nerr_internal - 4

>> 000000000000002c  0000001600000002 R_X86_64_PC32

>> 0000000000000000 _sys_errlist_internal - 4

>>

>> ---

>> char *

>> __strerror_r (int errnum, char *buf, size_t buflen)

>> {

>>   ...

>>   return (char *) _(_sys_errlist_internal[errnum]);

>> }

>> ---

>>

>> Also with -g, _sys_nerr_internal is also referenced in debug info.  We

>> just can't change .symver to rename.


OK.

>> > If the definition of "memcpy" also exists, I think it is somewhat special cased

>> > in GNU ld and/or gold. In GNU ld, the actual implementation may be more complex

>> > with indirect symbol involved. I believe the whole thing can be simplified a

>> > lot by using renaming semantic. I debugged this area last year and may

>> > misremember something.

>>

>> It is OK to extend .symver directive.  Change it to rename semantic isn't an

>> option.

>>

>

>Updated patch to call symbol_remove to remove the symbol

>if it isn't used in relocation.

>

>-- 

>H.J.


Is the second .symver required to be after the definition?

Case A

.symver foo,foo@@v2
.symver foo,foo@v1

.globl foo
foo:
   ret

% as-new a.s
a.s: Assembler messages:
a.s: Error: local label `"0" (instance number 0 of a dollar label)' is not defined

Case B

.globl foo
foo:
   ret

.symver foo,foo@@v2
.symver foo,foo@v1
.hidden foo

      5: 0000000000000000     0 NOTYPE  GLOBAL HIDDEN     1 foo
      6: 0000000000000000     0 NOTYPE  GLOBAL HIDDEN     1 foo@@v2
      7: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT    1 foo@v1

Patch

From 42e4d9ac4a880fb6ad829a2959b992df9b816349 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Mon, 6 Apr 2020 11:30:53 -0700
Subject: [PATCH] gas: Extend .symver directive

Extend .symver directive to update visibility of the original symbol and
assign one original symbol to different versioned symbols:

  .symver foo, foo@VERS_1, local    # Change foo to a local symbol.
  .symver foo, foo@VERS_2, hidden   # Change foo to a hidden symbol.
  .symver foo, foo@@VERS_3, remove  # Remove foo from symbol table.
  .symver foo, bar@V1               # Assign foo to bar@V1 and baz@V2.
  .symver foo, baz@V2

	PR gas/23840
	PR gas/25295
	* NEWS: Mention .symver extension.
	* config/obj-elf.c (obj_elf_copy_symbol): New function.
	(obj_elf_symver): Make a fake copy of the symbol and mark it to
	be removed if there is an existing versioned symbol.  Add local,
	hidden and remove visibility support.
	(elf_frob_symbol): Use obj_elf_copy_symbol.  Update the original
	symbol to local, hidden or remove it from the symbol table.
	* config/obj-elf.h (original_visibility): New.
	(elf_obj_sy): Change local to bitfield. Add visibility.
	* doc/as.texi: Update .symver directive.
	* testsuite/gas/symver/symver.exp: Run all *.d tests.
	* testsuite/gas/symver/symver6.d: New file.
	* testsuite/gas/symver/symver7.d: Likewise.
	* testsuite/gas/symver/symver7.s: Likewise.
	* testsuite/gas/symver/symver8.d: Likewise.
	* testsuite/gas/symver/symver8.s: Likewise.
	* testsuite/gas/symver/symver9.s: Likewise.
	* testsuite/gas/symver/symver9a.d: Likewise.
	* testsuite/gas/symver/symver9b.d: Likewise.
	* testsuite/gas/symver/symver10.s: Likewise.
	* testsuite/gas/symver/symver10a.d: Likewise.
	* testsuite/gas/symver/symver10b.d: Likewise.
	* testsuite/gas/symver/symver11.d: Likewise.
	* testsuite/gas/symver/symver11.s: Likewise.
	* testsuite/gas/symver/symver6.l: Removed.
	* testsuite/gas/symver/symver6.s: Updated.
---
 gas/NEWS                             |   3 +
 gas/config/obj-elf.c                 | 132 +++++++++++++++++++--------
 gas/config/obj-elf.h                 |  13 ++-
 gas/doc/as.texi                      |  16 +++-
 gas/testsuite/gas/symver/symver.exp  |  10 +-
 gas/testsuite/gas/symver/symver10.s  |   8 ++
 gas/testsuite/gas/symver/symver10a.d |   8 ++
 gas/testsuite/gas/symver/symver10b.d |   8 ++
 gas/testsuite/gas/symver/symver11.d  |   8 ++
 gas/testsuite/gas/symver/symver11.s  |   9 ++
 gas/testsuite/gas/symver/symver6.d   |  11 +++
 gas/testsuite/gas/symver/symver6.l   |   3 -
 gas/testsuite/gas/symver/symver6.s   |   4 +-
 gas/testsuite/gas/symver/symver7.d   |   8 ++
 gas/testsuite/gas/symver/symver7.s   |   8 ++
 gas/testsuite/gas/symver/symver8.d   |   9 ++
 gas/testsuite/gas/symver/symver8.s   |   8 ++
 gas/testsuite/gas/symver/symver9.s   |   8 ++
 gas/testsuite/gas/symver/symver9a.d  |   8 ++
 gas/testsuite/gas/symver/symver9b.d  |   8 ++
 20 files changed, 237 insertions(+), 53 deletions(-)
 create mode 100644 gas/testsuite/gas/symver/symver10.s
 create mode 100644 gas/testsuite/gas/symver/symver10a.d
 create mode 100644 gas/testsuite/gas/symver/symver10b.d
 create mode 100644 gas/testsuite/gas/symver/symver11.d
 create mode 100644 gas/testsuite/gas/symver/symver11.s
 create mode 100644 gas/testsuite/gas/symver/symver6.d
 delete mode 100644 gas/testsuite/gas/symver/symver6.l
 create mode 100644 gas/testsuite/gas/symver/symver7.d
 create mode 100644 gas/testsuite/gas/symver/symver7.s
 create mode 100644 gas/testsuite/gas/symver/symver8.d
 create mode 100644 gas/testsuite/gas/symver/symver8.s
 create mode 100644 gas/testsuite/gas/symver/symver9.s
 create mode 100644 gas/testsuite/gas/symver/symver9a.d
 create mode 100644 gas/testsuite/gas/symver/symver9b.d

diff --git a/gas/NEWS b/gas/NEWS
index 6748c179f1..58d79caa41 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -1,5 +1,8 @@ 
 -*- text -*-
 
+* Extend .symver directive to update visibility of the original symbol
+  and assign one original symbol to different versioned symbols.
+
 * Add support for Intel SERIALIZE and TSXLDTRK instructions.
 
 * Add -mlfence-after-load=, -mlfence-before-indirect-branch= and
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index a6dcdaf4a7..a53a97fda1 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -1515,6 +1515,35 @@  obj_elf_line (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+static symbolS *
+obj_elf_copy_symbol (const char *name2, symbolS *symp)
+{
+  symbolS *symp2 = symbol_find_or_make (name2);
+
+  S_SET_SEGMENT (symp2, S_GET_SEGMENT (symp));
+
+  /* Subtracting out the frag address here is a hack
+     because we are in the middle of the final loop.  */
+  S_SET_VALUE (symp2,
+	       (S_GET_VALUE (symp)
+		- symbol_get_frag (symp)->fr_address));
+
+  symbol_set_frag (symp2, symbol_get_frag (symp));
+
+  /* This will copy over the size information.  */
+  copy_symbol_attributes (symp2, symp);
+
+  S_SET_OTHER (symp2, S_GET_OTHER (symp));
+
+  if (S_IS_WEAK (symp))
+    S_SET_WEAK (symp2);
+
+  if (S_IS_EXTERNAL (symp))
+    S_SET_EXTERNAL (symp2);
+
+  return symp2;
+}
+
 /* This handles the .symver pseudo-op, which is used to specify a
    symbol version.  The syntax is ``.symver NAME,SYMVERNAME''.
    SYMVERNAME may contain ELF_VER_CHR ('@') characters.  This
@@ -1528,6 +1557,7 @@  obj_elf_symver (int ignore ATTRIBUTE_UNUSED)
   char c;
   char old_lexat;
   symbolS *sym;
+  symbolS *sym_orig;
 
   sym = get_sym_from_input_line_and_check ();
 
@@ -1555,12 +1585,20 @@  obj_elf_symver (int ignore ATTRIBUTE_UNUSED)
       return;
     }
 
+  sym_orig = sym;
+  if (symbol_get_obj (sym)->versioned_name
+      && strcmp (symbol_get_obj (sym)->versioned_name, name))
+    {
+      /* Make a fake copy of the symbol and mark it to be removed if
+	 there is an existing versioned symbol.  */
+      sym = obj_elf_copy_symbol (FAKE_LABEL_NAME, sym);
+      symbol_get_obj (sym)->visibility = visibility_remove;
+    }
+
   if (symbol_get_obj (sym)->versioned_name == NULL)
     {
       symbol_get_obj (sym)->versioned_name = xstrdup (name);
 
-      (void) restore_line_pointer (c);
-
       if (strchr (symbol_get_obj (sym)->versioned_name,
 		  ELF_VER_CHR) == NULL)
 	{
@@ -1571,18 +1609,32 @@  obj_elf_symver (int ignore ATTRIBUTE_UNUSED)
 	  return;
 	}
     }
-  else
+
+  (void) restore_line_pointer (c);
+
+  if (*input_line_pointer == ',')
     {
-      if (strcmp (symbol_get_obj (sym)->versioned_name, name))
+      char *save = input_line_pointer;
+
+      ++input_line_pointer;
+      SKIP_WHITESPACE ();
+      if (strncmp (input_line_pointer, "local", 5) == 0)
 	{
-	  as_bad (_("multiple versions [`%s'|`%s'] for symbol `%s'"),
-		  name, symbol_get_obj (sym)->versioned_name,
-		  S_GET_NAME (sym));
-	  ignore_rest_of_line ();
-	  return;
+	  input_line_pointer += 5;
+	  symbol_get_obj (sym_orig)->visibility = visibility_local;
 	}
-
-      (void) restore_line_pointer (c);
+      else if (strncmp (input_line_pointer, "hidden", 6) == 0)
+	{
+	  input_line_pointer += 6;
+	  symbol_get_obj (sym_orig)->visibility = visibility_hidden;
+	}
+      else if (strncmp (input_line_pointer, "remove", 6) == 0)
+	{
+	  input_line_pointer += 6;
+	  symbol_get_obj (sym_orig)->visibility = visibility_remove;
+	}
+      else
+	input_line_pointer = save;
     }
 
   demand_empty_rest_of_line ();
@@ -2453,18 +2505,9 @@  elf_frob_symbol (symbolS *symp, int *puntp)
 	    }
 	  else
 	    {
-	      symbolS *symp2;
+	      asymbol *bfdsym;
+	      elf_symbol_type *elfsym;
 
-	      /* FIXME: Creating a new symbol here is risky.  We're
-		 in the final loop over the symbol table.  We can
-		 get away with it only because the symbol goes to
-		 the end of the list, where the loop will still see
-		 it.  It would probably be better to do this in
-		 obj_frob_file_before_adjust.  */
-
-	      symp2 = symbol_find_or_make (sy_obj->versioned_name);
-
-	      /* Now we act as though we saw symp2 = sym.  */
 	      if (S_IS_COMMON (symp))
 		{
 		  as_bad (_("`%s' can't be versioned to common symbol '%s'"),
@@ -2473,26 +2516,35 @@  elf_frob_symbol (symbolS *symp, int *puntp)
 		  return;
 		}
 
-	      S_SET_SEGMENT (symp2, S_GET_SEGMENT (symp));
-
-	      /* Subtracting out the frag address here is a hack
-		 because we are in the middle of the final loop.  */
-	      S_SET_VALUE (symp2,
-			   (S_GET_VALUE (symp)
-			    - symbol_get_frag (symp)->fr_address));
-
-	      symbol_set_frag (symp2, symbol_get_frag (symp));
-
-	      /* This will copy over the size information.  */
-	      copy_symbol_attributes (symp2, symp);
-
-	      S_SET_OTHER (symp2, S_GET_OTHER (symp));
+	      /* FIXME: Creating a new symbol here is risky.  We're
+		 in the final loop over the symbol table.  We can
+		 get away with it only because the symbol goes to
+		 the end of the list, where the loop will still see
+		 it.  It would probably be better to do this in
+		 obj_frob_file_before_adjust.  */
 
-	      if (S_IS_WEAK (symp))
-		S_SET_WEAK (symp2);
+	      obj_elf_copy_symbol (sy_obj->versioned_name, symp);
 
-	      if (S_IS_EXTERNAL (symp))
-		S_SET_EXTERNAL (symp2);
+	      switch (symbol_get_obj (symp)->visibility)
+		{
+		case visibility_unchanged:
+		  break;
+		case visibility_hidden:
+		  bfdsym = symbol_get_bfdsym (symp);
+		  elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym),
+					    bfdsym);
+		  elfsym->internal_elf_sym.st_other &= ~3;
+		  elfsym->internal_elf_sym.st_other |= STV_HIDDEN;
+		  break;
+		case visibility_remove:
+		  /* Remove the symbol if it isn't used in relocation.  */
+		  if (!symbol_used_in_reloc_p (symp))
+		    symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+		  break;
+		case visibility_local:
+		  S_CLEAR_EXTERNAL (symp);
+		  break;
+		}
 	    }
 	}
     }
diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h
index 54af9ebc0e..b85dfbcffe 100644
--- a/gas/config/obj-elf.h
+++ b/gas/config/obj-elf.h
@@ -55,11 +55,22 @@  extern int mips_flag_mdebug;
 #endif
 #endif
 
+enum original_visibility
+{
+  visibility_unchanged = 0,
+  visibility_local,
+  visibility_hidden,
+  visibility_remove
+};
+
 /* Additional information we keep for each symbol.  */
 struct elf_obj_sy
 {
   /* Whether the symbol has been marked as local.  */
-  int local;
+  unsigned int local : 1;
+
+  /* Whether visibility of the original symbol should be changed.  */
+  ENUM_BITFIELD (original_visibility) visibility : 2;
 
   /* Use this to keep track of .size expressions that involve
      differences that we can't compute yet.  */
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 0a6727ef84..8669879c87 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -4509,7 +4509,7 @@  Some machine configurations provide additional directives.
 * Struct::			@code{.struct @var{expression}}
 @ifset ELF
 * SubSection::                  @code{.subsection}
-* Symver::                      @code{.symver @var{name},@var{name2@@nodename}}
+* Symver::                      @code{.symver @var{name},@var{name2@@nodename}[,@var{visibility}]}
 @end ifset
 
 @ifset COFF
@@ -7112,9 +7112,9 @@  shared library.
 
 For ELF targets, the @code{.symver} directive can be used like this:
 @smallexample
-.symver @var{name}, @var{name2@@nodename}
+.symver @var{name}, @var{name2@@nodename}[ ,@var{visibility}]
 @end smallexample
-If the symbol @var{name} is defined within the file
+If the original symbol @var{name} is defined within the file
 being assembled, the @code{.symver} directive effectively creates a symbol
 alias with the name @var{name2@@nodename}, and in fact the main reason that we
 just don't try and create a regular alias is that the @var{@@} character isn't
@@ -7127,7 +7127,15 @@  function is being mentioned.  The @var{nodename} portion of the alias should be
 the name of a node specified in the version script supplied to the linker when
 building a shared library.  If you are attempting to override a versioned
 symbol from a shared library, then @var{nodename} should correspond to the
-nodename of the symbol you are trying to override.
+nodename of the symbol you are trying to override.  The optional argument
+@var{visibility} updates the visibility of the original symbol.  The valid
+visibilities are @code{local}, @code {hidden}, and @code {remove}.  The
+@code{local} visibility makes the original symbol a local symbol
+(@pxref{Local}).  The @code{hidden} visibility sets the visibility of the
+original symbol to @code{hidden} (@pxref{Hidden}).  The @code{remove}
+visibility removes the original symbol from the symbol table if it isn't
+used in relocation.  If visibility isn't specified, the original symbol
+is unchanged.
 
 If the symbol @var{name} is not defined within the file being assembled, all
 references to @var{name} will be changed to @var{name2@@nodename}.  If no
diff --git a/gas/testsuite/gas/symver/symver.exp b/gas/testsuite/gas/symver/symver.exp
index de122eb61c..b33af960da 100644
--- a/gas/testsuite/gas/symver/symver.exp
+++ b/gas/testsuite/gas/symver/symver.exp
@@ -46,8 +46,13 @@  if { [is_elf_format] } then {
       return
     }
 
-    run_dump_test "symver0" 
-    run_dump_test "symver1" 
+    set test_list [lsort [glob -nocomplain $srcdir/$subdir/*.d]]
+    foreach t $test_list {
+	# We need to strip the ".d", but can leave the dirname.
+	verbose [file rootname $t]
+	run_dump_test [file rootname $t]
+    }
+
     run_error_test "symver2" ""
     run_error_test "symver3" ""
     # We have to comment out symver4 and symver5, which check the
@@ -56,5 +61,4 @@  if { [is_elf_format] } then {
     # version name.
 #    run_error_test "symver4" ""
 #    run_error_test "symver5" ""
-    run_error_test "symver6" ""
 }
diff --git a/gas/testsuite/gas/symver/symver10.s b/gas/testsuite/gas/symver/symver10.s
new file mode 100644
index 0000000000..967a692a73
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver10.s
@@ -0,0 +1,8 @@ 
+	.data
+	.globl foo
+	.type foo,%object
+foo:
+	.byte 0
+	.size foo,.-foo
+	.symver foo,foo@@version2,remove
+	.symver foo,foo@version1
diff --git a/gas/testsuite/gas/symver/symver10a.d b/gas/testsuite/gas/symver/symver10a.d
new file mode 100644
index 0000000000..e19ed2b0bf
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver10a.d
@@ -0,0 +1,8 @@ 
+#source: symver10.s
+#readelf: -sW
+#name: symver symver10a
+
+#...
+ +[0-9]+: +0+ +1 +OBJECT +GLOBAL +DEFAULT +[0-9]+ +foo@@version2
+ +[0-9]+: +0+ +1 +OBJECT +GLOBAL +DEFAULT +[0-9]+ +foo@version1
+#pass
diff --git a/gas/testsuite/gas/symver/symver10b.d b/gas/testsuite/gas/symver/symver10b.d
new file mode 100644
index 0000000000..17d0bfdd19
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver10b.d
@@ -0,0 +1,8 @@ 
+#source: symver10.s
+#readelf: -sW
+#name: symver symver10b
+
+#failif
+#...
+ +[0-9]+: +0+ +1 +OBJECT +GLOBAL +DEFAULT +[0-9]+ +foo
+#pass
diff --git a/gas/testsuite/gas/symver/symver11.d b/gas/testsuite/gas/symver/symver11.d
new file mode 100644
index 0000000000..0e3e7f14b7
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver11.d
@@ -0,0 +1,8 @@ 
+#readelf: -rsW
+#name: symver symver11
+
+#...
+[0-9a-f]+ +[0-9a-f]+ +R_.* +[0-9a-f]+ +foo *.*
+#...
+ +[0-9]+: +0+ +1 +OBJECT +GLOBAL +DEFAULT +[0-9]+ +foo
+#pass
diff --git a/gas/testsuite/gas/symver/symver11.s b/gas/testsuite/gas/symver/symver11.s
new file mode 100644
index 0000000000..547e8123f0
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver11.s
@@ -0,0 +1,9 @@ 
+	.data
+	.globl foo
+	.type foo,%object
+foo:
+	.byte 0
+	.size foo,.-foo
+	.symver foo,foo@@version2,remove
+	.symver foo,foo@version1
+	.dc.a foo
diff --git a/gas/testsuite/gas/symver/symver6.d b/gas/testsuite/gas/symver/symver6.d
new file mode 100644
index 0000000000..cddf7ec703
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver6.d
@@ -0,0 +1,11 @@ 
+#nm: -n
+#name: symver symver6
+#
+
+#...
+[ 	]+U foo
+#...
+0+00000.. D foo1
+0+0000000 D foo@@version1
+0+00000.. D foo@version1
+0+00000.. d L_foo1
diff --git a/gas/testsuite/gas/symver/symver6.l b/gas/testsuite/gas/symver/symver6.l
deleted file mode 100644
index c2d12ae060..0000000000
--- a/gas/testsuite/gas/symver/symver6.l
+++ /dev/null
@@ -1,3 +0,0 @@ 
-.*: Assembler messages:
-.*:7: Error: multiple versions \[`foo@version1'|`foo@@version1'\] for symbol `foo'
-#pass
diff --git a/gas/testsuite/gas/symver/symver6.s b/gas/testsuite/gas/symver/symver6.s
index 23d9fe20ee..b0bc0b8b22 100644
--- a/gas/testsuite/gas/symver/symver6.s
+++ b/gas/testsuite/gas/symver/symver6.s
@@ -3,7 +3,7 @@ 
 	.type foo1,object
 foo1:
 	.long foo
-	.symver foo,foo@@version1
-	.symver foo,foo@version1
+	.symver foo1,foo@@version1
+	.symver foo1,foo@version1
 L_foo1:
 	.size foo1,L_foo1-foo1
diff --git a/gas/testsuite/gas/symver/symver7.d b/gas/testsuite/gas/symver/symver7.d
new file mode 100644
index 0000000000..eb680083da
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver7.d
@@ -0,0 +1,8 @@ 
+#readelf: -sW
+#name: symver symver7
+
+#...
+ +[0-9]+: +0+ +1 +OBJECT +GLOBAL +HIDDEN +[0-9]+ +foo
+ +[0-9]+: +0+ +1 +OBJECT +GLOBAL +DEFAULT +[0-9]+ +foo@@version2
+ +[0-9]+: +0+ +1 +OBJECT +GLOBAL +DEFAULT +[0-9]+ +foo@version1
+#pass
diff --git a/gas/testsuite/gas/symver/symver7.s b/gas/testsuite/gas/symver/symver7.s
new file mode 100644
index 0000000000..20c11b7cc0
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver7.s
@@ -0,0 +1,8 @@ 
+	.data
+	.globl foo
+	.type foo,%object
+foo:
+	.byte 0
+	.size foo,.-foo
+	.symver foo,foo@@version2,local
+	.symver foo,foo@version1,hidden
diff --git a/gas/testsuite/gas/symver/symver8.d b/gas/testsuite/gas/symver/symver8.d
new file mode 100644
index 0000000000..3026c15383
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver8.d
@@ -0,0 +1,9 @@ 
+#readelf: -sW
+#name: symver symver8
+
+#...
+ +[0-9]+: +0+ +1 +OBJECT +LOCAL +DEFAULT +[0-9]+ +foo
+#...
+ +[0-9]+: +0+ +1 +OBJECT +GLOBAL +DEFAULT +[0-9]+ +foo@@version2
+ +[0-9]+: +0+ +1 +OBJECT +GLOBAL +DEFAULT +[0-9]+ +foo@version1
+#pass
diff --git a/gas/testsuite/gas/symver/symver8.s b/gas/testsuite/gas/symver/symver8.s
new file mode 100644
index 0000000000..17ab037040
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver8.s
@@ -0,0 +1,8 @@ 
+	.data
+	.globl foo
+	.type foo,%object
+foo:
+	.byte 0
+	.size foo,.-foo
+	.symver foo,foo@@version2,hidden
+	.symver foo,foo@version1,local
diff --git a/gas/testsuite/gas/symver/symver9.s b/gas/testsuite/gas/symver/symver9.s
new file mode 100644
index 0000000000..2f608972f5
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver9.s
@@ -0,0 +1,8 @@ 
+	.data
+	.globl foo
+	.type foo,%object
+foo:
+	.byte 0
+	.size foo,.-foo
+	.symver foo,foo@@version2
+	.symver foo,foo@version1,remove
diff --git a/gas/testsuite/gas/symver/symver9a.d b/gas/testsuite/gas/symver/symver9a.d
new file mode 100644
index 0000000000..62dda20bed
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver9a.d
@@ -0,0 +1,8 @@ 
+#source: symver9.s
+#readelf: -sW
+#name: symver symver9a
+
+#...
+ +[0-9]+: +0+ +1 +OBJECT +GLOBAL +DEFAULT +[0-9]+ +foo@@version2
+ +[0-9]+: +0+ +1 +OBJECT +GLOBAL +DEFAULT +[0-9]+ +foo@version1
+#pass
diff --git a/gas/testsuite/gas/symver/symver9b.d b/gas/testsuite/gas/symver/symver9b.d
new file mode 100644
index 0000000000..383d1bd080
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver9b.d
@@ -0,0 +1,8 @@ 
+#source: symver9.s
+#readelf: -sW
+#name: symver symver9b
+
+#failif
+#...
+ +[0-9]+: +0+ +1 +OBJECT +GLOBAL +DEFAULT +[0-9]+ +foo
+#pass
-- 
2.25.2