[binutils-gdb] x86/Intel: don't modify equates' expressions

Jan Beulich jbeulich@sourceware.org
Mon Apr 20 06:40:15 GMT 2026


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=22f8905d9f38011727c85ab828d2c0aeaf980a83

commit 22f8905d9f38011727c85ab828d2c0aeaf980a83
Author: Jan Beulich <jbeulich@suse.com>
Date:   Mon Apr 20 08:38:35 2026 +0200

    x86/Intel: don't modify equates' expressions
    
    Equates involving registers were mis-treated when parsing insn operand
    expressions: When "pulling out" the register(s), they would have got
    converted to O_constant. While other (local) parsing code was able to cope
    with this, the generic part of the assembler was misled. A visible bad
    effect would be that local absolute symbols would appear in the symbol
    table, when really that should be register symbols (which wouldn't be put
    in the symbol table at all).
    
    Clone symbols / expressions as necessary before modifying them.

Diff:
---
 gas/config/tc-i386-intel.c      | 131 ++++++++++++++++++++++++++++------------
 gas/config/tc-i386.c            |   4 +-
 gas/testsuite/gas/i386/equ2.d   |  10 +++
 gas/testsuite/gas/i386/i386.exp |   1 +
 4 files changed, 105 insertions(+), 41 deletions(-)

diff --git a/gas/config/tc-i386-intel.c b/gas/config/tc-i386-intel.c
index afa6bbf4d25..e3c3613b808 100644
--- a/gas/config/tc-i386-intel.c
+++ b/gas/config/tc-i386-intel.c
@@ -378,30 +378,56 @@ i386_intel_simplify_register (expressionS *e)
   return 2;
 }
 
-static int
-i386_intel_simplify_symbol (symbolS *sym)
+static symbolS *
+i386_intel_simplify_symbol (symbolS *sym, bool in_equate)
 {
   if (symbol_resolving_p (sym))
-    return 1;
+    return sym;
 
-  symbol_mark_resolving (sym);
-  int ret = i386_intel_simplify (symbol_get_value_expression (sym));
-  if (ret == 2)
+  segT seg = S_GET_SEGMENT (sym);
+  if (seg != expr_section && seg != reg_section && !symbol_equated_p(sym))
+    return sym;
+
+  for (;;)
     {
-      S_SET_SEGMENT (sym, absolute_section);
-      ret = 1;
+      /* While we're after equates, symbol_equated_p() isn't suitable here.  */
+      if (symbol_on_chain(sym, symbol_rootP, symbol_lastP))
+	{
+	  in_equate = true;
+	  sym = symbol_clone (sym, 0);
+	}
+      else if (in_equate)
+	{
+	  expressionS *e = symbol_get_value_expression (sym);
+
+	  if (e->X_op == O_symbol && !e->X_add_number)
+	    {
+	      sym = e->X_add_symbol;
+	      continue;
+	    }
+	  sym = make_expr_symbol (e);
+	}
+
+      break;
     }
+
+  symbol_mark_resolving (sym);
+  int ret = i386_intel_simplify (symbol_get_value_expression (sym), in_equate);
+  if (ret == 2)
+    S_SET_SEGMENT (sym, absolute_section);
   symbol_clear_resolving (sym);
-  return ret;
+
+  return ret ? sym : NULL;
 }
 
 static int
