aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gas/ChangeLog22
-rw-r--r--gas/config/tc-i386.c37
-rw-r--r--gas/config/tc-mips.c3
-rw-r--r--gas/expr.c42
-rw-r--r--gas/symbols.c125
-rw-r--r--gas/symbols.h1
-rw-r--r--gas/write.c20
7 files changed, 174 insertions, 76 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog
index 0c27bef..8f25096 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,25 @@
+2001-09-09 Alan Modra <amodra@bigpond.net.au>
+
+ * expr.c (expr): Move code setting "retval" to the end of the loop,
+ and rearrange for efficiency. For "PIC code" subtraction, use
+ "rightseg" rather than recalculating. For "symbol OP symbol"
+ subtract, set "retval" to absolute_section if symbols in same
+ section.
+ * symbols.c (resolve_symbol_value): Resolve "sym +/- expr" to an
+ O_symbol. Simplify a +/- b code. Allow equality and non-equality
+ comparisons on symbols from any section. Allow other comparison
+ operators as for subtraction.
+ (symbol_equated_reloc_p): New predicate function.
+ * symbols.h (symbol_equated_reloc_p): Declare.
+ * write.c (adjust_reloc_syms): Use symbol_equated_reloc_p.
+ (write_relocs): Likewise.
+ (write_object_file): Likewise.
+ (relax_segment <rs_machine_dependent>): Ensure segment for
+ expression syms is set correctly.
+ * config/tc-mips.c (md_estimate_size_before_relax): Likewise.
+ * config/tc-i386.c (md_assemble <Output jumps>): Don't lose part
+ of a complex expression when setting up frag_var.
+
2001-09-07 Richard Henderson <rth@redhat.com>
* config/tc-alpha.c (alpha_reloc_op_tag): Replace need_seq with
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 275b3e6..739ed58 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -2741,6 +2741,9 @@ md_assemble (line)
{
int code16;
int prefix;
+ relax_substateT subtype;
+ symbolS *sym;
+ offsetT off;
code16 = 0;
if (flag_code == CODE_16BIT)
@@ -2785,19 +2788,29 @@ md_assemble (line)
if (i.prefix[REX_PREFIX])
*p++ = i.prefix[REX_PREFIX];
*p = i.tm.base_opcode;
- /* 1 possible extra opcode + displacement go in var part.
+
+ if ((unsigned char) *p == JUMP_PC_RELATIVE)
+ subtype = ENCODE_RELAX_STATE (UNCOND_JUMP, SMALL);
+ else if ((cpu_arch_flags & Cpu386) != 0)
+ subtype = ENCODE_RELAX_STATE (COND_JUMP, SMALL);
+ else
+ subtype = ENCODE_RELAX_STATE (COND_JUMP86, SMALL);
+ subtype |= code16;
+
+ sym = i.op[0].disps->X_add_symbol;
+ off = i.op[0].disps->X_add_number;
+
+ if (i.op[0].disps->X_op != O_constant
+ && i.op[0].disps->X_op != O_symbol)
+ {
+ /* Handle complex expressions. */
+ sym = make_expr_symbol (i.op[0].disps);
+ off = 0;
+ }
+
+ /* 1 possible extra opcode + 4 byte displacement go in var part.
Pass reloc in fr_var. */
- frag_var (rs_machine_dependent,
- 1 + 4,
- i.reloc[0],
- ((unsigned char) *p == JUMP_PC_RELATIVE
- ? ENCODE_RELAX_STATE (UNCOND_JUMP, SMALL) | code16
- : ((cpu_arch_flags & Cpu386) != 0
- ? ENCODE_RELAX_STATE (COND_JUMP, SMALL) | code16
- : ENCODE_RELAX_STATE (COND_JUMP86, SMALL) | code16)),
- i.op[0].disps->X_add_symbol,
- i.op[0].disps->X_add_number,
- p);
+ frag_var (rs_machine_dependent, 5, i.reloc[0], subtype, sym, off, p);
}
else if (i.tm.opcode_modifier & (JumpByte | JumpDword))
{
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index 0855af0..f9b5811 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -11143,8 +11143,7 @@ md_estimate_size_before_relax (fragp, segtype)
sym = fragp->fr_symbol;
/* Handle the case of a symbol equated to another symbol. */
- while (symbol_equated_p (sym)
- && (! S_IS_DEFINED (sym) || S_IS_COMMON (sym)))
+ while (symbol_equated_reloc_p (sym))
{
symbolS *n;
diff --git a/gas/expr.c b/gas/expr.c
index 4258868..4f7f225 100644
--- a/gas/expr.c
+++ b/gas/expr.c
@@ -1699,21 +1699,6 @@ expr (rankarg, resultP)
}
}
- if (retval == undefined_section)
- {
- if (SEG_NORMAL (rightseg))
- retval = rightseg;
- }
- else if (! SEG_NORMAL (retval))
- retval = rightseg;
- else if (SEG_NORMAL (rightseg)
- && retval != rightseg
-#ifdef DIFF_EXPR_OK
- && op_left != O_subtract
-#endif
- )
- as_bad (_("operation combines symbols in different segments"));
-
op_right = operator (&op_chars);
know (op_right == O_illegal
@@ -1769,8 +1754,7 @@ expr (rankarg, resultP)
&& resultP->X_op == O_symbol
&& (symbol_get_frag (right.X_add_symbol)
== symbol_get_frag (resultP->X_add_symbol))
- && SEG_NORMAL (S_GET_SEGMENT (right.X_add_symbol)))
-
+ && SEG_NORMAL (rightseg))
{
resultP->X_add_number -= right.X_add_number;
resultP->X_add_number += (S_GET_VALUE (resultP->X_add_symbol)
@@ -1865,7 +1849,14 @@ expr (rankarg, resultP)
if (op_left == O_add)
resultP->X_add_number += right.X_add_number;
else if (op_left == O_subtract)
- resultP->X_add_number -= right.X_add_number;
+ {
+ resultP->X_add_number -= right.X_add_number;
+ if (retval == rightseg && SEG_NORMAL (retval))
+ {
+ retval = absolute_section;
+ rightseg = absolute_section;
+ }
+ }
}
else
{
@@ -1877,6 +1868,21 @@ expr (rankarg, resultP)
resultP->X_unsigned = 1;
}
+ if (retval != rightseg)
+ {
+ if (! SEG_NORMAL (retval))
+ {
+ if (retval != undefined_section || SEG_NORMAL (rightseg))
+ retval = rightseg;
+ }
+ else if (SEG_NORMAL (rightseg)
+#ifdef DIFF_EXPR_OK
+ && op_left != O_subtract
+#endif
+ )
+ as_bad (_("operation combines symbols in different segments"));
+ }
+
op_left = op_right;
} /* While next operator is >= this rank. */
diff --git a/gas/symbols.c b/gas/symbols.c
index 1f4e988..201b03e 100644
--- a/gas/symbols.c
+++ b/gas/symbols.c
@@ -914,13 +914,16 @@ resolve_symbol_value (symp)
case O_symbol:
case O_symbol_rva:
left = resolve_symbol_value (add_symbol);
- do_symbol:
+ seg_left = S_GET_SEGMENT (add_symbol);
+ if (finalize_syms)
+ symp->sy_value.X_op_symbol = NULL;
+ do_symbol:
if (symp->sy_mri_common)
{
/* This is a symbol inside an MRI common section. The
- relocation routines are going to handle it specially.
- Don't change the value. */
+ relocation routines are going to handle it specially.
+ Don't change the value. */
resolved = symbol_resolved_p (add_symbol);
break;
}
@@ -933,31 +936,51 @@ resolve_symbol_value (symp)
copy_symbol_attributes (symp, add_symbol);
}
- /* If we have equated this symbol to an undefined symbol, we
- keep X_op set to O_symbol, and we don't change
- X_add_number. This permits the routine which writes out
- relocation to detect this case, and convert the
- relocation to be against the symbol to which this symbol
- is equated. */
+ /* If we have equated this symbol to an undefined or common
+ symbol, keep X_op set to O_symbol, and don't change
+ X_add_number. This permits the routine which writes out
+ relocation to detect this case, and convert the
+ relocation to be against the symbol to which this symbol
+ is equated. */
if (! S_IS_DEFINED (add_symbol) || S_IS_COMMON (add_symbol))
{
if (finalize_syms)
{
- final_seg = S_GET_SEGMENT (add_symbol);
symp->sy_value.X_op = O_symbol;
symp->sy_value.X_add_symbol = add_symbol;
symp->sy_value.X_add_number = final_val;
+ /* Use X_op_symbol as a flag. */
+ symp->sy_value.X_op_symbol = add_symbol;
+ final_seg = seg_left;
}
final_val = 0;
resolved = symbol_resolved_p (add_symbol);
symp->sy_resolving = 0;
goto exit_dont_set_value;
}
+ else if (finalize_syms && final_seg == expr_section
+ && seg_left != expr_section)
+ {
+ /* If the symbol is an expression symbol, do similarly
+ as for undefined and common syms above. Handles
+ "sym +/- expr" where "expr" cannot be evaluated
+ immediately, and we want relocations to be against
+ "sym", eg. because it is weak. */
+ symp->sy_value.X_op = O_symbol;
+ symp->sy_value.X_add_symbol = add_symbol;
+ symp->sy_value.X_add_number = final_val;
+ symp->sy_value.X_op_symbol = add_symbol;
+ final_seg = seg_left;
+ final_val += symp->sy_frag->fr_address + left;
+ resolved = symbol_resolved_p (add_symbol);
+ symp->sy_resolving = 0;
+ goto exit_dont_set_value;
+ }
else
{
final_val += symp->sy_frag->fr_address + left;
if (final_seg == expr_section || final_seg == undefined_section)
- final_seg = S_GET_SEGMENT (add_symbol);
+ final_seg = seg_left;
}
resolved = symbol_resolved_p (add_symbol);
@@ -1008,42 +1031,49 @@ resolve_symbol_value (symp)
/* Simplify addition or subtraction of a constant by folding the
constant into X_add_number. */
- if (op == O_add || op == O_subtract)
+ if (op == O_add)
{
if (seg_right == absolute_section)
{
- if (op == O_add)
- final_val += right;
- else
- final_val -= right;
- op = O_symbol;
- op_symbol = NULL;
+ final_val += right;
goto do_symbol;
}
- else if (seg_left == absolute_section && op == O_add)
+ else if (seg_left == absolute_section)
{
- op = O_symbol;
final_val += left;
add_symbol = op_symbol;
left = right;
- op_symbol = NULL;
+ seg_left = seg_right;
+ goto do_symbol;
+ }
+ }
+ else if (op == O_subtract)
+ {
+ if (seg_right == absolute_section)
+ {
+ final_val -= right;
goto do_symbol;
}
}
- /* Subtraction is permitted if both operands are in the same
- section. Otherwise, both operands must be absolute. We
- already handled the case of addition or subtraction of a
- constant above. This will probably need to be changed
- for an object file format which supports arbitrary
- expressions, such as IEEE-695. */
- /* Don't emit messages unless we're finalizing the symbol value,
+ /* Equality and non-equality tests are permitted on anything.
+ Subtraction, and other comparison operators are permitted if
+ both operands are in the same section. Otherwise, both
+ operands must be absolute. We already handled the case of
+ addition or subtraction of a constant above. This will
+ probably need to be changed for an object file format which
+ supports arbitrary expressions, such as IEEE-695.
+
+ Don't emit messages unless we're finalizing the symbol value,
otherwise we may get the same message multiple times. */
- if ((seg_left != absolute_section
- || seg_right != absolute_section)
- && (op != O_subtract
+ if (op != O_eq && op != O_ne
+ && (seg_left != absolute_section
+ || seg_right != absolute_section)
+ && ((op != O_subtract
+ && op != O_lt && op != O_le && op != O_ge && op != O_gt)
|| seg_left != seg_right
- || seg_left == undefined_section)
+ || (seg_left == undefined_section
+ && add_symbol != op_symbol))
&& finalize_syms)
{
char *file;
@@ -1085,7 +1115,7 @@ resolve_symbol_value (symp)
if ((op == O_divide || op == O_modulus) && right == 0)
{
/* If seg_right is not absolute_section, then we've
- already issued a warning about using a bad symbol. */
+ already issued a warning about using a bad symbol. */
if (seg_right == absolute_section && finalize_syms)
{
char *file;
@@ -1114,8 +1144,15 @@ resolve_symbol_value (symp)
case O_bit_and: left &= right; break;
case O_add: left += right; break;
case O_subtract: left -= right; break;
- case O_eq: left = left == right ? ~ (offsetT) 0 : 0; break;
- case O_ne: left = left != right ? ~ (offsetT) 0 : 0; break;
+ case O_eq:
+ case O_ne:
+ left = (left == right && seg_left == seg_right
+ && (seg_left != undefined_section
+ || add_symbol == op_symbol)
+ ? ~ (offsetT) 0 : 0);
+ if (symp->sy_value.X_op == O_ne)
+ left = ~left;
+ break;
case O_lt: left = left < right ? ~ (offsetT) 0 : 0; break;
case O_le: left = left <= right ? ~ (offsetT) 0 : 0; break;
case O_ge: left = left >= right ? ~ (offsetT) 0 : 0; break;
@@ -2146,6 +2183,24 @@ symbol_equated_p (s)
return s->sy_value.X_op == O_symbol;
}
+/* Return whether a symbol is equated to another symbol, and should be
+ treated specially when writing out relocs. */
+
+int
+symbol_equated_reloc_p (s)
+ symbolS *s;
+{
+ if (LOCAL_SYMBOL_CHECK (s))
+ return 0;
+ /* X_op_symbol, normally not used for O_symbol, is set by
+ resolve_symbol_value to flag expression syms that have been
+ equated. */
+ return (s->sy_value.X_op == O_symbol
+ && ((s->sy_resolved && s->sy_value.X_op_symbol != NULL)
+ || ! S_IS_DEFINED (s)
+ || S_IS_COMMON (s)));
+}
+
/* Return whether a symbol has a constant value. */
int
diff --git a/gas/symbols.h b/gas/symbols.h
index e798905..a80ca8c 100644
--- a/gas/symbols.h
+++ b/gas/symbols.h
@@ -185,6 +185,7 @@ extern void symbol_mark_resolved PARAMS ((symbolS *));
extern int symbol_resolved_p PARAMS ((symbolS *));
extern int symbol_section_p PARAMS ((symbolS *));
extern int symbol_equated_p PARAMS ((symbolS *));
+extern int symbol_equated_reloc_p PARAMS ((symbolS *));
extern int symbol_constant_p PARAMS ((symbolS *));
#ifdef BFD_ASSEMBLER
diff --git a/gas/write.c b/gas/write.c
index c096eb5..14fbf04 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -769,8 +769,7 @@ adjust_reloc_syms (abfd, sec, xxx)
/* If this symbol is equated to an undefined symbol, convert
the fixup to being against that symbol. */
- if (sym != NULL && symbol_equated_p (sym)
- && (! S_IS_DEFINED (sym) || S_IS_COMMON (sym)))
+ if (sym != NULL && symbol_equated_reloc_p (sym))
{
fixp->fx_offset += symbol_get_value_expression (sym)->X_add_number;
sym = symbol_get_value_expression (sym)->X_add_symbol;
@@ -983,11 +982,10 @@ write_relocs (abfd, sec, xxx)
}
/* If this is an undefined symbol which was equated to another
- symbol, then use generate the reloc against the latter symbol
+ symbol, then generate the reloc against the latter symbol
rather than the former. */
sym = fixp->fx_addsy;
- while (symbol_equated_p (sym)
- && (! S_IS_DEFINED (sym) || S_IS_COMMON (sym)))
+ while (symbol_equated_reloc_p (sym))
{
symbolS *n;
@@ -1059,8 +1057,7 @@ write_relocs (abfd, sec, xxx)
symbol, then generate the reloc against the latter symbol
rather than the former. */
sym = fixp->fx_addsy;
- while (symbol_equated_p (sym)
- && (! S_IS_DEFINED (sym) || S_IS_COMMON (sym)))
+ while (symbol_equated_reloc_p (sym))
sym = symbol_get_value_expression (sym)->X_add_symbol;
fixp->fx_addsy = sym;
@@ -1960,8 +1957,7 @@ write_object_file ()
/* Skip symbols which were equated to undefined or common
symbols. */
- if (symbol_equated_p (symp)
- && (! S_IS_DEFINED (symp) || S_IS_COMMON (symp)))
+ if (symbol_equated_reloc_p (symp))
{
symbol_remove (symp, &symbol_rootP, &symbol_lastP);
continue;
@@ -2264,6 +2260,12 @@ relax_segment (segment_frag_root, segment)
break;
case rs_machine_dependent:
+ /* If fr_symbol is an expression, this call to
+ resolve_symbol_value sets up the correct segment, which will
+ likely be needed in md_estimate_size_before_relax. */
+ if (fragP->fr_symbol)
+ resolve_symbol_value (fragP->fr_symbol);
+
address += md_estimate_size_before_relax (fragP, segment);
break;