[PATCH] x86: accept LOCK on control register accesses only with ModR/M.reg == 0

Jan Beulich <[email protected]>
Newsgroups gmane.comp.gnu.binutils
Message-ID <[email protected]>
Reportedly (e.g. [1]) the LOCK handling is special to %cr0 / %cr8 only.
Deal with it this way also in assembler and disassembler.

For the assembler also introduce a separate feature indicator: Not all
64-bit CPUs support this insn form; only most AMD (and presumably all
Hygon) ones do. Register names %cr9 ... %cr15 thus become invalid outside
of 64-bit mode altogether (unprefixed forms become ordinary symbol names),
while %cr8's availability outside of 64-bit mode now depends on the new
feature indicator.

For the disassembler don't limit this handling to non-64-bit modes. Use
of LOCK is similarly permitted in 64-bit mode. Instead don't handle LOCK
this way when "intel64" was specified as an option.

[1] https://lists.xen.org/archives/html/xen-devel/2026-07/msg00391.html

--- a/gas/doc/c-i386.texi
+++ b/gas/doc/c-i386.texi
@@ -153,6 +153,7 @@ accept various extension mnemonics.  For
 @code{sse4},
 @code{avx},
 @code{avx2},
+@code{altmovcr8},
 @code{lahf_sahf},
 @code{monitor},
 @code{adx},
@@ -1708,9 +1709,9 @@ supported on the CPU specified.  The cho
 @item @samp{.rdrnd} @tab @samp{.f16c} @tab @samp{.avx2} @tab @samp{.bmi2}
 @item @samp{.lzcnt} @tab @samp{.popcnt} @tab @samp{.invpcid} @tab @samp{.vmfunc}
 @item @samp{.monitor} @tab @samp{.hle} @tab @samp{.rtm} @tab @samp{.tsx}
-@item @samp{.lahf_sahf} @tab @samp{.adx} @tab @samp{.rdseed} @tab @samp{.prfchw}
-@item @samp{.smap} @tab @samp{.mpx} @tab @samp{.sha} @tab @samp{.prefetchwt1}
-@item @samp{.clflushopt} @tab @samp{.xsavec} @tab @samp{.xsaves} @tab @samp{.se1}
+@item @samp{.altmovcr8} @tab @samp{.lahf_sahf} @tab @samp{.adx} @tab @samp{.rdseed}
+@item @samp{.smap} @tab @samp{.mpx} @tab @samp{.sha} @tab @samp{.prfchw}
+@item @samp{.prefetchwt1} @tab @samp{.clflushopt} @tab @samp{.xsavec} @tab @samp{.xsaves}
 @item @samp{.avx512f} @tab @samp{.avx512cd} @tab @samp{.avx512er} @tab @samp{.avx512pf}
 @item @samp{.avx512vl} @tab @samp{.avx512bw} @tab @samp{.avx512dq} @tab @samp{.avx512ifma}
 @item @samp{.avx512vbmi} @tab @samp{.avx512_4fmaps} @tab @samp{.avx512_4vnniw}
@@ -1719,7 +1720,7 @@ supported on the CPU specified.  The cho
 @item @samp{.tdx} @tab @samp{.avx_vnni} @tab @samp{.avx512_fp16} @tab @samp{avx512_bmm}
 @item @samp{.avx10.1} @tab @samp{.clwb} @tab @samp{.rdpid} @tab @samp{.ptwrite}
 @item @samp{.ibt} @tab @samp{.prefetchi} @tab @samp{.avx_ifma} @tab @samp{.avx_vnni_int8}
-@item @samp{.cmpccxadd} @tab @samp{.wrmsrns} @tab @samp{.msrlist}
+@item @samp{.cmpccxadd} @tab @samp{.wrmsrns} @tab @samp{.msrlist} @tab @samp{.se1}
 @item @samp{.avx_ne_convert} @tab @samp{.rao_int} @tab @samp{.fred} @tab @samp{.lkgs}
 @item @samp{.avx_vnni_int16} @tab @samp{.sha512} @tab @samp{.sm3} @tab @samp{.sm4}
 @item @samp{.pbndkb} @tab @samp{.user_msr} @tab @samp{.msr_imm} @tab @samp{.avx10.2}
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -1147,6 +1147,7 @@ static const arch_entry cpu_arch[] =
   SUBARCH (lwp, LWP, ANY_LWP, false),
   SUBARCH (movbe, MOVBE, MOVBE, false),
   SUBARCH (cx16, CX16, CX16, false),
