diff mbox series

Fix symver attribute with LTO

Message ID 4e112e2d2afb5f579fac32caec3741f7e140e556.camel@mengyan1223.wang
State New
Headers show
Series Fix symver attribute with LTO | expand

Commit Message

Xi Ruoyao Dec. 17, 2019, 6:39 a.m. UTC
Hi,
with Jan's patch commited in r278878 we can use symver attribute for functions
and variables.  The symver attribute is designed for replacing toplevel asm
statements containing ".symver" which may be removed by LTO.  Unfortunately,
a quick test shown GCC still generates buggy so file with LTO and new symver
attribute.

Two issues:

1. The symver node in symtab is marked as PREVAILING_DEF_IRONLY (no EXP) and
   then removed by LTO.
2. The actual function body implementing the symver-ed function is also marked
   as PREVAILING_DEF_IRONLY and then removed or marked as local.  So no ".globl"
   directive is outputed for it.

Both issue cause symbols with symver missing in DSO (with LTO enabled).

I modified fuse-3.9.0 code to use new symver attribute and tried to build it
with GCC trunk and LTO.  The result is a buggy DSO.  With this patch applied,
fuse-3.9.0 can be built with LTO enabled and no problem.

I'll test symver patch and this patch with more packages.

Bootstrapped/regtested x86_64-linux.  I'm not a maintainer.

gcc/ChangeLog:

2019-12-17  Xi Ruoyao  <xry111@mengyan1223.wang>

	* cgraph.h (symtab_node::used_from_object_file_p): Symver nodes are
	part of DSO ABI so always used by non-LTO object files.
	* ipa-visibility.c (cgraph_externally_visible_p): Functions with symver
	attributes should always be visible.

Comments

Jan Hubicka Dec. 17, 2019, 8:32 a.m. UTC | #1
> Hi,
> with Jan's patch commited in r278878 we can use symver attribute for functions
> and variables.  The symver attribute is designed for replacing toplevel asm
> statements containing ".symver" which may be removed by LTO.  Unfortunately,
> a quick test shown GCC still generates buggy so file with LTO and new symver
> attribute.

Thanks for looking into this.  It was on my TODo list to actually
convert some packages, so it is great you did that.
> 
> Two issues:
> 
> 1. The symver node in symtab is marked as PREVAILING_DEF_IRONLY (no EXP) and
>    then removed by LTO.

This is however wrong - linker should not mark it as
PREVAILING_DEF_IRONLY if it is used externally.  What linker do you use?
On my testcases this was working with
 GNU ld (GNU Binutils) 2.31.51.20181222
I could easily imagine that some linkers get it wrong which should be
reported to bintuils bugzilla but it is also easy to work around as done
in your patch.

> 2. The actual function body implementing the symver-ed function is also marked
>    as PREVAILING_DEF_IRONLY and then removed or marked as local.  So no ".globl"
>    directive is outputed for it.

Here is the symver-ed function exported from the DSO (or is it set
to have hidden attribute)?
Again this was working for me, so it would be good to understand this
issue.

I was thinking to extend the patch to also use name@@@nodename syntax in
case the target node is static.  This also needs bit extra work since
during LTO partitioning we can not bring such symbol hidden and need to
introduce additional attribute.  I can look into that tomorrow.

Honza
> 
> Both issue cause symbols with symver missing in DSO (with LTO enabled).
> 
> I modified fuse-3.9.0 code to use new symver attribute and tried to build it
> with GCC trunk and LTO.  The result is a buggy DSO.  With this patch applied,
> fuse-3.9.0 can be built with LTO enabled and no problem.
> 
> I'll test symver patch and this patch with more packages.
> 
> Bootstrapped/regtested x86_64-linux.  I'm not a maintainer.
> 
> gcc/ChangeLog:
> 
> 2019-12-17  Xi Ruoyao  <xry111@mengyan1223.wang>
> 
> 	* cgraph.h (symtab_node::used_from_object_file_p): Symver nodes are
> 	part of DSO ABI so always used by non-LTO object files.
> 	* ipa-visibility.c (cgraph_externally_visible_p): Functions with symver
> 	attributes should always be visible.
> 
> Index: gcc/cgraph.h
> ===================================================================
> --- gcc/cgraph.h	(revision 279452)
> +++ gcc/cgraph.h	(working copy)
> @@ -2682,7 +2682,7 @@ symtab_node::used_from_object_file_p (vo
>  {
>    if (!TREE_PUBLIC (decl) || DECL_EXTERNAL (decl))
>      return false;
> -  if (resolution_used_from_other_file_p (resolution))
> +  if (symver || resolution_used_from_other_file_p (resolution))
>      return true;
>    return false;
>  }
> Index: gcc/ipa-visibility.c
> ===================================================================
> --- gcc/ipa-visibility.c	(revision 279452)
> +++ gcc/ipa-visibility.c	(working copy)
> @@ -216,6 +216,8 @@ cgraph_externally_visible_p (struct cgra
>      return true;
>    if (lookup_attribute ("noipa", DECL_ATTRIBUTES (node->decl)))
>      return true;
> +  if (lookup_attribute ("symver", DECL_ATTRIBUTES (node->decl)))
> +    return true;
>    if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
>        && lookup_attribute ("dllexport",
>  			   DECL_ATTRIBUTES (node->decl)))
> -- 
> Xi Ruoyao <xry111@mengyan1223.wang>
> School of Aerospace Science and Technology, Xidian University
>
Xi Ruoyao Dec. 17, 2019, 11:02 a.m. UTC | #2
On 2019-12-17 09:32 +0100, Jan Hubicka wrote:
> > Hi,
> > with Jan's patch commited in r278878 we can use symver attribute for
> > functions
> > and variables.  The symver attribute is designed for replacing toplevel asm
> > statements containing ".symver" which may be removed by LTO.  Unfortunately,
> > a quick test shown GCC still generates buggy so file with LTO and new symver
> > attribute.
> 
> Thanks for looking into this.  It was on my TODo list to actually
> convert some packages, so it is great you did that.
> > Two issues:
> > 
> > 1. The symver node in symtab is marked as PREVAILING_DEF_IRONLY (no EXP) and
> >    then removed by LTO.
> 
> This is however wrong - linker should not mark it as
> PREVAILING_DEF_IRONLY if it is used externally.  What linker do you use?
> On my testcases this was working with
>  GNU ld (GNU Binutils) 2.31.51.20181222
> I could easily imagine that some linkers get it wrong which should be
> reported to bintuils bugzilla but it is also easy to work around as done
> in your patch.

Hi Jan,

I'm using GNU ld 2.33.1.

I'll attach a testcase simplified from fuse-3.9 code.  "local: *;" in the
versioning script triggers the issue.  Without it there would be no problem.

> > 2. The actual function body implementing the symver-ed function is also
> > marked
> >    as PREVAILING_DEF_IRONLY and then removed or marked as local.  So no
> > ".globl"
> >    directive is outputed for it.
> 
> Here is the symver-ed function exported from the DSO (or is it set
> to have hidden attribute)?
> Again this was working for me, so it would be good to understand this
> issue.

It's also triggered by "local: *;".

Untar the attachment and use "make" to build it, then "make show-dynamic-syms"
to dump the dynamic symbol table.  I believe (with 99% chance) you'll see only
foo (VERS_1) and foo_v1 (because foo_v1 is marked as global in the version
script).  And foo (VERS_2) would be missing.  With this patch foo (VERS_2) would
show up.

We can't mark "foo_v2" to be "global" because it should not be a part of DSO
ABI.

The other 1% chance would be a regression in Binutils.
Jan Hubicka Dec. 17, 2019, 4:27 p.m. UTC | #3
> Hi Jan,
> 
> I'm using GNU ld 2.33.1.
> 
> I'll attach a testcase simplified from fuse-3.9 code.  "local: *;" in the
> versioning script triggers the issue.  Without it there would be no problem.

Thanks.
You are right that I did not play with local:. Now I wonder what is the
intended behaviour here.

In resolution file I see:
1
foo.o 4
205 dc8dc21a4ac8d072 PREVAILING_DEF_IRONLY_EXP foo_v1
207 dc8dc21a4ac8d072 PREVAILING_DEF_IRONLY foo@VERS_1
216 dc8dc21a4ac8d072 PREVAILING_DEF_IRONLY foo_v2
218 dc8dc21a4ac8d072 PREVAILING_DEF_IRONLY foo@@VERS_2

If I link the DSO w/o -flto I get with objdump -T:
0000000000001100 g    DF .text  0000000000000006 (VERS_1)     foo
0000000000001100 g    DF .text  0000000000000006  VERS_2      foo_v1
0000000000000000 g    DO *ABS*  0000000000000000  VERS_1      VERS_1
0000000000001110 g    DF .text  0000000000000006  VERS_2      foo
0000000000000000 g    DO *ABS*  0000000000000000  VERS_2      VERS_2

So I think linker is right here that foo_v1 is exported.  I would have
expected PREVAILING_DEF_IRONLY_EXP for foo@VERS_1 and foo@@VERS_2 since
that symbols do get exported even though they land in special way in the
DSO symbol table.  So I think meaingful behaviour would be
 1) make linker plugin interface to not annotate symbol version symbols
    with any resolution at all, since they are "special"
 2) make them PREVAILING_DEF_IRONLY_EXP/PREVAILING_DEF since they always
    get exported to non-LTO land.
