as: Replace the removed symbol with the versioned symbol
authorH.J. Lu <hjl.tools@gmail.com>
Mon, 16 Aug 2021 13:46:44 +0000 (06:46 -0700)
committerH.J. Lu <hjl.tools@gmail.com>
Mon, 16 Aug 2021 13:46:44 +0000 (06:46 -0700)
When a symbol removed by .symver is used in relocation and there is one
and only one versioned symbol, don't remove the symbol.  Instead, mark
it to be removed and replace the removed symbol used in relocation with
the versioned symbol before generating relocation.

PR gas/28157
* symbols.c (symbol_flags): Add removed.
(symbol_entry_find): Updated.
(symbol_mark_removed): New function.
(symbol_removed_p): Likewise.
* symbols.h (symbol_mark_removed): New prototype.
(symbol_removed_p): Likewise.
* write.c (write_relocs): Call obj_fixup_removed_symbol on
removed fixp->fx_addsy and fixp->fx_subsy if defined.
(set_symtab): Don't add a symbol if symbol_removed_p returns true.
* config/obj-elf.c (elf_frob_symbol):  Don't remove the symbol
if it is used on relocation.  Instead, mark it as to be removed
and issue an error if the symbol has more than one versioned name.
(elf_fixup_removed_symbol): New function.
* config/obj-elf.h (elf_fixup_removed_symbol): New prototype.
(obj_fixup_removed_symbol): New.
* testsuite/gas/symver/symver11.d: Updated expected error
message.
* testsuite/gas/symver/symver16.d: New file.
* testsuite/gas/symver/symver16.s: Likewise.

gas/config/obj-elf.c
gas/config/obj-elf.h
gas/symbols.c
gas/symbols.h
gas/testsuite/gas/symver/symver11.d
gas/testsuite/gas/symver/symver16.d [new file with mode: 0644]
gas/testsuite/gas/symver/symver16.s [new file with mode: 0644]
gas/write.c

index 42a3851e7722d15bd69f870a40b352ed592186f8..a6087a21eb39d899a484a372d980945992740704 100644 (file)
@@ -2705,7 +2705,7 @@ elf_frob_symbol (symbolS *symp, int *puntp)
                S_SET_EXTERNAL (symp2);
            }
 
-         switch (symbol_get_obj (symp)->visibility)
+         switch (sy_obj->visibility)
            {
            case visibility_unchanged:
              break;
@@ -2716,7 +2716,18 @@ elf_frob_symbol (symbolS *symp, int *puntp)
              elfsym->internal_elf_sym.st_other |= STV_HIDDEN;
              break;
            case visibility_remove:
-             symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+             /* Don't remove the symbol if it is used in relocation.
+                Instead, mark it as to be removed and issue an error
+                if the symbol has more than one versioned name.  */
+             if (symbol_used_in_reloc_p (symp))
+               {
+                 if (sy_obj->versioned_name->next != NULL)
+                   as_bad (_("symbol '%s' with multiple versions cannot be used in relocation"),
+                           S_GET_NAME (symp));
+                 symbol_mark_removed (symp);
+               }
+             else
+               symbol_remove (symp, &symbol_rootP, &symbol_lastP);
              break;
            case visibility_local:
              S_CLEAR_EXTERNAL (symp);
@@ -2734,6 +2745,19 @@ elf_frob_symbol (symbolS *symp, int *puntp)
     }
 }
 
+/* Fix up SYMPP which has been marked to be removed by .symver.  */
+
+void
+elf_fixup_removed_symbol (symbolS **sympp)
+{
+  symbolS *symp = *sympp;
+  struct elf_obj_sy *sy_obj = symbol_get_obj (symp);
+
+  /* Replace the removed symbol with the versioned symbol.  */
+  symp = symbol_find (sy_obj->versioned_name->name);
+  *sympp = symp;
+}
+
 struct group_list
 {
   asection **head;             /* Section lists.  */
index d1fd3152998dad94ce995a800b8053967d9a8ffd..763c58dfcad6c88820ae7c7a95fec0726860a16f 100644 (file)
@@ -273,6 +273,11 @@ extern void elf_frob_symbol (symbolS *, int *);
 #define obj_frob_symbol(symp, punt) elf_frob_symbol (symp, &punt)
 #endif
 
+extern void elf_fixup_removed_symbol (symbolS **);
+#ifndef obj_fixup_removed_symbol
+#define obj_fixup_removed_symbol(sympp) elf_fixup_removed_symbol (sympp)
+#endif
+
 extern void elf_pop_insert (void);
 #ifndef obj_pop_insert
 #define obj_pop_insert()       elf_pop_insert()
index 302eb4bd6f7fc86c9436619beb5ba50df37963d0..3cb9425c4ce3d71530bc5a4f90b3a1c32873b7d3 100644 (file)
@@ -78,6 +78,10 @@ struct symbol_flags
      before.  It is cleared as soon as any direct reference to the
      symbol is present.  */
   unsigned int weakrefd : 1;
+
+  /* Whether the symbol has been marked to be removed by a .symver
+     directive.  */
+  unsigned int removed : 1;
 };
 
 /* A pointer in the symbol may point to either a complete symbol
@@ -194,7 +198,7 @@ static void *
 symbol_entry_find (htab_t table, const char *name)
 {
   hashval_t hash = htab_hash_string (name);
-  symbol_entry_t needle = { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+  symbol_entry_t needle = { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
                              hash, name, 0, 0, 0 } };
   return htab_find_with_hash (table, &needle, hash);
 }
@@ -2807,6 +2811,26 @@ symbol_written_p (symbolS *s)
   return s->flags.written;
 }
 
+/* Mark a symbol as to be removed.  */
+
+void
+symbol_mark_removed (symbolS *s)
+{
+  if (s->flags.local_symbol)
+    return;
+  s->flags.removed = 1;
+}
+
+/* Return whether a symbol has been marked to be removed.  */
+
+int
+symbol_removed_p (symbolS *s)
+{
+  if (s->flags.local_symbol)
+    return 0;
+  return s->flags.removed;
+}
+
 /* Mark a symbol has having been resolved.  */
 
 void
