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

Jan Beulich via Binutils-cvs <[email protected]>
Newsgroups gmane.comp.gnu.binutils.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=b0a3af6eb0a19eee3565877218f38ee31bfe7c55

commit b0a3af6eb0a19eee3565877218f38ee31bfe7c55
Author: Jan Beulich <[email protected]>
Date:   Mon Apr 20 08:39:12 2026 +0200

    x86/Intel: make PC-relative expressions work (again?)
    
    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.

Diff:
---
 gas/config/tc-i386-intel.c          | 107 +++++++++++++++++++++++++++++++++---
 gas/testsuite/gas/i386/i386.exp     |   1 +
 gas/testsuite/gas/i386/intelpcrel.d |  33 +++++++++++
 gas/testsuite/gas/i386/intelpcrel.s |  36 ++++++++++++
 4 files changed, 169 insertions(+), 8 deletions(-)

diff --git a/gas/config/tc-i386-intel.c b/gas/config/tc-i386-intel.c
index e3c3613b808..09248e8f8f9 100644
--- a/gas/config/tc-i386-intel.c
+++ b/gas/config/tc-i386-intel.c
@@ -279,7 +279,9 @@ static INLINE int i386_intel_check (const reg_entry *rreg,
 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, bool in_equate)
       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, bool in_equate)
       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, bool in_equate)
 	      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, bool in_equate)
 	      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, bool in_equate)
 
   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);
 
diff --git a/gas/testsuite/gas/i386/i386.exp b/gas/testsuite/gas/i386/i386.exp
index 0c964225f89..80ae827e0a2 100644
--- 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"
diff --git a/gas/testsuite/gas/i386/intelpcrel.d b/gas/testsuite/gas/i386/intelpcrel.d
new file mode 100644
index 00000000000..81548a05222
--- /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
diff --git a/gas/testsuite/gas/i386/intelpcrel.s b/gas/testsuite/gas/i386/intelpcrel.s
new file mode 100644
index 00000000000..e5ca9f21a25
--- /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]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.