+  SUBARCH (altmovcr8, ALTMOVCR8, ALTMOVCR8, false),
   SUBARCH (lahf_sahf, LAHF_SAHF, LAHF_SAHF, false),
   SUBARCH (ept, EPT, ANY_EPT, false),
   SUBARCH (lzcnt, LZCNT, LZCNT, false),
@@ -11655,6 +11656,7 @@ build_modrm_byte (void)
   if (flag_code != CODE_64BIT && (i.rex & REX_R))
     {
       gas_assert (i.types[!i.tm.opcode_modifier.regmem].bitfield.class == RegCR);
+      gas_assert (i.op[!i.tm.opcode_modifier.regmem].regs->reg_num == 0);
       i.rex &= ~REX_R;
       add_prefix (LOCK_PREFIX_OPCODE);
     }
@@ -17001,10 +17003,11 @@ static bool check_register (const reg_en
     }
 
   if (((r->reg_flags & (RegRex64 | RegRex)) || r->reg_type.bitfield.qword)
-      && (!cpu_arch_flags.bitfield.cpu64
+      && flag_code != CODE_64BIT
+      && (!cpu_arch_flags.bitfield.cpualtmovcr8
 	  || r->reg_type.bitfield.class != RegCR
-	  || dot_insn ())
-      && flag_code != CODE_64BIT)
+	  || r->reg_num != 0
+	  || dot_insn ()))
     return false;
 
   if (r->reg_type.bitfield.class == SReg && r->reg_num == RegFlat
--- a/gas/testsuite/gas/i386/cr-err.l
+++ b/gas/testsuite/gas/i386/cr-err.l
@@ -2,7 +2,7 @@
 .*:[0-9]+: Error: .\(%cr0\). is not a valid base/index expression
 .*:[0-9]+: Error: .\(%cr7\). is not a valid base/index expression
 .*:[0-9]+: Error: .\(%cr8\). is not a valid base/index expression
-.*:[0-9]+: Error: .\(%cr15\). is not a valid base/index expression
+.*:[0-9]+: Error: bad register name `%cr15.*'
 .*:[0-9]+: Error: .\(%db0\). is not a valid base/index expression
 .*:[0-9]+: Error: .\(%db7\). is not a valid base/index expression
 .*:[0-9]+: Error: .\(%dr0\). is not a valid base/index expression
@@ -12,7 +12,6 @@
 .*:[0-9]+: Error: .\(cr0\). is not a valid base/index expression
 .*:[0-9]+: Error: .\(cr7\). is not a valid base/index expression
 .*:[0-9]+: Error: .\(cr8\). is not a valid base/index expression
-.*:[0-9]+: Error: .\(cr15\). is not a valid base/index expression
 .*:[0-9]+: Error: .\(db0\). is not a valid base/index expression
 .*:[0-9]+: Error: .\(db7\). is not a valid base/index expression
 .*:[0-9]+: Error: .\(dr0\). is not a valid base/index expression
@@ -22,7 +21,6 @@
 .*:[0-9]+: Error: .\[cr0\]. is not a valid base/index expression
 .*:[0-9]+: Error: .\[cr7\]. is not a valid base/index expression
 .*:[0-9]+: Error: .\[cr8\]. is not a valid base/index expression
-.*:[0-9]+: Error: .\[cr15\]. is not a valid base/index expression
 .*:[0-9]+: Error: .\[dr0\]. is not a valid base/index expression
 .*:[0-9]+: Error: .\[dr7\]. is not a valid base/index expression
 .*:[0-9]+: Error: .\[tr0\]. is not a valid base/index expression
--- a/gas/testsuite/gas/i386/x86-64-crx.d
+++ b/gas/testsuite/gas/i386/x86-64-crx.d
@@ -11,6 +11,8 @@ Disassembly of section .text:
 [ 	]*[0-9a-f]+:	44 0f 20 c7[ 	]+movq?[ 	]+?%cr8,%rdi
 [ 	]*[0-9a-f]+:	44 0f 22 c0[ 	]+movq?[ 	]+?%rax,%cr8
 [ 	]*[0-9a-f]+:	44 0f 22 c7[ 	]+movq?[ 	]+?%rdi,%cr8
+[ 	]*[0-9a-f]+:	f0 0f 20 c1[ 	]+movq?[ 	]+%cr8,%rcx
+[ 	]*[0-9a-f]+:	f0 44 0f 20 c1[ 	]+lock movq?[ 	]+%cr8,%rcx
 [ 	]*[0-9a-f]+:	44 0f 20 c0[ 	]+movq?[ 	]+?%cr8,%rax
 [ 	]*[0-9a-f]+:	44 0f 20 c7[ 	]+movq?[ 	]+?%cr8,%rdi
 [ 	]*[0-9a-f]+:	44 0f 22 c0[ 	]+movq?[ 	]+?%rax,%cr8
--- a/gas/testsuite/gas/i386/x86-64-crx.s
+++ b/gas/testsuite/gas/i386/x86-64-crx.s
@@ -5,6 +5,9 @@ _start:
 	movq	%rax, %cr8
 	movq	%rdi, %cr8
 
+	lock; mov %cr0, %rcx
+	lock; mov %cr8, %rcx
+
 .att_syntax noprefix
 	movq	cr8, rax
 	movq	cr8, rdi
--- a/gas/testsuite/gas/i386/x86-64-crx-suffix.d
+++ b/gas/testsuite/gas/i386/x86-64-crx-suffix.d
@@ -11,6 +11,8 @@ Disassembly of section .text:
 [ 	]*[0-9a-f]+:	44 0f 20 c7[ 	]+movq[ 	]+?%cr8,%rdi
 [ 	]*[0-9a-f]+:	44 0f 22 c0[ 	]+movq[ 	]+?%rax,%cr8
 [ 	]*[0-9a-f]+:	44 0f 22 c7[ 	]+movq[ 	]+?%rdi,%cr8
+[ 	]*[0-9a-f]+:	f0 0f 20 c1[ 	]+movq[ 	]+%cr8,%rcx
+[ 	]*[0-9a-f]+:	f0 44 0f 20 c1[ 	]+lock movq[ 	]+%cr8,%rcx
 [ 	]*[0-9a-f]+:	44 0f 20 c0[ 	]+movq[ 	]+?%cr8,%rax
 [ 	]*[0-9a-f]+:	44 0f 20 c7[ 	]+movq[ 	]+?%cr8,%rdi
 [ 	]*[0-9a-f]+:	44 0f 22 c0[ 	]+movq[ 	]+?%rax,%cr8
--- a/opcodes/i386-dis.c
+++ b/opcodes/i386-dis.c
@@ -13297,7 +13297,11 @@ OP_C (instr_info *ins, int dummy ATTRIBU
       USED_REX (REX_R);
       add = 8;
     }
-  else if (ins->address_mode != mode_64bit && (ins->prefixes & PREFIX_LOCK))
+  else if (/* Only %cr0 -> %cr8 is dealt with this way (and also really only on
+	      most AMD hardware).  */
+	   ins->modrm.reg == 0
+	   && (ins->prefixes & PREFIX_LOCK)
+	   && ins->isa64 != intel64)
     {
       ins->all_prefixes[ins->last_lock_prefix] = 0;
       ins->used_prefixes |= PREFIX_LOCK;
--- a/opcodes/i386-gen.c
+++ b/opcodes/i386-gen.c
@@ -392,6 +392,7 @@ static bitfield cpu_flags[] =
   BITFIELD (TBM),
   BITFIELD (Movbe),
   BITFIELD (CX16),
+  BITFIELD (AltMovCr8),
   BITFIELD (LAHF_SAHF),
   BITFIELD (EPT),
   BITFIELD (Rdtscp),
--- a/opcodes/i386-opc.h
+++ b/opcodes/i386-opc.h
@@ -135,6 +135,8 @@ enum i386_cpu
   CpuMovbe,
   /* CMPXCHG16B instruction support required.  */
   CpuCX16,
+  /* MOV to/from %cr8 instruction support required (in 32-bit mode).  */
+  CpuAltMovCr8,
   /* LAHF/SAHF instruction support required (in 64-bit mode).  */
   CpuLAHF_SAHF,
   /* EPT Instructions required */
@@ -470,6 +472,7 @@ typedef union i386_cpu_flags
       unsigned int cputbm:1;
       unsigned int cpumovbe:1;
       unsigned int cpucx16:1;
+      unsigned int cpualtmovcr8:1;
       unsigned int cpulahf_sahf:1;
       unsigned int cpuept:1;
       unsigned int cpurdtscp:1;
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.