aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gas/config/obj-elf.c28
-rw-r--r--gas/config/obj-elf.h5
-rw-r--r--gas/symbols.c26
-rw-r--r--gas/symbols.h2
-rw-r--r--gas/testsuite/gas/symver/symver11.d2
-rw-r--r--gas/testsuite/gas/symver/symver16.d13
-rw-r--r--gas/testsuite/gas/symver/symver16.s16
-rw-r--r--gas/write.c21
8 files changed, 103 insertions, 10 deletions
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index 42a3851..a6087a2 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -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. */
diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h
index d1fd315..763c58d 100644
--- a/gas/config/obj-elf.h
+++ b/gas/config/obj-elf.h
@@ -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()
diff --git a/gas/symbols.c b/gas/symbols.c
index 302eb4b..3cb9425 100644
--- a/gas/symbols.c
+++ b/gas/symbols.c
@@ -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
diff --git a/gas/symbols.h b/gas/symbols.h
index 91f6988..317252c 100644
--- a/gas/symbols.h
+++ b/gas/symbols.h
@@ -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 *);
diff --git a/gas/testsuite/gas/symver/symver11.d b/gas/testsuite/gas/symver/symver11.d
index caa76e1..10f8ef8 100644
--- a/gas/testsuite/gas/symver/symver11.d
+++ b/gas/testsuite/gas/symver/symver11.d
@@ -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
index 0000000..cdf0ddd
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver16.d
@@ -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
index 0000000..df330fd
--- /dev/null
+++ b/gas/testsuite/gas/symver/symver16.s
@@ -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
diff --git a/gas/write.c b/gas/write.c
index 253dfc4..e2c7bf2 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -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