diff --git a/api/docs/release.dox b/api/docs/release.dox index 9552c2e9e5d..5359fe8d8d3 100644 --- a/api/docs/release.dox +++ b/api/docs/release.dox @@ -141,6 +141,8 @@ Further non-compatibility-affecting changes include: supported only when fast FP save and restore is supported. And mixed mode is not supported. - Added the AArch64 FPMR register as DR_REG_FPMR. + - Added OPSZ_addr which chooses the appropriate operand size on X86 from 2/4/8 based + on the 32/64 bit mode and the presence or absence of the address size prefix. **************************************************
diff --git a/core/ir/decode_shared.c b/core/ir/decode_shared.c index 8379562188c..5380d80fd36 100644 --- a/core/ir/decode_shared.c +++ b/core/ir/decode_shared.c @@ -153,6 +153,7 @@ const char *const size_names[] = { "OPSZ_8x16", "OPSZ_256", "OPSZ_192", + "OPSZ_addr", "OPSZ_1_of_4", "OPSZ_2_of_4", "OPSZ_1_of_8", diff --git a/core/ir/disassemble_shared.c b/core/ir/disassemble_shared.c index a7d0d11cbb9..8293ed2d91c 100644 --- a/core/ir/disassemble_shared.c +++ b/core/ir/disassemble_shared.c @@ -261,6 +261,7 @@ opnd_size_suffix_intel(opnd_t opnd) case 12: return ""; case 16: return "oword"; case 32: return "yword"; + case 64: return "zword"; } return ""; } diff --git a/core/ir/opnd_api.h b/core/ir/opnd_api.h index aeb75c3fc7a..d9e61a848be 100644 --- a/core/ir/opnd_api.h +++ b/core/ir/opnd_api.h @@ -224,6 +224,7 @@ enum { */ OPSZ_256, /**< 256 bytes. Needed for RISC-V vector extension with LMUL. */ OPSZ_192, /**< 192 bytes. The size of 3 512-bit SVE Z registers. */ + OPSZ_addr, /**< OPSZ_4x8 but varies by the address prefix, not the data prefix. */ /* Add new size here. Also update size_names[] in decode_shared.c along with * the size routines in opnd_shared.c. */ diff --git a/core/ir/opnd_shared.c b/core/ir/opnd_shared.c index f526e196026..11ae3eb0dc7 100644 --- a/core/ir/opnd_shared.c +++ b/core/ir/opnd_shared.c @@ -1920,6 +1920,7 @@ opnd_size_in_bytes(opnd_size_t size) case OPSZ_4x8: /* default size */ case OPSZ_4x8_short2: /* default size */ case OPSZ_4x8_short2xi8: /* default size */ + case OPSZ_addr: /* default size */ #endif case OPSZ_4_short2xi4: /* default size */ case OPSZ_4_rex8_short2: /* default size */ @@ -1940,6 +1941,7 @@ opnd_size_in_bytes(opnd_size_t size) case OPSZ_4x8: /* default size */ case OPSZ_4x8_short2: /* default size */ case OPSZ_4x8_short2xi8: /* default size */ + case OPSZ_addr: /* default size */ #endif case OPSZ_8_rex16: /* default size */ case OPSZ_8_rex16_short4: /* default size */ diff --git a/core/ir/x86/decode.c b/core/ir/x86/decode.c index 7a6b0dc155f..efc1bbb1cd9 100644 --- a/core/ir/x86/decode.c +++ b/core/ir/x86/decode.c @@ -194,7 +194,8 @@ is_variable_size(opnd_size_t sz) case OPSZ_16_vex32: case OPSZ_16_vex32_evex64: case OPSZ_vex32_evex64: - case OPSZ_8x16: return true; + case OPSZ_8x16: + case OPSZ_addr: return true; default: return false; } } @@ -316,6 +317,9 @@ resolve_variable_size(decode_info_t *di /*IN: x86_mode, prefixes*/, opnd_size_t ? OPSZ_8 : (TEST(PREFIX_VEX_L, di->prefixes) ? OPSZ_4 : OPSZ_2)); case OPSZ_8x16: return IF_X64_ELSE(OPSZ_16, OPSZ_8); + case OPSZ_addr: + return (TEST(PREFIX_ADDR, di->prefixes) ? (X64_MODE(di) ? OPSZ_4 : OPSZ_2) + : (X64_MODE(di) ? OPSZ_8 : OPSZ_4)); } return sz; @@ -1585,6 +1589,10 @@ decode_reg(decode_reg_t which_reg, decode_info_t *di, byte optype, opnd_size_t o case TYPE_FLOATMEM: /* GPR: fall-through since variable subset of full register */ break; + case TYPE_G_ES_VAR_REG_SIZE: { + opsize = OPSZ_addr; + break; + } default: CLIENT_ASSERT(false, "internal unknown reg error"); } @@ -2214,6 +2222,15 @@ decode_operand(decode_info_t *di, byte optype, opnd_size_t opsize, opnd_t *opnd) return true; } case TYPE_T_MODRM: return decode_modrm(di, optype, opsize, NULL, opnd); + case TYPE_G_ES_VAR_REG_SIZE: { + /* NB: we want the register size to match the address size, not opsize. */ + if (!decode_modrm(di, optype, OPSZ_addr, opnd, NULL)) { + return false; + } + reg_id_t reg = opnd_get_reg(*opnd); + *opnd = opnd_create_far_base_disp(DR_SEG_ES, reg, REG_NULL, 0, 0, opsize); + return true; + } default: /* ok to assert, types coming only from instr_info_t */ CLIENT_ASSERT(false, "decode error: unknown operand type"); diff --git a/core/ir/x86/decode_private.h b/core/ir/x86/decode_private.h index 1f037c4e83f..5c959d78cfd 100644 --- a/core/ir/x86/decode_private.h +++ b/core/ir/x86/decode_private.h @@ -478,6 +478,11 @@ enum { * memory in 32-bit mode, or 16 bytes memory in 64-bit * mode. */ + TYPE_G_ES_VAR_REG_SIZE, /* modrm.reg selects register (like TYPE_G) + * containing an offset from ES, but (unlike TYPE_G) + * treated as a memory operand, with size controlled + * by PREFIX_ADDR. + */ /* when adding new types, update type_names[] in encode.c */ TYPE_BEYOND_LAST_ENUM, }; diff --git a/core/ir/x86/decode_table.c b/core/ir/x86/decode_table.c index d169597fb2e..dc1ca5b24ca 100644 --- a/core/ir/x86/decode_table.c +++ b/core/ir/x86/decode_table.c @@ -1642,6 +1642,12 @@ const instr_info_t * const op_instr[] = /* SERIALIZE */ /* OP_serialize */ &prefix_extensions[191][0], + + /* MOVDIRI */ + /* OP_movdiri */ &third_byte_38[173], + + /* MOVDIR64B */ + /* OP_movdir64b */ &prefix_extensions[192][2], }; @@ -1858,7 +1864,8 @@ const instr_info_t * const op_instr[] = #define My TYPE_M, OPSZ_4_rex8 #define Mw TYPE_M, OPSZ_2 #define Mm TYPE_M, OPSZ_lea -#define Moq TYPE_M, OPSZ_512 +#define Moq TYPE_M, OPSZ_64 +#define M512 TYPE_M, OPSZ_512 #define Mxsave TYPE_M, OPSZ_xsave #define Mps TYPE_M, OPSZ_16 #define Mpd TYPE_M, OPSZ_16 @@ -1900,6 +1907,7 @@ const instr_info_t * const op_instr[] = #define c1 TYPE_1, OPSZ_0 /* we pick the right constant based on the opcode */ #define cF TYPE_FLOATCONST, OPSZ_0 +#define GesvS_oq TYPE_G_ES_VAR_REG_SIZE, OPSZ_64 /* registers that are base 32 but vary down or up */ #define eAX TYPE_VAR_REG, REG_EAX @@ -5905,6 +5913,19 @@ const instr_info_t prefix_extensions[][12] = { {INVALID, 0xf301e808, catUncategorized, "(bad)" , xx, xx, xx, xx, xx, no, x, NA}, {INVALID, 0x6601e808, catUncategorized, "(bad)", xx, xx, xx, xx, xx, no, x, NA}, {INVALID, 0xf201e808, catUncategorized, "(bad)", xx, xx, xx, xx, xx, no, x, NA}, + },{ /* prefix extension 192 */ + {INVALID, 0x38f808, catUncategorized, "(bad)", xx, xx, xx, xx, xx, no, x, NA}, + {INVALID, 0xf338f808, catUncategorized, "(bad)", xx, xx, xx, xx, xx, no, x, NA}, + {OP_movdir64b, 0x6638f808, catMove, "movdir64b", GesvS_oq, xx, Moq, xx, xx, mrm, x, END_LIST}, + {INVALID, 0xf238f808, catUncategorized, "(bad)", xx, xx, xx, xx, xx, no, x, NA}, + {INVALID, 0x38f808, catUncategorized, "(bad)", xx, xx, xx, xx, xx, no, x, NA}, + {INVALID, 0xf338f808, catUncategorized, "(bad)", xx, xx, xx, xx, xx, no, x, NA}, + {INVALID, 0x6638f808, catUncategorized, "(bad)", xx, xx, xx, xx, xx, no, x, NA}, + {INVALID, 0xf238f808, catUncategorized, "(bad)", xx, xx, xx, xx, xx, no, x, NA}, + {INVALID, 0x38f808, catUncategorized, "(bad)", xx, xx, xx, xx, xx, no, x, NA}, + {INVALID, 0xf338f808, catUncategorized, "(bad)" , xx, xx, xx, xx, xx, no, x, NA}, + {INVALID, 0x6638f808, catUncategorized, "(bad)", xx, xx, xx, xx, xx, no, x, NA}, + {INVALID, 0xf238f808, catUncategorized, "(bad)", xx, xx, xx, xx, xx, no, x, NA}, } }; /**************************************************************************** @@ -7243,12 +7264,12 @@ const instr_info_t rex_b_extensions[][2] = { */ const instr_info_t rex_w_extensions[][2] = { { /* rex.w extension 0 */ - {OP_fxsave32, 0x0fae30, catFP | catState, "fxsave", Moq, xx, xx, xx, xx, mrm, x, END_LIST}, - {OP_fxsave64, 0x0fae30, catFP | catState, "fxsave64", Moq, xx, xx, xx, xx, mrm|rex, x, END_LIST}, + {OP_fxsave32, 0x0fae30, catFP | catState, "fxsave", M512, xx, xx, xx, xx, mrm, x, END_LIST}, + {OP_fxsave64, 0x0fae30, catFP | catState, "fxsave64", M512, xx, xx, xx, xx, mrm|rex, x, END_LIST}, }, { /* rex.w extension 1 */ - {OP_fxrstor32, 0x0fae31, catFP | catState, "fxrstor", xx, xx, Moq, xx, xx, mrm, x, END_LIST}, - {OP_fxrstor64, 0x0fae31, catFP | catState, "fxrstor64", xx, xx, Moq, xx, xx, mrm|rex, o64, END_LIST}, + {OP_fxrstor32, 0x0fae31, catFP | catState, "fxrstor", xx, xx, M512, xx, xx, mrm, x, END_LIST}, + {OP_fxrstor64, 0x0fae31, catFP | catState, "fxrstor64", xx, xx, M512, xx, xx, mrm|rex, o64, END_LIST}, }, { /* rex.w extension 2 */ {OP_xsave32, 0x0fae34, catFP | catState, "xsave", Mxsave, xx, edx, eax, xx, mrm, x, END_LIST}, @@ -7313,7 +7334,7 @@ const byte third_byte_38_index[256] = { 0, 0, 0, 0, 155, 0,163,164, 154,165,131,132, 152,153, 0, 0, /* C */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 53, 54, 55, /* D */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E */ - 47, 48,100, 99, 0,101,102, 98, 0, 0, 0, 0, 0, 0, 0, 0 /* F */ + 47, 48,100, 99, 0,101,102, 98, 172,173, 0, 0, 0, 0, 0, 0 /* F */ }; const instr_info_t third_byte_38[] = { @@ -7508,7 +7529,9 @@ const instr_info_t third_byte_38[] = { {E_VEX_EXT, 0x66385308, catUncategorized, "(e_vex ext 152)", xx, xx, xx, xx, xx, mrm|evex|reqp, x, 152},/*169*/ {PREFIX_EXT, 0x387208, catUncategorized, "(prefix ext 190)", xx, xx, xx, xx, xx, mrm|evex, x, 190},/*170*/ /* AVX512 VPOPCNTDQ */ - {EVEX_Wb_EXT, 0x66385518, catUncategorized, "(evex_Wb ext 274)", xx, xx, xx, xx, xx, mrm|evex|reqp, x, 274}/*171*/ + {EVEX_Wb_EXT, 0x66385518, catUncategorized, "(evex_Wb ext 274)", xx, xx, xx, xx, xx, mrm|evex|reqp, x, 274},/*171*/ + {PREFIX_EXT, 0x38f808, catUncategorized, "(prefix ext 192)", xx, xx, xx, xx, xx, mrm, x, 192},/*172*/ + {OP_movdiri, 0x38f908, catMove, "movdiri", My, xx, Gy, xx, xx, mrm, x, END_LIST},/*173*/ }; /* N.B.: every 0x3a instr so far has an immediate. If a version w/o an immed diff --git a/core/ir/x86/disassemble.c b/core/ir/x86/disassemble.c index 63918db5b6a..58ddb818060 100644 --- a/core/ir/x86/disassemble.c +++ b/core/ir/x86/disassemble.c @@ -208,6 +208,13 @@ opnd_disassemble_noimplicit(char *buf, size_t bufsz, size_t *sofar DR_PARAM_INOU print_to_buffer(buf, bufsz, sofar, ", "); internal_opnd_disassemble(buf, bufsz, sofar, dcontext, opnd, false); return true; + case TYPE_G_ES_VAR_REG_SIZE: { + if (prev) + print_to_buffer(buf, bufsz, sofar, ", "); + reg_id_t reg = opnd_get_base(opnd); + reg_disassemble(buf, bufsz, sofar, reg, 0, "", ""); + return true; + } case TYPE_X: case TYPE_XLAT: case TYPE_MASKMOVQ: @@ -276,6 +283,18 @@ instr_opcode_name(instr_t *instr) return NULL; } +static bool +suppress_memory_size_annotations(instr_t *instr) +{ + /* A more principled approach would be to examine all operands for the presence of + * TYPE_G_ES_VAR_REG_SIZE but this is sufficient for now. + */ + switch (instr_get_opcode(instr)) { + case OP_movdir64b: return true; + default: return false; + } +} + static const char * instr_opcode_name_suffix(instr_t *instr) { @@ -336,7 +355,8 @@ instr_opcode_name_suffix(instr_t *instr) * and then go back and add the suffix. This will do for now. */ if (instr_num_srcs(instr) > 0 && !opnd_is_reg(instr_get_src(instr, 0)) && - instr_num_dsts(instr) > 0 && !opnd_is_reg(instr_get_dst(instr, 0))) { + instr_num_dsts(instr) > 0 && !opnd_is_reg(instr_get_dst(instr, 0)) && + !suppress_memory_size_annotations(instr)) { uint sz = instr_memory_reference_size(instr); if (sz == 1) return "b"; @@ -382,7 +402,8 @@ print_instr_prefixes(dcontext_t *dcontext, instr_t *instr, char *buf, size_t buf if (!TEST(DR_DISASM_INTEL, DYNAMO_OPTION(disasm_mask))) { if (TEST(PREFIX_DATA, instr->prefixes)) print_to_buffer(buf, bufsz, sofar, "data16 "); - if (TEST(PREFIX_ADDR, instr->prefixes)) { + if (TEST(PREFIX_ADDR, instr->prefixes) && + !suppress_memory_size_annotations(instr)) { print_to_buffer(buf, bufsz, sofar, X64_MODE_DC(dcontext) ? "addr32 " : "addr16 "); } diff --git a/core/ir/x86/encode.c b/core/ir/x86/encode.c index a30b5284a9b..b8387d37788 100644 --- a/core/ir/x86/encode.c +++ b/core/ir/x86/encode.c @@ -119,6 +119,7 @@ const char *const type_names[] = { "TYPE_K_EVEX", "TYPE_T_REG", "TYPE_T_MODRM", + "TYPE_G_ES_VAR_REG_SIZE", }; /* order corresponds to enum of REG_ and SEG_ constants */ @@ -689,7 +690,7 @@ type_uses_evex_aaa_bits(int type) } } -/* Helper routine that sets/checks rex.w or data prefix, if necessary, for +/* Helper routine that sets/checks rex.w, data, or addr prefix, if necessary, for * variable-sized OPSZ_ constants that the user asks for. We try to be flexible * setting/checking only enough prefix flags to ensure that the final template size * is one of the possible sizes in the request. @@ -943,12 +944,12 @@ size_ok(decode_info_t *di /*prefixes field is IN/OUT; x86_mode is IN*/, /* for OPSZ_4x8_short2, does the addr prefix select 4 instead of 2 bytes? */ bool addr_short4 = X64_MODE(di) && addr; /* Assumption: the only addr-specified operands that can be short - * are OPSZ_4x8_short2 and OPSZ_4x8_short2xi8, or + * are OPSZ_4x8_short2, OPSZ_4x8_short2xi8 and OPSZ_addr, or * OPSZ_4_short2 for x86 mode on x64. * Stack memrefs can pass addr==true and OPSZ_4x8. */ CLIENT_ASSERT(!addr || size_template == OPSZ_4x8 || - size_template == OPSZ_4x8_short2xi8 || + size_template == OPSZ_4x8_short2xi8 || size_template == OPSZ_addr || size_template == OPSZ_4x8_short2 IF_X64( || (!X64_MODE(di) && size_template == OPSZ_4_short2)), @@ -980,6 +981,10 @@ size_ok(decode_info_t *di /*prefixes field is IN/OUT; x86_mode is IN*/, } return false; case OPSZ_2: + if (!X64_MODE(di) && size_template == OPSZ_addr) { + di->prefixes |= prefix_data_addr; + return true; + } if (size_template == OPSZ_2_short1) return !TEST(prefix_data_addr, di->prefixes); if (size_template == OPSZ_4_short2 || size_template == OPSZ_8_short2) { @@ -1000,6 +1005,12 @@ size_ok(decode_info_t *di /*prefixes field is IN/OUT; x86_mode is IN*/, } return false; case OPSZ_4: + if (size_template == OPSZ_addr) { + if (!X64_MODE(di)) + return !TEST(prefix_data_addr, di->prefixes); + di->prefixes |= prefix_data_addr; + return true; + } if (size_template == OPSZ_4_short2) return !TEST(prefix_data_addr, di->prefixes); if (size_template == OPSZ_4_rex8_short2) @@ -1053,7 +1064,8 @@ size_ok(decode_info_t *di /*prefixes field is IN/OUT; x86_mode is IN*/, di->prefixes |= PREFIX_REX_W; /* rex.w trumps data prefix */ return true; } - if (size_template == OPSZ_8_short4 || size_template == OPSZ_8_short2) + if ((X64_MODE(di) && size_template == OPSZ_addr) || + size_template == OPSZ_8_short4 || size_template == OPSZ_8_short2) return !TEST(prefix_data_addr, di->prefixes); if (size_template == OPSZ_8_rex16 || size_template == OPSZ_8_rex16_short4) return !TESTANY(prefix_data_addr | PREFIX_REX_W, di->prefixes); @@ -1806,6 +1818,11 @@ opnd_type_ok(decode_info_t *di /*prefixes field is IN/OUT; x86_mode is IN*/, opn return (opnd_is_reg(opnd) && reg_size_ok(di, opnd_get_reg(opnd), optype, opsize, false /*!addr*/) && reg_is_bnd(opnd_get_reg(opnd))); + case TYPE_G_ES_VAR_REG_SIZE: + return (opnd_is_far_base_disp(opnd) && opnd_get_segment(opnd) == DR_SEG_ES && + opnd_get_disp(opnd) == 0 && opnd_get_index(opnd) == REG_NULL && + reg_is_gpr(opnd_get_base(opnd)) && + reg_size_ok(di, opnd_get_base(opnd), optype, OPSZ_addr, true /*addr*/)); default: CLIENT_ASSERT(false, "encode error: type ok: unknown operand type"); return false; @@ -2727,6 +2744,24 @@ encode_operand(decode_info_t *di, int optype, opnd_size_t opsize, opnd_t opnd) di->evex_aaa = (byte)(reg - DR_REG_START_OPMASK); return; } + case TYPE_G_ES_VAR_REG_SIZE: + CLIENT_ASSERT(opnd_is_memory_reference(opnd), + "encode error: operand must be a memory reference"); + CLIENT_ASSERT(opnd_is_far_base_disp(opnd), + "encode error: operand must be a far base disp"); + /* NB: We don't actually set di->seg_override because the ES segment is + * inherent to the operand type. + */ + CLIENT_ASSERT(opnd_get_segment(opnd) == DR_SEG_ES, + "encode error: operand must be ES-relative"); + CLIENT_ASSERT(opnd_get_disp(opnd) == 0, "encode error: operand must have 0 disp"); + CLIENT_ASSERT(opnd_get_index(opnd) == REG_NULL, + "encode error: operand must not have an index"); + reg_id_t reg = opnd_get_base(opnd); + CLIENT_ASSERT(reg_is_gpr(reg), "encode error: base reg must be GPR"); + encode_reg_ext_prefixes(di, reg, PREFIX_REX_R); + di->reg = reg_get_bits(reg); + return; default: CLIENT_ASSERT(false, "encode error: unknown operand type"); } diff --git a/core/ir/x86/instr_create_api.h b/core/ir/x86/instr_create_api.h index c83e848f8a4..7320eebc4c9 100644 --- a/core/ir/x86/instr_create_api.h +++ b/core/ir/x86/instr_create_api.h @@ -1578,6 +1578,11 @@ #define INSTR_CREATE_bndmk(dc, d, s) instr_create_1dst_1src((dc), OP_bndmk, (d), (s)) #define INSTR_CREATE_bndldx(dc, d, s) instr_create_1dst_1src((dc), OP_bndldx, (d), (s)) #define INSTR_CREATE_bndstx(dc, d, s) instr_create_1dst_1src((dc), OP_bndstx, (d), (s)) +/* MOVDIRI */ +#define INSTR_CREATE_movdiri(dc, d, s) instr_create_1dst_1src((dc), OP_movdiri, (d), (s)) +/* MOVDIR64B */ +#define INSTR_CREATE_movdir64b(dc, d, s) \ + instr_create_1dst_1src((dc), OP_movdir64b, (d), (s)) /** @} */ /* end doxygen group */ /* 1 destination, 1 implicit source */ diff --git a/core/ir/x86/opcode_api.h b/core/ir/x86/opcode_api.h index cdb4ac48b4e..4d824aaa5de 100644 --- a/core/ir/x86/opcode_api.h +++ b/core/ir/x86/opcode_api.h @@ -1629,6 +1629,12 @@ enum { /* SERIALIZE */ /* 1443 */ OP_serialize, /**< IA-32/AMD64 serialize opcode. */ + /* MOVDIRI */ + /* 1444 */ OP_movdiri, /**< IA-32/AMD64 movdiri opcode. */ + + /* MOVDIR64B */ + /* 1445 */ OP_movdir64b, /**< IA-32/AMD64 movdir64b opcode. */ + OP_AFTER_LAST, OP_FIRST = OP_add, /**< First real opcode. */ OP_LAST = OP_AFTER_LAST - 1, /**< Last real opcode. */ diff --git a/suite/tests/api/ir_x86_2args_mm.h b/suite/tests/api/ir_x86_2args_mm.h index 56da1a943dd..0f1412b8eac 100644 --- a/suite/tests/api/ir_x86_2args_mm.h +++ b/suite/tests/api/ir_x86_2args_mm.h @@ -325,3 +325,31 @@ OPCODE(bndldx_b0ld, bndldx, bndldx, 0, REGARG(BND0), MEMARG(OPSZ_bnd)) OPCODE(bndldx_b3ld, bndldx, bndldx, 0, REGARG(BND3), MEMARG(OPSZ_bnd)) OPCODE(bndstx_b0st, bndstx, bndstx, 0, MEMARG(OPSZ_bnd), REGARG(BND0)) OPCODE(bndstx_b3st, bndstx, bndstx, 0, MEMARG(OPSZ_bnd), REGARG(BND3)) + +/* MOVDIRI */ +OPCODE(movdiri32, movdiri, movdiri, 0, MEMARG(OPSZ_4), REGARG(EAX)) +OPCODE(movdiri64, movdiri, movdiri, X64_ONLY, MEMARG(OPSZ_8), REGARG(RAX)) + +/* MOVDIR64B */ +/* NB: We can never use MEMARG for the dst because we need the segment selector. */ +/* NB: Can't use MEMARG for the src because it doesn't work with addr16 prefix. */ +OPCODE(movdir64b16, movdir64b, movdir64b, X86_ONLY, + opnd_create_far_base_disp(DR_SEG_ES, DR_REG_AX, DR_REG_NULL, 0, 0, OPSZ_64), + opnd_create_base_disp(DR_REG_SI, DR_REG_NULL, 0, memarg_disp, OPSZ_64)) +/* NB: Can't use MEMARG for the src because the base register needs to be the size of EAX. + */ +OPCODE(movdir64b32, movdir64b, movdir64b, 0, + opnd_create_far_base_disp(DR_SEG_ES, DR_REG_EAX, DR_REG_NULL, 0, 0, OPSZ_64), + opnd_create_base_disp(DR_REG_ECX, DR_REG_NULL, 0, memarg_disp, OPSZ_64)) +OPCODE(movdir64b32lohi, movdir64b, movdir64b, X64_ONLY, + opnd_create_far_base_disp(DR_SEG_ES, DR_REG_R8D, DR_REG_NULL, 0, 0, OPSZ_64), + opnd_create_base_disp(DR_REG_ECX, DR_REG_NULL, 0, memarg_disp, OPSZ_64)) +OPCODE(movdir64b32hilo, movdir64b, movdir64b, X64_ONLY, + opnd_create_far_base_disp(DR_SEG_ES, DR_REG_EAX, DR_REG_NULL, 0, 0, OPSZ_64), + opnd_create_base_disp(DR_REG_R9D, DR_REG_NULL, 0, memarg_disp, OPSZ_64)) +OPCODE(movdir64b64, movdir64b, movdir64b, X64_ONLY, + opnd_create_far_base_disp(DR_SEG_ES, DR_REG_RAX, DR_REG_NULL, 0, 0, OPSZ_64), + MEMARG(OPSZ_64)) +OPCODE(movdir64b64lohi, movdir64b, movdir64b, X64_ONLY, + opnd_create_far_base_disp(DR_SEG_ES, DR_REG_R9, DR_REG_NULL, 0, 0, OPSZ_64), + MEMARG(OPSZ_64)) diff --git a/third_party/binutils/test_decenc/drdecode_decenc_x86.expect b/third_party/binutils/test_decenc/drdecode_decenc_x86.expect index 0bcc5bfe599..f199a399912 100644 --- a/third_party/binutils/test_decenc/drdecode_decenc_x86.expect +++ b/third_party/binutils/test_decenc/drdecode_decenc_x86.expect @@ -138821,7 +138821,6 @@ test_s: 90 nop 90 nop 90 nop - 0f 01 ca clac 0f 01 cb stac 90 nop @@ -138840,6 +138839,37 @@ test_s: 90 nop 90 nop 90 nop + 0f 38 f9 01 movdiri %eax, (%ecx) + 66 0f 38 f9 01 data16 movdiri %eax, (%ecx) + 67 66 0f 38 f8 04 movdir64b (%si), %ax + 67 66 0f 38 f8 0e 00 movdir64b 0x00, %cx + 00 + 67 66 0f 38 f8 0e 34 movdir64b 0x1234, %cx + 12 + 0f 38 f9 01 movdiri %eax, (%ecx) + 0f 38 f9 01 movdiri %eax, (%ecx) + 66 0f 38 f9 01 data16 movdiri %eax, (%ecx) + 67 66 0f 38 f8 04 movdir64b (%si), %ax + 67 66 0f 38 f8 0e 00 movdir64b 0x00, %cx + 00 + 67 66 0f 38 f8 0e 34 movdir64b 0x1234, %cx + 12 + 90 nop + 90 nop + 90 nop + 90 nop + 90 nop + 90 nop + 90 nop + 90 nop + 90 nop + 90 nop + 90 nop + 90 nop + 90 nop + 90 nop + 90 nop + 90 nop 90 nop 90 nop 90 nop diff --git a/third_party/binutils/test_decenc/drdecode_decenc_x86_64.expect b/third_party/binutils/test_decenc/drdecode_decenc_x86_64.expect index 51aaf58db02..ecc4c96c11b 100644 --- a/third_party/binutils/test_decenc/drdecode_decenc_x86_64.expect +++ b/third_party/binutils/test_decenc/drdecode_decenc_x86_64.expect @@ -102852,6 +102852,31 @@ test_x86_64_s: 49 0f c7 1c 00 xrstors64 (%r8,%rax) 4a 0f c7 1c 00 xrstors64 (%rax,%r8) 4b 0f c7 1c 38 xrstors64 (%r8,%r15) + 48 0f 38 f9 01 movdiri %rax, (%rcx) + 66 0f 38 f8 01 movdir64b (%rcx), %rax + 67 66 0f 38 f8 01 movdir64b (%ecx), %eax + 66 0f 38 f8 0d 00 00 movdir64b 0x0000000010070ac4, %rcx + 00 00 + 67 66 0f 38 f8 0d 00 movdir64b 0x0000000010070ace, %ecx + 00 00 00 + 67 66 0f 38 f8 0c 25 movdir64b 0x00, %ecx + 00 00 00 00 + 67 66 0f 38 f8 0c 25 movdir64b 0x12345678, %ecx + 78 56 34 12 + 0f 38 f9 01 movdiri %eax, (%rcx) + 48 0f 38 f9 01 movdiri %rax, (%rcx) + 0f 38 f9 01 movdiri %eax, (%rcx) + 48 0f 38 f9 01 movdiri %rax, (%rcx) + 66 0f 38 f8 01 movdir64b (%rcx), %rax + 67 66 0f 38 f8 01 movdir64b (%ecx), %eax + 66 0f 38 f8 0d 00 00 movdir64b 0x0000000010070b0a, %rcx + 00 00 + 67 66 0f 38 f8 0d 00 movdir64b 0x0000000010070b14, %ecx + 00 00 00 + 67 66 0f 38 f8 0c 25 movdir64b 0x00, %ecx + 00 00 00 00 + 67 66 0f 38 f8 0c 25 movdir64b 0x12345678, %ecx + 78 56 34 12 0f 01 ca clac 0f 01 cb stac 90 nop diff --git a/third_party/binutils/test_decenc/test_decenc_x86.asm b/third_party/binutils/test_decenc/test_decenc_x86.asm index 7457a389868..3058afd345c 100644 --- a/third_party/binutils/test_decenc/test_decenc_x86.asm +++ b/third_party/binutils/test_decenc/test_decenc_x86.asm @@ -139896,5 +139896,19 @@ GLOBAL_LABEL(FUNCNAME:) #endif /* DISABLED_UNTIL_BUG_3581_IS_FIXED */ + /* movdir.s */ + RAW(0f) RAW(38) RAW(f9) RAW(01) + RAW(66) RAW(0f) RAW(38) RAW(f9) RAW(01) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(04) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(0e) RAW(00) RAW(00) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(0e) RAW(34) RAW(12) + RAW(0f) RAW(38) RAW(f9) RAW(01) + RAW(0f) RAW(38) RAW(f9) RAW(01) + RAW(66) RAW(0f) RAW(38) RAW(f9) RAW(01) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(04) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(0e) RAW(00) RAW(00) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(0e) RAW(34) RAW(12) + END_OF_SUBTEST_MARKER + END_OF_FUNCTION_MARKER END_FUNC(FUNCNAME) diff --git a/third_party/binutils/test_decenc/test_decenc_x86_64.asm b/third_party/binutils/test_decenc/test_decenc_x86_64.asm index 644ea3c9e2e..323f4729e5c 100644 --- a/third_party/binutils/test_decenc/test_decenc_x86_64.asm +++ b/third_party/binutils/test_decenc/test_decenc_x86_64.asm @@ -106680,6 +106680,24 @@ GLOBAL_LABEL(FUNCNAME:) RAW(4a) RAW(0f) RAW(c7) RAW(1c) RAW(00) RAW(4b) RAW(0f) RAW(c7) RAW(1c) RAW(38) + /* x86_64_movdir.s */ + RAW(48) RAW(0f) RAW(38) RAW(f9) RAW(01) + RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(01) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(01) + RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(0d) RAW(00) RAW(00) RAW(00) RAW(00) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(0d) RAW(00) RAW(00) RAW(00) RAW(00) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(0c) RAW(25) RAW(00) RAW(00) RAW(00) RAW(00) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(0c) RAW(25) RAW(78) RAW(56) RAW(34) RAW(12) + RAW(0f) RAW(38) RAW(f9) RAW(01) + RAW(48) RAW(0f) RAW(38) RAW(f9) RAW(01) + RAW(0f) RAW(38) RAW(f9) RAW(01) + RAW(48) RAW(0f) RAW(38) RAW(f9) RAW(01) + RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(01) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(01) + RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(0d) RAW(00) RAW(00) RAW(00) RAW(00) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(0d) RAW(00) RAW(00) RAW(00) RAW(00) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(0c) RAW(25) RAW(00) RAW(00) RAW(00) RAW(00) + RAW(67) RAW(66) RAW(0f) RAW(38) RAW(f8) RAW(0c) RAW(25) RAW(78) RAW(56) RAW(34) RAW(12) /* TODO i#5505: Move the following back under * x86_64_arch_3.s in a separate PR to keep the huge