if ((symbolP = symbol_find (sym_name)) != 0)
{
+ S_CLEAR_WEAKREFR (symbolP);
#ifdef RESOLVE_SYMBOL_REDEFINITION
if (RESOLVE_SYMBOL_REDEFINITION (symbolP))
return symbolP;
local_symbol_set_frag (locsym, frag_now);
locsym->lsy_value = frag_now_fix ();
}
- else if (!S_IS_DEFINED (symbolP) || S_IS_COMMON (symbolP))
+ else if (!(S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP))
+ || S_IS_COMMON (symbolP)
+ || S_IS_VOLATILE (symbolP))
{
+ if (S_IS_VOLATILE (symbolP))
+ {
+ symbolP = symbol_clone (symbolP, 1);
+ S_SET_VALUE (symbolP, 0);
+ S_CLEAR_VOLATILE (symbolP);
+ }
if (S_GET_VALUE (symbolP) == 0)
{
symbolP->sy_frag = frag_now;
&& S_IS_EXTERNAL (symbolP))
|| S_GET_SEGMENT (symbolP) == bss_section)
&& (now_seg == data_section
+ || now_seg == bss_section
|| now_seg == S_GET_SEGMENT (symbolP)))
{
/* Select which of the 2 cases this is. */
if (!(frag_now == symbolP->sy_frag
&& S_GET_VALUE (symbolP) == frag_now_fix ()
&& S_GET_SEGMENT (symbolP) == now_seg))
- as_bad (_("symbol `%s' is already defined"), sym_name);
+ {
+ as_bad (_("symbol `%s' is already defined"), sym_name);
+ symbolP = symbol_clone (symbolP, 0);
+ }
}
}
symbol_clone (symbolS *orgsymP, int replace)
{
symbolS *newsymP;
+ asymbol *bsymorg, *bsymnew;
/* Running local_symbol_convert on a clone that's not the one currently
in local_hash would incorrectly replace the hash entry. Thus the
depends on not encountering an unconverted symbol. */
if (LOCAL_SYMBOL_CHECK (orgsymP))
orgsymP = local_symbol_convert ((struct local_symbol *) orgsymP);
+ bsymorg = orgsymP->bsym;
know (S_IS_DEFINED (orgsymP));
newsymP = obstack_alloc (¬es, sizeof (*newsymP));
*newsymP = *orgsymP;
+ bsymnew = bfd_make_empty_symbol (bfd_asymbol_bfd (bsymorg));
+ if (bsymnew == NULL)
+ as_perror ("%s", "bfd_make_empty_symbol");
+ newsymP->bsym = bsymnew;
+ bsymnew->name = bsymorg->name;
+ bsymnew->flags = bsymorg->flags;
+ bsymnew->section = bsymorg->section;
+ bsymnew->udata.p = (PTR) newsymP;
+ bfd_copy_private_symbol_data (bfd_asymbol_bfd (bsymorg), bsymorg,
+ bfd_asymbol_bfd (bsymnew), bsymnew);
+
+#ifdef obj_symbol_clone_hook
+ obj_symbol_clone_hook (newsymP, orgsymP);
+#endif
+
+#ifdef tc_symbol_clone_hook
+ tc_symbol_clone_hook (newsymP, orgsymP);
+#endif
if (replace)
{
symbolS *
symbol_find_exact (const char *name)
+{
+ return symbol_find_exact_noref (name, 0);
+}
+
+symbolS *
+symbol_find_exact_noref (const char *name, int noref)
{
struct local_symbol *locsym;
+ symbolS* sym;
locsym = (struct local_symbol *) hash_find (local_hash, name);
if (locsym != NULL)
return (symbolS *) locsym;
- return ((symbolS *) hash_find (sy_hash, name));
+ sym = ((symbolS *) hash_find (sy_hash, name));
+
+ /* Any references to the symbol, except for the reference in
+ .weakref, must clear this flag, such that the symbol does not
+ turn into a weak symbol. Note that we don't have to handle the
+ local_symbol case, since a weakrefd is always promoted out of the
+ local_symbol table when it is turned into a weak symbol. */
+ if (sym && ! noref)
+ S_CLEAR_WEAKREFD (sym);
+
+ return sym;
}
symbolS *
symbol_find (const char *name)
+{
+ return symbol_find_noref (name, 0);
+}
+
+symbolS *
+symbol_find_noref (const char *name, int noref)
{
#ifdef tc_canonicalize_symbol_name
{
*copy = '\0';
}
- return symbol_find_exact (name);
+ return symbol_find_exact_noref (name, noref);
}
/* Once upon a time, symbols were kept in a singly linked list. At
symp->sy_value.X_op_symbol = NULL;
do_symbol:
+ if (S_IS_WEAKREFR (symp))
+ {
+ assert (final_val == 0);
+ if (S_IS_WEAKREFR (add_symbol))
+ {
+ assert (add_symbol->sy_value.X_op == O_symbol
+ && add_symbol->sy_value.X_add_number == 0);
+ add_symbol = add_symbol->sy_value.X_add_symbol;
+ assert (! S_IS_WEAKREFR (add_symbol));
+ symp->sy_value.X_add_symbol = add_symbol;
+ }
+ }
+
if (symp->sy_mri_common)
{
/* This is a symbol inside an MRI common section. The
}
resolved = symbol_resolved_p (add_symbol);
+ if (S_IS_WEAKREFR (symp))
+ goto exit_dont_set_value;
break;
case O_uminus:
sub-expressions used. */
int
-snapshot_symbol (symbolS *symbolP, valueT *valueP, segT *segP, fragS **fragPP)
+snapshot_symbol (symbolS **symbolPP, valueT *valueP, segT *segP, fragS **fragPP)
{
+ symbolS *symbolP = *symbolPP;
+
if (LOCAL_SYMBOL_CHECK (symbolP))
{
struct local_symbol *locsym = (struct local_symbol *) symbolP;
{
case O_constant:
case O_register:
- /* This check wouldn't be needed if pseudo_set() didn't set
- symbols equated to bare symbols to undefined_section. */
- if (symbolP->bsym->section != undefined_section
- || symbolP->sy_value.X_op != O_symbol)
+ if (!symbol_equated_p (symbolP))
break;
/* Fall thru. */
case O_symbol:
}
}
+ /* Never change a defined symbol. */
+ if (symbolP->bsym->section == undefined_section
+ || symbolP->bsym->section == expr_section)
+ *symbolPP = symbolP;
*valueP = expr.X_add_number;
*segP = symbolP->bsym->section;
*fragPP = symbolP->sy_frag;
if (!finalize_syms)
return val;
}
+ if (S_IS_WEAKREFR (s))
+ return S_GET_VALUE (s->sy_value.X_add_symbol);
+
if (s->sy_value.X_op != O_constant)
{
- static symbolS *recur;
-
- /* FIXME: In non BFD assemblers, S_IS_DEFINED and S_IS_COMMON
- may call S_GET_VALUE. We use a static symbol to avoid the
- immediate recursion. */
- if (recur == s)
- return (valueT) s->sy_value.X_add_number;
- recur = s;
if (! s->sy_resolved
|| s->sy_value.X_op != O_symbol
|| (S_IS_DEFINED (s) && ! S_IS_COMMON (s)))
as_bad (_("attempt to get value of unresolved symbol `%s'"),
S_GET_NAME (s));
- recur = NULL;
}
return (valueT) s->sy_value.X_add_number;
}
s->sy_value.X_op = O_constant;
s->sy_value.X_add_number = (offsetT) val;
s->sy_value.X_unsigned = 0;
+ S_CLEAR_WEAKREFR (s);
}
void
{
if (LOCAL_SYMBOL_CHECK (s))
return 0;
+ /* Conceptually, a weakrefr is weak if the referenced symbol is. We
+ could probably handle a WEAKREFR as always weak though. E.g., if
+ the referenced symbol has lost its weak status, there's no reason
+ to keep handling the weakrefr as if it was weak. */
+ if (S_IS_WEAKREFR (s))
+ return S_IS_WEAK (s->sy_value.X_add_symbol);
return (s->bsym->flags & BSF_WEAK) != 0;
}
+int
+S_IS_WEAKREFR (symbolS *s)
+{
+ if (LOCAL_SYMBOL_CHECK (s))
+ return 0;
+ return s->sy_weakrefr != 0;
+}
+
+int
+S_IS_WEAKREFD (symbolS *s)
+{
+ if (LOCAL_SYMBOL_CHECK (s))
+ return 0;
+ return s->sy_weakrefd != 0;
+}
+
int
S_IS_COMMON (symbolS *s)
{
{
if (LOCAL_SYMBOL_CHECK (s))
s = local_symbol_convert ((struct local_symbol *) s);
+#ifdef obj_set_weak_hook
+ obj_set_weak_hook (s);
+#endif
s->bsym->flags |= BSF_WEAK;
s->bsym->flags &= ~(BSF_GLOBAL | BSF_LOCAL);
}
+void
+S_SET_WEAKREFR (symbolS *s)
+{
+ if (LOCAL_SYMBOL_CHECK (s))
+ s = local_symbol_convert ((struct local_symbol *) s);
+ s->sy_weakrefr = 1;
+ /* If the alias was already used, make sure we mark the target as
+ used as well, otherwise it might be dropped from the symbol
+ table. This may have unintended side effects if the alias is
+ later redirected to another symbol, such as keeping the unused
+ previous target in the symbol table. Since it will be weak, it's
+ not a big deal. */
+ if (s->sy_used)
+ symbol_mark_used (s->sy_value.X_add_symbol);
+}
+
+void
+S_CLEAR_WEAKREFR (symbolS *s)
+{
+ if (LOCAL_SYMBOL_CHECK (s))
+ return;
+ s->sy_weakrefr = 0;
+}
+
+void
+S_SET_WEAKREFD (symbolS *s)
+{
+ if (LOCAL_SYMBOL_CHECK (s))
+ s = local_symbol_convert ((struct local_symbol *) s);
+ s->sy_weakrefd = 1;
+ S_SET_WEAK (s);
+}
+
+void
+S_CLEAR_WEAKREFD (symbolS *s)
+{
+ if (LOCAL_SYMBOL_CHECK (s))
+ return;
+ if (s->sy_weakrefd)
+ {
+ s->sy_weakrefd = 0;
+ /* If a weakref target symbol is weak, then it was never
+ referenced directly before, not even in a .global directive,
+ so decay it to local. If it remains undefined, it will be
+ later turned into a global, like any other undefined
+ symbol. */
+ if (s->bsym->flags & BSF_WEAK)
+ {
+#ifdef obj_clear_weak_hook
+ obj_clear_weak_hook (s);
+#endif
+ s->bsym->flags &= ~BSF_WEAK;
+ s->bsym->flags |= BSF_LOCAL;
+ }
+ }
+}
+
void
S_SET_THREAD_LOCAL (symbolS *s)
{
s->sy_volatile = 1;
}
+void
+S_CLEAR_VOLATILE (symbolS *s)
+{
+ if (!LOCAL_SYMBOL_CHECK (s))
+ s->sy_volatile = 0;
+}
+
void
S_SET_FORWARD_REF (symbolS *s)
{
if (LOCAL_SYMBOL_CHECK (s))
s = local_symbol_convert ((struct local_symbol *) s);
s->sy_value = *exp;
+ S_CLEAR_WEAKREFR (s);
}
/* Return a pointer to the X_add_number component of a symbol. */
return;
}
s->sy_frag = f;
+ S_CLEAR_WEAKREFR (s);
}
/* Return the frag of a symbol. */
if (LOCAL_SYMBOL_CHECK (s))
return;
s->sy_used = 1;
+ if (S_IS_WEAKREFR (s))
+ symbol_mark_used (s->sy_value.X_add_symbol);
}
/* Clear the mark of whether a symbol has been used. */
fprintf (file, " local");
if (S_IS_EXTERNAL (sym))
fprintf (file, " extern");
+ if (S_IS_WEAK (sym))
+ fprintf (file, " weak");
if (S_IS_DEBUG (sym))
fprintf (file, " debug");
if (S_IS_DEFINED (sym))
fprintf (file, " defined");
}
+ if (S_IS_WEAKREFR (sym))
+ fprintf (file, " weakrefr");
+ if (S_IS_WEAKREFD (sym))
+ fprintf (file, " weakrefd");
fprintf (file, " %s", segment_name (S_GET_SEGMENT (sym)));
if (symbol_resolved_p (sym))
{