We could workaround that on GCC side but if you agree with this
understanding I would fill in binutils PR. Also since we use resolution
info at many places, I would simply add logic working around this at a
time we read resolution rahter than on one of places we use it.

Comparing to objedump -T

0000000000001100 g    DF .text  0000000000000006  VERS_2      foo_v1
0000000000000000 g    DO *ABS*  0000000000000000  VERS_1      VERS_1
0000000000000000 g    DO *ABS*  0000000000000000  VERS_2      VERS_2

Why we also miss
0000000000001100 g    DF .text  0000000000000006 (VERS_1)     foo
and what does it mean?

I am not too happy about forcing GCC to keep foo_v2 exported when it is
not.  For example, calls to foo_v2 will then not be optimized as they
could if GCC knew it is not used by non-LTO world (except for fact that
symver is bound to it).

Would it be equivalent to:
1) output foo_v2 local
2) producing static alias with local name (.L1)
3) do .symver .L1,foo@@@VERS_2
That is somewhat more systematic and would not lead to false
visibilities.

Honza

> 
> > > 2. The actual function body implementing the symver-ed function is also
> > > marked
> > >    as PREVAILING_DEF_IRONLY and then removed or marked as local.  So no
> > > ".globl"
> > >    directive is outputed for it.
> > 
> > Here is the symver-ed function exported from the DSO (or is it set
> > to have hidden attribute)?
> > Again this was working for me, so it would be good to understand this
> > issue.
> 
> It's also triggered by "local: *;".
> 
> Untar the attachment and use "make" to build it, then "make show-dynamic-syms"
> to dump the dynamic symbol table.  I believe (with 99% chance) you'll see only
> foo (VERS_1) and foo_v1 (because foo_v1 is marked as global in the version
> script).  And foo (VERS_2) would be missing.  With this patch foo (VERS_2) would
> show up.
> 
> We can't mark "foo_v2" to be "global" because it should not be a part of DSO
> ABI.
> 
> The other 1% chance would be a regression in Binutils.
> -- 
> Xi Ruoyao <xry111@mengyan1223.wang>
> School of Aerospace Science and Technology, Xidian University
Jan Hubicka Dec. 17, 2019, 5:47 p.m. UTC | #4
> Would it be equivalent to:
> 1) output foo_v2 local
> 2) producing static alias with local name (.L1)
> 3) do .symver .L1,foo@@@VERS_2
> That is somewhat more systematic and would not lead to false
> visibilities.

I spent some time playing with this.  An in order to 
1) be able to handle foo_v2 according to the resolution info
   (so it behaves like a regular symbol and can be called dirrectly,
    localized and optimized)
2) get intended objdump -T relocations
3) do not polute global symbol tables

I ended up with the following codegen:

	.type	foo_v2, @function
foo_v2:
.LFB1:
	.cfi_startproc
	movl	$2, %eax
	ret
	.cfi_endproc
.LFE1:
	.size	foo_v2, .-foo_v2
	.globl	.LSYMVER0
	.set	.LSYMVER0,foo_v2
	.symver	.LSYMVER0, foo@@@VERS_2

This uses @@@ symver version of gas which seems to have odd semantics of
requiring to be passed global symbol name which it then tkes away and
produces foo@@VERS_2.

So the nm outoutp of the ltrans unit is:
0000000000000000 T foo_v1
0000000000000010 t foo_v2
0000000000000000 T foo@VERS_1
0000000000000010 T foo@@VERS_2

So the difference to your patch is that foo_v2 is static which enables
normal optimizations.

Since additional symbol alias is produced this would also make it
possible to attach multiple symver attributes with @@ string.

Does somehting like this make sense to you? Modulo the obvious buffer
overflow issue?
Honza

Index: lto/lto-common.c
===================================================================
--- lto/lto-common.c	(revision 279178)
+++ lto/lto-common.c	(working copy)
@@ -2818,6 +2818,10 @@ read_cgraph_and_symbols (unsigned nfiles
 			   IDENTIFIER_POINTER
 			     (DECL_ASSEMBLER_NAME (snode->decl)));
 	  }
+	/* Symbol versions are always used externally, but linker does not
+	   report that correctly.  */
+	else if (snode->symver && *res == LDPR_PREVAILING_DEF_IRONLY)
+	  snode->resolution = LDPR_PREVAILING_DEF_IRONLY_EXP;
 	else
 	  snode->resolution = *res;
       }
