gas: Extend .symver directive

Message ID 20200407121048.265065-1-hjl.tools@gmail.com
State New
Headers show
Series
  • gas: Extend .symver directive
Related show

Commit Message

H.J. Lu via Binutils April 7, 2020, 12:10 p.m.
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 local copy of the symbol if there
	is an existing versioned symbol.  Add local and hidden 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/symver6.l: Removed.
	* testsuite/gas/symver/symver6.s: Updated.
---
 gas/NEWS                             |   3 +
 gas/config/obj-elf.c                 | 131 +++++++++++++++++++--------
 gas/config/obj-elf.h                 |  13 ++-
 gas/doc/as.texi                      |  15 ++-
 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/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   |   8 ++
 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 ++
 18 files changed, 217 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/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

-- 
2.25.1

Comments

Andreas Schwab April 7, 2020, 12:41 p.m. | #1
On Apr 07 2020, H.J. Lu via Binutils wrote:

> @@ -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,14 @@ 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 visibility of the original symbol.  The valid

                           the

> +visibilities are @code{local}, @code {hidden}, and @code {remove}.

> +@code{local} makes the original symbol a local symbol (@pxref{Local}).

> +@code{hidden} sets the visibility of the original symbol to

> +@code{hidden} (@pxref{Hidden}).  @code{remove} removes the original


A sentence should always start with a capitalized word, please reword.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510  2552 DF73 E780 A9DA AEC1
"And now for something completely different."
H.J. Lu via Binutils April 7, 2020, 12:49 p.m. | #2
On Tue, Apr 7, 2020 at 5:41 AM Andreas Schwab <schwab@linux-m68k.org> wrote:
>

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

>

> > @@ -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,14 @@ 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 visibility of the original symbol.  The valid

>                            the

>

> > +visibilities are @code{local}, @code {hidden}, and @code {remove}.

> > +@code{local} makes the original symbol a local symbol (@pxref{Local}).

> > +@code{hidden} sets the visibility of the original symbol to

> > +@code{hidden} (@pxref{Hidden}).  @code{remove} removes the original

>

> A sentence should always start with a capitalized word, please reword.


How about this?

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.

-- 
H.J.
Andreas Schwab April 7, 2020, 12:55 p.m. | #3
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.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510  2552 DF73 E780 A9DA AEC1
"And now for something completely different."
H.J. Lu via Binutils April 7, 2020, 12:57 p.m. | #4
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.
Fangrui Song April 7, 2020, 9:22 p.m. | #5
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.

   However, given the general conservative attitude of the binutils
   community, I think there might be some objection.

   Reusing the same stem name is very likely a mistake.
   
   .globl foo_v1
   .symver foo_v1,foo@v1
   foo_v1:
   
   A non-default version can be used to reject static archive linking
   while keep shared object compatibility. An undefined reference foo
   without a version cannot be satisfied by the definition.

   Keeping `foo` + .symver foo,foo@v1 will nullify the use case.

B. Add an optional argument to change binding/visibility or remove the
   original symbol (this patch).
   I am mostly worried that this makes the .symver semantics even harder
   to understand / explain in the documentation.

   There seems to make @@@ even more inconsistent with @/@@.

   BTW, have you checked .localentry (which can set the non-visibility
   bits of st_other)

   I ask for clarification from GCC developers why some special
   attributes (local/hidden) are needed, not other general attribute
   flags.
H.J. Lu via Binutils April 7, 2020, 11:15 p.m. | #6
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.

>    However, given the general conservative attitude of the binutils

>    community, I think there might be some objection.

>

>    Reusing the same stem name is very likely a mistake.

>

>    .globl foo_v1

>    .symver foo_v1,foo@v1

>    foo_v1:

>

>    A non-default version can be used to reject static archive linking

>    while keep shared object compatibility. An undefined reference foo

>    without a version cannot be satisfied by the definition.

>

>    Keeping `foo` + .symver foo,foo@v1 will nullify the use case.

>

> B. Add an optional argument to change binding/visibility or remove the

>    original symbol (this patch).

>    I am mostly worried that this makes the .symver semantics even harder

>    to understand / explain in the documentation.

>

>    There seems to make @@@ even more inconsistent with @/@@.

>

>    BTW, have you checked .localentry (which can set the non-visibility

>    bits of st_other)


That is a PPC specific directive.

>    I ask for clarification from GCC developers why some special

>    attributes (local/hidden) are needed, not other general attribute

>    flags.


.symver was designed to be used in assembly codes for glibc.
Only recently GCC is trying to use it.  That is one reason why
it is the way it is.

-- 
H.J.
Fangrui Song April 7, 2020, 11:58 p.m. | #7
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.

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.
H.J. Lu via Binutils April 8, 2020, 12:53 p.m. | #8
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.

-- 
H.J.

Patch

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..8b14c91b2d 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 local copy of the symbol if there is an existing
+	 versioned symbol.  */
+      sym = obj_elf_copy_symbol (FAKE_LABEL_NAME, sym);
+      symbol_get_obj (sym)->visibility = visibility_local;
+    }
+
   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;
-
-	      /* 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);
+	      asymbol *bfdsym;
+	      elf_symbol_type *elfsym;
 
-	      /* 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,34 @@  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:
+		  /* Change the remooved label to fake label.  */
+		  S_SET_NAME (symp, FAKE_LABEL_NAME);
+		  /* FALLTHROUGH.  */
+		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..77f99defbc 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,14 @@  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
+@var{visibility} updates visibility of the original symbol.  The valid
+visibilities are @code{local}, @code {hidden}, and @code {remove}.
+@code{local} makes the original symbol a local symbol (@pxref{Local}).
+@code{hidden} sets the visibility of the original symbol to
+@code{hidden} (@pxref{Hidden}).  @code{remove} removes the original
+symbol from the symbol table.  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/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..52c83a7aa3
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver8.d
@@ -0,0 +1,8 @@ 
+#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