From b871849b6c9c8c76523c19a94e0118daa8d58d6c Mon Sep 17 00:00:00 2001 From: raaller Date: Fri, 18 Sep 2026 15:29:39 +0300 Subject: [PATCH] x86: fix JO/JNO operand size in 64-bit mode by adding Intel/AMD mode flags (#3067) * x86: fix JO/JNO operand size in 64-bit mode * x86: add explicit Intel/AMD near-Jcc decoding modes --------- Co-authored-by: Rot127 --- arch/X86/X86ATTInstPrinter.c | 22 ++- arch/X86/X86Disassembler.c | 10 +- arch/X86/X86DisassemblerDecoder.c | 43 +++-- arch/X86/X86DisassemblerDecoder.h | 6 +- arch/X86/X86IntelInstPrinter.c | 21 ++- arch/X86/X86Mapping.c | 37 +++-- arch/X86/X86Mapping.h | 10 ++ arch/X86/X86Module.c | 16 +- bindings/python/capstone/__init__.py | 4 + cs.c | 2 +- docs/cs_v6_release_guide.md | 4 + include/capstone/capstone.h | 7 + suite/cstest/include/test_mapping.h | 2 + tests/features/x86_jcc_mode.yaml | 236 +++++++++++++++++++++++++++ tests/issues/issues.yaml | 22 +++ 15 files changed, 383 insertions(+), 59 deletions(-) create mode 100644 tests/features/x86_jcc_mode.yaml diff --git a/arch/X86/X86ATTInstPrinter.c b/arch/X86/X86ATTInstPrinter.c index b6087d8fe..0d85cfb86 100644 --- a/arch/X86/X86ATTInstPrinter.c +++ b/arch/X86/X86ATTInstPrinter.c @@ -75,7 +75,7 @@ static void set_mem_access(MCInst *MI, bool status) static void printopaquemem(MCInst *MI, unsigned OpNo, SStream *O) { - switch (MI->csh->mode) { + switch (x86_get_bit_mode(MI->csh->mode)) { case CS_MODE_16: switch (MI->flat_insn->id) { default: @@ -185,7 +185,7 @@ static void printf32mem(MCInst *MI, unsigned OpNo, SStream *O) case X86_FSTENVm: case X86_FLDENVm: // TODO: fix this in tablegen instead - switch (MI->csh->mode) { + switch (x86_get_bit_mode(MI->csh->mode)) { default: // never reach break; case CS_MODE_16: @@ -438,7 +438,7 @@ static void printDstIdx(MCInst *MI, unsigned Op, SStream *O) } // DI accesses are always ES-based on non-64bit mode - if (MI->csh->mode != CS_MODE_64) { + if (!(x86_has_feature(MI->csh->mode, CS_MODE_64))) { SStream_concat0(O, "%es:("); if (MI->csh->detail_opt) { MI->flat_insn->detail->x86 @@ -562,8 +562,10 @@ static void printMemOffset(MCInst *MI, unsigned Op, SStream *O) .operands[MI->flat_insn->detail->x86.op_count] .mem.disp = imm; if (imm < 0) { - SStream_concat(O, "0x%" PRIx64, - arch_masks[MI->csh->mode] & imm); + SStream_concat( + O, "0x%" PRIx64, + arch_masks[x86_get_bit_mode(MI->csh->mode)] & + imm); } else { if (imm > HEX_THRESHOLD) SStream_concat(O, "0x%" PRIx64, imm); @@ -635,8 +637,11 @@ static void printPCRelImm(MCInst *MI, unsigned OpNo, SStream *O) MI->address; // truncate imm for non-64bit - if (MI->csh->mode != CS_MODE_64) { + if (!(x86_has_feature(MI->csh->mode, CS_MODE_64))) { imm = imm & 0xffffffff; + } else if ((x86_has_feature(MI->csh->mode, CS_MODE_X86_AMD)) && + MI->imm_size == 2) { + imm &= 0xffff; } if (imm < 0) { @@ -937,7 +942,8 @@ static void printMemReference(MCInst *MI, unsigned Op, SStream *O) if (DispVal < 0) { SStream_concat( O, "0x%" PRIx64, - arch_masks[MI->csh->mode] & + arch_masks[x86_get_bit_mode( + MI->csh->mode)] & DispVal); } else { if (DispVal > HEX_THRESHOLD) @@ -1049,7 +1055,7 @@ void X86_ATT_printInst(MCInst *MI, SStream *OS, void *info) // TODO: Probably this hack should be redesigned via InstAlias in // InstrInfo.td as soon as Requires clause is supported properly // for InstAlias. - if (MI->csh->mode == CS_MODE_64 && + if ((x86_has_feature(MI->csh->mode, CS_MODE_64)) && MCInst_getOpcode(MI) == X86_CALLpcrel32) { SStream_concat0(OS, "callq\t"); MCInst_setOpcodePub(MI, X86_INS_CALL); diff --git a/arch/X86/X86Disassembler.c b/arch/X86/X86Disassembler.c index 635064e56..fb6f12025 100644 --- a/arch/X86/X86Disassembler.c +++ b/arch/X86/X86Disassembler.c @@ -1347,15 +1347,7 @@ bool X86_getInstruction(csh ud, const uint8_t *code, size_t code_len, //memset(instr->flat_insn->detail, 0, offsetof(cs_detail, x86)+offsetof(cs_x86, operands)); } - if (handle->mode & CS_MODE_16) - ret = decodeInstruction(&insn, reader, &info, address, - MODE_16BIT); - else if (handle->mode & CS_MODE_32) - ret = decodeInstruction(&insn, reader, &info, address, - MODE_32BIT); - else - ret = decodeInstruction(&insn, reader, &info, address, - MODE_64BIT); + ret = decodeInstruction(&insn, reader, &info, address, handle->mode); if (ret) { // *size = (uint16_t)(insn.readerCursor - address); diff --git a/arch/X86/X86DisassemblerDecoder.c b/arch/X86/X86DisassemblerDecoder.c index 8d58cd410..2011474b3 100644 --- a/arch/X86/X86DisassemblerDecoder.c +++ b/arch/X86/X86DisassemblerDecoder.c @@ -1139,7 +1139,7 @@ static uint16_t resolveMandatoryPrefixConflict(struct InternalInstruction *insn, * @return - 0 if the ModR/M could be read when needed or was not needed; * nonzero otherwise. */ -static int getID(struct InternalInstruction *insn) +static int getID(struct InternalInstruction *insn, cs_mode mode) { uint16_t attrMask; uint16_t instructionID; @@ -1273,8 +1273,8 @@ static int getID(struct InternalInstruction *insn) attrMask ^= ATTR_ADSIZE; /* - * In 64-bit mode all f64 superscripted opcodes ignore opcode size prefix - * CALL/JMP/JCC instructions need to ignore 0x66 and consume 4 bytes + * CALL/JMP ignore 66 in 64-bit mode. Near Jcc preserve their previous + * behavior unless an Intel or AMD mode is selected. */ if ((insn->mode == MODE_64BIT) && insn->hasOpSize) { switch (insn->opcode) { @@ -1287,6 +1287,8 @@ static int getID(struct InternalInstruction *insn) insn->displacementSize = 4; } break; + case 0x80: + case 0x81: case 0x82: case 0x83: case 0x84: @@ -1302,7 +1304,25 @@ static int getID(struct InternalInstruction *insn) case 0x8E: case 0x8F: // Take care of lea and three byte ops. - if (insn->opcodeType == TWOBYTE) { + if (insn->opcodeType != TWOBYTE) + break; + + if ((x86_has_feature(mode, CS_MODE_X86_INTEL) || + x86_has_feature(mode, CS_MODE_X86_AMD)) && + insn->vectorExtensionType == TYPE_NO_VEX_XOP) { + if ((x86_has_feature(mode, CS_MODE_X86_AMD)) && + !wFromREX(insn->rexPrefix)) { + attrMask |= ATTR_OPSIZE; + insn->immediateSize = 2; + insn->displacementSize = 2; + insn->immSize = 2; + } else { + attrMask &= ~ATTR_OPSIZE; + insn->immediateSize = 4; + insn->displacementSize = 4; + insn->immSize = 8; + } + } else if (insn->opcode >= 0x82) { attrMask ^= ATTR_OPSIZE; insn->immediateSize = 4; insn->displacementSize = 4; @@ -2507,22 +2527,25 @@ static bool checkPrefix(struct InternalInstruction *insn) * any internal state. * @param startLoc - The address (in the reader's address space) of the first * byte in the instruction. - * @param mode - The mode (real mode, IA-32e, or IA-32e in 64-bit mode) to - * decode the instruction in. + * @param mode - Capstone mode flags. * @return - 0 if instruction is valid; nonzero if not. */ int decodeInstruction(struct InternalInstruction *insn, byteReader_t reader, - const void *readerArg, uint64_t startLoc, - DisassemblerMode mode) + const void *readerArg, uint64_t startLoc, cs_mode mode) { insn->reader = reader; insn->readerArg = readerArg; insn->startLocation = startLoc; insn->readerCursor = startLoc; - insn->mode = mode; + if (x86_has_feature(mode, CS_MODE_16)) + insn->mode = MODE_16BIT; + else if (x86_has_feature(mode, CS_MODE_32)) + insn->mode = MODE_32BIT; + else + insn->mode = MODE_64BIT; insn->numImmediatesConsumed = 0; - if (readPrefixes(insn) || readOpcode(insn) || getID(insn) || + if (readPrefixes(insn) || readOpcode(insn) || getID(insn, mode) || insn->instructionID == 0 || checkPrefix(insn) || readOperands(insn)) return -1; diff --git a/arch/X86/X86DisassemblerDecoder.h b/arch/X86/X86DisassemblerDecoder.h index 1b665c34d..91083f879 100644 --- a/arch/X86/X86DisassemblerDecoder.h +++ b/arch/X86/X86DisassemblerDecoder.h @@ -25,6 +25,7 @@ #include #endif +#include "../../include/capstone/capstone.h" #include "X86DisassemblerDecoderCommon.h" /* @@ -700,12 +701,11 @@ typedef struct InternalInstruction { * specific to the logger. May be NULL. * @param startLoc - The address (in the reader's address space) of the first * byte in the instruction. - * @param mode - The mode (16-bit, 32-bit, 64-bit) to decode in. + * @param mode - Capstone mode flags. * @return - Nonzero if there was an error during decode, 0 otherwise. */ int decodeInstruction(struct InternalInstruction *insn, byteReader_t reader, - const void *readerArg, uint64_t startLoc, - DisassemblerMode mode); + const void *readerArg, uint64_t startLoc, cs_mode mode); //const char *x86DisassemblerGetInstrName(unsigned Opcode, const void *mii); diff --git a/arch/X86/X86IntelInstPrinter.c b/arch/X86/X86IntelInstPrinter.c index 9450e5b0c..d66ec449c 100644 --- a/arch/X86/X86IntelInstPrinter.c +++ b/arch/X86/X86IntelInstPrinter.c @@ -93,7 +93,7 @@ static void printopaquemem(MCInst *MI, unsigned OpNo, SStream *O) break; } - switch (MI->csh->mode) { + switch (x86_get_bit_mode(MI->csh->mode)) { case CS_MODE_16: switch (MI->flat_insn->id) { default: @@ -209,7 +209,7 @@ static void printf32mem(MCInst *MI, unsigned OpNo, SStream *O) case X86_FSTENVm: case X86_FLDENVm: // TODO: fix this in tablegen instead - switch (MI->csh->mode) { + switch (x86_get_bit_mode(MI->csh->mode)) { default: // never reach break; case CS_MODE_16: @@ -564,7 +564,7 @@ static void printDstIdx(MCInst *MI, unsigned Op, SStream *O) } // DI accesses are always ES-based on non-64bit mode - if (MI->csh->mode != CS_MODE_64) { + if (!(x86_has_feature(MI->csh->mode, CS_MODE_64))) { SStream_concat0(O, "es:["); if (MI->csh->detail_opt) { MI->flat_insn->detail->x86 @@ -700,7 +700,10 @@ static void printMemOffset(MCInst *MI, unsigned Op, SStream *O) .mem.disp = imm; if (imm < 0) - printImm(MI, O, arch_masks[MI->csh->mode] & imm, true); + printImm(MI, O, + arch_masks[x86_get_bit_mode(MI->csh->mode)] & + imm, + true); else printImm(MI, O, imm, true); } @@ -865,8 +868,11 @@ static void printPCRelImm(MCInst *MI, unsigned OpNo, SStream *O) uint8_t opsize = X86_immediate_size(MI->Opcode, NULL); // truncate imm for non-64bit - if (MI->csh->mode != CS_MODE_64) { + if (!(x86_has_feature(MI->csh->mode, CS_MODE_64))) { imm = imm & 0xffffffff; + } else if ((x86_has_feature(MI->csh->mode, CS_MODE_X86_AMD)) && + MI->imm_size == 2) { + imm &= 0xffff; } printImm(MI, O, imm, true); @@ -1205,11 +1211,12 @@ static void printMemReference(MCInst *MI, unsigned Op, SStream *O) } } else { // memory reference to an immediate address - if (MI->csh->mode == CS_MODE_64) + if (x86_has_feature(MI->csh->mode, CS_MODE_64)) MI->op1_size = 8; if (DispVal < 0) { printImm(MI, O, - arch_masks[MI->csh->mode] & + arch_masks[x86_get_bit_mode( + MI->csh->mode)] & DispVal, true); } else { diff --git a/arch/X86/X86Mapping.c b/arch/X86/X86Mapping.c index 1258bd039..5a6f47736 100644 --- a/arch/X86/X86Mapping.c +++ b/arch/X86/X86Mapping.c @@ -715,9 +715,9 @@ const char *X86_reg_name(csh handle, unsigned int reg) return NULL; if (reg == X86_REG_EFLAGS) { - if (ud->mode & CS_MODE_32) + if (x86_has_feature(ud->mode, CS_MODE_32)) return "eflags"; - if (ud->mode & CS_MODE_64) + if (x86_has_feature(ud->mode, CS_MODE_64)) return "rflags"; } @@ -900,6 +900,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) if (h->detail_opt) { #ifndef CAPSTONE_DIET + cs_mode mode = x86_get_bit_mode(h->mode); memcpy(insn->detail->regs_read, insns[i].regs_use, sizeof(insns[i].regs_use)); insn->detail->regs_read_count = @@ -916,7 +917,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) insns[i].regs_mod); break; case X86_RDTSC: - if (h->mode == CS_MODE_64) { + if (mode == CS_MODE_64) { memcpy(insn->detail->regs_write, insns[i].regs_mod, sizeof(insns[i].regs_mod)); @@ -932,7 +933,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) } break; case X86_RDTSCP: - if (h->mode == CS_MODE_64) { + if (mode == CS_MODE_64) { memcpy(insn->detail->regs_write, insns[i].regs_mod, sizeof(insns[i].regs_mod)); @@ -958,7 +959,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) case X86_INS_LOOPE: case X86_INS_LOOPNE: // The instruction pointer register follows the mode. - switch (h->mode) { + switch (mode) { default: break; case CS_MODE_16: @@ -1012,7 +1013,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) case X86_INS_LODSD: case X86_INS_LODSQ: case X86_INS_LODSW: - switch (h->mode) { + switch (mode) { default: break; case CS_MODE_16: @@ -1046,7 +1047,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) case X86_INS_STOSD: case X86_INS_STOSQ: case X86_INS_STOSW: - switch (h->mode) { + switch (mode) { default: break; case CS_MODE_16: @@ -1080,7 +1081,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) case X86_INS_MOVSW: case X86_INS_MOVSD: case X86_INS_MOVSQ: - switch (h->mode) { + switch (mode) { default: break; case CS_MODE_16: @@ -1124,7 +1125,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) case X86_INS_ENTER: case X86_INS_LEAVE: - switch (h->mode) { + switch (mode) { default: break; case CS_MODE_16: @@ -1168,7 +1169,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) case X86_INS_INSB: case X86_INS_INSW: case X86_INS_INSD: - switch (h->mode) { + switch (mode) { default: break; case CS_MODE_16: @@ -1197,7 +1198,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) case X86_INS_OUTSB: case X86_INS_OUTSW: case X86_INS_OUTSD: - switch (h->mode) { + switch (mode) { default: break; case CS_MODE_64: @@ -1239,7 +1240,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) case X86_INS_OUTSB: case X86_INS_OUTSW: case X86_INS_OUTSD: - switch (h->mode) { + switch (mode) { default: break; case CS_MODE_16: @@ -1254,7 +1255,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) case X86_INS_JMP: case X86_INS_LJMP: - switch (h->mode) { + switch (mode) { default: break; case CS_MODE_16: @@ -1281,7 +1282,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) break; case X86_INS_SYSENTER: { - switch (h->mode) { + switch (mode) { default: break; case CS_MODE_16: @@ -1308,7 +1309,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) break; } break; case X86_INS_SYSEXIT: { - switch (h->mode) { + switch (mode) { default: break; case CS_MODE_16: @@ -1364,7 +1365,7 @@ void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) X86_GRP_JUMP; insn->detail->groups_count++; - switch (h->mode) { + switch (mode) { default: break; case CS_MODE_16: @@ -2222,9 +2223,9 @@ static void add_cx(MCInst *MI) if (MI->csh->detail_opt) { x86_reg cx; - if (MI->csh->mode & CS_MODE_16) + if (x86_has_feature(MI->csh->mode, CS_MODE_16)) cx = X86_REG_CX; - else if (MI->csh->mode & CS_MODE_32) + else if (x86_has_feature(MI->csh->mode, CS_MODE_32)) cx = X86_REG_ECX; else // 64-bit cx = X86_REG_RCX; diff --git a/arch/X86/X86Mapping.h b/arch/X86/X86Mapping.h index 2465db8bc..52af3743b 100644 --- a/arch/X86/X86Mapping.h +++ b/arch/X86/X86Mapping.h @@ -104,4 +104,14 @@ void X86_build_lookup_tables(cs_struct *h); void X86_postprinter(csh handle, cs_insn *insn, SStream *mnem, MCInst *mci); +static inline bool x86_has_feature(cs_mode mode, int flag) +{ + return (mode & flag) == flag; +} + +static inline int x86_get_bit_mode(cs_mode mode) +{ + return mode & (CS_MODE_64 | CS_MODE_32 | CS_MODE_16); +} + #endif diff --git a/arch/X86/X86Module.c b/arch/X86/X86Module.c index c1024a797..6fcbe3f49 100644 --- a/arch/X86/X86Module.c +++ b/arch/X86/X86Module.c @@ -13,6 +13,10 @@ cs_err X86_global_init(cs_struct *ud) { MCRegisterInfo *mri; + if (x86_has_feature(ud->mode, CS_MODE_X86_AMD) && + x86_has_feature(ud->mode, CS_MODE_X86_INTEL)) + return CS_ERR_MODE; + mri = cs_mem_calloc(1, sizeof(*mri)); if (!mri) return CS_ERR_MEM; @@ -33,7 +37,7 @@ cs_err X86_global_init(cs_struct *ud) ud->reg_access = X86_reg_access; #endif - if (ud->mode == CS_MODE_64) + if (x86_has_feature(ud->mode, CS_MODE_64)) ud->regsize_map = regsize_map_64; else ud->regsize_map = regsize_map_32; @@ -49,12 +53,18 @@ cs_err X86_option(cs_struct *handle, cs_opt_type type, size_t value) default: break; case CS_OPT_MODE: - if (value == CS_MODE_64) + if ((x86_has_feature(value, CS_MODE_X86_INTEL) && + x86_has_feature(value, CS_MODE_X86_AMD))) + return CS_ERR_OPTION; + + if (x86_has_feature(value, CS_MODE_64)) handle->regsize_map = regsize_map_64; else handle->regsize_map = regsize_map_32; - handle->mode = (cs_mode)value; + handle->mode = (cs_mode)(value & + (CS_MODE_16 | CS_MODE_32 | CS_MODE_64 | + CS_MODE_X86_INTEL | CS_MODE_X86_AMD)); break; case CS_OPT_SYNTAX: switch (value) { diff --git a/bindings/python/capstone/__init__.py b/bindings/python/capstone/__init__.py index ad91ecdba..50f0736f1 100755 --- a/bindings/python/capstone/__init__.py +++ b/bindings/python/capstone/__init__.py @@ -45,6 +45,8 @@ __all__ = [ "CS_MODE_16", "CS_MODE_32", "CS_MODE_64", + "CS_MODE_X86_INTEL", + "CS_MODE_X86_AMD", "CS_MODE_ARM", "CS_MODE_THUMB", "CS_MODE_MCLASS", @@ -353,6 +355,8 @@ CS_MODE_ARM = 0 # ARM mode CS_MODE_16 = 1 << 1 # 16-bit mode (for X86) CS_MODE_32 = 1 << 2 # 32-bit mode (for X86) CS_MODE_64 = 1 << 3 # 64-bit mode (for X86, PPC) +CS_MODE_X86_INTEL = 1 << 4 +CS_MODE_X86_AMD = 1 << 5 CS_MODE_THUMB = 1 << 4 # ARM's Thumb mode, including Thumb-2 CS_MODE_MCLASS = 1 << 5 # ARM's Cortex-M series CS_MODE_V8 = 1 << 6 # ARMv8 A32 encodings for ARM diff --git a/cs.c b/cs.c index 493369fe3..0444db8b7 100644 --- a/cs.c +++ b/cs.c @@ -123,7 +123,7 @@ typedef struct cs_arch_config { X86_global_init, \ X86_option, \ ~(CS_MODE_LITTLE_ENDIAN | CS_MODE_32 | CS_MODE_64 | \ - CS_MODE_16), \ + CS_MODE_16 | CS_MODE_X86_INTEL | CS_MODE_X86_AMD), \ } #define CS_ARCH_CONFIG_PPC \ { \ diff --git a/docs/cs_v6_release_guide.md b/docs/cs_v6_release_guide.md index a9da65518..c5e0ea40b 100644 --- a/docs/cs_v6_release_guide.md +++ b/docs/cs_v6_release_guide.md @@ -285,6 +285,10 @@ Nonetheless, we hope this additional information is useful to you. - Decoding of conflicting segment overrides was changed to match CPU behavior: For instructions with both an FS/GS and a ES/CS/SS/DS overrides the FS/GS override now takes priority, regardless of prefix ordering. - Decoding of instructions with multiple mandatory prefixes was fixed. (e.g., `shld` with a data size override and a redundant `F3` prefix, or `addss` with an additional `66` prefix) +- Added `CS_MODE_X86_INTEL` and `CS_MODE_X86_AMD` to select ISA specific quirks. + Currently this is only: + - How `66`-prefixed near conditional jumps are decoded in 64-bit mode (see flag documentation). + Without either flag, the previous LLVM behavior is preserved. **BPF** diff --git a/include/capstone/capstone.h b/include/capstone/capstone.h index 296ce3903..ead2d1527 100644 --- a/include/capstone/capstone.h +++ b/include/capstone/capstone.h @@ -148,6 +148,13 @@ typedef enum cs_mode { CS_MODE_16 = 1 << 1, ///< 16-bit mode (X86) CS_MODE_32 = 1 << 2, ///< 32-bit mode (X86) CS_MODE_64 = 1 << 3, ///< 64-bit mode (X86, PPC) + // X86 + /// x86 Intel specific quirks: + /// - Ignore 66-prefix near Jcc in 64-bit mode. + CS_MODE_X86_INTEL = 1 << 4, + /// x86 AMD specific quirks: + /// - Honor 66-prefix near Jcc in 64-bit mode, unless REX.W overrides it. + CS_MODE_X86_AMD = 1 << 5, // ARM CS_MODE_THUMB = 1 << 4, ///< ARM's Thumb mode, including Thumb-2 CS_MODE_MCLASS = 1 << 5, ///< ARM's Cortex-M series diff --git a/suite/cstest/include/test_mapping.h b/suite/cstest/include/test_mapping.h index 7cfab9239..7212bcfbb 100644 --- a/suite/cstest/include/test_mapping.h +++ b/suite/cstest/include/test_mapping.h @@ -253,6 +253,8 @@ static const cs_enum_id_map test_mode_map[] = { { .str = "CS_MODE_TRICORE_180", .val = CS_MODE_TRICORE_180 }, { .str = "CS_MODE_V8", .val = CS_MODE_V8 }, { .str = "CS_MODE_V9", .val = CS_MODE_V9 }, + { .str = "CS_MODE_X86_AMD", .val = CS_MODE_X86_AMD }, + { .str = "CS_MODE_X86_INTEL", .val = CS_MODE_X86_INTEL }, { .str = "CS_MODE_XTENSA_ESP32", .val = CS_MODE_XTENSA_ESP32 }, { .str = "CS_MODE_XTENSA_ESP32S2", .val = CS_MODE_XTENSA_ESP32S2 }, { .str = "CS_MODE_XTENSA_ESP32S3", .val = CS_MODE_XTENSA_ESP32S3 }, diff --git a/tests/features/x86_jcc_mode.yaml b/tests/features/x86_jcc_mode.yaml new file mode 100644 index 000000000..8738fb7b9 --- /dev/null +++ b/tests/features/x86_jcc_mode.yaml @@ -0,0 +1,236 @@ +test_cases: + - input: + name: "Jcc DEFAULT: JO rel16" + bytes: [0x66, 0x0f, 0x80, 0xce, 0xfa, 0xa5, 0xd8] + arch: CS_ARCH_X86 + options: [CS_MODE_64, CS_OPT_DETAIL] + address: 0 + expected: + insns: + - asm_text: "jo 0xfffffffffffffad3" + size: 5 + details: + x86: + enc_imm_offset: 3 + enc_imm_size: 2 + operands: + - type: X86_OP_IMM + imm: -1325 + size: 4 + - asm_text: "movsd dword ptr [rdi], dword ptr [rsi]" + size: 1 + - input: + name: "Jcc DEFAULT: JB rel32" + bytes: [0x66, 0x0f, 0x82, 0xce, 0xfa, 0xa5, 0xd8] + arch: CS_ARCH_X86 + options: [CS_MODE_64, CS_OPT_DETAIL] + address: 0 + expected: + insns: + - asm_text: "jb 0xffffffffd8a5fad5" + size: 7 + details: + x86: + enc_imm_size: 4 + operands: + - type: X86_OP_IMM + imm: -660210987 + size: 4 + - input: + name: "Jcc AMD: JO rel16 and following MOVSD" + bytes: [0x66, 0x0f, 0x80, 0xce, 0xfa, 0xa5, 0xd8] + arch: CS_ARCH_X86 + options: [CS_MODE_64, CS_OPT_DETAIL, CS_MODE_X86_AMD] + address: 0x123450000 + expected: + insns: + - asm_text: "jo 0xfad3" + size: 5 + details: + x86: + enc_imm_offset: 3 + enc_imm_size: 2 + operands: + - type: X86_OP_IMM + imm: 0xfad3 + size: 2 + - asm_text: "movsd dword ptr [rdi], dword ptr [rsi]" + size: 1 + - input: + name: "Jcc AMD: JNO in AT&T syntax" + bytes: [0x66, 0x0f, 0x81, 0xce, 0xfa, 0xa5, 0xd8] + arch: CS_ARCH_X86 + options: [CS_MODE_64, CS_OPT_DETAIL, CS_MODE_X86_AMD, CS_OPT_SYNTAX_ATT] + address: 0 + expected: + insns: + - asm_text: "jno 0xfad3" + size: 5 + details: + x86: + enc_imm_size: 2 + operands: + - type: X86_OP_IMM + imm: 0xfad3 + size: 2 + - asm_text: "movsl (%rsi), (%rdi)" + size: 1 + - input: + name: "Jcc AMD: JB target wraparound" + bytes: [0x66, 0x0f, 0x82, 0x01, 0x00] + arch: CS_ARCH_X86 + options: [CS_MODE_64, CS_OPT_DETAIL, CS_MODE_X86_AMD] + address: 0xfffe + expected: + insns: + - asm_text: "jb 4" + size: 5 + details: + x86: + enc_imm_size: 2 + operands: + - type: X86_OP_IMM + imm: 4 + size: 2 + - input: + name: "Jcc AMD: REX.W overrides 66" + bytes: [0x66, 0x48, 0x0f, 0x80, 0xce, 0xfa, 0xa5, 0xd8] + arch: CS_ARCH_X86 + options: [CS_MODE_64, CS_OPT_DETAIL, CS_MODE_X86_AMD] + address: 0 + expected: + insns: + - asm_text: "jo 0xffffffffd8a5fad6" + size: 8 + details: + x86: + enc_imm_offset: 4 + enc_imm_size: 4 + operands: + - type: X86_OP_IMM + imm: -660210986 + size: 8 + - input: + name: "Jcc AMD: 66 after REX.W" + bytes: [0x48, 0x66, 0x0f, 0x80, 0xce, 0xfa, 0xa5, 0xd8] + arch: CS_ARCH_X86 + options: [CS_MODE_64, CS_OPT_DETAIL, CS_MODE_X86_AMD] + address: 0 + expected: + insns: + - asm_text: "jo 0xfad4" + size: 6 + details: + x86: + enc_imm_offset: 4 + enc_imm_size: 2 + operands: + - type: X86_OP_IMM + imm: 0xfad4 + size: 2 + - asm_text: "movsd dword ptr [rdi], dword ptr [rsi]" + size: 1 + - input: + name: "Jcc Intel: JG rel32" + bytes: [0x66, 0x0f, 0x8f, 0xce, 0xfa, 0xa5, 0xd8] + arch: CS_ARCH_X86 + options: [CS_MODE_64, CS_OPT_DETAIL, CS_MODE_X86_INTEL] + address: 0 + expected: + insns: + - asm_text: "jg 0xffffffffd8a5fad5" + size: 7 + details: + x86: + enc_imm_size: 4 + operands: + - type: X86_OP_IMM + imm: -660210987 + size: 8 + - input: + name: "Jcc AMD: JG rel16" + bytes: [0x66, 0x0f, 0x8f, 0xce, 0xfa] + arch: CS_ARCH_X86 + options: [CS_MODE_64, CS_OPT_DETAIL, CS_MODE_X86_AMD] + address: 0 + expected: + insns: + - asm_text: "jg 0xfad3" + size: 5 + details: + x86: + enc_imm_size: 2 + operands: + - type: X86_OP_IMM + imm: 0xfad3 + size: 2 + - input: + name: "Jcc Intel flag preserves 64-bit operands and registers" + bytes: [0x48, 0xc7, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x48, 0xa1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x31, 0xd9, 0x20, 0xa5, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xc3] + arch: CS_ARCH_X86 + options: [CS_MODE_64, CS_MODE_X86_INTEL, CS_OPT_DETAIL] + address: 0x100000000 + expected: + insns: + - asm_text: "mov rax, 0xffffffffffffffff" + details: + x86: + operands: + - type: X86_OP_REG + reg: rax + size: 8 + - type: X86_OP_IMM + imm: -1 + size: 8 + - asm_text: "movabs rax, qword ptr [0xffffffffffffffff]" + - asm_text: "rdtsc" + details: + regs_write: [rax, rdx] + - asm_text: "fldenv [rax]" + details: + x86: + operands: + - type: X86_OP_MEM + mem_base: rax + size: 28 + - asm_text: "movsd dword ptr [rdi], dword ptr [rsi]" + details: + regs_read: [rdi, rsi, rflags] + regs_write: [rdi, rsi] + - asm_text: "call 0x10000001b" + - asm_text: "ret" + - input: + name: "Jcc AMD flag preserves 64-bit operands and registers in AT&T syntax" + bytes: [0x48, 0xc7, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x48, 0xa1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x31, 0xd9, 0x20, 0xa5, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xc3] + arch: CS_ARCH_X86 + options: [CS_MODE_64, CS_MODE_X86_AMD, CS_OPT_DETAIL, CS_OPT_SYNTAX_ATT] + address: 0x100000000 + expected: + insns: + - asm_text: "movq $0xffffffffffffffff, %rax" + details: + x86: + operands: + - type: X86_OP_IMM + imm: -1 + size: 8 + - type: X86_OP_REG + reg: rax + size: 8 + - asm_text: "movabsq 0xffffffffffffffff, %rax" + - asm_text: "rdtsc" + details: + regs_write: [rax, rdx] + - asm_text: "fldenv (%rax)" + details: + x86: + operands: + - type: X86_OP_MEM + mem_base: rax + size: 28 + - asm_text: "movsl (%rsi), (%rdi)" + details: + regs_read: [rdi, rsi, rflags] + regs_write: [rdi, rsi] + - asm_text: "callq 0x10000001b" + - asm_text: "retq" diff --git a/tests/issues/issues.yaml b/tests/issues/issues.yaml index fec69f017..434f11107 100644 --- a/tests/issues/issues.yaml +++ b/tests/issues/issues.yaml @@ -7207,3 +7207,25 @@ test_cases: regs_read: [ rsp, ss ] regs_write: [ rsp, rip ] groups: [ ret, mode64 ] + - + input: + name: "issue 3037 x86-64 Intel JO ignores operand-size override" + bytes: [ 0x66,0x0f,0x80,0xce,0xfa,0xa5,0xd8 ] + arch: "CS_ARCH_X86" + options: [ CS_MODE_64, CS_OPT_DETAIL, CS_MODE_X86_INTEL ] + address: 0x0 + expected: + insns: + - + asm_text: "jo 0xffffffffd8a5fad5" + - + input: + name: "issue 3037 x86-64 Intel JNO ignores operand-size override" + bytes: [ 0x66,0x0f,0x81,0xce,0xfa,0xa5,0xd8 ] + arch: "CS_ARCH_X86" + options: [ CS_MODE_64, CS_OPT_DETAIL, CS_MODE_X86_INTEL ] + address: 0x0 + expected: + insns: + - + asm_text: "jno 0xffffffffd8a5fad5"