Index: varasm.c
===================================================================
--- varasm.c	(revision 279178)
+++ varasm.c	(working copy)
@@ -5970,9 +5970,47 @@ do_assemble_symver (tree decl, tree targ
   ultimate_transparent_alias_target (&id);
   ultimate_transparent_alias_target (&target);
 #ifdef ASM_OUTPUT_SYMVER_DIRECTIVE
-  ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
-			       IDENTIFIER_POINTER (target),
-			       IDENTIFIER_POINTER (id));
+  if (TREE_PUBLIC (target) && DECL_VISIBILITY (target) == VISIBILITY_DEFAULT)
+    ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
+			         IDENTIFIER_POINTER
+				   (DECL_ASSEMBLER_NAME (target)),
+			         IDENTIFIER_POINTER (id));
+  else
+    {
+      int nameend;
+      for (nameend = 0; IDENTIFIER_POINTER (id)[nameend] != '@'; nameend++)
+	;
+      if (IDENTIFIER_POINTER (id)[nameend + 1] != '@'
+	  || IDENTIFIER_POINTER (id)[nameend + 2] == '@')
+	{
+	  sorry_at (DECL_SOURCE_LOCATION (target),
+		    "can not produce %<symver%> of a symbol that is "
+		    "not exported with default visibility");
+	  return;
+	}
+      tree tmpdecl = copy_node (decl);
+      char buf[256];
+      static int symver_labelno;
+      targetm.asm_out.generate_internal_label (buf,
+					       "LSYMVER", symver_labelno++);
+      SET_DECL_ASSEMBLER_NAME (tmpdecl, get_identifier (buf));
+      globalize_decl (tmpdecl);
+#ifdef ASM_OUTPUT_DEF_FROM_DECLS
+      ASM_OUTPUT_DEF_FROM_DECLS (asm_out_file, tmpdecl,
+				 DECL_ASSEMBLER_NAME (target));
+#else
+      ASM_OUTPUT_DEF (asm_out_file,
+		      IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (tmpdecl)),
+		      IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (target)));
+#endif
+      memcpy (buf, IDENTIFIER_POINTER (id), nameend + 2);
+      buf[nameend + 2] = '@';
+      strcpy (buf + nameend + 3, IDENTIFIER_POINTER (id) + nameend + 2);
+      ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
+				   IDENTIFIER_POINTER
+					 (DECL_ASSEMBLER_NAME (tmpdecl)),
+				   buf);
+    }
 #else
   error ("symver is only supported on ELF platforms");
 #endif
Xi Ruoyao Dec. 18, 2019, 8:46 a.m. UTC | #5
On 2019-12-17 18:47 +0100, Jan Hubicka wrote:
> > Would it be equivalent to:
> > 1) output foo_v2 local
> > 2) producing static alias with local name (.L1)
> > 3) do .symver .L1,foo@@@VERS_2
> > That is somewhat more systematic and would not lead to false
> > visibilities.
> 
> I spent some time playing with this.  An in order to 
> 1) be able to handle foo_v2 according to the resolution info
>    (so it behaves like a regular symbol and can be called dirrectly,
>     localized and optimized)
> 2) get intended objdump -T relocations
> 3) do not polute global symbol tables
> 
> I ended up with the following codegen:
> 
> 	.type	foo_v2, @function
> foo_v2:
> .LFB1:
> 	.cfi_startproc
> 	movl	$2, %eax
> 	ret
> 	.cfi_endproc
> .LFE1:
> 	.size	foo_v2, .-foo_v2
> 	.globl	.LSYMVER0
> 	.set	.LSYMVER0,foo_v2
> 	.symver	.LSYMVER0, foo@@@VERS_2
> 
> This uses @@@ symver version of gas which seems to have odd semantics of
> requiring to be passed global symbol name which it then tkes away and
> produces foo@@VERS_2.
> 
> So the nm outoutp of the ltrans unit is:
> 0000000000000000 T foo_v1
> 0000000000000010 t foo_v2
> 0000000000000000 T foo@VERS_1
> 0000000000000010 T foo@@VERS_2
> 
> So the difference to your patch is that foo_v2 is static which enables
> normal optimizations.
> 
> Since additional symbol alias is produced this would also make it
> possible to attach multiple symver attributes with @@ string.
> 
> Does somehting like this make sense to you? Modulo the obvious buffer
> overflow issue?
> Honza

Unfortunately, I got an ICE with my testcase with the patch applied to trunk.

lto1: internal compiler error: tree check: expected tree that contains ‘decl
minimal’ structure, have ‘identifier_node’ in do_assemble_symver, at
varasm.c:5986
0x6fa648 tree_contains_struct_check_failed(tree_node const*,
tree_node_structure_enum, char const*, int, char const*)
	../../gcc/gcc/tree.c:9859
0x71466e contains_struct_check(tree_node*, tree_node_structure_enum, char
const*, int, char const*)
	../../gcc/gcc/tree.h:3387
0x71466e do_assemble_symver(tree_node*, tree_node*)
	../../gcc/gcc/varasm.c:5986
0x89e409 cgraph_node::assemble_thunks_and_aliases()
	../../gcc/gcc/cgraphunit.c:2225
0x89e698 cgraph_node::expand()
	../../gcc/gcc/cgraphunit.c:2351
0x89f62f expand_all_functions
	../../gcc/gcc/cgraphunit.c:2456
0x89f62f symbol_table::compile()
	../../gcc/gcc/cgraphunit.c:2806
0x7fb589 lto_main()
	../../gcc/gcc/lto/lto.c:658
Please submit a full bug report,
with preprocessed source if appropriate.
Please include the complete backtrace with any bug report.
See <https://gcc.gnu.org/bugs/> for instructions.
lto-wrapper: fatal error: /home/xry111/gcc-test/bin/gcc returned 1 exit status
compilation terminated.
/usr/bin/ld: error: lto-wrapper failed
collect2: error: ld returned 1 exit status
make: *** [Makefile:4: obj/test.so] Error 1

The change to lto/lto-common.c makes sense.  I tried it instead of my change to
cgraph.h and everything is OK.  I'll investigate the change to varasm.c a
little.
Jan Hubicka Dec. 18, 2019, 9:26 a.m. UTC | #6
Hi,
sorry I forgot to include cgraph and varpool changes in the patch.

Index: varpool.c
===================================================================
--- varpool.c	(revision 279467)
+++ varpool.c	(working copy)
@@ -539,8 +539,7 @@ varpool_node::assemble_aliases (void)
     {
       varpool_node *alias = dyn_cast <varpool_node *> (ref->referring);
       if (alias->symver)
-	do_assemble_symver (alias->decl,
-			    DECL_ASSEMBLER_NAME (decl));
+	do_assemble_symver (alias->decl, decl);
       else if (!alias->transparent_alias)
 	do_assemble_alias (alias->decl,
 			   DECL_ASSEMBLER_NAME (decl));
Index: cgraphunit.c
===================================================================
--- cgraphunit.c	(revision 279467)
+++ cgraphunit.c	(working copy)
@@ -2222,8 +2222,7 @@ cgraph_node::assemble_thunks_and_aliases
 	     of buffering it in same alias pairs.  */
 	  TREE_ASM_WRITTEN (decl) = 1;
 	  if (alias->symver)
-	    do_assemble_symver (alias->decl,
-				DECL_ASSEMBLER_NAME (decl));
+	    do_assemble_symver (alias->decl, decl);
 	  else
 	    do_assemble_alias (alias->decl,
 			       DECL_ASSEMBLER_NAME (decl));
Index: varasm.c
===================================================================
--- varasm.c	(revision 279467)
+++ varasm.c	(working copy)
@@ -5970,9 +5970,47 @@ do_assemble_symver (tree decl, tree targ
   ultimate_transparent_alias_target (&id);
   ultimate_transparent_alias_target (&target);
 #ifdef ASM_OUTPUT_SYMVER_DIRECTIVE
-  ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
-			       IDENTIFIER_POINTER (target),
-			       IDENTIFIER_POINTER (id));
+  if (TREE_PUBLIC (target) && DECL_VISIBILITY (target) == VISIBILITY_DEFAULT)
+    ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
+			         IDENTIFIER_POINTER
+				   (DECL_ASSEMBLER_NAME (target)),
+			         IDENTIFIER_POINTER (id));
+  else
+    {
+      int nameend;
+      for (nameend = 0; IDENTIFIER_POINTER (id)[nameend] != '@'; nameend++)
+	;
+      if (IDENTIFIER_POINTER (id)[nameend + 1] != '@'
+	  || IDENTIFIER_POINTER (id)[nameend + 2] == '@')
+	{
+	  sorry_at (DECL_SOURCE_LOCATION (target),
+		    "can not produce %<symver%> of a symbol that is "
+		    "not exported with default visibility");
+	  return;
+	}
+      tree tmpdecl = copy_node (decl);
+      char buf[256];
+      static int symver_labelno;
+      targetm.asm_out.generate_internal_label (buf,
+					       "LSYMVER", symver_labelno++);
+      SET_DECL_ASSEMBLER_NAME (tmpdecl, get_identifier (buf));
+      globalize_decl (tmpdecl);
+#ifdef ASM_OUTPUT_DEF_FROM_DECLS
+      ASM_OUTPUT_DEF_FROM_DECLS (asm_out_file, tmpdecl,
+				 DECL_ASSEMBLER_NAME (target));
+#else
+      ASM_OUTPUT_DEF (asm_out_file,
+		      IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (tmpdecl)),
+		      IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (target)));
+#endif
+      memcpy (buf, IDENTIFIER_POINTER (id), nameend + 2);
+      buf[nameend + 2] = '@';
+      strcpy (buf + nameend + 3, IDENTIFIER_POINTER (id) + nameend + 2);
+      ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
+				   IDENTIFIER_POINTER
+					 (DECL_ASSEMBLER_NAME (tmpdecl)),
+				   buf);
+    }
 #else
   error ("symver is only supported on ELF platforms");
 #endif
