From: Jan Beulich Date: Fri, 7 Mar 2025 07:30:36 +0000 (+0100) Subject: gas: leave expression symbols alone when processing equates X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9479e1deba8c30f7756f1d1d8a219d6ee0e570d9;p=thirdparty%2Fbinutils-gdb.git gas: leave expression symbols alone when processing equates PR gas/32721 In this bogus piece of code distilled from fuzzing and slightly edited: A=%eax|%! Y=A Z=A or $6,Z the first of the equates with A on the rhs changes A's section (due to the use of S_GET_VALUE()), from expression to register, thus yielding Y in the expression section (and X_op being O_symbol), but Z in the register section (and X_op being O_register with X_add_value being -1). There shouldn't be random O_register expressions, though, for targets setting md_register_arithmetic to false. Plus both Y and Z would better be exchangeable. In pseudo_set() wire handling of O_symbol expressions referencing a symbol in the expression section to that of other stuff ending up in this section. Also avoid bogus O_register expressions to be created, for targets setting md_register_arithmetic to false: S_GET_VALUE() would resolve any arithmetic, which must not happen for such targets. To be on the safe side for such targets, also amend resolve_register(). Correct another earlier oversight there too (affecting at least Z80), by using the new expr_copy() helper there as well. Undo 46b9f07dfe79 ("PR 32721, internal error in tc-i386.c:parse_register"), albeit without losing the simplification it did. --- diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c index 2aab0e99f01..3879001ee9e 100644 --- a/gas/config/tc-i386.c +++ b/gas/config/tc-i386.c @@ -16847,9 +16847,9 @@ parse_register (const char *reg_string, char **end_op) { const expressionS *e = symbol_get_value_expression (symbolP); - if (e->X_op == O_register - && (valueT) e->X_add_number < i386_regtab_size) + if (e->X_op == O_register) { + know ((valueT) e->X_add_number < i386_regtab_size); r = i386_regtab + e->X_add_number; *end_op = (char *) reg_string + (input_line_pointer - buf); } diff --git a/gas/expr.c b/gas/expr.c index 099be4361b4..bf58fc3a768 100644 --- a/gas/expr.c +++ b/gas/expr.c @@ -769,6 +769,16 @@ expr_build_dot (void) return expr_build_uconstant (abs_section_offset); } +/* Copy an expression, preserving X_md. */ + +static void expr_copy (expressionS *dst, const expressionS *src) +{ + unsigned short md = dst->X_md; + + *dst = *src; + dst->X_md = md; +} + #ifndef md_register_arithmetic # define md_register_arithmetic 1 #endif @@ -1396,8 +1406,17 @@ operand (expressionS *expressionP, enum expr_mode mode) } else if (!expr_defer_p (mode) && segment == reg_section) { - expressionP->X_op = O_register; - expressionP->X_add_number = S_GET_VALUE (symbolP); + if (md_register_arithmetic) + { + expressionP->X_op = O_register; + expressionP->X_add_number = S_GET_VALUE (symbolP); + } + else + { + expr_copy (expressionP, + symbol_get_value_expression (symbolP)); + resolve_register (expressionP); + } } else { @@ -2489,6 +2508,8 @@ void resolve_register (expressionS *expP) do { + if (!md_register_arithmetic && e->X_add_number) + break; sym = e->X_add_symbol; acc += e->X_add_number; e = symbol_get_value_expression (sym); @@ -2497,7 +2518,7 @@ void resolve_register (expressionS *expP) if (e->X_op == O_register) { - *expP = *e; + expr_copy (expP, e); expP->X_add_number += acc; } } diff --git a/gas/read.c b/gas/read.c index e451721aff9..2acb4e03a7f 100644 --- a/gas/read.c +++ b/gas/read.c @@ -4111,6 +4111,8 @@ pseudo_set (symbolS *symbolP) case O_symbol: seg = S_GET_SEGMENT (exp.X_add_symbol); + if (seg == expr_section) + goto expr; /* For x=undef+const, create an expression symbol. For x=x+const, just update x except when x is an undefined symbol For x=defined+const, evaluate x. */ @@ -4142,6 +4144,7 @@ pseudo_set (symbolS *symbolP) break; default: + expr: /* The value is some complex expression. */ S_SET_SEGMENT (symbolP, expr_section); symbol_set_value_expression (symbolP, &exp); diff --git a/gas/testsuite/gas/i386/i386.exp b/gas/testsuite/gas/i386/i386.exp index 329efc71b01..9b51d4946e2 100644 --- a/gas/testsuite/gas/i386/i386.exp +++ b/gas/testsuite/gas/i386/i386.exp @@ -848,6 +848,9 @@ run_list_test "unspec" "" run_dump_test "fp" run_dump_test "cond" run_dump_test pr30248 +if {![is_aout_format]} then { + run_list_test pr32721 "" +} if {[is_elf_format] || [istarget "*-*-vxworks*"]} then { run_list_test_stdin "list-1" "-al" run_list_test_stdin "list-2" "-al" diff --git a/gas/testsuite/gas/i386/pr32721.l b/gas/testsuite/gas/i386/pr32721.l new file mode 100644 index 00000000000..54532bc5c3c --- /dev/null +++ b/gas/testsuite/gas/i386/pr32721.l @@ -0,0 +1,7 @@ +.*: Assembler messages: +.*:1: Error: bad expression +.*:1: Warning: missing operand; zero assumed +.*:1: Error: invalid operands .* for `|' +#?.*: Error: invalid operands .* when setting .* +#?.*: Error: invalid operands .* when setting .* +#?.*: Error: invalid operands .* when setting .* diff --git a/gas/testsuite/gas/i386/pr32721.s b/gas/testsuite/gas/i386/pr32721.s new file mode 100644 index 00000000000..c66e3710514 --- /dev/null +++ b/gas/testsuite/gas/i386/pr32721.s @@ -0,0 +1,7 @@ + A=%eax|%! + Y=A + Z=A + + .text +pr32721: + orl $6,Z