diff options
Diffstat (limited to 'gas')
-rw-r--r-- | gas/ChangeLog | 22 | ||||
-rw-r--r-- | gas/config/tc-i386.c | 37 | ||||
-rw-r--r-- | gas/config/tc-mips.c | 3 | ||||
-rw-r--r-- | gas/expr.c | 42 | ||||
-rw-r--r-- | gas/symbols.c | 125 | ||||
-rw-r--r-- | gas/symbols.h | 1 | ||||
-rw-r--r-- | gas/write.c | 20 |
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; @@ -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; |