#9635·ghidra

Regression of GP-7018: operands are swapped in mov instruction

Author: hochwasserCreated Sep 14, 2026Updated Sep 14, 2026
LabelsType: BugFeature: Processor/x86Status: Internal

Describe the bug Commit f9ed6f4 "GP-7018: Fixed register opsize for mov to/from segment registers" introduced a swap of the two operands in a mov instruction. Before the commit, the code reads:

start                                           
       3c0f:03d9 b8 8e 3d        MOV        AX,0x3d8e
       3c0f:03dc 8e d8           MOV        DS,AX
       3c0f:03de c7 06 02        MOV        word ptr [0x2],0x0
                 00 00 00

After the commit:

start                                           
       3c0f:03d9 b8 8e 3d        MOV        AX,0x3d8e
       3c0f:03dc 8e d8           MOV        AX, DS
       3c0f:03de c7 06 02        MOV        word ptr [0x2],0x0
                 00 00 00

To Reproduce

  1. Install Ghidra 12.1.2
  2. Load a real-mode application with a mov instruction that sets a segment register with a normal register
  3. Note the operand order
  4. Install Ghidra 12.1.3
  5. Load the same application from 2.
  6. Note the operand order => the order is swapped

Expected behavior The mov instructions has first the segment register then the normal register

Environment (please complete the following information):

  • OS: Debian testing
  • Java Version: 25.0.4.1
  • Ghidra Version: 12.1.3
  • Ghidra Origin: official GitHub distro

Additional context I tried swapping the segment and register in the ia.sinc for the lines the commit changed. This seems to work. The change is:

--- ia.sinc     2026-09-14 17:24:44.977196694 +0200
+++ ia_fixed.sinc       2026-09-14 16:52:03.758250858 +0200
@@ -4007,10 +4007,10 @@
 :MOV Rmr64,Sreg     is $(LONGMODE_ON) & vexMode=0 & opsize=2 & byte=0x8c; mod=3 & Rmr64 & Sreg    { Rmr64 = zext(Sreg); }
 @endif
 :MOV Sreg,m16       is vexMode=0 & byte=0x8e; (mod != 3 & Sreg) ... & m16                         { Sreg = m16; }
-:MOV Rmr16,Sreg     is vexMode=0 & opsize=0 & byte=0x8e; mod=3 & Rmr16 & Sreg                     { Sreg = Rmr16; }
-:MOV Rmr32,Sreg     is vexMode=0 & opsize=1 & byte=0x8e; mod=3 & Rmr32 & Sreg                     { Sreg = Rmr32(0); }
+:MOV Sreg,Rmr16     is vexMode=0 & opsize=0 & byte=0x8e; mod=3 & Rmr16 & Sreg                     { Sreg = Rmr16; }
+:MOV Sreg,Rmr32     is vexMode=0 & opsize=1 & byte=0x8e; mod=3 & Rmr32 & Sreg                     { Sreg = Rmr32(0); }
 @ifdef IA64
-:MOV Rmr64,Sreg     is $(LONGMODE_ON) & vexMode=0 & opsize=2 & byte=0x8e; mod=3 & Rmr64 & Sreg    { Sreg = Rmr64(0); }
+:MOV Sreg,Rmr64     is $(LONGMODE_ON) & vexMode=0 & opsize=2 & byte=0x8e; mod=3 & Rmr64 & Sreg    { Sreg = Rmr64(0); }
 @endif
 :MOV AL,moffs8      is vexMode=0 & byte=0xa0; AL & moffs8               { AL=moffs8; }
 :MOV AX,moffs16     is vexMode=0 & opsize=0 & byte=0xa1; AX & moffs16         { AX=moffs16; }

But I don't know if this is enough.

Source: NationalSecurityAgency/ghidra