-i386_intel_simplify (expressionS *e)
+i386_intel_simplify (expressionS *e, bool in_equate)
 {
   const reg_entry *the_reg = (this_operand >= 0
 			      ? i.op[this_operand].regs : NULL);
   const reg_entry *base = intel_state.base;
   const reg_entry *state_index = intel_state.index;
+  symbolS *newsym;
   int ret;
 
   if (!intel_syntax)
@@ -412,18 +438,21 @@ i386_intel_simplify (expressionS *e)
     case O_index:
       if (e->X_add_symbol)
 	{
-	  if (!i386_intel_simplify_symbol (e->X_add_symbol)
+	  newsym = i386_intel_simplify_symbol (e->X_add_symbol, in_equate);
+	  if (!newsym
 	      || !i386_intel_check(the_reg, intel_state.base,
 				   intel_state.index))
 	    return 0;
+	  e->X_add_symbol = newsym;
 	}
       if (!intel_state.in_offset)
 	++intel_state.in_bracket;
-      ret = i386_intel_simplify_symbol (e->X_op_symbol);
+      newsym = i386_intel_simplify_symbol (e->X_op_symbol, in_equate);
       if (!intel_state.in_offset)
 	--intel_state.in_bracket;
-      if (!ret)
+      if (!newsym)
 	return 0;
+      e->X_op_symbol = newsym;
       if (e->X_add_symbol)
 	e->X_op = O_add;
       else
@@ -433,12 +462,13 @@ i386_intel_simplify (expressionS *e)
     case O_offset:
       intel_state.has_offset = 1;
       ++intel_state.in_offset;
-      ret = i386_intel_simplify_symbol (e->X_add_symbol);
+      newsym = i386_intel_simplify_symbol (e->X_add_symbol, in_equate);
       --intel_state.in_offset;
-      if (!ret || !i386_intel_check(the_reg, base, state_index))
+      if (!newsym || !i386_intel_check(the_reg, base, state_index))
 	return 0;
+      e->X_add_symbol = newsym;
       i386_intel_fold (e, e->X_add_symbol);
-      return ret;
+      return 1;
 
     case O_byte_ptr:
     case O_word_ptr:
@@ -461,8 +491,10 @@ i386_intel_simplify (expressionS *e)
 	  as_bad (_("invalid use of register"));
 	  return 0;
 	}
-      if (!i386_intel_simplify_symbol (e->X_add_symbol))
+      newsym = i386_intel_simplify_symbol (e->X_add_symbol, in_equate);
+      if (!newsym)
 	return 0;
+      e->X_add_symbol = newsym;
       i386_intel_fold (e, e->X_add_symbol);
       break;
 
@@ -473,10 +505,12 @@ i386_intel_simplify (expressionS *e)
 	  as_bad (_("invalid use of register"));
 	  return 0;
 	}
