Skip to content

Proposal for improving x86 eflags/fpuflags #2776

@hainest

Description

@hainest

The current set of eflags is incomplete; it doesn't include any of the System/IOPL flags. It also includes access specifiers that aren't particularly valuable for semantic analysis (e.g., X86_EFLAGS_PRIOR* that indicate reserved bits should be set to their prior value). There are also register/access combinations that aren't used by any instructions that I could find in the latest SDM[1] or in the datafiles from Xed v2024.11.04 (e.g., X86_EFLAGS_SET_IF).

It's not possible to fix these within the confines of the current implementation because the X86_EFLAGS macros already use 58 of the available 64 bits, macros can't be removed, and the unused/unusable ones can't be set to a dummy value (this could lead to redundant switch cases in user code). We're left with a need for a new implementation. Here is my proposal based on what Zydis does (code comments omitted for brevity).

                         // shift step could be 1?
#define CS_CPUFLAG_CF    (1UL <<  0)
#define CS_CPUFLAG_PF    (1UL <<  2)
#define CS_CPUFLAG_AF    (1UL <<  4)
#define CS_CPUFLAG_ZF    (1UL <<  6)
#define CS_CPUFLAG_SF    (1UL <<  7)
#define CS_CPUFLAG_TF    (1UL <<  8)
#define CS_CPUFLAG_IF    (1UL <<  9)
#define CS_CPUFLAG_DF    (1UL << 10)
#define CS_CPUFLAG_OF    (1UL << 11)
#define CS_CPUFLAG_IOPL  (1UL << 12)
#define CS_CPUFLAG_NT    (1UL << 14)
#define CS_CPUFLAG_RF    (1UL << 16)
#define CS_CPUFLAG_VM    (1UL << 17)
#define CS_CPUFLAG_AC    (1UL << 18)
#define CS_CPUFLAG_VIF   (1UL << 19)
#define CS_CPUFLAG_VIP   (1UL << 20)
#define CS_CPUFLAG_ID    (1UL << 21)

#define CS_FPUFLAG_C0    (1UL <<  0)
#define CS_FPUFLAG_C1    (1UL <<  1)
#define CS_FPUFLAG_C2    (1UL <<  2)
#define CS_FPUFLAG_C3    (1UL <<  3)

typedef struct cs_cpu_flag_state {
  uint32_t tested;
  uint32_t modified;
  uint32_t set_0;
  uint32_t set_1;
  uint32_t undefined;
} cs_cpu_flag_state;

typedef struct cs_cpu_flags {
  cs_cpu_flag_state eflags;
  cs_cpu_flag_state fpu_flags;
} cs_cpu_flags;

typedef struct cs_x86 {
  // ...
	union {
    /// EFLAGS updated by this instruction.
    /// This can be formed from OR combination of X86_EFLAGS_* symbols in x86.h
    uint64_t eflags;
    /// FPU_FLAGS updated by this instruction.
    /// This can be formed from OR combination of X86_FPU_FLAGS_* symbols in x86.h
    uint64_t fpu_flags;
    /// Version 2 of all cpu flags updated by this instruction.
    cs_cpu_flags const* flags;
	};
  // ...
} cs_x86;

As long as sizeof(void*) == sizeof(uint64_t), this wouldn't be an ABI break.

The hard part will be determining how to fill out both flag inputs. One possibility would be to simply duplicate the information in X86MappingInsnOp.inc.

// X86Mapping.c

typedef struct insn_op {
  uint64_t flags;	// how this instruction update EFLAGS(arithmetic instructions) of FPU FLAGS(for FPU instructions)
  uint8_t access[6];
  cs_cpu_flags flags_v2;
} insn_op;


// X86MappingInsnOp.inc
{	/* X86_COM_FIr, X86_INS_FCOMI: fcomi */
  {X86_EFLAGS_MODIFY_CF | X86_EFLAGS_MODIFY_PF | X86_EFLAGS_RESET_AF | X86_EFLAGS_MODIFY_ZF | X86_EFLAGS_RESET_SF | X86_EFLAGS_MODIFY_OF},
  { CS_AC_READ | CS_AC_WRITE, 0 },
  {
   {// CPU
    {}, // tested
    {CS_CPUFLAG_CF | CS_CPUFLAG_PF | CS_CPUFLAG_ZF}, // modified
    {CS_CPUFLAG_AF | CS_CPUFLAG_SF | CS_CPUFLAG_OF}, // set_0
    {}, // set_1
    {}, // undefined
   },
   {// FPU
    {}, // tested
    {}, // modified
    {}, // set_0
    {CS_FPUFLAG_C1}, // set_1
    {}, // undefined
   }
  },
},

Of course, nearly all instructions wouldn't need to completely fill out the FPU part. This is a "maximal" example that also solves #2680.

There are lots of details left out like how to manage moving this information into the frontend. But my goal is to start a conversation on how we might be able to do this without needing to wait for a big rewrite like moving to Zydis (ping #2505).


Here is the complete set of flags and specifiers from Xed:
Note: pop and ah are really mod, 0 is reset, and 1 is set.

eflags
----------------------------------
ac = {mod, pop, tst}
af = {0, ah, mod, pop, tst, u}
cf = {0, 1, ah, mod, pop, tst, u}
df = {0, 1, mod, pop, tst}
id = {mod, pop, tst}
if = {0, mod, pop, tst}
iopl = {mod, pop, tst}
nt = {mod, pop, tst}
of = {0, mod, pop, tst, u}
pf = {0, ah, mod, pop, tst, u}
rf = {0, mod, pop, tst}
sf = {0, ah, mod, pop, tst, u}
tf = {0, mod, pop, tst}
vif = {mod, pop, tst}
vip = {mod, pop, tst}
vm = {0, mod, pop, tst}
zf = {ah, mod, pop, tst, u}

fpu flags
----------------------------------
fc0 = {mod, u}
fc1 = {mod, u}
fc2 = {mod, u}
fc3 = {mod, u}
Image Image

[1] Intel 64 and IA-32 Architectures Software Developer’s Manual
Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4
June 2025

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions