[v4,5/5] x86-64: Intel64 adjustments for insns dealing with far pointers

Message ID beb6d000-b85a-5e23-56da-4e23db038b3c@suse.com
State Superseded
Headers show
Series
  • x86: operand size handling improvements
Related show

Commit Message

Jan Beulich Feb. 10, 2020, 1:53 p.m.
AMD and Intel differ in their handling of far indirect branches as well
as LFS/LGS/LSS: AMD CPUs ignore REX.W while Intel ones honors it. (Note
how the latter three were hybrids so far, while far branches were fully
AMD-like.)

gas/
2020-01-XX  Jan Beulich  <jbeulich@suse.com>

	PR gas/24546
	* config/tc-i386.c (match_template): Apply AMD64/Intel64 check
	to 64-bit code only.
	* config/tc-i386.c (i386_intel_operand): Also handle CALL/JMP in
	O_tbyte_ptr case.
	* testsuite/gas/i386/x86-64-branch-3.s,
	testsuite/gas/i386/x86-64-intel64.s: Add 64-bit far call cases.
	* testsuite/gas/i386/x86-64-branch-3.d,
	testsuite/gas/i386/x86-64-intel64.d: Adjust expectations.
	* testsuite/gas/i386/x86-64-branch-5.l,
	testsuite/gas/i386/x86-64-branch-5.s: New.
	* testsuite/gas/i386/i386.exp: Run new test.

opcodes/
2020-01-XX  Jan Beulich  <jbeulich@suse.com>

	PR gas/24546
	* i386-dis.c (putop): Handle REX.W in '^' case for Intel64 mode.
	* i386-opc.tbl (lfs, lgs, lss, lcall, ljmp): Split into
	AMD64 and Intel64 templates.
	(call, jmp): Likewise for far indirect variants. Dro
	Unspecified.
	* i386-tbl.h: Re-generate.
---
v4: Add documentation.
v3: New.

Patch

--- a/gas/config/tc-i386-intel.c
+++ b/gas/config/tc-i386-intel.c
@@ -694,9 +694,11 @@  i386_intel_operand (char *operand_string
 	  if (got_a_float == 1)
 	    suffix = LONG_DOUBLE_MNEM_SUFFIX;
 	  else if ((current_templates->start->operand_types[0].bitfield.fword
-		    || current_templates->start->operand_types[0].bitfield.tbyte)
+		    || current_templates->start->operand_types[0].bitfield.tbyte
+		    || current_templates->start->opcode_modifier.jump == JUMP_DWORD
+		    || current_templates->start->opcode_modifier.jump == JUMP)
 		   && flag_code == CODE_64BIT)
-	    suffix = QWORD_MNEM_SUFFIX; /* l[fgs]s, [ls][gi]dt */
+	    suffix = QWORD_MNEM_SUFFIX; /* l[fgs]s, [ls][gi]dt, call, jmp */
 	  else
 	    i.types[this_operand].bitfield.byte = 1; /* cause an error */
 	  break;
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -5788,10 +5788,11 @@  match_template (char mnem_suffix)
 
       /* Check AT&T/Intel syntax and Intel64/AMD64 ISA.   */
       i.error = unsupported_syntax;
-      if ((intel_syntax && t->opcode_modifier.attsyntax)
-	  || (!intel_syntax && t->opcode_modifier.intelsyntax)
-	  || (intel64 && t->opcode_modifier.amd64)
-	  || (!intel64 && t->opcode_modifier.intel64))
+      if ((intel_syntax ? t->opcode_modifier.attsyntax
+			: t->opcode_modifier.intelsyntax)
+	  || (flag_code == CODE_64BIT
+	      && (intel64 ? t->opcode_modifier.amd64
+			  : t->opcode_modifier.intel64)))
 	continue;
 
       /* Check the suffix.  */
--- a/gas/doc/c-i386.texi
+++ b/gas/doc/c-i386.texi
@@ -1454,6 +1454,18 @@  There are some discrepancies between AMD
 @item For @samp{movsxd} with 16-bit destination register, AMD64
 supports 32-bit source operand and Intel64 supports 16-bit source
 operand.