Index: lto/lto-common.c
===================================================================
--- lto/lto-common.c	(revision 279467)
+++ lto/lto-common.c	(working copy)
@@ -2818,6 +2818,10 @@ read_cgraph_and_symbols (unsigned nfiles
 			   IDENTIFIER_POINTER
 			     (DECL_ASSEMBLER_NAME (snode->decl)));
 	  }
+	/* Symbol versions are always used externally, but linker does not
+	   report that correctly.  */
+	else if (snode->symver && *res == LDPR_PREVAILING_DEF_IRONLY)
+	  snode->resolution = LDPR_PREVAILING_DEF_IRONLY_EXP;
 	else
 	  snode->resolution = *res;
       }
Xi Ruoyao Dec. 18, 2019, 9:53 a.m. UTC | #7
On 2019-12-18 10:26 +0100, Jan Hubicka wrote:
> Hi,
> sorry I forgot to include cgraph and varpool changes in the patch.
> 
> Index: varpool.c
> ===================================================================
> --- varpool.c	(revision 279467)
> +++ varpool.c	(working copy)
> @@ -539,8 +539,7 @@ varpool_node::assemble_aliases (void)
>      {
>        varpool_node *alias = dyn_cast <varpool_node *> (ref->referring);
>        if (alias->symver)
> -	do_assemble_symver (alias->decl,
> -			    DECL_ASSEMBLER_NAME (decl));
> +	do_assemble_symver (alias->decl, decl);
>        else if (!alias->transparent_alias)
>  	do_assemble_alias (alias->decl,
>  			   DECL_ASSEMBLER_NAME (decl));
> Index: cgraphunit.c
> ===================================================================
> --- cgraphunit.c	(revision 279467)
> +++ cgraphunit.c	(working copy)
> @@ -2222,8 +2222,7 @@ cgraph_node::assemble_thunks_and_aliases
>  	     of buffering it in same alias pairs.  */
>  	  TREE_ASM_WRITTEN (decl) = 1;
>  	  if (alias->symver)
> -	    do_assemble_symver (alias->decl,
> -				DECL_ASSEMBLER_NAME (decl));
> +	    do_assemble_symver (alias->decl, decl);
>  	  else
>  	    do_assemble_alias (alias->decl,
>  			       DECL_ASSEMBLER_NAME (decl));
> Index: varasm.c
> ===================================================================
> --- varasm.c	(revision 279467)
> +++ varasm.c	(working copy)
> @@ -5970,9 +5970,47 @@ do_assemble_symver (tree decl, tree targ
>    ultimate_transparent_alias_target (&id);
>    ultimate_transparent_alias_target (&target);

ICE here.

lto1: internal compiler error: tree check: expected identifier_node, have
function_decl in ultimate_transparent_alias_target, at varasm.c:1308
0x6f9cfe tree_check_failed(tree_node const*, char const*, int, char const*, ...)
	../../gcc/gcc/tree.c:9685
0x714541 tree_check(tree_node*, char const*, int, char const*, tree_code)
	../../gcc/gcc/tree.h:3273
0x714541 ultimate_transparent_alias_target
	../../gcc/gcc/varasm.c:1308
0x714541 do_assemble_symver(tree_node*, tree_node*)
	../../gcc/gcc/varasm.c:5971

>  #ifdef ASM_OUTPUT_SYMVER_DIRECTIVE
> -  ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
> -			       IDENTIFIER_POINTER (target),
> -			       IDENTIFIER_POINTER (id));
> +  if (TREE_PUBLIC (target) && DECL_VISIBILITY (target) == VISIBILITY_DEFAULT)
> +    ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
> +			         IDENTIFIER_POINTER
> +				   (DECL_ASSEMBLER_NAME (target)),
> +			         IDENTIFIER_POINTER (id));
> +  else
> +    {
> +      int nameend;
> +      for (nameend = 0; IDENTIFIER_POINTER (id)[nameend] != '@'; nameend++)
> +	;
> +      if (IDENTIFIER_POINTER (id)[nameend + 1] != '@'
> +	  || IDENTIFIER_POINTER (id)[nameend + 2] == '@')
> +	{
> +	  sorry_at (DECL_SOURCE_LOCATION (target),
> +		    "can not produce %<symver%> of a symbol that is "
> +		    "not exported with default visibility");
> +	  return;

I think this does not make sense.  Some library authors may export "foo@VER_1"
but not "foo_v1" to ensure the programmers using the library upgrade their code
to use new "correct" ABI, instead of an old one.   This error makes it
impossible.

(Try to comment out "foo_v1" in version.map, in the testcase.)

> +	}
> +      tree tmpdecl = copy_node (decl);
> +      char buf[256];
> +      static int symver_labelno;
> +      targetm.asm_out.generate_internal_label (buf,
> +					       "LSYMVER", symver_labelno++);
> +      SET_DECL_ASSEMBLER_NAME (tmpdecl, get_identifier (buf));
> +      globalize_decl (tmpdecl);
> +#ifdef ASM_OUTPUT_DEF_FROM_DECLS
> +      ASM_OUTPUT_DEF_FROM_DECLS (asm_out_file, tmpdecl,
> +				 DECL_ASSEMBLER_NAME (target));
> +#else
> +      ASM_OUTPUT_DEF (asm_out_file,
> +		      IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (tmpdecl)),
> +		      IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (target)));
> +#endif
> +      memcpy (buf, IDENTIFIER_POINTER (id), nameend + 2);
> +      buf[nameend + 2] = '@';
> +      strcpy (buf + nameend + 3, IDENTIFIER_POINTER (id) + nameend + 2);