index 91f69882a7ee7621a10253e185db0caa1518b17b..317252c7fa198e100a5a7ff9f5d37b0a1ceded08 100644 (file)
@@ -193,6 +193,8 @@ extern int symbol_mri_common_p (symbolS *);
 extern void symbol_mark_written (symbolS *);
 extern void symbol_clear_written (symbolS *);
 extern int symbol_written_p (symbolS *);
+extern void symbol_mark_removed (symbolS *);
+extern int symbol_removed_p (symbolS *);
 extern void symbol_mark_resolved (symbolS *);
 extern int symbol_resolved_p (symbolS *);
 extern int symbol_section_p (symbolS *);
index caa76e167da58735b8941e509374164be285b36e..10f8ef810b2a083983db1f943d2105ef72d57d35 100644 (file)
@@ -1,2 +1,2 @@
 #name: symver symver11
-#error: .*symbol cannot be used on reloc
+#error: .*: symbol 'foo' with multiple versions cannot be used in relocation
diff --git a/gas/testsuite/gas/symver/symver16.d b/gas/testsuite/gas/symver/symver16.d
new file mode 100644 (file)
index 0000000..cdf0ddd
--- /dev/null
@@ -0,0 +1,13 @@
+#name: symver symver16
+#readelf: -srW
+
+#...
+Relocation section .*
+#...
+[0-9a-f]+[ \t]+[0-9a-f]+[ \t]+R_.*[ \t]+[0-9a-f]+[ \t]+foo@@VERS_1.*
+#...
+[0-9a-f]+[ \t]+[0-9a-f]+[ \t]+R_.*[ \t]+[0-9a-f]+[ \t]+bar@VERS_1.*
+#...
+ +[0-9]+: 0+ +1 +OBJECT +GLOBAL +DEFAULT +[0-9]+ +foo@@VERS_1
+ +[0-9]+: 0+1 +1 +OBJECT +GLOBAL +DEFAULT +[0-9]+ +bar@VERS_1
+#pass
diff --git a/gas/testsuite/gas/symver/symver16.s b/gas/testsuite/gas/symver/symver16.s
new file mode 100644 (file)
index 0000000..df330fd
--- /dev/null
@@ -0,0 +1,16 @@
+       .data
+       .type foo,%object
+foo:
+       .byte 0
+       .size foo,.-foo
+       .globl foo
+       .symver foo,foo@@VERS_1,remove
+       .globl bar
+       .symver bar,bar@VERS_1,remove
+       .type bar,%object
+bar:
+       .byte 0
+       .size bar,.-bar
+       .balign 8
+       .dc.a foo
+       .dc.a bar
index 253dfc476f8c9cd894f5debd4cf4a93ed03f9b52..e2c7bf2924976ef886e54cb2669b02bd234e1a86 100644 (file)
@@ -1289,6 +1289,13 @@ write_relocs (bfd *abfd ATTRIBUTE_UNUSED, asection *sec,
        as_bad_where (fixp->fx_file, fixp->fx_line,
                      _("internal error: fixup not contained within frag"));
 
+#ifdef obj_fixup_removed_symbol
+      if (fixp->fx_addsy && symbol_removed_p (fixp->fx_addsy))
+       obj_fixup_removed_symbol (&fixp->fx_addsy);
+      if (fixp->fx_subsy && symbol_removed_p (fixp->fx_subsy))
+       obj_fixup_removed_symbol (&fixp->fx_subsy);
+#endif
+
 #ifndef RELOC_EXPANSION_POSSIBLE
       *reloc = tc_gen_reloc (sec, fixp);
 #else
@@ -1755,9 +1762,10 @@ set_symtab (void)
      two.  Generate unused section symbols only if needed.  */
   nsyms = 0;
   for (symp = symbol_rootP; symp; symp = symbol_next (symp))
-    if (bfd_keep_unused_section_symbols (stdoutput)
-       || !symbol_section_p (symp)
-       || symbol_used_in_reloc_p (symp))
+    if (!symbol_removed_p (symp)
+       && (bfd_keep_unused_section_symbols (stdoutput)
+           || !symbol_section_p (symp)
+           || symbol_used_in_reloc_p (symp)))
       nsyms++;
 
   if (nsyms)
@@ -1768,9 +1776,10 @@ set_symtab (void)
       asympp = (asymbol **) bfd_alloc (stdoutput, amt);
       symp = symbol_rootP;
       for (i = 0; i < nsyms; symp = symbol_next (symp))
-       if (bfd_keep_unused_section_symbols (stdoutput)
-           || !symbol_section_p (symp)
-           || symbol_used_in_reloc_p (symp))
+       if (!symbol_removed_p (symp)
+           && (bfd_keep_unused_section_symbols (stdoutput)
+               || !symbol_section_p (symp)
+               || symbol_used_in_reloc_p (symp)))
          {
            asympp[i] = symbol_get_bfdsym (symp);
            if (asympp[i]->flags != BSF_SECTION_SYM