+
+@item For far branches (with explicit memory operand), both ISAs support
+32- and 16-bit operand size.  Intel64 additionally supports 64-bit
+operand size, encoded as @samp{ljmpq} and @samp{lcallq} in AT&T syntax
+and with an explicit @samp{tbyte ptr} operand size specifier in Intel
+syntax.
+
+@item @samp{lfs}, @samp{lgs}, and @samp{lss} similarly allow for 16-
+and 32-bit operand size (32- and 48-bit memory operand) in both ISAs,
+while Intel64 additionally supports 64-bit operand sise (80-bit memory
+operands).
+
 @end itemize
 
 @node i386-Bugs
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -1141,6 +1141,7 @@  if [expr ([istarget "i*86-*-*"] || [ista
 	run_dump_test "x86-64-branch-2"
 	run_dump_test "x86-64-branch-3"
 	run_list_test "x86-64-branch-4" "-al -mintel64"
+	run_list_test "x86-64-branch-5" "-al"
 
 	run_dump_test "x86-64-gotpcrel"
 	run_dump_test "x86-64-gotpcrel-no-relax"
--- a/gas/testsuite/gas/i386/x86-64-branch-3.d
+++ b/gas/testsuite/gas/i386/x86-64-branch-3.d
@@ -16,4 +16,6 @@  Disassembly of section .text:
 [ 	]*[a-f0-9]+:	66 48 e8 00 00 00 00 	data16 rex\.W callq 1c <bar\+0xf>	18: R_X86_64_PLT32	foo-0x4
 [ 	]*[a-f0-9]+:	66 c7 f8 00 00       	xbeginw 21 <bar\+0x14>	1f: R_X86_64_PC16	foo-0x2
 [ 	]*[a-f0-9]+:	66 48 c7 f8 00 00 00 00 	data16 xbeginq 29 <bar\+0x1c>	25: R_X86_64_PLT32	foo-0x4
+[ 	]*[a-f0-9]+:	48 ff 18             	lcallq \*\(%rax\)
+[ 	]*[a-f0-9]+:	48 ff 29             	ljmpq  \*\(%rcx\)
 #pass
--- a/gas/testsuite/gas/i386/x86-64-branch-3.s
+++ b/gas/testsuite/gas/i386/x86-64-branch-3.s
@@ -10,3 +10,6 @@  bar:
 
 	data16 xbegin foo
 	data16 rex.w xbegin foo
+
+	lcallq *(%rax)
+	ljmpq *(%rcx)
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-branch-5.l
@@ -0,0 +1,19 @@ 
+.*: Assembler messages:
+.*:2: Error: unsupported syntax for `lcall'
+.*:3: Error: unsupported syntax for `lfs'
+.*:4: Error: unsupported syntax for `lfs'
+.*:5: Error: unsupported syntax for `lgs'
+.*:6: Error: unsupported syntax for `lgs'
+.*:7: Error: unsupported syntax for `ljmp'
+.*:8: Error: unsupported syntax for `lss'
+.*:9: Error: unsupported syntax for `lss'
+.*:12: Error: unsupported syntax for `call'
+.*:13: Error: unsupported syntax for `lfs'
+.*:14: Error: unsupported syntax for `lfs'
+.*:15: Error: unsupported syntax for `lgs'
+.*:16: Error: unsupported syntax for `lgs'
+.*:17: Error: unsupported syntax for `jmp'
+.*:18: Error: unsupported syntax for `lss'
+.*:19: Error: unsupported syntax for `lss'
+GAS LISTING .*
+#pass
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-branch-5.s
@@ -0,0 +1,19 @@ 
+	.text
+	lcallq	*(%rax)
+	lfs	(%rax), %rax
+	lfsq	(%rax), %rax
+	lgs	(%rax), %rax
+	lgsq	(%rax), %rax
+	ljmpq	*(%rax)
+	lss	(%rax), %rax
+	lssq	(%rax), %rax
+
+	.intel_syntax noprefix
+	call	TBYTE PTR [rax]
+	lfs	rax, [rax]
+	lfs	rax, TBYTE PTR [rax]
+	lgs	rax, [rax]
+	lgs	rax, TBYTE PTR [rax]
+	jmp	TBYTE PTR [rax]
+	lss	rax, [rax]
+	lss	rax, TBYTE PTR [rax]
--- a/gas/testsuite/gas/i386/x86-64-intel64.d
+++ b/gas/testsuite/gas/i386/x86-64-intel64.d
@@ -12,6 +12,8 @@  Disassembly of section .text:
 [ 	]*[a-f0-9]+:	48 0f b5 11          	lgs    \(%rcx\),%rdx
 [ 	]*[a-f0-9]+:	48 0f b2 1a          	lss    \(%rdx\),%rbx
 [ 	]*[a-f0-9]+:	48 0f b2 1a          	lss    \(%rdx\),%rbx
+[ 	]*[a-f0-9]+:	48 ff 18             	rex\.W lcall \*\(%rax\)
+[ 	]*[a-f0-9]+:	48 ff 29             	rex\.W ljmp \*\(%rcx\)
 [ 	]*[a-f0-9]+:	0f 05                	syscall 
 [ 	]*[a-f0-9]+:	0f 07                	sysret 
 [ 	]*[a-f0-9]+:	48 0f 07             	sysretq *
@@ -21,4 +23,6 @@  Disassembly of section .text:
 [ 	]*[a-f0-9]+:	48 0f b5 0a          	lgs    \(%rdx\),%rcx
 [ 	]*[a-f0-9]+:	48 0f b2 13          	lss    \(%rbx\),%rdx
 [ 	]*[a-f0-9]+:	48 0f b2 13          	lss    \(%rbx\),%rdx
+[ 	]*[a-f0-9]+:	48 ff 19             	rex\.W lcall \*\(%rcx\)
+[ 	]*[a-f0-9]+:	48 ff 2a             	rex\.W ljmp \*\(%rdx\)
 #pass
--- a/gas/testsuite/gas/i386/x86-64-intel64.s
+++ b/gas/testsuite/gas/i386/x86-64-intel64.s
@@ -10,6 +10,9 @@  _start:
 	lss	(%rdx), %rbx
 	lssq	(%rdx), %rbx
 
+	lcallq	*(%rax)
+	ljmpq	*(%rcx)
+
 	syscall
 	sysretl
 	sysretq
@@ -21,3 +24,6 @@  _start:
 	lgs	rcx, tbyte ptr [rdx]
 	lss	rdx, [rbx]
 	lss	rdx, tbyte ptr [rbx]
+
+	call	tbyte ptr [rcx]
+	jmp	tbyte ptr [rdx]
--- a/opcodes/i386-dis.c
+++ b/opcodes/i386-dis.c
@@ -2331,8 +2331,8 @@  struct dis386 {
    'Z' => print 'q' in 64bit mode and behave as 'L' otherwise
    '!' => change condition from true to false or from false to true.
    '%' => add 1 upper case letter to the macro.
-   '^' => print 'w' or 'l' depending on operand size prefix or
-	  suffix_always is true (lcall/ljmp).
+   '^' => print 'w', 'l', or 'q' (Intel64 ISA only) depending on operand size
+	  prefix or suffix_always is true (lcall/ljmp).
    '@' => print 'q' for Intel64 ISA, 'w' or 'q' for AMD64 ISA depending
 	  on operand size prefix.
    '&' => print 'q' in 64bit mode for Intel64 ISA or if instruction
@@ -13296,6 +13296,12 @@  case_S:
 	case '^':
 	  if (intel_syntax)
 	    break;
+	  if (isa64 == intel64 && (rex & REX_W))
+	    {
+	      USED_REX (REX_W);
+	      *obufp++ = 'q';
+	      break;
+	    }
 	  if ((prefixes & PREFIX_DATA) || (sizeflag & SUFFIX_ALWAYS))
 	    {
 	      if (sizeflag & DFLAG)
--- a/opcodes/i386-opc.tbl
+++ b/opcodes/i386-opc.tbl
@@ -193,9 +193,12 @@  lea, 2, 0x8d, None, 1, 0, Modrm|Anysize|
 // Load segment registers from memory.
 lds, 2, 0xc5, None, 1, CpuNo64, Modrm|No_bSuf|No_sSuf|No_qSuf|No_ldSuf, { DWord|Fword|Unspecified|BaseIndex, Reg16|Reg32 }
 les, 2, 0xc4, None, 1, CpuNo64, Modrm|No_bSuf|No_sSuf|No_qSuf|No_ldSuf, { DWord|Fword|Unspecified|BaseIndex, Reg16|Reg32 }
-lfs, 2, 0xfb4, None, 2, Cpu386, Modrm|No_bSuf|No_sSuf|No_ldSuf, { DWord|Fword|Tbyte|Unspecified|BaseIndex, Reg16|Reg32|Reg64 }
-lgs, 2, 0xfb5, None, 2, Cpu386, Modrm|No_bSuf|No_sSuf|No_ldSuf, { DWord|Fword|Tbyte|Unspecified|BaseIndex, Reg16|Reg32|Reg64 }
-lss, 2, 0xfb2, None, 2, Cpu386, Modrm|No_bSuf|No_sSuf|No_ldSuf, { DWord|Fword|Tbyte|Unspecified|BaseIndex, Reg16|Reg32|Reg64 }
+lfs, 2, 0xfb4, None, 2, Cpu386, AMD64|Modrm|No_bSuf|No_sSuf|No_qSuf|No_ldSuf, { Dword|Fword|Unspecified|BaseIndex, Reg16|Reg32 }
+lfs, 2, 0xfb4, None, 2, Cpu64, Intel64|Modrm|No_bSuf|No_sSuf|No_ldSuf, { Dword|Fword|Tbyte|Unspecified|BaseIndex, Reg16|Reg32|Reg64 }
+lgs, 2, 0xfb5, None, 2, Cpu386, AMD64|Modrm|No_bSuf|No_sSuf|No_qSuf|No_ldSuf, { Dword|Fword|Unspecified|BaseIndex, Reg16|Reg32 }
+lgs, 2, 0xfb5, None, 2, Cpu64, Intel64|Modrm|No_bSuf|No_sSuf|No_ldSuf, { Dword|Fword|Tbyte|Unspecified|BaseIndex, Reg16|Reg32|Reg64 }
+lss, 2, 0xfb2, None, 2, Cpu386, AMD64|Modrm|No_bSuf|No_sSuf|No_qSuf|No_ldSuf, { Dword|Fword|Unspecified|BaseIndex, Reg16|Reg32 }
+lss, 2, 0xfb2, None, 2, Cpu64, Intel64|Modrm|No_bSuf|No_sSuf|No_ldSuf, { Dword|Fword|Tbyte|Unspecified|BaseIndex, Reg16|Reg32|Reg64 }
 
 // Flags register instructions.
 clc, 0, 0xf8, None, 1, 0, No_bSuf|No_wSuf|No_lSuf|No_sSuf|No_qSuf|No_ldSuf, { 0 }
@@ -371,12 +374,13 @@  call, 1, 0xe8, None, 1, Cpu64, Intel64|J
 call, 1, 0xff, 0x2, 1, CpuNo64, Modrm|JumpAbsolute|DefaultSize|No_bSuf|No_sSuf|No_qSuf|No_ldSuf|BNDPrefixOk|NoTrackPrefixOk, { Reg16|Reg32|Unspecified|BaseIndex }
 call, 1, 0xff, 0x2, 1, Cpu64, AMD64|Modrm|JumpAbsolute|DefaultSize|No_bSuf|No_lSuf|No_sSuf|No_ldSuf|NoRex64|BNDPrefixOk|NoTrackPrefixOk, { Reg16|Reg64|Unspecified|BaseIndex }
 call, 1, 0xff, 0x2, 1, Cpu64, Intel64|Modrm|JumpAbsolute|No_bSuf|No_wSuf|No_lSuf|No_sSuf|No_ldSuf|NoRex64|BNDPrefixOk|NoTrackPrefixOk, { Reg64|Unspecified|BaseIndex }
-// Intel Syntax
+// Intel Syntax remaining call instances.
 call, 2, 0x9a, None, 1, CpuNo64, JumpInterSegment|DefaultSize|No_bSuf|No_sSuf|No_qSuf|No_ldSuf, { Imm16, Imm16|Imm32 }
-// Intel Syntax
-call, 1, 0xff, 0x3, 1, 0, Modrm|JumpAbsolute|DefaultSize|No_bSuf|No_wSuf|No_lSuf|No_sSuf|No_qSuf, { Dword|Fword|Unspecified|BaseIndex }
+call, 1, 0xff, 0x3, 1, 0, AMD64|Modrm|JumpAbsolute|DefaultSize|No_bSuf|No_wSuf|No_lSuf|No_sSuf|No_qSuf, { Dword|Fword|BaseIndex }
+call, 1, 0xff, 0x3, 1, Cpu64, Intel64|Modrm|JumpAbsolute|No_bSuf|No_wSuf|No_lSuf|No_sSuf|No_qSuf|No_ldSuf, { Dword|Fword|Tbyte|BaseIndex }
 lcall, 2, 0x9a, None, 1, CpuNo64, JumpInterSegment|DefaultSize|No_bSuf|No_sSuf|No_qSuf|No_ldSuf, { Imm16, Imm16|Imm32 }
-lcall, 1, 0xff, 0x3, 1, 0, Modrm|JumpAbsolute|DefaultSize|No_bSuf|No_sSuf|No_qSuf|No_ldSuf, { Unspecified|BaseIndex }
+lcall, 1, 0xff, 0x3, 1, 0, AMD64|Modrm|JumpAbsolute|DefaultSize|No_bSuf|No_sSuf|No_qSuf|No_ldSuf, { Unspecified|BaseIndex }
+lcall, 1, 0xff, 0x3, 1, Cpu64, Intel64|Modrm|JumpAbsolute|No_bSuf|No_sSuf|No_ldSuf, { Unspecified|BaseIndex }
 
 jmp, 1, 0xeb, None, 1, CpuNo64, Jump|No_bSuf|No_wSuf|No_lSuf|No_sSuf|No_qSuf|No_ldSuf|BNDPrefixOk, { Disp8|Disp16|Disp32 }
 jmp, 1, 0xeb, None, 1, Cpu64, AMD64|Jump|No_bSuf|No_wSuf|No_lSuf|No_sSuf|No_qSuf|No_ldSuf|BNDPrefixOk, { Disp8|Disp16|Disp32S }
@@ -384,12 +388,13 @@  jmp, 1, 0xeb, None, 1, Cpu64, Intel64|Ju
 jmp, 1, 0xff, 0x4, 1, CpuNo64, Modrm|JumpAbsolute|No_bSuf|No_sSuf|No_qSuf|No_ldSuf|BNDPrefixOk|NoTrackPrefixOk, { Reg16|Reg32|Unspecified|BaseIndex }
 jmp, 1, 0xff, 0x4, 1, Cpu64, AMD64|Modrm|JumpAbsolute|No_bSuf|No_lSuf|No_sSuf|No_ldSuf|NoRex64|BNDPrefixOk|NoTrackPrefixOk, { Reg16|Reg64|Unspecified|BaseIndex }
 jmp, 1, 0xff, 0x4, 1, Cpu64, Intel64|Modrm|JumpAbsolute|No_bSuf|No_wSuf|No_lSuf|No_sSuf|No_ldSuf|NoRex64|BNDPrefixOk|NoTrackPrefixOk, { Reg64|Unspecified|BaseIndex }
-// Intel Syntax.
+// Intel Syntax remaining jmp instances.
 jmp, 2, 0xea, None, 1, CpuNo64, JumpInterSegment|No_bSuf|No_sSuf|No_qSuf|No_ldSuf, { Imm16, Imm16|Imm32 }
-// Intel Syntax.
-jmp, 1, 0xff, 0x5, 1, 0, Modrm|JumpAbsolute|No_bSuf|No_wSuf|No_lSuf|No_sSuf|No_qSuf, { Dword|Fword|Unspecified|BaseIndex }
+jmp, 1, 0xff, 0x5, 1, 0, AMD64|Modrm|JumpAbsolute|No_bSuf|No_wSuf|No_lSuf|No_sSuf|No_qSuf, { Dword|Fword|BaseIndex }
+jmp, 1, 0xff, 0x5, 1, Cpu64, Intel64|Modrm|JumpAbsolute|No_bSuf|No_wSuf|No_lSuf|No_sSuf|No_qSuf|No_ldSuf, { Dword|Fword|Tbyte|BaseIndex }
 ljmp, 2, 0xea, None, 1, CpuNo64, JumpInterSegment|No_bSuf|No_sSuf|No_qSuf|No_ldSuf, { Imm16, Imm16|Imm32 }
-ljmp, 1, 0xff, 0x5, 1, 0, Modrm|JumpAbsolute|No_bSuf|No_sSuf|No_qSuf|No_ldSuf, { Unspecified|BaseIndex }
+ljmp, 1, 0xff, 0x5, 1, 0, AMD64|Modrm|JumpAbsolute|No_bSuf|No_sSuf|No_qSuf|No_ldSuf, { Unspecified|BaseIndex }
+ljmp, 1, 0xff, 0x5, 1, Cpu64, Intel64|Modrm|JumpAbsolute|No_bSuf|No_sSuf|No_ldSuf, { Unspecified|BaseIndex }
 
 ret, 0, 0xc3, None, 1, CpuNo64, DefaultSize|No_bSuf|No_sSuf|No_qSuf|No_ldSuf|RepPrefixOk|BNDPrefixOk, { 0 }
 ret, 1, 0xc2, None, 1, CpuNo64, DefaultSize|No_bSuf|No_sSuf|No_qSuf|No_ldSuf|RepPrefixOk|BNDPrefixOk, { Imm16 }