We can't replace a single "@" with "@@@".  So I think producing .LSYMVERx is not
an option for "old" versions like "foo@VER_1".

> +      ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
> +				   IDENTIFIER_POINTER
> +					 (DECL_ASSEMBLER_NAME (tmpdecl)),
> +				   buf);
> +    }
>  #else
>    error ("symver is only supported on ELF platforms");
>  #endif
> Index: lto/lto-common.c
> ===================================================================
> --- lto/lto-common.c	(revision 279467)
> +++ lto/lto-common.c	(working copy)
> @@ -2818,6 +2818,10 @@ read_cgraph_and_symbols (unsigned nfiles
>  			   IDENTIFIER_POINTER
>  			     (DECL_ASSEMBLER_NAME (snode->decl)));
>  	  }
> +	/* Symbol versions are always used externally, but linker does not
> +	   report that correctly.  */
> +	else if (snode->symver && *res == LDPR_PREVAILING_DEF_IRONLY)
> +	  snode->resolution = LDPR_PREVAILING_DEF_IRONLY_EXP;

This is absolutely correct.

>  	else
>  	  snode->resolution = *res;
>        }

I still believe we should consider symver targets to be externally visible in
cgraph_externally_visible_p.  There is a comment saying "if linker counts on us,
we must preserve the function".  That's true in our case.

And, I think

.globl  .LSYMVER0
.set    .LSYMVER0, foo_v2
.symver .LSYMVER0, foo@@VERS_2

is exactly same as

.globl  foo_v2
.symver foo_v2, foo@@VERS_2

except there is an unnecessary ".LSYMVER0".  Adding ".globl foo_v2" or ".globl
foo_v1" won't cause them to be "global" in the final DSO because the linker will
hide them according to the version script.

So if it's safe we can force a ".globl foo_v2" before ".symver foo_v2,
foo@@VERS_2".  But I can't prove it's safe so I think it's better to consider
this case in cgraph_externally_visible_p.
Jan Hubicka Dec. 18, 2019, 1:19 p.m. UTC | #8
> ICE here.
> 
> lto1: internal compiler error: tree check: expected identifier_node, have
> function_decl in ultimate_transparent_alias_target, at varasm.c:1308
> 0x6f9cfe tree_check_failed(tree_node const*, char const*, int, char const*, ...)
> 	../../gcc/gcc/tree.c:9685
> 0x714541 tree_check(tree_node*, char const*, int, char const*, tree_code)
> 	../../gcc/gcc/tree.h:3273
> 0x714541 ultimate_transparent_alias_target
> 	../../gcc/gcc/varasm.c:1308
> 0x714541 do_assemble_symver(tree_node*, tree_node*)
> 	../../gcc/gcc/varasm.c:5971

