From d50c498a1ba7249ccf1d429ce39ab247219c7205 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Fri, 12 May 2023 08:55:48 +0200 Subject: [PATCH] gas: equates of registers There are two problems: symbol_equated_p() doesn't recognize equates of registers, and S_CAN_BE_REDEFINED() goes by section rather than by expression type. Both together undermine .eqv and .equiv clearly meaning to guard the involved symbols against re-definition (both ways). To compensate pseudo_set() now using O_symbol and S_CAN_BE_REDEFINED() now checking for O_register, - for targets creating register symbols through symbol_{new,create}() -> symbol_init() -> S_SET_VALUE() (alpha, arc, dlx, ia64, m68k, mips, mmix, tic4x, tic54x, plus anything using cgen or itbl-ops), have symbol_init() set their expressions to O_register, - x86'es parse_register() also can't go by section anymore when trying to "look through" equates; probably symbol_equated_p() should have been used there from the beginning, if only that had worked for equates of registers, - various targets need to "look through" equates when parsing insn operands (which also helps transitive forward equates); perhaps even more ought to, but many don't look to consider the possibility of register equates in the first place. This was uncovered by code reported in PR gas/30274 (duplicating PR gas/30272), except that there .eqv was used when really .equ was meant. Therefore that bug report is addressed here only in so far as gas wouldn't crash anymore; the code there still won't assemble successfully, just that now the issues there are properly diagnosed. --- gas/cgen.c | 2 ++ gas/config/tc-alpha.c | 3 +++ gas/config/tc-arc.c | 2 ++ gas/config/tc-dlx.c | 1 + gas/config/tc-i386.c | 4 ++-- gas/config/tc-ia64.c | 1 + gas/config/tc-mmix.c | 2 ++ gas/config/tc-mn10200.c | 1 + gas/config/tc-mn10300.c | 1 + gas/config/tc-msp430.c | 2 ++ gas/config/tc-nds32.c | 2 ++ gas/config/tc-ppc.c | 2 ++ gas/config/tc-s390.c | 5 ++++- gas/config/tc-spu.c | 1 + gas/config/tc-tic4x.c | 1 + gas/config/tc-v850.c | 1 + gas/config/tc-xgate.c | 2 ++ gas/config/tc-z80.c | 1 + gas/expr.c | 25 +++++++++++++++++++++++++ gas/expr.h | 1 + gas/read.c | 4 ++++ gas/symbols.c | 4 +++- 22 files changed, 64 insertions(+), 4 deletions(-) diff --git a/gas/cgen.c b/gas/cgen.c index cbac7452a3c..bf8791fef86 100644 --- a/gas/cgen.c +++ b/gas/cgen.c @@ -385,6 +385,8 @@ gas_cgen_parse_operand (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, /* FIXME: Need to check `want'. */ + resolve_register (&exp); + switch (exp.X_op) { case O_illegal: diff --git a/gas/config/tc-alpha.c b/gas/config/tc-alpha.c index a3c62b2dada..9ae091725c7 100644 --- a/gas/config/tc-alpha.c +++ b/gas/config/tc-alpha.c @@ -987,6 +987,7 @@ tokenize_arguments (char *str, /* First try for parenthesized register ... */ expression (tok); + resolve_register (tok); if (*input_line_pointer == ')' && tok->X_op == O_register) { tok->X_op = (saw_comma ? O_cpregister : O_pregister); @@ -1010,6 +1011,8 @@ tokenize_arguments (char *str, if (tok->X_op == O_illegal || tok->X_op == O_absent) goto err; + resolve_register (tok); + saw_comma = 0; saw_arg = 1; ++tok; diff --git a/gas/config/tc-arc.c b/gas/config/tc-arc.c index 455f5488316..8d16cb57088 100644 --- a/gas/config/tc-arc.c +++ b/gas/config/tc-arc.c @@ -1312,6 +1312,8 @@ tokenize_arguments (char *str, relocation type as well. */ if (*input_line_pointer == '@') parse_reloc_symbol (tok); + else + resolve_register (tok); debug_exp (tok); diff --git a/gas/config/tc-dlx.c b/gas/config/tc-dlx.c index af5633e4abb..9058cc21e3b 100644 --- a/gas/config/tc-dlx.c +++ b/gas/config/tc-dlx.c @@ -632,6 +632,7 @@ parse_operand (char *s, expressionS *operandp) /* Normal operand parsing. */ input_line_pointer = s; (void) expression (operandp); + resolve_register (operandp); } new_pos = input_line_pointer; diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c index 29b7734b1ac..8856ccc495c 100644 --- a/gas/config/tc-i386.c +++ b/gas/config/tc-i386.c @@ -13830,11 +13830,11 @@ parse_register (const char *reg_string, char **end_op) input_line_pointer = buf; get_symbol_name (&name); symbolP = symbol_find (name); - while (symbolP && S_GET_SEGMENT (symbolP) != reg_section) + while (symbolP && symbol_equated_p (symbolP)) { const expressionS *e = symbol_get_value_expression(symbolP); - if (e->X_op != O_symbol || e->X_add_number) + if (e->X_add_number) break; symbolP = e->X_add_symbol; } diff --git a/gas/config/tc-ia64.c b/gas/config/tc-ia64.c index 66b0680c124..0b97986f10b 100644 --- a/gas/config/tc-ia64.c +++ b/gas/config/tc-ia64.c @@ -5987,6 +5987,7 @@ parse_operand (expressionS *e, int more) e->X_op = O_absent; SKIP_WHITESPACE (); expression (e); + resolve_register (e); sep = *input_line_pointer; if (more && (sep == ',' || sep == more)) ++input_line_pointer; diff --git a/gas/config/tc-mmix.c b/gas/config/tc-mmix.c index 1e358abf400..881534509b3 100644 --- a/gas/config/tc-mmix.c +++ b/gas/config/tc-mmix.c @@ -624,6 +624,8 @@ get_putget_operands (struct mmix_opcode *insn, char *operands, regno = get_spec_regno (sregp); *sregend = c; + resolve_register (expp_reg); + /* Let the caller issue errors; we've made sure the operands are invalid. */ if (expp_reg->X_op != O_illegal diff --git a/gas/config/tc-mn10200.c b/gas/config/tc-mn10200.c index bab79040711..c0cf9e5d9f4 100644 --- a/gas/config/tc-mn10200.c +++ b/gas/config/tc-mn10200.c @@ -1025,6 +1025,7 @@ md_assemble (char *str) else { expression (&ex); + resolve_register (&ex); } switch (ex.X_op) diff --git a/gas/config/tc-mn10300.c b/gas/config/tc-mn10300.c index eea903bf3e2..a983c1554c3 100644 --- a/gas/config/tc-mn10300.c +++ b/gas/config/tc-mn10300.c @@ -1669,6 +1669,7 @@ md_assemble (char *str) else { expression (&ex); + resolve_register (&ex); } switch (ex.X_op) diff --git a/gas/config/tc-msp430.c b/gas/config/tc-msp430.c index d6fedd8fcfd..c1604517f67 100644 --- a/gas/config/tc-msp430.c +++ b/gas/config/tc-msp430.c @@ -415,6 +415,8 @@ parse_exp (char * s, expressionS * op) expression (op); if (op->X_op == O_absent) as_bad (_("missing operand")); + else + resolve_register (op); /* Our caller is likely to check that the entire expression was parsed. If we have found a hex constant with an 'h' suffix, ilp will be left diff --git a/gas/config/tc-nds32.c b/gas/config/tc-nds32.c index d8248e2bd03..d576c426d76 100644 --- a/gas/config/tc-nds32.c +++ b/gas/config/tc-nds32.c @@ -2519,6 +2519,7 @@ parse_expression (char *str, expressionS *exp) tmp = input_line_pointer; /* Save line pointer. */ input_line_pointer = str; expression (exp); + resolve_register (exp); s = input_line_pointer; input_line_pointer = tmp; /* Restore line pointer. */ @@ -4571,6 +4572,7 @@ nds32_asm_parse_operand (struct nds32_asm_desc *pdesc ATTRIBUTE_UNUSED, hold = input_line_pointer; input_line_pointer = *pstr; expression (pexp); + resolve_register (pexp); *pstr = input_line_pointer; input_line_pointer = hold; diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c index ae14fae44bd..8b1d995da6d 100644 --- a/gas/config/tc-ppc.c +++ b/gas/config/tc-ppc.c @@ -3475,6 +3475,8 @@ md_assemble (char *str) str = input_line_pointer; input_line_pointer = hold; + resolve_register (&ex); + if (ex.X_op == O_illegal) as_bad (_("illegal operand")); else if (ex.X_op == O_absent) diff --git a/gas/config/tc-s390.c b/gas/config/tc-s390.c index f6bcb9e855e..955851946f9 100644 --- a/gas/config/tc-s390.c +++ b/gas/config/tc-s390.c @@ -1308,7 +1308,10 @@ md_gather_operands (char *str, /* Parse the operand. */ if (! register_name (&ex)) - expression (&ex); + { + expression (&ex); + resolve_register (&ex); + } str = input_line_pointer; input_line_pointer = hold; diff --git a/gas/config/tc-spu.c b/gas/config/tc-spu.c index 96d03b7b5b4..9c1a23c4f5e 100644 --- a/gas/config/tc-spu.c +++ b/gas/config/tc-spu.c @@ -573,6 +573,7 @@ get_reg (const char *param, struct spu_insn *insn, int arg, int accept_expr) expression (&ex); param = input_line_pointer; input_line_pointer = save_ptr; + resolve_register (&ex); if (ex.X_op == O_register || ex.X_op == O_constant) { insn->opcode |= ex.X_add_number << arg_encode[arg].pos; diff --git a/gas/config/tc-tic4x.c b/gas/config/tc-tic4x.c index 72c50068fcf..7dd5e4b6de9 100644 --- a/gas/config/tc-tic4x.c +++ b/gas/config/tc-tic4x.c @@ -649,6 +649,7 @@ tic4x_expression (char *str, expressionS *exp) t = input_line_pointer; /* Save line pointer. */ input_line_pointer = str; expression (exp); + resolve_register (exp); s = input_line_pointer; input_line_pointer = t; /* Restore line pointer. */ return s; /* Return pointer to where parsing stopped. */ diff --git a/gas/config/tc-v850.c b/gas/config/tc-v850.c index 56726e54de9..565f4edb558 100644 --- a/gas/config/tc-v850.c +++ b/gas/config/tc-v850.c @@ -2909,6 +2909,7 @@ md_assemble (char *str) else { expression (&ex); + resolve_register (&ex); if ((operand->flags & V850_NOT_IMM0) && ex.X_op == O_constant diff --git a/gas/config/tc-xgate.c b/gas/config/tc-xgate.c index 5ad6e1b0775..f311ed422e8 100644 --- a/gas/config/tc-xgate.c +++ b/gas/config/tc-xgate.c @@ -893,6 +893,8 @@ xgate_parse_exp (char *s, expressionS * op) expression (op); if (op->X_op == O_absent) as_bad (_("missing operand")); + else + resolve_register (op); return input_line_pointer; } diff --git a/gas/config/tc-z80.c b/gas/config/tc-z80.c index 9e989fb3df5..577c58479da 100644 --- a/gas/config/tc-z80.c +++ b/gas/config/tc-z80.c @@ -926,6 +926,7 @@ parse_exp_not_indexed (const char *s, expressionS *op) } input_line_pointer = (char*) s ; expression (op); + resolve_register (op); switch (op->X_op) { case O_absent: diff --git a/gas/expr.c b/gas/expr.c index 05b210ba98b..055745f4b06 100644 --- a/gas/expr.c +++ b/gas/expr.c @@ -2382,6 +2382,31 @@ resolve_expression (expressionS *expressionP) return 1; } + +/* "Look through" register equates. */ +void resolve_register (expressionS *expP) +{ + symbolS *sym; + offsetT acc = 0; + const expressionS *e = expP; + + if (expP->X_op != O_symbol) + return; + + do + { + sym = e->X_add_symbol; + acc += e->X_add_number; + e = symbol_get_value_expression (sym); + } + while (symbol_equated_p (sym)); + + if (e->X_op == O_register) + { + *expP = *e; + expP->X_add_number += acc; + } +} /* This lives here because it belongs equally in expr.c & read.c. expr.c is just a branch office read.c anyway, and putting it diff --git a/gas/expr.h b/gas/expr.h index 52574bd0115..73bc398a80e 100644 --- a/gas/expr.h +++ b/gas/expr.h @@ -190,5 +190,6 @@ extern symbolS *expr_build_dot (void); extern uint32_t generic_bignum_to_int32 (void); extern uint64_t generic_bignum_to_int64 (void); extern int resolve_expression (expressionS *); +extern void resolve_register (expressionS *); extern bool literal_prefix_dollar_hex; diff --git a/gas/read.c b/gas/read.c index ac39fc9582e..b4b628f2e5c 100644 --- a/gas/read.c +++ b/gas/read.c @@ -4000,6 +4000,10 @@ pseudo_set (symbolS *symbolP) return; } #endif + /* Make sure symbol_equated_p() recognizes the symbol as an equate. */ + exp.X_add_symbol = make_expr_symbol (&exp); + exp.X_add_number = 0; + exp.X_op = O_symbol; symbol_set_value_expression (symbolP, &exp); S_SET_SEGMENT (symbolP, reg_section); set_zero_frag (symbolP); diff --git a/gas/symbols.c b/gas/symbols.c index 308150908c1..a335c316903 100644 --- a/gas/symbols.c +++ b/gas/symbols.c @@ -387,6 +387,8 @@ symbol_init (symbolS *symbolP, const char *name, asection *sec, } S_SET_VALUE (symbolP, valu); + if (sec == reg_section) + symbolP->x->value.X_op = O_register; symbol_clear_list_pointers (symbolP); @@ -2463,7 +2465,7 @@ S_CAN_BE_REDEFINED (const symbolS *s) return (((struct local_symbol *) s)->frag == &predefined_address_frag); /* Permit register names to be redefined. */ - return s->bsym->section == reg_section; + return s->x->value.X_op == O_register; } int -- 2.30.2