[8/8] x86/Intel: make PC-relative expressions work (again?)

Message ID b7b1111e-093d-430f-92fd-57fbb3357a78@suse.com
State New
Headers
Series gas/x86: towards better Intel syntax expression evaluation |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 fail Patch failed to apply

Commit Message

Jan Beulich March 27, 2026, 2:14 p.m. UTC
  Associating fixups with too complex expressions will prevent the detection
and conversion of expressions which are actually PC-relative. Such a
situation can arise when converting O_index expressions: An extra O_add
(with a constant) or O_symbol is already "too complex", let alone an
O_multiply by zero.
  

Patch

--- a/gas/config/tc-i386-intel.c
+++ b/gas/config/tc-i386-intel.c
@@ -279,7 +279,9 @@  static INLINE int i386_intel_check (cons
 static INLINE void i386_intel_fold (expressionS *e, symbolS *sym)
 {
   expressionS *exp = symbol_get_value_expression (sym);
-  if (S_GET_SEGMENT (sym) == absolute_section)
+
+  if (S_GET_SEGMENT (sym) == absolute_section
+      || S_GET_SEGMENT (sym) == expr_section)
     {
       offsetT val = e->X_add_number;
 
@@ -453,10 +455,22 @@  i386_intel_simplify (expressionS *e, boo
       if (!newsym)
 	return 0;
       e->X_op_symbol = newsym;
-      if (e->X_add_symbol)
-	e->X_op = O_add;
-      else
+      if (!e->X_add_symbol)
 	i386_intel_fold (e, e->X_op_symbol);
+      else if (S_GET_SEGMENT (e->X_add_symbol) == expr_section
+	       && (S_GET_SEGMENT (e->X_op_symbol) == absolute_section
+		   || S_GET_SEGMENT (e->X_op_symbol) == expr_section)
+	       && symbol_get_value_expression (e->X_op_symbol)->X_op
+		  == O_constant)
+	{
+	  offsetT val
+	    = symbol_get_value_expression (e->X_op_symbol)->X_add_number;
+
+	  i386_intel_fold (e, e->X_add_symbol);
+	  e->X_add_number += val;
+	}
+      else
+	e->X_op = O_add;
       break;
 
     case O_offset:
@@ -528,10 +542,68 @@  i386_intel_simplify (expressionS *e, boo
       i386_intel_fold (e, e->X_op_symbol);
       break;
 
+    case O_add:
+      if (this_operand >= 0 && intel_state.in_bracket)
+	{
+	  expressionS *left = NULL, *right = NULL;
+	  segT leftseg = NULL, rightseg = NULL;
+
+	  newsym = i386_intel_simplify_symbol (e->X_add_symbol, in_equate);
+	  if (newsym)
+	    {
+	      e->X_add_symbol = newsym;
+
+	      if (base != intel_state.base || state_index != intel_state.index)
+		{
+		  base = intel_state.base;
+		  state_index = intel_state.index;
+		  left = symbol_get_value_expression (newsym);
+		  resolve_expression (left);
+		  leftseg = S_GET_SEGMENT (newsym);
+		}
+	    }
+
+	  newsym = i386_intel_simplify_symbol (e->X_op_symbol, in_equate);
+	  if (newsym)
+	    {
+	      e->X_op_symbol = newsym;
+
+	      if (base != intel_state.base || state_index != intel_state.index)
+		{
+		  base = intel_state.base;
+		  state_index = intel_state.index;
+		  right = symbol_get_value_expression (newsym);
+		  resolve_expression (right);
+		  rightseg = S_GET_SEGMENT (newsym);
+		}
+	    }
+
+	  if (left && right
+	      && (leftseg == absolute_section || leftseg == expr_section)
+	      && left->X_op == O_constant
+	      && (rightseg == absolute_section || rightseg == expr_section)
+	      && right->X_op == O_constant)
+	    {
+	       e->X_op = O_constant;
+	       e->X_add_number += left->X_add_number + right->X_add_number;
+	       e->X_add_symbol = NULL;
+	       e->X_op_symbol = NULL;
+	    }
+	  else if (left
+		   && (leftseg == absolute_section || leftseg == expr_section)
+		   && left->X_op == O_constant)
+	    i386_intel_fold (e, e->X_op_symbol);
+	  else if (right
+		   && (rightseg == absolute_section || rightseg == expr_section)
+		   && right->X_op == O_constant)
+	    i386_intel_fold (e, e->X_add_symbol);
+	}
+      goto fallthrough;
+
     case O_multiply:
       if (this_operand >= 0 && intel_state.in_bracket)
 	{
-	  expressionS *scale = NULL;
+	  expressionS *scale = NULL, *other = NULL;
 	  int has_index = (intel_state.index != NULL);
 
 	  if (!intel_state.in_scale++)
@@ -543,7 +615,10 @@  i386_intel_simplify (expressionS *e, boo
 	      e->X_add_symbol = newsym;
 
 	      if (!has_index && intel_state.index)
-		scale = symbol_get_value_expression (e->X_op_symbol);
+		{
+		  scale = symbol_get_value_expression (e->X_op_symbol);
+		  other = symbol_get_value_expression (e->X_add_symbol);
+		}
 
 	      newsym = i386_intel_simplify_symbol (e->X_op_symbol, in_equate);
 	    }
@@ -553,16 +628,32 @@  i386_intel_simplify (expressionS *e, boo
 	      e->X_op_symbol = newsym;
 
 	      if (!scale && !has_index && intel_state.index)
-		scale = symbol_get_value_expression (e->X_add_symbol);
+		{
+		  scale = symbol_get_value_expression (e->X_add_symbol);
+		  other = symbol_get_value_expression (e->X_op_symbol);
+		}
 	    }
 
 	  if (newsym && scale)
 	    {
+	      segT seg;
+
 	      resolve_expression (scale);
 	      if (scale->X_op != O_constant
 		  || intel_state.index->reg_type.bitfield.word)
 		scale->X_add_number = 0;
 	      intel_state.scale_factor *= scale->X_add_number;
+
+	      resolve_expression (other);
+	      seg = S_GET_SEGMENT (newsym);
+	      if ((seg == absolute_section || seg == expr_section)
+		  && other->X_op == O_constant
+		  && other->X_add_number == 0)
+		{
+		  e->X_op = O_constant;
+		  e->X_add_symbol = NULL;
+		  e->X_op_symbol = NULL;
+		}
 	    }
 
 	  --intel_state.in_scale;
@@ -646,7 +737,7 @@  i386_intel_simplify (expressionS *e, boo
 
   if (this_operand >= 0
       && e->X_op == O_symbol
-      && !intel_state.in_offset)
+      && !intel_state.has_offset)
     {
       segT seg = S_GET_SEGMENT (e->X_add_symbol);
 
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -67,6 +67,7 @@  if [gas_32_check] then {
     run_list_test "intelbad" ""
     run_dump_test "intelok"
     run_dump_test "intelok2"
+    run_dump_test "intelpcrel"
     run_dump_test "prefix"
     run_list_test "prefix32" "-al -march=+mpx"
     run_dump_test "insn-32"
--- /dev/null
+++ b/gas/testsuite/gas/i386/intelpcrel.d
@@ -0,0 +1,33 @@ 
+#objdump: -dwMintel
+#name: i386 intel-PC-rel
+#notarget: *-*-msdos* *-*-*go32* *-*-bsd* *-*-darwin*
+
+.*: +file format .*
+
+Disassembly of section .text:
+
+0+ <\.text>:
+[ 	]*[0-9a-f]+:	b8 0[15] 00 00 00[ 	]+mov[ 	]+eax,(0x)?[15]
+[ 	]*[0-9a-f]+:	b8 0[ae] 00 00 00[ 	]+mov[ 	]+eax,0x[ae]
+[ 	]*[0-9a-f]+:	8d 91 (0c|10) 00 00 00[ 	]+lea[ 	]+edx,\[ecx\+0x(c|10)\]
+[ 	]*[0-9a-f]+:	8d 91 1[6a] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\+0x1[6a]\]
+[ 	]*[0-9a-f]+:	8d 91 (1c|20) 00 00 00[ 	]+lea[ 	]+edx,\[ecx\+0x(1c|20)\]
+[ 	]*[0-9a-f]+:	8d 14 8d (1f|23) 00 00 00[ 	]+lea[ 	]+edx,\[ecx\*4\+0x(1f|23)\]
+[ 	]*[0-9a-f]+:	8d 14 8d 2[6a] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\*4\+0x2[6a]\]
+[ 	]*[0-9a-f]+:	8d 14 8d (2d|31) 00 00 00[ 	]+lea[ 	]+edx,\[ecx\*4\+0x(2d|31)\]
+[ 	]*[0-9a-f]+:	8d 14 8d 3[48] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\*4\+0x3[48]\]
+[ 	]*[0-9a-f]+:	8d 14 8d 3[bf] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\*4\+0x3[bf]\]
+[ 	]*[0-9a-f]+:	8d 91 4[15] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\+0x4[15]\]
+[ 	]*[0-9a-f]+:	8d 91 4[7b] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\+0x4[7b]\]
+[ 	]*[0-9a-f]+:	8d 91 (4d|51) 00 00 00[ 	]+lea[ 	]+edx,\[ecx\+0x(4d|51)\]
+[ 	]*[0-9a-f]+:	8d 91 5[37] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\+0x5[37]\]
+[ 	]*[0-9a-f]+:	8d 91 5[9d] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\+0x5[9d]\]
+[ 	]*[0-9a-f]+:	8d 14 8d 6[04] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\*4\+0x6[04]\]
+[ 	]*[0-9a-f]+:	8d 14 8d 6[7b] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\*4\+0x6[7b]\]
+[ 	]*[0-9a-f]+:	8d 14 8d (6e|72) 00 00 00[ 	]+lea[ 	]+edx,\[ecx\*4\+0x(6e|72)\]
+[ 	]*[0-9a-f]+:	8d 14 8d 7[59] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\*4\+0x7[59]\]
+[ 	]*[0-9a-f]+:	8d 14 8d (7c|80) 00 00 00[ 	]+lea[ 	]+edx,\[ecx\*4\+0x(7c|80)\]
+[ 	]*[0-9a-f]+:	8d 94 31 8[37] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\+esi\*1\+0x8[37]\]
+[ 	]*[0-9a-f]+:	8d 94 b1 8[ae] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\+esi\*4\+0x8[ae]\]
+[ 	]*[0-9a-f]+:	8d 94 b1 9[59] 00 00 00[ 	]+lea[ 	]+edx,\[ecx\+esi\*4\+0x9[59]\]
+#pass
--- /dev/null
+++ b/gas/testsuite/gas/i386/intelpcrel.s
@@ -0,0 +1,36 @@ 
+	.intel_syntax noprefix
+
+	.data
+0:	.long 0
+1:	.long 0
+
+	.text
+.Lorg:
+	mov	eax, 0b - .Lorg
+	mov	eax, 1b - .Lorg
+
+	lea	edx, 0b - .Lorg[ecx]
+	lea	edx, 1b - .Lorg[ecx]
+	lea	edx, 0b - .Lorg[ecx][4]
+
+	lea	edx, 0b - .Lorg[ecx*4]
+	lea	edx, 0b - .Lorg[4*ecx]
+	lea	edx, 0b - .Lorg[2*2*ecx]
+	lea	edx, 0b - .Lorg[2*ecx*2]
+	lea	edx, 0b - .Lorg[ecx*2*2]
+
+	lea	edx, [(0b - .Lorg) + ecx]
+	lea	edx, [0b - .Lorg + ecx]
+	lea	edx, [ecx + (0b - .Lorg)]
+	lea	edx, [ecx + 0b - .Lorg]
+	lea	edx, [0b + ecx - .Lorg]
+
+	lea	edx, [(0b - .Lorg) + ecx*4]
+	lea	edx, [0b - .Lorg + ecx*4]
+	lea	edx, [ecx*4 + (0b - .Lorg)]
+	lea	edx, [ecx*4 + 0b - .Lorg]
+	lea	edx, [0b + ecx*4 - .Lorg]
+
+	lea	edx, 0b - .Lorg[ecx][esi]
+	lea	edx, 0b - .Lorg[ecx][esi*4]
+	lea	edx, 0b - .Lorg[ecx][esi*4][4]