Interesting that it works for me, but indeed we can remove that call
since there is no way to do weakref of symbol version.
> 
> >  #ifdef ASM_OUTPUT_SYMVER_DIRECTIVE
> > -  ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
> > -			       IDENTIFIER_POINTER (target),
> > -			       IDENTIFIER_POINTER (id));
> > +  if (TREE_PUBLIC (target) && DECL_VISIBILITY (target) == VISIBILITY_DEFAULT)
> > +    ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
> > +			         IDENTIFIER_POINTER
> > +				   (DECL_ASSEMBLER_NAME (target)),
> > +			         IDENTIFIER_POINTER (id));
> > +  else
> > +    {
> > +      int nameend;
> > +      for (nameend = 0; IDENTIFIER_POINTER (id)[nameend] != '@'; nameend++)
> > +	;
> > +      if (IDENTIFIER_POINTER (id)[nameend + 1] != '@'
> > +	  || IDENTIFIER_POINTER (id)[nameend + 2] == '@')
> > +	{
> > +	  sorry_at (DECL_SOURCE_LOCATION (target),
> > +		    "can not produce %<symver%> of a symbol that is "
> > +		    "not exported with default visibility");
> > +	  return;
> 
> I think this does not make sense.  Some library authors may export "foo@VER_1"
> but not "foo_v1" to ensure the programmers using the library upgrade their code
> to use new "correct" ABI, instead of an old one.   This error makes it
> impossible.
> 
> (Try to comment out "foo_v1" in version.map, in the testcase.)

The problem here is that we lie to the compiler (by pretending that
foo_v2 is exported from DSO while it is not) and force user to do the
same.

We support two ways to hide symbol - either at compile time via
attribute((visibility("hidden"))) or at link-time via map file.  The
first produces better code because compiler can do more optimizations
knowing that the symbol can not be interposed.

Generally we want users to use visiblity attribute or -fvisibility since
it leads to better code. However now we tell users to use
attribute((symver("..."))) to produce symbol version, but at the same
time not use attribute((visibility("hidden"))).

> > +      memcpy (buf, IDENTIFIER_POINTER (id), nameend + 2);
> > +      buf[nameend + 2] = '@';
> > +      strcpy (buf + nameend + 3, IDENTIFIER_POINTER (id) + nameend + 2);
> 
> We can't replace a single "@" with "@@@".  So I think producing .LSYMVERx is not
> an option for "old" versions like "foo@VER_1".

I wonder why gas implements the .symver this way at first place. Does
the linker really need the global symbol foo_v1 to produce the
version (in addition to foo@VER_1 that is in symbol table as well)?

> > +	/* Symbol versions are always used externally, but linker does not
> > +	   report that correctly.  */
> > +	else if (snode->symver && *res == LDPR_PREVAILING_DEF_IRONLY)
> > +	  snode->resolution = LDPR_PREVAILING_DEF_IRONLY_EXP;
> 
> This is absolutely correct.

Good, I will go ahead with filling in binutils PR on the wrong LDPR
value and apply the hack.
> 
> >  	else
> >  	  snode->resolution = *res;
> >        }
> 
> I still believe we should consider symver targets to be externally visible in
> cgraph_externally_visible_p.  There is a comment saying "if linker counts on us,
> we must preserve the function".  That's true in our case.
> 
> And, I think
> 
> .globl  .LSYMVER0
> .set    .LSYMVER0, foo_v2
> .symver .LSYMVER0, foo@@VERS_2
I produce
  .symver .LSYMVER0, foo@@@VERS_2

> 
> is exactly same as
> 
> .globl  foo_v2
> .symver foo_v2, foo@@VERS_2
> 
> except there is an unnecessary ".LSYMVER0".  Adding ".globl foo_v2" or ".globl
> foo_v1" won't cause them to be "global" in the final DSO because the linker will
> hide them according to the version script.

The difference is that in first case compiler can fully control foo_v2
symbol (with LTO it will turn it into static symbol, it will inline
calls to it and do other things), while in the second case we need to
treat foo_v2 specially.
> 
> So if it's safe we can force a ".globl foo_v2" before ".symver foo_v2,
> foo@@VERS_2".  But I can't prove it's safe so I think it's better to consider
> this case in cgraph_externally_visible_p.

It sort of makes things work, but for example it will prevent gcc from
inlining calls to foo_v2.  I think we will still need to do something
about -fvisibility=hidden.

It is sad that we do not have way to produce symbol version without a
corresponding symbol of global visiblity.  If we had we could support
multiple symver aliases from one symbol and avoid the need to explicitly
hide the unnecesary symbols in the map files...

Honza
Xi Ruoyao Dec. 18, 2019, 2:19 p.m. UTC | #9
On 2019-12-18 14:19 +0100, Jan Hubicka wrote:
> > ICE here.
> > 
> > lto1: internal compiler error: tree check: expected identifier_node, have
> > function_decl in ultimate_transparent_alias_target, at varasm.c:1308
> > 0x6f9cfe tree_check_failed(tree_node const*, char const*, int, char const*,
> > ...)
> > 	../../gcc/gcc/tree.c:9685
> > 0x714541 tree_check(tree_node*, char const*, int, char const*, tree_code)
> > 	../../gcc/gcc/tree.h:3273
> > 0x714541 ultimate_transparent_alias_target
> > 	../../gcc/gcc/varasm.c:1308
> > 0x714541 do_assemble_symver(tree_node*, tree_node*)
> > 	../../gcc/gcc/varasm.c:5971
> 
> Interesting that it works for me, but indeed we can remove that call
> since there is no way to do weakref of symbol version.
> > >  #ifdef ASM_OUTPUT_SYMVER_DIRECTIVE
> > > -  ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
> > > -			       IDENTIFIER_POINTER (target),
> > > -			       IDENTIFIER_POINTER (id));
> > > +  if (TREE_PUBLIC (target) && DECL_VISIBILITY (target) ==
> > > VISIBILITY_DEFAULT)
> > > +    ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
> > > +			         IDENTIFIER_POINTER
> > > +				   (DECL_ASSEMBLER_NAME (target)),
> > > +			         IDENTIFIER_POINTER (id));
> > > +  else
> > > +    {
> > > +      int nameend;
> > > +      for (nameend = 0; IDENTIFIER_POINTER (id)[nameend] != '@';
> > > nameend++)
> > > +	;
> > > +      if (IDENTIFIER_POINTER (id)[nameend + 1] != '@'
> > > +	  || IDENTIFIER_POINTER (id)[nameend + 2] == '@')
> > > +	{
> > > +	  sorry_at (DECL_SOURCE_LOCATION (target),
> > > +		    "can not produce %<symver%> of a symbol that is "
> > > +		    "not exported with default visibility");
> > > +	  return;
> > 
> > I think this does not make sense.  Some library authors may export "foo@VER_
> > 1"
> > but not "foo_v1" to ensure the programmers using the library upgrade their
> > code
> > to use new "correct" ABI, instead of an old one.   This error makes it
> > impossible.
> > 
> > (Try to comment out "foo_v1" in version.map, in the testcase.)
> 
> The problem here is that we lie to the compiler (by pretending that
> foo_v2 is exported from DSO while it is not) and force user to do the
> same.
> 
> We support two ways to hide symbol - either at compile time via
> attribute((visibility("hidden"))) or at link-time via map file.  The
> first produces better code because compiler can do more optimizations
> knowing that the symbol can not be interposed.
> 
> Generally we want users to use visiblity attribute or -fvisibility since
> it leads to better code. However now we tell users to use
> attribute((symver("..."))) to produce symbol version, but at the same
> time not use attribute((visibility("hidden"))).

Could a symver symbol be interposed?  I'll do some test to see.

> > > +      memcpy (buf, IDENTIFIER_POINTER (id), nameend + 2);
> > > +      buf[nameend + 2] = '@';
> > > +      strcpy (buf + nameend + 3, IDENTIFIER_POINTER (id) + nameend + 2);
> > 
> > We can't replace a single "@" with "@@@".  So I think producing .LSYMVERx is
> > not
> > an option for "old" versions like "foo@VER_1".
> 
> I wonder why gas implements the .symver this way at first place. Does
> the linker really need the global symbol foo_v1 to produce the
> version (in addition to foo@VER_1 that is in symbol table as well)?

I don't think the global symbol foo_v1 is necessary.  But I can't find a way
telling gas to make foo@VER_1 global and foo_v1 local.

> > > +	/* Symbol versions are always used externally, but linker does not
> > > +	   report that correctly.  */
> > > +	else if (snode->symver && *res == LDPR_PREVAILING_DEF_IRONLY)
> > > +	  snode->resolution = LDPR_PREVAILING_DEF_IRONLY_EXP;
> > 
> > This is absolutely correct.
> 
> Good, I will go ahead with filling in binutils PR on the wrong LDPR
> value and apply the hack.
> > >  	else
> > >  	  snode->resolution = *res;
> > >        }
> > 
> > I still believe we should consider symver targets to be externally visible
> > in
> > cgraph_externally_visible_p.  There is a comment saying "if linker counts on
> > us,
> > we must preserve the function".  That's true in our case.
> > 
> > And, I think
> > 
> > .globl  .LSYMVER0
> > .set    .LSYMVER0, foo_v2
> > .symver .LSYMVER0, foo@@VERS_2
> I produce
>   .symver .LSYMVER0, foo@@@VERS_2
> 
> > is exactly same as
> > 
> > .globl  foo_v2
> > .symver foo_v2, foo@@VERS_2
> > 
> > except there is an unnecessary ".LSYMVER0".  Adding ".globl foo_v2" or
> > ".globl
> > foo_v1" won't cause them to be "global" in the final DSO because the linker
> > will
> > hide them according to the version script.
> 
> The difference is that in first case compiler can fully control foo_v2
> symbol (with LTO it will turn it into static symbol, it will inline
> calls to it and do other things), while in the second case we need to
> treat foo_v2 specially.
> > So if it's safe we can force a ".globl foo_v2" before ".symver foo_v2,
> > foo@@VERS_2".  But I can't prove it's safe so I think it's better to
> > consider
> > this case in cgraph_externally_visible_p.
> 
> It sort of makes things work, but for example it will prevent gcc from
> inlining calls to foo_v2.  I think we will still need to do something
> about -fvisibility=hidden.
> 
> It is sad that we do not have way to produce symbol version without a
> corresponding symbol of global visiblity.  If we had we could support
> multiple symver aliases from one symbol and avoid the need to explicitly
> hide the unnecesary symbols in the map files...

Explicitly hiding the unnecessary symbols in map files is now how we handle this
[with __asm__(".symver foo_v2, foo@@VERS_2")].  We can continue to do in this
way and leave it as an enhancement.
Xi Ruoyao Dec. 18, 2019, 2:30 p.m. UTC | #10
On 2019-12-18 14:19 +0100, Jan Hubicka wrote:
> The problem here is that we lie to the compiler (by pretending that
> foo_v2 is exported from DSO while it is not) and force user to do the
> same.
> 
> We support two ways to hide symbol - either at compile time via
> attribute((visibility("hidden"))) or at link-time via map file.  The
> first produces better code because compiler can do more optimizations
> knowing that the symbol can not be interposed.

I just get your point: if the library calls foo_v2 it won't be interposed.  If
it supposes a call to be interposed it should call foo() [foo@@VER_2] instead of
foo_v2().

But it seems there is no way we can do this [even with traditional
__asm__("symver foo, foo@@VER_2")].  For this purpose we should either:

1. Change GAS (introducing some new syntax like '@@@@' or '.symver_export')

or

2. Add some mangled symbol name in GCC (like ".LSYMVERx" or
"foo_v2.symver_export").
Jan Hubicka Dec. 19, 2019, 10:06 a.m. UTC | #11
> On 2019-12-18 14:19 +0100, Jan Hubicka wrote:
> > The problem here is that we lie to the compiler (by pretending that
> > foo_v2 is exported from DSO while it is not) and force user to do the
> > same.
> > 
> > We support two ways to hide symbol - either at compile time via
> > attribute((visibility("hidden"))) or at link-time via map file.  The
> > first produces better code because compiler can do more optimizations
> > knowing that the symbol can not be interposed.
> 
> I just get your point: if the library calls foo_v2 it won't be interposed.  If
> it supposes a call to be interposed it should call foo() [foo@@VER_2] instead of
> foo_v2().
> 
> But it seems there is no way we can do this [even with traditional
> __asm__("symver foo, foo@@VER_2")].  For this purpose we should either:
> 
> 1. Change GAS (introducing some new syntax like '@@@@' or '.symver_export')
> 
> or
> 
> 2. Add some mangled symbol name in GCC (like ".LSYMVERx" or
> "foo_v2.symver_export").

I agree.  The problem with mangled symbol names is that we will require
users to hide them from map files, so I think it is not best answer
either.  I have filled binutils
https://sourceware.org/bugzilla/show_bug.cgi?id=25295

This is variant of your patch I comitted. It also adds verification so
we get ICE rather then wrong code.  In addition I moved the checks away
rom used_from_object_file.  This function is about non-LTO objects
linked into the DSO and thus does not really fit for the check.
Lastly we can not rely on symver attribute to still be present here.

Regtested x86_64-linux and comitted.
Honza
	* cgraph.c (cgraph_node_cannot_be_local_p_1): Prevent targets of
	symver attributes to be localized.
	* ipa-visibility.c (cgraph_externally_visible_p,
	varpool_node::externally_visible_p): Likewise.
	* symtab.c (symtab_node::verify_base): Check visibility of symbol
	versions.

	* lto-common.c (read_cgraph_and_symbols): Work around binutils
	PR25424

Index: cgraph.c
===================================================================
--- cgraph.c	(revision 279523)
+++ cgraph.c	(working copy)
@@ -2226,6 +2226,9 @@ cgraph_node_cannot_be_local_p_1 (cgraph_
 {
   return !(!node->force_output
 	   && !node->ifunc_resolver
+	   /* Limitation of gas requires us to output targets of symver aliases
+	      as global symbols.  This is binutils PR 25295.  */
+	   && !node->symver
 	   && ((DECL_COMDAT (node->decl)
 		&& !node->forced_by_abi
 		&& !node->used_from_object_file_p ()
Index: ipa-visibility.c
===================================================================
--- ipa-visibility.c	(revision 279523)
+++ ipa-visibility.c	(working copy)
@@ -220,6 +220,14 @@ cgraph_externally_visible_p (struct cgra
       && lookup_attribute ("dllexport",
 			   DECL_ATTRIBUTES (node->decl)))
     return true;
+
+  /* Limitation of gas requires us to output targets of symver aliases as
+     global symbols.  This is binutils PR 25295.  */
+  ipa_ref *ref;
+  FOR_EACH_ALIAS (node, ref)
+    if (ref->referring->symver)
+      return true;
+
   if (node->resolution == LDPR_PREVAILING_DEF_IRONLY)
     return false;
   /* When doing LTO or whole program, we can bring COMDAT functoins static.
@@ -284,14 +292,13 @@ varpool_node::externally_visible_p (void
 			   DECL_ATTRIBUTES (decl)))
     return true;
 
-  /* See if we have linker information about symbol not being used or
-     if we need to make guess based on the declaration.
+  /* Limitation of gas requires us to output targets of symver aliases as
+     global symbols.  This is binutils PR 25295.  */
+  ipa_ref *ref;
+  FOR_EACH_ALIAS (this, ref)
+    if (ref->referring->symver)
+      return true;
 
-     Even if the linker clams the symbol is unused, never bring internal
-     symbols that are declared by user as used or externally visible.
-     This is needed for i.e. references from asm statements.   */
-  if (used_from_object_file_p ())
-    return true;
   if (resolution == LDPR_PREVAILING_DEF_IRONLY)
     return false;
 
Index: lto/lto-common.c
===================================================================
--- lto/lto-common.c	(revision 279523)
+++ lto/lto-common.c	(working copy)
@@ -2818,6 +2818,11 @@ read_cgraph_and_symbols (unsigned nfiles
 			   IDENTIFIER_POINTER
 			     (DECL_ASSEMBLER_NAME (snode->decl)));
 	  }
+	/* Symbol versions are always used externally, but linker does not
+	   report that correctly.
+	   This is binutils PR25924.  */
+	else if (snode->symver && *res == LDPR_PREVAILING_DEF_IRONLY)
+	  snode->resolution = LDPR_PREVAILING_DEF_IRONLY_EXP;
 	else
 	  snode->resolution = *res;
       }
Index: symtab.c
===================================================================
--- symtab.c	(revision 279523)
+++ symtab.c	(working copy)
@@ -1156,6 +1156,22 @@ symtab_node::verify_base (void)
       error ("node is symver but not alias");
       error_found = true;
     }
+  /* Limitation of gas requires us to output targets of symver aliases as
+     global symbols.  This is binutils PR 25295.  */
+  if (symver
+      && (!TREE_PUBLIC (get_alias_target ()->decl)
+	  || DECL_VISIBILITY (get_alias_target ()->decl) != VISIBILITY_DEFAULT))
+    {
+      error ("symver target is not exported with default visibility");
+      error_found = true;
+    }
+  if (symver
+      && (!TREE_PUBLIC (decl)
+	  || DECL_VISIBILITY (decl) != VISIBILITY_DEFAULT))
+    {
+      error ("symver is not exported with default visibility");
+      error_found = true;
+    }
   if (same_comdat_group)
     {
       symtab_node *n = same_comdat_group;
Xi Ruoyao Dec. 19, 2019, 11:12 a.m. UTC | #12
On 2019-12-19 11:06 +0100, Jan Hubicka wrote:
> This is variant of your patch I comitted. It also adds verification so
> we get ICE rather then wrong code.  In addition I moved the checks away
> rom used_from_object_file.  This function is about non-LTO objects
> linked into the DSO and thus does not really fit for the check.
> Lastly we can not rely on symver attribute to still be present here.
> 
> Regtested x86_64-linux and comitted.
> Honza
> 	* cgraph.c (cgraph_node_cannot_be_local_p_1): Prevent targets of
> 	symver attributes to be localized.
> 	* ipa-visibility.c (cgraph_externally_visible_p,
> 	varpool_node::externally_visible_p): Likewise.
> 	* symtab.c (symtab_node::verify_base): Check visibility of symbol
> 	versions.
> 
> 	* lto-common.c (read_cgraph_and_symbols): Work around binutils
> 	PR25424

/* snip */

> Index: ipa-visibility.c
> ===================================================================
> --- ipa-visibility.c	(revision 279523)
> +++ ipa-visibility.c	(working copy)
> @@ -220,6 +220,14 @@ cgraph_externally_visible_p (struct cgra
>        && lookup_attribute ("dllexport",
>  			   DECL_ATTRIBUTES (node->decl)))
>      return true;
> +
> +  /* Limitation of gas requires us to output targets of symver aliases as
> +     global symbols.  This is binutils PR 25295.  */
> +  ipa_ref *ref;
> +  FOR_EACH_ALIAS (node, ref)
> +    if (ref->referring->symver)
> +      return true;
> +
>    if (node->resolution == LDPR_PREVAILING_DEF_IRONLY)
>      return false;
>    /* When doing LTO or whole program, we can bring COMDAT functoins static.
> @@ -284,14 +292,13 @@ varpool_node::externally_visible_p (void
>  			   DECL_ATTRIBUTES (decl)))
>      return true;
>  
> -  /* See if we have linker information about symbol not being used or
> -     if we need to make guess based on the declaration.
> +  /* Limitation of gas requires us to output targets of symver aliases as
> +     global symbols.  This is binutils PR 25295.  */
> +  ipa_ref *ref;
> +  FOR_EACH_ALIAS (this, ref)
> +    if (ref->referring->symver)
> +      return true;
>  
> -     Even if the linker clams the symbol is unused, never bring internal
> -     symbols that are declared by user as used or externally visible.
> -     This is needed for i.e. references from asm statements.   */
> -  if (used_from_object_file_p ())
> -    return true;

Are these two lines removed intentionally?

>    if (resolution == LDPR_PREVAILING_DEF_IRONLY)
>      return false;
>  
> Index: lto/lto-common.c
> ===================================================================
Xi Ruoyao Dec. 19, 2019, 12:12 p.m. UTC | #13
On 2019-12-19 19:12 +0800, Xi Ruoyao wrote:
> On 2019-12-19 11:06 +0100, Jan Hubicka wrote:
> > -  /* See if we have linker information about symbol not being used or
> > -     if we need to make guess based on the declaration.
> > +  /* Limitation of gas requires us to output targets of symver aliases as
> > +     global symbols.  This is binutils PR 25295.  */
> > +  ipa_ref *ref;
> > +  FOR_EACH_ALIAS (this, ref)
> > +    if (ref->referring->symver)
> > +      return true;
> >  
> > -     Even if the linker clams the symbol is unused, never bring internal
> > -     symbols that are declared by user as used or externally visible.
> > -     This is needed for i.e. references from asm statements.   */
> > -  if (used_from_object_file_p ())
> > -    return true;
> 
> Are these two lines removed intentionally?

Oh I see, it was a duplicated branch.

Sorry for noise.
Jan Hubicka Dec. 19, 2019, 12:17 p.m. UTC | #14
> On 2019-12-19 19:12 +0800, Xi Ruoyao wrote:
> > On 2019-12-19 11:06 +0100, Jan Hubicka wrote:
> > > -  /* See if we have linker information about symbol not being used or
> > > -     if we need to make guess based on the declaration.
> > > +  /* Limitation of gas requires us to output targets of symver aliases as
> > > +     global symbols.  This is binutils PR 25295.  */
> > > +  ipa_ref *ref;
> > > +  FOR_EACH_ALIAS (this, ref)
> > > +    if (ref->referring->symver)
> > > +      return true;
> > >  
> > > -     Even if the linker clams the symbol is unused, never bring internal
> > > -     symbols that are declared by user as used or externally visible.
> > > -     This is needed for i.e. references from asm statements.   */
> > > -  if (used_from_object_file_p ())
> > > -    return true;
> > 
> > Are these two lines removed intentionally?
> 
> Oh I see, it was a duplicated branch.
> 
> Sorry for noise.
I should have mentioned that in the changelog. Indeed that function was
checked twice.

Looking into it more, we still have issue since
	call foo_v1
tranlsates as
5:   e8 00 00 00 00          callq  a <foo@VERS_1+0xa>
while gcc believes that really foo_v1 is called.  
So after symver i guess foo_v1 symbol becomes completely unreachable
from the current unit which is not what gcc expect.

Since we do not allow any references to the symbol and we hacked gcc to
believe that symvered symbol is externaly visible, I suppose things are
more or less safe, but it is not working at it should.

It would probably be best if gas supported something like
.symver_set
with similar semantics of .set but creating a symbol version alias
which woud not add the translation of all references from symbol to its
version.

Honza
Jeff Law Jan. 14, 2020, 8:51 p.m. UTC | #15
On Wed, 2019-12-18 at 10:26 +0100, Jan Hubicka wrote:
> Hi,
> sorry I forgot to include cgraph and varpool changes in the patch.
> 
> Index: varpool.c
> ===================================================================
> --- varpool.c	(revision 279467)
> +++ varpool.c	(working copy)
> @@ -539,8 +539,7 @@ varpool_node::assemble_aliases (void)
>      {
>        varpool_node *alias = dyn_cast <varpool_node *> (ref->referring);
>        if (alias->symver)
> -	do_assemble_symver (alias->decl,
> -			    DECL_ASSEMBLER_NAME (decl));
> +	do_assemble_symver (alias->decl, decl);
>        else if (!alias->transparent_alias)
>  	do_assemble_alias (alias->decl,
>  			   DECL_ASSEMBLER_NAME (decl));
[ ... ]
Do you plan on committing these bits?

jeff
>
Jan Hubicka Jan. 14, 2020, 9:39 p.m. UTC | #16
> On Wed, 2019-12-18 at 10:26 +0100, Jan Hubicka wrote:
> > Hi,
> > sorry I forgot to include cgraph and varpool changes in the patch.
> > 
> > Index: varpool.c
> > ===================================================================
> > --- varpool.c	(revision 279467)
> > +++ varpool.c	(working copy)
> > @@ -539,8 +539,7 @@ varpool_node::assemble_aliases (void)
> >      {
> >        varpool_node *alias = dyn_cast <varpool_node *> (ref->referring);
> >        if (alias->symver)
> > -	do_assemble_symver (alias->decl,
> > -			    DECL_ASSEMBLER_NAME (decl));
> > +	do_assemble_symver (alias->decl, decl);
> >        else if (!alias->transparent_alias)
> >  	do_assemble_alias (alias->decl,
> >  			   DECL_ASSEMBLER_NAME (decl));
> [ ... ]
> Do you plan on committing these bits?

I think we got into agreement that gas extension is needed, since there
is name@@@nodename that does what we need for name@@nodename
but there is nothing that would do it for name@nodename.  I filled in
GAS PR for that.

Honza
> 
> jeff
> > 
>
diff mbox series

Patch

Index: gcc/cgraph.h
===================================================================
--- gcc/cgraph.h	(revision 279452)
+++ gcc/cgraph.h	(working copy)
@@ -2682,7 +2682,7 @@  symtab_node::used_from_object_file_p (vo
 {
   if (!TREE_PUBLIC (decl) || DECL_EXTERNAL (decl))
     return false;
-  if (resolution_used_from_other_file_p (resolution))
+  if (symver || resolution_used_from_other_file_p (resolution))
     return true;
   return false;
 }
Index: gcc/ipa-visibility.c
===================================================================
--- gcc/ipa-visibility.c	(revision 279452)
+++ gcc/ipa-visibility.c	(working copy)
@@ -216,6 +216,8 @@  cgraph_externally_visible_p (struct cgra
     return true;
   if (lookup_attribute ("noipa", DECL_ATTRIBUTES (node->decl)))
     return true;
+  if (lookup_attribute ("symver", DECL_ATTRIBUTES (node->decl)))
+    return true;
   if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
       && lookup_attribute ("dllexport",
 			   DECL_ATTRIBUTES (node->decl)))