-      if (!i386_intel_simplify_symbol (e->X_op_symbol)
+      newsym = i386_intel_simplify_symbol (e->X_op_symbol, in_equate);
+      if (!newsym
 	  || !i386_intel_check(the_reg, intel_state.base,
 			       intel_state.index))
 	return 0;
+      e->X_op_symbol = newsym;
       if (!intel_state.in_offset)
 	{
 	  if (!intel_state.seg)
@@ -503,16 +537,26 @@ i386_intel_simplify (expressionS *e)
 	  if (!intel_state.in_scale++)
 	    intel_state.scale_factor = 1;
 
-	  ret = i386_intel_simplify_symbol (e->X_add_symbol);
-	  if (ret && !has_index && intel_state.index)
-	    scale = symbol_get_value_expression (e->X_op_symbol);
+	  newsym = i386_intel_simplify_symbol (e->X_add_symbol, in_equate);
+	  if (newsym)
+	    {
+	      e->X_add_symbol = newsym;
 
-	  if (ret)
-	    ret = i386_intel_simplify_symbol (e->X_op_symbol);
-	  if (ret && !scale && !has_index && intel_state.index)
-	    scale = symbol_get_value_expression (e->X_add_symbol);
+	      if (!has_index && intel_state.index)
+		scale = symbol_get_value_expression (e->X_op_symbol);
+
+	      newsym = i386_intel_simplify_symbol (e->X_op_symbol, in_equate);
+	    }
+
+	  if (newsym)
+	    {
+	      e->X_op_symbol = newsym;
+
+	      if (!scale && !has_index && intel_state.index)
+		scale = symbol_get_value_expression (e->X_add_symbol);
+	    }
 
-	  if (ret && scale)
+	  if (newsym && scale)
 	    {
 	      resolve_expression (scale);
 	      if (scale->X_op != O_constant
@@ -522,7 +566,7 @@ i386_intel_simplify (expressionS *e)
 	    }
 
 	  --intel_state.in_scale;
-	  if (!ret)
+	  if (!newsym)
 	    return 0;
 
 	  if (!intel_state.in_scale)
@@ -568,9 +612,13 @@ i386_intel_simplify (expressionS *e)
       /* FALLTHROUGH */
     default:
     fallthrough:
-      if (e->X_add_symbol
-	  && !i386_intel_simplify_symbol (e->X_add_symbol))
-	return 0;
+      if (e->X_add_symbol)
+	{
+	  newsym = i386_intel_simplify_symbol (e->X_add_symbol, in_equate);
+	  if (!newsym)
+	    return 0;
+	  e->X_add_symbol = newsym;
+	}
       if (!the_reg && this_operand >= 0
 	  && e->X_op == O_symbol && !e->X_add_number)
 	the_reg = i.op[this_operand].regs;
@@ -579,14 +627,19 @@ i386_intel_simplify (expressionS *e)
 	  base = intel_state.base;
 	  state_index = intel_state.index;
 	}
-      if (!i386_intel_check (the_reg, base, state_index)
-	  || (e->X_op_symbol
-	      && !i386_intel_simplify_symbol (e->X_op_symbol))
-	  || !i386_intel_check (the_reg,
-				(e->X_op != O_add
-				 ? base : intel_state.base),
-				(e->X_op != O_add
-				 ? state_index : intel_state.index)))
+      if (!i386_intel_check (the_reg, base, state_index))
+	return 0;
+      if (e->X_op_symbol)
+	{
+	  newsym = i386_intel_simplify_symbol (e->X_op_symbol, in_equate);
+	  if (!newsym)
+	    return 0;
+	  e->X_op_symbol = newsym;
+	}
+      if (!i386_intel_check (the_reg,
+			     e->X_op != O_add ? base : intel_state.base,
+			     (e->X_op != O_add ? state_index
+					       : intel_state.index)))
 	return 0;
       break;
     }
@@ -654,7 +707,7 @@ i386_intel_operand (char *operand_string, int got_a_float)
   expr_mode = expr_operator_none;
   memset (&exp, 0, sizeof(exp));
   exp_seg = expression (&exp);
-  ret = i386_intel_simplify (&exp);
+  ret = i386_intel_simplify (&exp, false);
   intel_syntax = 1;
 
   SKIP_WHITESPACE ();
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 988b024f0b8..d3287513a75 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -167,7 +167,7 @@ static int i386_finalize_displacement (segT, expressionS *, i386_operand_type,
 				       const char *);
 static int i386_att_operand (char *);
 static int i386_intel_operand (char *, int);
-static int i386_intel_simplify (expressionS *);
+static int i386_intel_simplify (expressionS *, bool);
 static int i386_intel_parse_name (const char *, expressionS *, enum expr_mode);
 static const reg_entry *parse_register (const char *, char **);
 static const char *parse_insn (const char *, char *, enum parse_mode);
@@ -13527,7 +13527,7 @@ x86_cons (expressionS *exp, int size)
   intel_syntax = -intel_syntax;
 
   if (intel_syntax)
-    i386_intel_simplify (exp);
+    i386_intel_simplify (exp, false);
 
   /* If not 64bit, massage value, to account for wraparound when !BFD64.  */
   if (size <= 4 && expr_mode == expr_operator_present
diff --git a/gas/testsuite/gas/i386/equ2.d b/gas/testsuite/gas/i386/equ2.d
new file mode 100644
index 00000000000..8471def1f6c
--- /dev/null
+++ b/gas/testsuite/gas/i386/equ2.d
@@ -0,0 +1,10 @@
+#source: equ.s
+#objdump: -t
+#name: i386 equates (symtab check)
+
+.*: +file format .*
+
+SYMBOL TABLE:
+.* \.text	.*
+!.* \*ABS\*	.*
+#pass
diff --git a/gas/testsuite/gas/i386/i386.exp b/gas/testsuite/gas/i386/i386.exp
index fdf0e4ac0be..0c964225f89 100644
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -726,6 +726,7 @@ if [gas_32_check] then {
 	run_dump_test "pcrel-elf"
 	run_dump_test "relax"
 	run_dump_test "gotpc"
+	run_dump_test "equ2"
 	run_dump_test "tlsd"
 	run_dump_test "tlspic"
 	run_dump_test "tlsnopic"


More information about the Binutils-cvs mailing list