| /* ********************************************************** |
| * Copyright (c) 2011-2014 Google, Inc. All rights reserved. |
| * Copyright (c) 2001-2010 VMware, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of VMware, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| /* Copyright (c) 2003-2007 Determina Corp. */ |
| /* Copyright (c) 2001-2003 Massachusetts Institute of Technology */ |
| /* Copyright (c) 2001 Hewlett-Packard Company */ |
| |
| /* encode.c -- an x86 encoder */ |
| |
| #include "../globals.h" |
| #include "arch.h" |
| #include "instr.h" |
| #include "decode.h" |
| #include "disassemble.h" |
| #include "decode_fast.h" |
| #include "decode_private.h" |
| |
| #include <string.h> /* memcpy, memset */ |
| |
| #ifdef DEBUG |
| /* case 10450: give messages to clients */ |
| /* we can't undef ASSERT b/c of DYNAMO_OPTION */ |
| # undef ASSERT_TRUNCATE |
| # undef ASSERT_BITFIELD_TRUNCATE |
| # undef ASSERT_NOT_REACHED |
| # define ASSERT_TRUNCATE DO_NOT_USE_ASSERT_USE_CLIENT_ASSERT_INSTEAD |
| # define ASSERT_BITFIELD_TRUNCATE DO_NOT_USE_ASSERT_USE_CLIENT_ASSERT_INSTEAD |
| # define ASSERT_NOT_REACHED DO_NOT_USE_ASSERT_USE_CLIENT_ASSERT_INSTEAD |
| #endif |
| |
| /* level at which encoding attempts get dumped out...lots of logging! */ |
| #define ENC_LEVEL 6 |
| |
| const char * const type_names[] = { |
| "TYPE_NONE", |
| "TYPE_A", /* immediate that is absolute address */ |
| "TYPE_B", /* vex.vvvv field selects general-purpose register */ |
| "TYPE_C", /* reg of modrm selects control reg */ |
| "TYPE_D", /* reg of modrm selects debug reg */ |
| "TYPE_E", /* modrm selects reg or mem addr */ |
| "TYPE_G", /* reg of modrm selects register */ |
| "TYPE_H", /* vex.vvvv field selects xmm/ymm register */ |
| "TYPE_I", /* immediate */ |
| "TYPE_J", /* immediate that is relative offset of EIP */ |
| "TYPE_L", /* top 4 bits of 8-bit immed select xmm/ymm register */ |
| "TYPE_M", /* modrm select mem addr */ |
| "TYPE_O", /* immediate that is memory offset */ |
| "TYPE_P", /* reg of modrm selects MMX */ |
| "TYPE_Q", /* modrm selects MMX or mem addr */ |
| "TYPE_R", /* mod of modrm selects register */ |
| "TYPE_S", /* reg of modrm selects segment register */ |
| "TYPE_V", /* reg of modrm selects XMM */ |
| "TYPE_W", /* modrm selects XMM or mem addr */ |
| "TYPE_X", /* DS:(RE)(E)SI */ |
| "TYPE_Y", /* ES:(RE)(E)SDI */ |
| "TYPE_P_MODRM", /* mod of modrm selects MMX */ |
| "TYPE_V_MODRM", /* mod of modrm selects XMM */ |
| "TYPE_1", |
| "TYPE_FLOATCONST", |
| "TYPE_XLAT", /* DS:(RE)(E)BX+AL */ |
| "TYPE_MASKMOVQ", /* DS:(RE)(E)DI */ |
| "TYPE_FLOATMEM", |
| "TYPE_VSIB", |
| "TYPE_REG", |
| "TYPE_XREG", |
| "TYPE_VAR_REG", |
| "TYPE_VARZ_REG", |
| "TYPE_VAR_XREG", |
| "TYPE_VAR_REGX", |
| "TYPE_VAR_ADDR_XREG", |
| "TYPE_REG_EX", |
| "TYPE_VAR_REG_EX", |
| "TYPE_VAR_XREG_EX", |
| "TYPE_VAR_REGX_EX", |
| "TYPE_INDIR_E", |
| "TYPE_INDIR_REG", |
| "TYPE_INDIR_VAR_XREG", |
| "TYPE_INDIR_VAR_REG", |
| "TYPE_INDIR_VAR_XIREG", |
| "TYPE_INDIR_VAR_XREG_OFFS_1", |
| "TYPE_INDIR_VAR_XREG_OFFS_8", |
| "TYPE_INDIR_VAR_XREG_OFFS_N", |
| "TYPE_INDIR_VAR_XIREG_OFFS_1", |
| "TYPE_INDIR_VAR_REG_OFFS_2", |
| "TYPE_INDIR_VAR_XREG_SIZEx8", |
| "TYPE_INDIR_VAR_REG_SIZEx2", |
| "TYPE_INDIR_VAR_REG_SIZEx3x5", |
| }; |
| |
| /* order corresponds to enum of REG_ and SEG_ constants */ |
| const char * const reg_names[] = { |
| "<NULL>", |
| "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", |
| "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", |
| "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", |
| "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d", |
| "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", |
| "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w", |
| "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", |
| "r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l", |
| "spl", "bpl", "sil", "dil", |
| "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", |
| "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", |
| "xmm8", "xmm9", "xmm10","xmm11","xmm12","xmm13","xmm14","xmm15", |
| "st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7", |
| "es", "cs", "ss", "ds", "fs", "gs", |
| "dr0", "dr1", "dr2", "dr3", "dr4", "dr5", "dr6", "dr7", |
| "dr8", "dr9", "dr10", "dr11", "dr12", "dr13", "dr14", "dr15", |
| "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", |
| "cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15", |
| "<invalid>", |
| "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", |
| "ymm8", "ymm9", "ymm10","ymm11","ymm12","ymm13","ymm14","ymm15", |
| /* when you update here, update dr_reg_fixer[] too */ |
| }; |
| |
| /* Maps sub-registers to their containing register. */ |
| const reg_id_t dr_reg_fixer[] = { |
| REG_NULL, |
| REG_XAX, REG_XCX, REG_XDX, REG_XBX, REG_XSP, REG_XBP, REG_XSI, REG_XDI, |
| REG_R8, REG_R9, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15, |
| REG_XAX, REG_XCX, REG_XDX, REG_XBX, REG_XSP, REG_XBP, REG_XSI, REG_XDI, |
| REG_R8, REG_R9, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15, |
| REG_XAX, REG_XCX, REG_XDX, REG_XBX, REG_XSP, REG_XBP, REG_XSI, REG_XDI, |
| REG_R8, REG_R9, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15, |
| REG_XAX, REG_XCX, REG_XDX, REG_XBX, REG_XAX, REG_XCX, REG_XDX, REG_XBX, |
| REG_R8, REG_R9, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15, |
| REG_XSP, REG_XBP, REG_XSI, REG_XDI, /* i#201 */ |
| REG_MM0, REG_MM1, REG_MM2, REG_MM3, REG_MM4, REG_MM5, REG_MM6, REG_MM7, |
| REG_YMM0, REG_YMM1, REG_YMM2, REG_YMM3, REG_YMM4, REG_YMM5, REG_YMM6, REG_YMM7, |
| REG_YMM8, REG_YMM9, REG_YMM10,REG_YMM11,REG_YMM12,REG_YMM13,REG_YMM14,REG_YMM15, |
| REG_ST0, REG_ST1, REG_ST2, REG_ST3, REG_ST4, REG_ST5, REG_ST6, REG_ST7, |
| SEG_ES, SEG_CS, SEG_SS, SEG_DS, SEG_FS, SEG_GS, |
| REG_DR0, REG_DR1, REG_DR2, REG_DR3, REG_DR4, REG_DR5, REG_DR6, REG_DR7, |
| REG_DR8, REG_DR9, REG_DR10, REG_DR11, REG_DR12, REG_DR13, REG_DR14, REG_DR15, |
| REG_CR0, REG_CR1, REG_CR2, REG_CR3, REG_CR4, REG_CR5, REG_CR6, REG_CR7, |
| REG_CR8, REG_CR9, REG_CR10, REG_CR11, REG_CR12, REG_CR13, REG_CR14, REG_CR15, |
| REG_INVALID, |
| REG_YMM0, REG_YMM1, REG_YMM2, REG_YMM3, REG_YMM4, REG_YMM5, REG_YMM6, REG_YMM7, |
| REG_YMM8, REG_YMM9, REG_YMM10,REG_YMM11,REG_YMM12,REG_YMM13,REG_YMM14,REG_YMM15, |
| }; |
| |
| #ifdef DEBUG |
| void |
| reg_check_reg_fixer(void) |
| { |
| CLIENT_ASSERT(sizeof(dr_reg_fixer)/sizeof(dr_reg_fixer[0]) == REG_LAST_ENUM + 1, |
| "internal register enum error"); |
| } |
| #endif |
| |
| #if defined(DEBUG) && defined(INTERNAL) && !defined(STANDALONE_DECODER) |
| /* These operand types store a reg_id_t as their operand "size" */ |
| static bool |
| template_optype_is_reg(int optype) |
| { |
| switch (optype) { |
| case TYPE_REG: |
| case TYPE_XREG: |
| case TYPE_VAR_REG: |
| case TYPE_VARZ_REG: |
| case TYPE_VAR_XREG: |
| case TYPE_VAR_REGX: |
| case TYPE_VAR_ADDR_XREG: |
| case TYPE_INDIR_REG: |
| case TYPE_INDIR_VAR_XREG: |
| case TYPE_INDIR_VAR_REG: |
| case TYPE_INDIR_VAR_XIREG: |
| case TYPE_INDIR_VAR_XREG_OFFS_1: |
| case TYPE_INDIR_VAR_XREG_OFFS_8: |
| case TYPE_INDIR_VAR_XREG_OFFS_N: |
| case TYPE_INDIR_VAR_XIREG_OFFS_1: |
| case TYPE_INDIR_VAR_REG_OFFS_2: |
| case TYPE_INDIR_VAR_XREG_SIZEx8: |
| case TYPE_INDIR_VAR_REG_SIZEx2: |
| case TYPE_INDIR_VAR_REG_SIZEx3x5: |
| case TYPE_REG_EX: |
| case TYPE_VAR_REG_EX: |
| case TYPE_VAR_XREG_EX: |
| case TYPE_VAR_REGX_EX: |
| return true; |
| } |
| return false; |
| } |
| #endif |
| |
| /*************************************************************************** |
| * Functions to see if instr operands match instr_info_t template |
| */ |
| |
| static bool |
| type_instr_uses_reg_bits(int type) |
| { |
| switch (type) { |
| case TYPE_C: |
| case TYPE_D: |
| case TYPE_G: |
| case TYPE_P: |
| case TYPE_S: |
| case TYPE_V: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static bool |
| type_uses_modrm_bits(int type) |
| { |
| switch (type) { |
| case TYPE_E: |
| case TYPE_M: |
| case TYPE_Q: |
| case TYPE_R: |
| case TYPE_W: |
| case TYPE_INDIR_E: |
| case TYPE_P_MODRM: |
| case TYPE_V_MODRM: |
| case TYPE_VSIB: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static bool |
| type_uses_vex_vvvv_bits(int type) |
| { |
| switch (type) { |
| case TYPE_B: |
| case TYPE_H: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| /* Helper routine that sets/checks rex.w or data 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. |
| */ |
| static bool |
| size_ok_varsz(decode_info_t *di/*prefixes field is IN/OUT; x86_mode is IN*/, |
| opnd_size_t size_op, opnd_size_t size_template, |
| uint prefix_data_addr) |
| { |
| /* FIXME: this code is getting long and complex: is there a better way? |
| * Any way to resolve these var sizes further first? Doesn't seem like it. |
| */ |
| /* if identical sizes we shouldn't be called */ |
| CLIENT_ASSERT(size_op != size_template, "size_ok_varsz: internal decoding error"); |
| switch (size_op) { |
| case OPSZ_2_short1: |
| if (size_template == OPSZ_2 || size_template == OPSZ_1) |
| return true; /* will take prefix or no prefix */ |
| if (size_template == OPSZ_4_short2 || size_template == OPSZ_8_short2) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| if (size_template == OPSZ_4_rex8_short2) { |
| if (TEST(PREFIX_REX_W, di->prefixes)) |
| return false; /* rex.w trumps data prefix */ |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| return false; |
| case OPSZ_4_short2: |
| if (size_template == OPSZ_4 || size_template == OPSZ_2) |
| return true; /* will take prefix or no prefix */ |
| if (size_template == OPSZ_4_rex8_short2 || size_template == OPSZ_4_rex8) |
| return !TEST(PREFIX_REX_W, di->prefixes); |
| if (size_template == OPSZ_8_short2 || size_template == OPSZ_8_short4) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| if (size_template == OPSZ_6_irex10_short4 && |
| (proc_get_vendor() == VENDOR_AMD || !TEST(PREFIX_REX_W, di->prefixes))) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| return false; |
| case OPSZ_4_rex8_short2: |
| if (size_template == OPSZ_4_short2 || size_template == OPSZ_4_rex8 || |
| size_template == OPSZ_8_short2 || size_template == OPSZ_8_short4 || |
| size_template == OPSZ_2 || size_template == OPSZ_4 || size_template == OPSZ_8) |
| return true; /* will take prefix or no prefix */ |
| if (size_template == OPSZ_6_irex10_short4 && |
| (proc_get_vendor() == VENDOR_AMD || !TEST(PREFIX_REX_W, di->prefixes))) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| return false; |
| case OPSZ_4_rex8: |
| if (size_template == OPSZ_8_short4 || size_template == OPSZ_4 || |
| size_template == OPSZ_8) |
| return true; /* will take prefix or no prefix */ |
| if (size_template == OPSZ_4_short2 || size_template == OPSZ_4_rex8_short2 || |
| size_template == OPSZ_8_short2) |
| return !TEST(prefix_data_addr, di->prefixes); |
| if (size_template == OPSZ_6_irex10_short4 && |
| (proc_get_vendor() == VENDOR_AMD || !TEST(PREFIX_REX_W, di->prefixes))) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| return false; |
| case OPSZ_6_irex10_short4: |
| if (size_template == OPSZ_6 || size_template == OPSZ_4 || |
| (size_template == OPSZ_10 && proc_get_vendor() != VENDOR_AMD)) |
| return true; /* will take prefix or no prefix */ |
| if (size_template == OPSZ_4_short2) |
| return !TEST(prefix_data_addr, di->prefixes); |
| if (size_template == OPSZ_4_rex8_short2) |
| return !TESTANY(prefix_data_addr|PREFIX_REX_W, di->prefixes); |
| if (size_template == OPSZ_4_rex8) |
| return !TEST(PREFIX_REX_W, di->prefixes); |
| if (size_template == OPSZ_8_short4) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| return false; |
| case OPSZ_8_short2: |
| if (size_template == OPSZ_8 || size_template == OPSZ_2) |
| return true; /* will take prefix or no prefix */ |
| if (size_template == OPSZ_4_short2) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| if (size_template == OPSZ_4_rex8_short2) { |
| if (TEST(prefix_data_addr, di->prefixes)) |
| return true; /* Already shrinking so ok */ |
| /* FIXME - ambiguous on 64-bit (could widen to 8 or shrink to 2). |
| * We choose to widen by default for 64-bit as that seems the more likely |
| * usage, but whatever choice we make here could conflict with a later |
| * operand and lead to encoding failure even if there was a possible match. */ |
| if (X64_MODE(di)) |
| di->prefixes |= PREFIX_REX_W; |
| else |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| if (X64_MODE(di) && size_template == OPSZ_4_rex8) { |
| di->prefixes |= PREFIX_REX_W; |
| return true; |
| } |
| if (size_template == OPSZ_8_short4) |
| return !TEST(prefix_data_addr, di->prefixes); |
| return false; |
| case OPSZ_8_short4: |
| if (size_template == OPSZ_4_rex8 || size_template == OPSZ_8 || |
| size_template == OPSZ_4) |
| return true; /* will take prefix or no prefix */ |
| if (size_template == OPSZ_4_short2 || size_template == OPSZ_4_rex8_short2 || |
| size_template == OPSZ_8_short2) |
| return !TEST(prefix_data_addr, di->prefixes); |
| if (size_template == OPSZ_6_irex10_short4 && |
| (proc_get_vendor() == VENDOR_AMD || !TEST(PREFIX_REX_W, di->prefixes))) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| return false; |
| case OPSZ_8_of_16_vex32: |
| if (size_template == OPSZ_8 || size_template == OPSZ_32) |
| return true; /* will take prefix or no prefix */ |
| return false; |
| case OPSZ_4_rex8_of_16: |
| if (size_template == OPSZ_4 || size_template == OPSZ_8) |
| return true; /* will take prefix or no prefix */ |
| return false; |
| case OPSZ_12_rex8_of_16: |
| if (size_template == OPSZ_12 || size_template == OPSZ_8) |
| return true; /* will take prefix or no prefix */ |
| return false; |
| case OPSZ_16_vex32: |
| if (size_template == OPSZ_16 || size_template == OPSZ_32) |
| return true; /* will take prefix or no prefix */ |
| return false; |
| case OPSZ_28_short14: |
| if (size_template == OPSZ_28 || size_template == OPSZ_14) |
| return true; /* will take prefix or no prefix */ |
| return false; |
| case OPSZ_108_short94: |
| if (size_template == OPSZ_108 || size_template == OPSZ_94) |
| return true; /* will take prefix or no prefix */ |
| return false; |
| default: |
| CLIENT_ASSERT(false, "size_ok_varsz() internal decoding error (invalid size)"); |
| break; |
| } |
| return false; |
| } |
| |
| static opnd_size_t |
| resolve_var_x64_size(decode_info_t *di/*x86_mode is IN*/, |
| opnd_size_t sz, bool addr_short4) |
| { |
| /* Resolve what we can based purely on x64 and addr_short4 |
| * (gets rid of all NxM sizes), as well as vendor where the size |
| * differences are static. FIXME - could also resolve rex |
| * availability and vendor rex-varying sizes here, |
| * but not without adding more types that would make |
| * size_ok routines more complicated. */ |
| switch (sz) { |
| case OPSZ_4x8: return (X64_MODE(di) ? OPSZ_8 : OPSZ_4); |
| case OPSZ_4_short2xi4: return (X64_MODE(di) && proc_get_vendor() == VENDOR_INTEL ? |
| OPSZ_4 : OPSZ_4_short2); |
| case OPSZ_4x8_short2: return (X64_MODE(di) ? |
| (addr_short4 ? OPSZ_8_short4 : OPSZ_8_short2) : |
| OPSZ_4_short2); |
| case OPSZ_4x8_short2xi8: return (X64_MODE(di) ? (proc_get_vendor() == VENDOR_INTEL ? |
| OPSZ_8 : OPSZ_8_short2) : |
| OPSZ_4_short2); |
| case OPSZ_6x10: return (X64_MODE(di) ? OPSZ_10 : OPSZ_6); |
| } |
| return sz; |
| } |
| |
| static opnd_size_t |
| collapse_subreg_size(opnd_size_t sz) |
| { |
| switch (sz) { |
| case OPSZ_1_of_16: |
| return OPSZ_1; |
| case OPSZ_2_of_8: |
| case OPSZ_2_of_16: |
| return OPSZ_2; |
| case OPSZ_4_of_8: |
| case OPSZ_4_of_16: |
| return OPSZ_4; |
| case OPSZ_8_of_16: |
| return OPSZ_8; |
| case OPSZ_12_of_16: |
| return OPSZ_12; |
| case OPSZ_14_of_16: |
| return OPSZ_14; |
| case OPSZ_15_of_16: |
| return OPSZ_15; |
| case OPSZ_16_of_32: |
| return OPSZ_16; |
| /* OPSZ_8_of_16_vex32, OPSZ_4_rex8_of_16, and OPSZ_12_rex8_of_16 are kept */ |
| } |
| return sz; |
| } |
| |
| /* Caller should resolve the OPSZ_*_reg* sizes prior to calling this |
| * routine, as here we don't know the operand types |
| * Note that this routine modifies prefixes, so it is not idempotent; the |
| * prefixes are stateful and are kept around as each operand is checked to |
| * ensure the later ones are ok w/ prefixes needed for the earlier ones. |
| */ |
| static bool |
| size_ok(decode_info_t *di/*prefixes field is IN/OUT; x86_mode is IN*/, |
| opnd_size_t size_op, opnd_size_t size_template, bool addr) |
| { |
| uint prefix_data_addr = (addr ? PREFIX_ADDR : PREFIX_DATA); |
| /* 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 |
| * OPSZ_4_short2 for x86 mode on x64. |
| */ |
| CLIENT_ASSERT(!addr || size_template == OPSZ_4x8_short2xi8 || |
| size_template == OPSZ_4x8_short2 |
| IF_X64(|| (!X64_MODE(di) && size_template == OPSZ_4_short2)), |
| "internal prefix assumption error"); |
| size_template = resolve_var_x64_size(di, size_template, addr_short4); |
| size_op = resolve_var_x64_size(di, size_op, addr_short4); |
| /* all NxM sizes should be resolved (size_op is checked in the switch statement as |
| * these values will hit the default assert) */ |
| CLIENT_ASSERT(size_template != OPSZ_6x10 && size_template != OPSZ_4x8_short2 && |
| size_template != OPSZ_4x8_short2xi8 && |
| size_template != OPSZ_4_short2xi4 && size_template != OPSZ_4x8, |
| "internal encoding error in size_ok()"); |
| |
| /* register size checks go through reg_size_ok, so collapse sub-reg |
| * sizes to the true sizes |
| */ |
| size_op = collapse_subreg_size(size_op); |
| size_template = collapse_subreg_size(size_template); |
| |
| /* First set/check rex.w or data prefix, if necessary |
| * if identical size then don't need to set or check anything */ |
| if (size_op != size_template) { |
| switch (size_op) { |
| case OPSZ_1: |
| if (size_template == OPSZ_2_short1) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| return false; |
| case OPSZ_2: |
| if (size_template == OPSZ_2_short1) |
| return !TEST(prefix_data_addr, di->prefixes); |
| if (size_template == OPSZ_4_short2 || size_template == OPSZ_8_short2) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| if (size_template == OPSZ_4_rex8_short2) { |
| if (TEST(PREFIX_REX_W, di->prefixes)) |
| return false; /* rex.w trumps data prefix */ |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| return false; |
| case OPSZ_4: |
| if (size_template == OPSZ_4_short2) |
| return !TEST(prefix_data_addr, di->prefixes); |
| if (size_template == OPSZ_4_rex8_short2) |
| return !TESTANY(prefix_data_addr|PREFIX_REX_W, di->prefixes); |
| if (size_template == OPSZ_4_rex8) |
| return !TEST(PREFIX_REX_W, di->prefixes); |
| if (size_template == OPSZ_6_irex10_short4) { |
| if (TEST(PREFIX_REX_W, di->prefixes) && proc_get_vendor() != VENDOR_AMD) |
| return false; /* rex.w trumps data prefix */ |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| if (size_template == OPSZ_8_short4 || size_template == OPSZ_8_rex16_short4) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| if (size_template == OPSZ_4_rex8_of_16) |
| return !TEST(PREFIX_REX_W, di->prefixes); |
| return false; |
| case OPSZ_6: |
| if (size_template == OPSZ_6_irex10_short4) |
| return !TEST(prefix_data_addr, di->prefixes) && |
| (!TEST(PREFIX_REX_W, di->prefixes) || proc_get_vendor() == VENDOR_AMD); |
| if (size_template == OPSZ_12_rex40_short6) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| return false; |
| case OPSZ_8: |
| if (X64_MODE(di) && |
| (size_template == OPSZ_4_rex8 || size_template == OPSZ_4_rex8_short2 || |
| size_template == OPSZ_4_rex8_of_16 || |
| size_template == OPSZ_12_rex8_of_16)) { |
| di->prefixes |= PREFIX_REX_W; /* rex.w trumps data prefix */ |
| return true; |
| } |
| if (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); |
| if (size_template == OPSZ_8_of_16_vex32) |
| return !TEST(PREFIX_VEX_L, di->prefixes); |
| return false; |
| case OPSZ_10: |
| if (X64_MODE(di) && size_template == OPSZ_6_irex10_short4 && |
| proc_get_vendor() != VENDOR_AMD) { |
| di->prefixes |= PREFIX_REX_W; /* rex.w trumps data prefix */ |
| return true; |
| } |
| return false; |
| case OPSZ_12: |
| if (size_template == OPSZ_12_rex40_short6) |
| return !TESTANY(prefix_data_addr|PREFIX_REX_W, di->prefixes); |
| if (size_template == OPSZ_12_rex8_of_16) |
| return !TEST(PREFIX_REX_W, di->prefixes); |
| return false; |
| case OPSZ_16: |
| if (X64_MODE(di) && |
| (size_template == OPSZ_8_rex16 || |
| size_template == OPSZ_8_rex16_short4)) { |
| di->prefixes |= PREFIX_REX_W; /* rex.w trumps data prefix */ |
| return true; |
| } |
| if (size_template == OPSZ_32_short16) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| if (size_template == OPSZ_16_vex32) |
| return !TEST(PREFIX_VEX_L, di->prefixes); |
| return false; /* no matching varsz, must be exact match */ |
| case OPSZ_14: |
| if (size_template == OPSZ_28_short14) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| return false; |
| case OPSZ_15: |
| return false; /* no variable sizes match, need identical request */ |
| case OPSZ_28: |
| if (size_template == OPSZ_28_short14) |
| return !TEST(prefix_data_addr, di->prefixes); |
| return false; |
| case OPSZ_32: |
| if (size_template == OPSZ_32_short16) |
| return !TEST(prefix_data_addr, di->prefixes); |
| if (size_template == OPSZ_16_vex32 || |
| size_template == OPSZ_8_of_16_vex32) { |
| di->prefixes |= PREFIX_VEX_L; |
| return true; |
| } |
| return false; |
| case OPSZ_40: |
| if (X64_MODE(di) && size_template == OPSZ_12_rex40_short6) { |
| di->prefixes |= PREFIX_REX_W; /* rex.w trumps data prefix */ |
| return true; |
| } |
| return false; |
| case OPSZ_94: |
| if (size_template == OPSZ_108_short94) { |
| di->prefixes |= prefix_data_addr; |
| return true; |
| } |
| return false; |
| case OPSZ_108: |
| if (size_template == OPSZ_108_short94) |
| return !TEST(prefix_data_addr, di->prefixes); |
| return false; |
| case OPSZ_512: |
| return false; /* no variable sizes match, need identical request */ |
| /* We do support variable-sized requests */ |
| case OPSZ_8_rex16: |
| case OPSZ_8_rex16_short4: |
| case OPSZ_12_rex40_short6: |
| case OPSZ_32_short16: |
| /* not supporting client asking for these when template is not identical |
| * (not worth the complexity). similarly we don't support the client |
| * asking for other var sizes when the template is one of these. |
| */ |
| CLIENT_ASSERT(false, "variable multi-stack-slot sizes not supported " |
| "as general-purpose sizes"); |
| break; |
| case OPSZ_2_short1: |
| case OPSZ_4_short2: |
| case OPSZ_4_rex8_short2: |
| case OPSZ_4_rex8: |
| case OPSZ_6_irex10_short4: |
| case OPSZ_8_short2: |
| case OPSZ_8_short4: |
| case OPSZ_16_vex32: |
| case OPSZ_28_short14: |
| case OPSZ_108_short94: |
| return size_ok_varsz(di, size_op, size_template, prefix_data_addr); |
| case OPSZ_1_reg4: |
| case OPSZ_2_reg4: |
| case OPSZ_4_reg16: |
| CLIENT_ASSERT(false, "error: cannot pass OPSZ_*_reg* to size_ok()"); |
| return false; |
| case OPSZ_2_of_8: |
| case OPSZ_4_of_8: |
| case OPSZ_1_of_16: |
| case OPSZ_2_of_16: |
| case OPSZ_4_of_16: |
| case OPSZ_4_rex8_of_16: |
| case OPSZ_8_of_16: |
| case OPSZ_12_of_16: |
| case OPSZ_12_rex8_of_16: |
| case OPSZ_14_of_16: |
| case OPSZ_15_of_16: |
| case OPSZ_8_of_16_vex32: |
| case OPSZ_16_of_32: |
| case OPSZ_0: |
| /* handled below */ |
| break; |
| default: |
| CLIENT_ASSERT(false, "error: unhandled OPSZ_ in size_ok()"); |
| return false; |
| } |
| } |
| |
| /* prefix doesn't come into play below here: do a direct comparison */ |
| |
| DOLOG(4, LOG_EMIT, { |
| if (size_op != size_template) { |
| LOG(THREAD_GET, LOG_EMIT, ENC_LEVEL, "size_ok: %s != %s\n", |
| size_names[size_op], size_names[size_template]); |
| } |
| }); |
| return (size_op == size_template); |
| } |
| |
| /* We assume size_ok() is called ahead of time to check whether a prefix |
| * is needed. |
| */ |
| static bool |
| immed_size_ok(decode_info_t *di/*prefixes field is IN/OUT; x86_mode is IN*/, |
| ptr_int_t immed, opnd_size_t opsize) |
| { |
| opsize = resolve_variable_size(di, opsize, false); |
| switch (opsize) { |
| case OPSZ_1: |
| return (immed >= INT8_MIN && immed <= INT8_MAX); |
| case OPSZ_2: /* unsigned max is 65535 */ |
| return (immed >= INT16_MIN && immed <= INT16_MAX); |
| #ifndef X64 |
| case OPSZ_4: |
| return true; |
| #else |
| case OPSZ_4: |
| return (immed >= INT32_MIN && immed <= INT32_MAX); |
| case OPSZ_8: |
| return true; |
| #endif |
| default: |
| CLIENT_ASSERT(false, "encode error: immediate has unknown size"); |
| return false; |
| } |
| } |
| |
| /* prefixes that aren't set by size_ok */ |
| static bool |
| reg_set_ext_prefixes(decode_info_t *di/*prefixes field is IN/OUT; x86_mode is IN*/, |
| reg_id_t reg, uint which_rex) |
| { |
| #ifdef X64 |
| if (reg >= REG_START_x64_8 && reg <= REG_STOP_x64_8) { |
| /* alternates to AH-BH that are specified via any rex prefix */ |
| if (!TESTANY(PREFIX_REX_ALL, di->prefixes)) |
| di->prefixes |= PREFIX_REX_GENERAL; |
| } else if (reg_is_extended(reg)) |
| di->prefixes |= which_rex; |
| #endif |
| return true; /* for use in && series */ |
| } |
| |
| static bool |
| reg_size_ok(decode_info_t *di/*prefixes field is IN/OUT; x86_mode is IN*/, |
| reg_id_t reg, int optype, opnd_size_t opsize, bool addr) |
| { |
| /* Although we now expose sub-register sizes (i#1382), we do not require |
| * them when encoding as we have no simple way to add auto-magic creation |
| * to the INSTR_CREATE_ macros. Plus, sub-register sizes never distinguish |
| * two opcodes. |
| */ |
| if ((opsize >= OPSZ_SUBREG_START && opsize <= OPSZ_SUBREG_END) || |
| opsize == OPSZ_4_reg16) { |
| opnd_size_t expanded = expand_subreg_size(opsize); |
| if (expanded == OPSZ_8 && |
| (optype == TYPE_P || optype == TYPE_Q || optype == TYPE_P_MODRM)) |
| return (reg >= REG_START_MMX && reg <= REG_STOP_MMX); |
| if (expanded == OPSZ_16 && |
| (optype == TYPE_V || optype == TYPE_V_MODRM || optype == TYPE_W || |
| optype == TYPE_H || optype == TYPE_L)) |
| return (reg >= REG_START_XMM && reg <= REG_STOP_XMM); |
| } |
| if (opsize == OPSZ_8_of_16_vex32 || optype == TYPE_VSIB) { |
| if (reg >= REG_START_XMM && reg <= REG_STOP_XMM) |
| return !TEST(PREFIX_VEX_L, di->prefixes); |
| if (reg >= REG_START_YMM && reg <= REG_STOP_YMM) { |
| di->prefixes |= PREFIX_VEX_L; |
| return true; |
| } |
| return false; |
| } |
| if (opsize == OPSZ_16_of_32) { |
| if (reg >= REG_START_YMM && reg <= REG_STOP_YMM) { |
| /* Set VEX.L since required for some opcodes and the rest don't care */ |
| di->prefixes |= PREFIX_VEX_L; |
| return true; |
| } else |
| return false; |
| } |
| /* We assume that only type p uses OPSZ_6_irex10_short4: w/ data16, even though it's |
| * 4 bytes and would fit in a register, this is invalid. |
| */ |
| if (opsize == OPSZ_6_irex10_short4) |
| return false; /* no register of size p */ |
| if (size_ok(di, reg_get_size(reg), resolve_var_reg_size(opsize, true), addr)) { |
| if (reg >= REG_START_YMM && reg <= REG_STOP_YMM) { |
| /* Set VEX.L since required for some opcodes and the rest don't care */ |
| di->prefixes |= PREFIX_VEX_L; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| static bool |
| reg_rm_selectable(reg_id_t reg) |
| { |
| /* assumption: GPR registers (of all sizes) and mmx and xmm are all in a row */ |
| return (reg >= REG_START_64 && reg <= REG_STOP_XMM) || |
| (reg >= REG_START_YMM && reg <= REG_STOP_YMM); |
| } |
| |
| static bool |
| mem_size_ok(decode_info_t *di/*prefixes field is IN/OUT; x86_mode is IN*/, |
| opnd_t opnd, int optype, opnd_size_t opsize) |
| { |
| opsize = resolve_var_reg_size(opsize, false); |
| if (!opnd_is_memory_reference(opnd)) |
| return false; |
| if (opnd_is_base_disp(opnd) && opnd_is_disp_short_addr(opnd)) |
| di->prefixes |= PREFIX_ADDR; |
| return (size_ok(di, opnd_get_size(opnd), opsize, false/*!addr*/) && |
| (!opnd_is_base_disp(opnd) || |
| opnd_get_base(opnd) == REG_NULL || |
| reg_size_ok(di, opnd_get_base(opnd), TYPE_M, |
| IF_X64(!X64_MODE(di) ? OPSZ_4_short2 :) OPSZ_4x8_short2, |
| true/*addr*/)) && |
| (!opnd_is_base_disp(opnd) || |
| opnd_get_index(opnd) == REG_NULL || |
| reg_size_ok(di, opnd_get_index(opnd), |
| optype == TYPE_VSIB ? TYPE_VSIB : TYPE_M, |
| IF_X64(!X64_MODE(di) ? OPSZ_4_short2 :) OPSZ_4x8_short2, |
| true/*addr*/))); |
| } |
| |
| static bool |
| opnd_type_ok(decode_info_t *di/*prefixes field is IN/OUT; x86_mode is IN*/, |
| opnd_t opnd, int optype, opnd_size_t opsize) |
| { |
| DOLOG(ENC_LEVEL, LOG_EMIT, { |
| dcontext_t *dcontext = get_thread_private_dcontext(); |
| LOG(THREAD, LOG_EMIT, ENC_LEVEL, "opnd_type_ok on operand "); |
| opnd_disassemble(dcontext, opnd, THREAD); |
| if (!opnd_is_pc(opnd) && !opnd_is_instr(opnd)) { |
| LOG(THREAD, LOG_EMIT, ENC_LEVEL, "with size %s (%d bytes)\n", |
| size_names[opnd_get_size(opnd)], |
| opnd_size_in_bytes(opnd_get_size(opnd))); |
| } |
| LOG(THREAD, LOG_EMIT, ENC_LEVEL, |
| "\tvs. template type %s with size %s (%d bytes)\n", |
| type_names[optype], |
| template_optype_is_reg(optype) ? |
| reg_names[opsize] : size_names[opsize], |
| template_optype_is_reg(optype) ? |
| opnd_size_in_bytes(reg_get_size(opsize)) : opnd_size_in_bytes(opsize)); |
| }); |
| switch (optype) { |
| case TYPE_NONE: |
| return opnd_is_null(opnd); |
| case TYPE_REG: |
| return (opnd_is_reg(opnd) && opnd_get_reg(opnd) == opsize); |
| case TYPE_XREG: |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, OPSZ_4x8, false/*!addr*/) && |
| opnd_get_reg(opnd) == resolve_var_reg(di, opsize, false, false |
| _IF_X64(true) _IF_X64(false) |
| _IF_X64(false/*!extendable*/))); |
| case TYPE_VAR_REG: |
| /* for TYPE_*REG*, opsize is really reg_id_t */ |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, OPSZ_4_rex8_short2, |
| false/*!addr*/) && |
| opnd_get_reg(opnd) == resolve_var_reg(di, opsize, false, true |
| _IF_X64(false) _IF_X64(true) |
| _IF_X64(false/*!extendable*/))); |
| case TYPE_VARZ_REG: |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, OPSZ_4_short2, |
| false/*!addr*/) && |
| opnd_get_reg(opnd) == resolve_var_reg(di, opsize, false, true |
| _IF_X64(false) _IF_X64(false) |
| _IF_X64(false/*!extendable*/))); |
| case TYPE_VAR_XREG: |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, OPSZ_4x8_short2, |
| false/*!addr*/) && |
| opnd_get_reg(opnd) == resolve_var_reg(di, opsize, false, true |
| _IF_X64(true) _IF_X64(true) |
| _IF_X64(false/*!extendable*/))); |
| case TYPE_VAR_REGX: |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, OPSZ_4_rex8, |
| false/*!addr*/) && |
| opnd_get_reg(opnd) == resolve_var_reg(di, opsize, false, false/*!shrink*/ |
| _IF_X64(false/*default 32*/) |
| _IF_X64(true/*can grow*/) |
| _IF_X64(false/*!extendable*/))); |
| case TYPE_VAR_ADDR_XREG: |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, OPSZ_4x8_short2, |
| true/*addr*/) && |
| opnd_get_reg(opnd) == resolve_var_reg(di, opsize, true, true |
| _IF_X64(true) _IF_X64(false) |
| _IF_X64(false/*!extendable*/))); |
| case TYPE_REG_EX: |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, reg_get_size(opsize), |
| false/*!addr*/) && |
| reg_set_ext_prefixes(di, opnd_get_reg(opnd), PREFIX_REX_B) && |
| opnd_get_reg(opnd) == resolve_var_reg(di, opsize, false, false |
| _IF_X64(false) _IF_X64(false) |
| _IF_X64(true/*extendable*/))); |
| case TYPE_VAR_REG_EX: |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, OPSZ_4_rex8_short2, |
| false/*!addr*/) && |
| reg_set_ext_prefixes(di, opnd_get_reg(opnd), PREFIX_REX_B) && |
| opnd_get_reg(opnd) == resolve_var_reg(di, opsize, false, true |
| _IF_X64(false) _IF_X64(true) |
| _IF_X64(true/*extendable*/))); |
| case TYPE_VAR_XREG_EX: |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, OPSZ_4x8_short2, |
| false/*!addr*/) && |
| reg_set_ext_prefixes(di, opnd_get_reg(opnd), PREFIX_REX_B) && |
| opnd_get_reg(opnd) == resolve_var_reg(di, opsize, false, true |
| _IF_X64(true) _IF_X64(true) |
| _IF_X64(true/*extendable*/))); |
| case TYPE_VAR_REGX_EX: |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, OPSZ_4_rex8, |
| false/*!addr*/) && |
| reg_set_ext_prefixes(di, opnd_get_reg(opnd), PREFIX_REX_B) && |
| opnd_get_reg(opnd) == resolve_var_reg(di, opsize, false, false |
| _IF_X64(false) _IF_X64(true) |
| _IF_X64(true/*extendable*/))); |
| case TYPE_VSIB: |
| #ifndef X64 |
| if (TEST(PREFIX_ADDR, di->prefixes)) |
| return false; /* VSIB invalid w/ 16-bit addressing */ |
| #endif |
| /* fall through */ |
| case TYPE_FLOATMEM: |
| case TYPE_M: |
| return mem_size_ok(di, opnd, optype, opsize); |
| case TYPE_E: |
| case TYPE_Q: |
| case TYPE_W: |
| case TYPE_INDIR_E: |
| return (mem_size_ok(di, opnd, optype, opsize) || |
| (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, opsize, false/*!addr*/) && |
| reg_rm_selectable(opnd_get_reg(opnd)))); |
| case TYPE_G: |
| case TYPE_R: |
| case TYPE_B: |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, opsize, |
| false/*!addr*/) && |
| reg_is_gpr(opnd_get_reg(opnd))); |
| case TYPE_P: |
| case TYPE_V: |
| case TYPE_P_MODRM: |
| case TYPE_V_MODRM: |
| /* We are able to rule out segment registers b/c they should use TYPE_S |
| * (OP_mov_seg) or hardcoded (push cs) (if we don't rule them out |
| * they can match a 16-bit GPR slot by size alone); CR and DR also |
| * have separate types (TYPE_C and TYPE_D). |
| */ |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, opsize, |
| false/*!addr*/) && |
| reg_rm_selectable(opnd_get_reg(opnd))); /* reg, not rm, but see above */ |
| case TYPE_C: |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, opsize, |
| false/*!addr*/) && |
| opnd_get_reg(opnd) >= REG_START_CR && opnd_get_reg(opnd) <= REG_STOP_CR); |
| case TYPE_D: |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, opsize, |
| false/*!addr*/) && |
| opnd_get_reg(opnd) >= REG_START_DR && opnd_get_reg(opnd) <= REG_STOP_DR); |
| case TYPE_S: |
| return (opnd_is_reg(opnd) && |
| opnd_get_reg(opnd) >= REG_START_SEGMENT && |
| opnd_get_reg(opnd) <= REG_STOP_SEGMENT); |
| case TYPE_I: |
| /* we allow instr: it means 4/8-byte immed equal to pc of instr */ |
| return ((opnd_is_near_instr(opnd) && |
| (size_ok(di, opnd_get_size(opnd), opsize, false/*!addr*/) || |
| /* Though we recommend using instrlist_insert_{mov,push}_instr_addr(), |
| * we will accept a pointer-sized 8-byte instr_t when encoded |
| * to low 2GB (w/o top bit set, else sign-extended). |
| */ |
| (X64_MODE(di) && ((ptr_uint_t)di->final_pc) + |
| (ptr_uint_t)opnd_get_instr(opnd)->note - di->cur_note < INT_MAX && |
| size_ok(di, OPSZ_4, opsize, false)))) || |
| (opnd_is_immed_int(opnd) && |
| size_ok(di, opnd_get_size(opnd), opsize, false/*!addr*/) && |
| immed_size_ok(di, opnd_get_immed_int(opnd), opsize))); |
| case TYPE_1: |
| /* FIXME (xref PR 229127): Ib vs c1: if say "1, OPSZ_1" will NOT match c1 and |
| * will get the Ib version: do we want to match c1? What if they really want |
| * an immed byte in the encoding? OTOH, we do match constant registers |
| * automatically w/ no control from the user. |
| * Currently, we document in instr_create.h that the user must |
| * specify OPSZ_0 in order to get c1. |
| */ |
| return (opnd_is_immed_int(opnd) && opnd_get_immed_int(opnd) == 1 && |
| size_ok(di, opnd_get_size(opnd), opsize, false/*!addr*/)); |
| case TYPE_FLOATCONST: |
| return (opnd_is_immed_float(opnd)); /* FIXME: is actual float const decoded? */ |
| case TYPE_J: |
| /* FIXME PR 225937: support 16-bit data16 immediates */ |
| /* FIXME: need relative pc offset to test immed_size_ok, but all we have |
| * here is absolute pc or instr: but we don't auto-select opcode, and opcode |
| * selects immed size (except for data16 which we don't support), |
| * so we don't need to choose among templates now: we'll complain |
| * at emit time if we have reachability issues. */ |
| return (opnd_is_near_pc(opnd) || opnd_is_near_instr(opnd)); |
| case TYPE_A: |
| { |
| CLIENT_ASSERT(!X64_MODE(di), "x64 has no type A instructions"); |
| #ifdef IA32_ON_IA64 |
| if (opsize != OPSZ_6_irex10_short4) { |
| return (opnd_is_near_instr(opnd) || |
| (opnd_is_near_pc(opnd) && |
| immed_size_ok(di, (uint)opnd_get_pc(opnd), opsize))); |
| } |
| #endif |
| return (opnd_is_far_pc(opnd) || opnd_is_far_instr(opnd)); |
| } |
| case TYPE_O: |
| return ((opnd_is_abs_addr(opnd) || |
| #ifdef X64 |
| /* We'll take a relative address that rip-rel won't reach: |
| * after all, OPND_CREATE_ABSMEM() makes a rip-rel. |
| */ |
| (opnd_is_rel_addr(opnd) && |
| (!REL32_REACHABLE(di->final_pc + MAX_INSTR_LENGTH, |
| (byte *)opnd_get_addr(opnd)) || |
| !REL32_REACHABLE(di->final_pc + 4, |
| (byte *)opnd_get_addr(opnd)))) || |
| #endif |
| (!X64_MODE(di) && opnd_is_mem_instr(opnd))) && |
| size_ok(di, opnd_get_size(opnd), opsize, false/*!addr*/)); |
| case TYPE_X: |
| /* this means the memory address DS:(RE)(E)SI */ |
| if (opnd_is_far_base_disp(opnd)) { |
| reg_id_t base = opnd_get_base(opnd); |
| /* reg_size_ok will set PREFIX_ADDR if necessary */ |
| return (reg_size_ok(di, base, optype, OPSZ_4x8_short2, true/*addr*/) && |
| reg_is_segment(opnd_get_segment(opnd)) && |
| base == resolve_var_reg(di, REG_ESI, true, true |
| _IF_X64(true) _IF_X64(false) |
| _IF_X64(false)) && |
| opnd_get_index(opnd) == REG_NULL && |
| opnd_get_disp(opnd) == 0 && |
| size_ok(di, opnd_get_size(opnd), opsize, false/*!addr*/)); |
| } else { |
| return false; |
| } |
| case TYPE_Y: |
| /* this means the memory address ES:(RE)(E)DI */ |
| if (opnd_is_far_base_disp(opnd)) { |
| reg_id_t base = opnd_get_base(opnd); |
| /* reg_size_ok will set PREFIX_ADDR if necessary */ |
| return (reg_size_ok(di, base, optype, OPSZ_4x8_short2, true/*addr*/) && |
| opnd_get_segment(opnd) == SEG_ES && |
| base == resolve_var_reg(di, REG_EDI, true, true |
| _IF_X64(true) _IF_X64(false) |
| _IF_X64(false)) && |
| opnd_get_index(opnd) == REG_NULL && |
| opnd_get_disp(opnd) == 0 && |
| size_ok(di, opnd_get_size(opnd), opsize, false/*!addr*/)); |
| } else { |
| return false; |
| } |
| case TYPE_XLAT: |
| /* this means the memory address DS:(RE)(E)BX+AL */ |
| if (opnd_is_far_base_disp(opnd)) { |
| reg_id_t base = opnd_get_base(opnd); |
| /* reg_size_ok will set PREFIX_ADDR if necessary */ |
| return (reg_size_ok(di, base, optype, OPSZ_4x8_short2, true/*addr*/) && |
| reg_is_segment(opnd_get_segment(opnd)) && |
| base == resolve_var_reg(di, REG_EBX, true, true |
| _IF_X64(true) _IF_X64(false) |
| _IF_X64(false)) && |
| opnd_get_index(opnd) == REG_AL && |
| opnd_get_scale(opnd) == 1 && |
| opnd_get_disp(opnd) == 0 && |
| size_ok(di, opnd_get_size(opnd), opsize, false/*!addr*/)); |
| } else { |
| return false; |
| } |
| case TYPE_MASKMOVQ: |
| /* this means the memory address DS:(RE)(E)DI */ |
| if (opnd_is_far_base_disp(opnd)) { |
| reg_id_t base = opnd_get_base(opnd); |
| /* reg_size_ok will set PREFIX_ADDR if necessary */ |
| return (reg_size_ok(di, base, optype, OPSZ_4x8_short2, true/*addr*/) && |
| reg_is_segment(opnd_get_segment(opnd)) && |
| base == resolve_var_reg(di, REG_EDI, true, true |
| _IF_X64(true) _IF_X64(false) |
| _IF_X64(false)) && |
| opnd_get_index(opnd) == REG_NULL && |
| opnd_get_disp(opnd) == 0 && |
| size_ok(di, opnd_get_size(opnd), opsize, false/*!addr*/)); |
| } else { |
| return false; |
| } |
| case TYPE_INDIR_REG: |
| /* far_ ok */ |
| return (opnd_is_base_disp(opnd) && |
| opnd_get_base(opnd) == opsize && |
| opnd_get_index(opnd) == REG_NULL && |
| opnd_get_disp(opnd) == 0 && |
| /* FIXME: how know data size? for now just use reg size... */ |
| size_ok(di, opnd_get_size(opnd), reg_get_size(opsize), false/*!addr*/)); |
| case TYPE_INDIR_VAR_XREG: /* indirect reg that varies (by addr16), base is 4x8, |
| * opsize that varies by data16 */ |
| case TYPE_INDIR_VAR_REG: /* indrect reg that varies (by addr16), base is 4x8, |
| * opsize that varies by rex & data16 */ |
| case TYPE_INDIR_VAR_XIREG: /* indrect reg that varies (by addr16), base is 4x8, |
| * opsize that varies by data16 except on 64-bit Intel */ |
| case TYPE_INDIR_VAR_XREG_OFFS_1: /* TYPE_INDIR_VAR_XREG + an offset */ |
| case TYPE_INDIR_VAR_XREG_OFFS_8: /* TYPE_INDIR_VAR_XREG + an offset + scale */ |
| case TYPE_INDIR_VAR_XREG_OFFS_N: /* TYPE_INDIR_VAR_XREG + an offset + scale */ |
| case TYPE_INDIR_VAR_XIREG_OFFS_1:/* TYPE_INDIR_VAR_XIREG + an offset + scale */ |
| case TYPE_INDIR_VAR_REG_OFFS_2: /* TYPE_INDIR_VAR_REG + offset + scale */ |
| case TYPE_INDIR_VAR_XREG_SIZEx8: /* TYPE_INDIR_VAR_XREG + scale */ |
| case TYPE_INDIR_VAR_REG_SIZEx2: /* TYPE_INDIR_VAR_REG + scale */ |
| case TYPE_INDIR_VAR_REG_SIZEx3x5:/* TYPE_INDIR_VAR_REG + scale */ |
| if (opnd_is_base_disp(opnd)) { |
| reg_id_t base = opnd_get_base(opnd); |
| /* NOTE - size needs to match decode_operand() and instr_create.h. */ |
| bool sz_ok = size_ok(di, opnd_get_size(opnd), indir_var_reg_size(di, optype), |
| false/*!addr*/); |
| /* must be after size_ok potentially sets di flags */ |
| opnd_size_t sz = |
| resolve_variable_size(di, opnd_get_size(opnd), false/*not reg*/); |
| int disp = indir_var_reg_offs_factor(optype) * (int)opnd_size_in_bytes(sz); |
| /* reg_size_ok will set PREFIX_ADDR if 16-bit reg is asked for. |
| * these are all specified as 32-bit, so we hardcode OPSZ_VARSTACK |
| * for reg_size_ok. |
| * to generalize we'll want opsize_var_size(reg_get_size(opsize)) or sthg. |
| */ |
| CLIENT_ASSERT(reg_get_size(opsize) == OPSZ_4, "internal decoding error"); |
| return (reg_size_ok(di, base, optype, OPSZ_VARSTACK, true/*addr*/) && |
| base == resolve_var_reg(di, opsize, true, |
| true _IF_X64(true) _IF_X64(false) |
| _IF_X64(false)) && |
| opnd_get_index(opnd) == REG_NULL && |
| /* we're forgiving here, rather than adding complexity |
| * of a disp_equals_minus_size flag or sthg (i#164) |
| */ |
| (opnd_get_disp(opnd) == disp || |
| opnd_get_disp(opnd) == disp/2 || |
| opnd_get_disp(opnd) == disp*2) && |
| sz_ok); |
| } else { |
| return false; |
| } |
| case TYPE_H: |
| case TYPE_L: |
| return (opnd_is_reg(opnd) && |
| reg_size_ok(di, opnd_get_reg(opnd), optype, opsize, false/*!addr*/) && |
| reg_is_xmm(opnd_get_reg(opnd))); |
| default: |
| CLIENT_ASSERT(false, "encode error: type ok: unknown operand type"); |
| return false; |
| } |
| } |
| |
| const instr_info_t * |
| instr_info_extra_opnds(const instr_info_t *info) |
| { |
| if (TEST(HAS_EXTRA_OPERANDS, info->flags)) { |
| if (TEST(EXTRAS_IN_CODE_FIELD, info->flags)) |
| return (const instr_info_t *)(info->code); |
| else /* extra operands are in next entry */ |
| return (info + 1); |
| } |
| return NULL; |
| } |
| |
| /* macro for speed so we don't have to pass opnds around */ |
| #define TEST_OPND(di, iitype, iisize, iinum, inst_num, get_op) \ |
| if (iitype != TYPE_NONE) { \ |
| if (inst_num < iinum) \ |
| return false; \ |
| if (!opnd_type_ok(di, get_op, iitype, iisize)) \ |
| return false; \ |
| if (type_instr_uses_reg_bits(iitype)) { \ |
| if (!opnd_is_null(using_reg_bits) && \ |
| !opnd_same(using_reg_bits, get_op)) \ |
| return false; \ |
| using_reg_bits = get_op; \ |
| } else if (type_uses_modrm_bits(iitype)) { \ |
| if (!opnd_is_null(using_modrm_bits) && \ |
| !opnd_same(using_modrm_bits, get_op)) \ |
| return false; \ |
| using_modrm_bits = get_op; \ |
| } else if (type_uses_vex_vvvv_bits(iitype)) { \ |
| if (!opnd_is_null(using_vvvv_bits) && \ |
| !opnd_same(using_vvvv_bits, get_op)) \ |
| return false; \ |
| using_vvvv_bits = get_op; \ |
| } \ |
| } else if (inst_num >= iinum) \ |
| return false; |
| |
| /* May be called a 2nd time to check size prefix consistency. |
| * FIXME optimization: in 2nd pass we only need to call opnd_type_ok() |
| * and don't need to check reg, modrm, numbers, etc. |
| */ |
| static bool |
| encoding_possible_pass(decode_info_t *di, instr_t *in, const instr_info_t * ii) |
| { |
| DEBUG_DECLARE(dcontext_t *dcontext = get_thread_private_dcontext();) |
| /* make sure multiple operands aren't using same modrm bits */ |
| opnd_t using_reg_bits = opnd_create_null(); |
| opnd_t using_modrm_bits = opnd_create_null(); |
| opnd_t using_vvvv_bits = opnd_create_null(); |
| |
| /* for efficiency we separately test 2 dsts, 3 srcs */ |
| TEST_OPND(di, ii->dst1_type, ii->dst1_size, 1, in->num_dsts, instr_get_dst(in, 0)); |
| TEST_OPND(di, ii->dst2_type, ii->dst2_size, 2, in->num_dsts, instr_get_dst(in, 1)); |
| TEST_OPND(di, ii->src1_type, ii->src1_size, 1, in->num_srcs, instr_get_src(in, 0)); |
| TEST_OPND(di, ii->src2_type, ii->src2_size, 2, in->num_srcs, instr_get_src(in, 1)); |
| TEST_OPND(di, ii->src3_type, ii->src3_size, 3, in->num_srcs, instr_get_src(in, 2)); |
| |
| if ((ii->flags & HAS_EXTRA_OPERANDS) != 0) { |
| /* extra operands to test! */ |
| int offs = 1; |
| ii = instr_info_extra_opnds(ii); |
| LOG(THREAD, LOG_EMIT, ENC_LEVEL, "encoding_possible extra operands\n"); |
| do { |
| LOG(THREAD, LOG_EMIT, ENC_LEVEL, |
| "encoding possible checking extra operands for "PFX"\n", |
| ii->opcode); |
| CLIENT_ASSERT(ii->type == OP_CONTD, |
| "encode error: extra operand template mismatch"); |
| |
| TEST_OPND(di, ii->dst1_type, ii->dst1_size, (offs*2 + 1), in->num_dsts, |
| instr_get_dst(in, (offs*2 + 0))); |
| TEST_OPND(di, ii->dst2_type, ii->dst2_size, (offs*2 + 2), in->num_dsts, |
| instr_get_dst(in, (offs*2 + 1))); |
| TEST_OPND(di, ii->src1_type, ii->src1_size, (offs*3 + 1), in->num_srcs, |
| instr_get_src(in, (offs*3 + 0))); |
| TEST_OPND(di, ii->src2_type, ii->src2_size, (offs*3 + 2), in->num_srcs, |
| instr_get_src(in, (offs*3 + 1))); |
| TEST_OPND(di, ii->src3_type, ii->src3_size, (offs*3 + 3), in->num_srcs, |
| instr_get_src(in, (offs*3 + 2))); |
| offs++; |
| ii = instr_info_extra_opnds(ii); |
| } while (ii != NULL); |
| } |
| |
| return true; |
| } |
| |
| /* Does not check operands beyond 2 dsts and 3 srcs! |
| * Modifies in's prefixes to reflect whether operand or data size |
| * prefixes are required. |
| * Assumes caller has set di->x86_mode (i.e., ignores in's mode). |
| */ |
| bool |
| encoding_possible(decode_info_t *di, instr_t *in, const instr_info_t * ii) |
| { |
| DEBUG_DECLARE(dcontext_t *dcontext = get_thread_private_dcontext();) |
| if (ii == NULL || in == NULL) |
| return false; |
| LOG(THREAD, LOG_EMIT, ENC_LEVEL, "\nencoding_possible on "PFX"\n", ii->opcode); |
| |
| if (TEST(X64_MODE(di) ? X64_INVALID : X86_INVALID, ii->flags)) |
| return false; |
| |
| /* For size prefixes we use the di prefix field since that's what |
| * the decode.c routines use; we transfer to the instr's prefix field |
| * when done. The first |
| * operand that would need a prefix to match its template sets the |
| * prefixes. Rather than force operands that don't want prefixes |
| * to say so (thus requiring a 3-value field: uninitialized, |
| * prefix, and no-prefix, and extra work in the common case) we |
| * instead do a 2nd pass if any operand wanted a prefix. |
| * If an operand wants no prefix and the flag is set, the match fails. |
| * I.e., first pass: does anyone want a prefix? If so, 2nd pass: does |
| * everyone want a prefix? We also re-check the immed sizes on the 2nd |
| * pass. |
| * |
| * If an operand specifies a variable-sized size, it will take on either of |
| * the default size or the prefix size. |
| */ |
| di->prefixes &= ~PREFIX_SIZE_SPECIFIERS; |
| if (!encoding_possible_pass(di, in, ii)) |
| return false; |
| if (TESTANY(PREFIX_SIZE_SPECIFIERS, di->prefixes)) { |
| LOG(THREAD, LOG_EMIT, ENC_LEVEL, "\tflags needed: "PFX"\n", in->prefixes); |
| if (!encoding_possible_pass(di, in, ii)) |
| return false; |
| } |
| LOG(THREAD, LOG_EMIT, ENC_LEVEL, "\ttemplate match w/ flags: "PFX"\n", in->prefixes); |
| return true; |
| } |
| |
| void |
| decode_info_init_for_instr(decode_info_t *di, instr_t *instr) |
| { |
| memset(di, 0, sizeof(*di)); |
| IF_X64(di->x86_mode = instr_get_x86_mode(instr)); |
| } |
| |
| /* num is 0-based */ |
| byte |
| instr_info_opnd_type(const instr_info_t *info, bool src, int num) |
| { |
| if (num < 0) { |
| CLIENT_ASSERT(false, "internal decode error"); |
| return TYPE_NONE; |
| } |
| if ((src && num >= 3) || (!src && num >= 2)) { |
| const instr_info_t *nxt = instr_info_extra_opnds(info); |
| if (nxt == NULL) { |
| CLIENT_ASSERT(false, "internal decode error"); |
| return TYPE_NONE; |
| } else |
| return instr_info_opnd_type(nxt, src, src ? (num - 3) : (num - 2)); |
| } else { |
| if (src) { |
| if (num == 0) |
| return info->src1_type; |
| else if (num == 1) |
| return info->src2_type; |
| else if (num == 2) |
| return info->src3_type; |
| else { |
| CLIENT_ASSERT(false, "internal decode error"); |
| return TYPE_NONE; |
| } |
| } else { |
| if (num == 0) |
| return info->dst1_type; |
| else if (num == 1) |
| return info->dst2_type; |
| else { |
| CLIENT_ASSERT(false, "internal decode error"); |
| return TYPE_NONE; |
| } |
| } |
| } |
| return TYPE_NONE; |
| } |
| |
| /*************************************************************************** |
| * Actual encoding |
| */ |
| |
| static byte * |
| encode_immed(decode_info_t * di, byte *pc) |
| { |
| /* 1st or 2nd immed? */ |
| ptr_int_t val; |
| opnd_size_t size; |
| if (di->size_immed != OPSZ_NA) { |
| /* do we need to pc-relativize a target pc? */ |
| if (di->immed_pc_relativize) { |
| int len; |
| size = resolve_variable_size(di, di->size_immed, false); |
| len = opnd_size_in_bytes(size); |
| /* offset is from start of next instruction */ |
| val = di->immed - ((ptr_int_t)pc + len); |
| } else if (di->immed_subtract_length) { |
| /* this code means that the immed holds not the absolute pc but |
| * the offset not counting the instruction length |
| */ |
| int len; |
| size = resolve_variable_size(di, di->size_immed, false); |
| len = opnd_size_in_bytes(size); |
| /* just need to subtract the total instr length from the offset */ |
| /* HACK: di->modrm was set with the number of instruction bytes |
| * prior to this immed |
| */ |
| val = di->immed - (len + di->modrm); |
| } else if (di->immed_pc_rel_offs) { |
| /* this code means that the immed holds not the absolute pc but |
| * the offset |
| */ |
| size = di->size_immed; /* TYPE_I put real size there */ |
| CLIENT_ASSERT((size == OPSZ_4_short2 && !TEST(PREFIX_DATA, di->prefixes)) || |
| (size == OPSZ_4) || IF_X64_ELSE((size == OPSZ_8), false), |
| "encode error: immediate has invalid size"); |
| /* we want val to be pc of target instr |
| * immed is the difference between us and target |
| * HACK: di->modrm was set with the number of instruction bytes |
| * prior to this immed |
| */ |
| val = di->immed + (ptr_int_t)pc - di->modrm; |
| if (di->immed_shift > 0) |
| val >>= di->immed_shift; |
| #ifdef X64 |
| /* we auto-truncate below to the proper size rather than complaining */ |
| #endif |
| } else { |
| val = di->immed; |
| size = di->size_immed; |
| } |
| di->size_immed = OPSZ_NA; /* mark as used */ |
| } else { |
| CLIENT_ASSERT(di->size_immed2 != OPSZ_NA, |
| "encode error: immediate has invalid size"); |
| val = di->immed2; |
| size = di->size_immed2; |
| di->size_immed2 = OPSZ_NA; /* mark as used */ |
| } |
| |
| /* variable-sized */ |
| size = resolve_variable_size(di, size, false); |
| |
| switch (size) { |
| case OPSZ_1: |
| *pc = (byte) (val); |
| pc += 1; |
| break; |
| case OPSZ_2: |
| *((short *)pc) = (short) (val); |
| pc += 2; |
| break; |
| case OPSZ_4: |
| *((int *)pc) = (int) val; |
| pc += 4; |
| break; |
| #ifdef X64 |
| case OPSZ_8: |
| *((int64 *)pc) = val; |
| pc += 8; |
| break; |
| #endif |
| case OPSZ_6: |
| CLIENT_ASSERT(di->size_immed2 == size, |
| "encode error: immediate has invalid size OPSZ_6"); |
| di->size_immed2 = OPSZ_NA; |
| *((int *)pc) = (int) di->immed2; |
| pc += 4; |
| *((short *)pc) = (short) (di->immed); |
| pc += 2; |
| break; |
| #ifdef X64 |
| case OPSZ_10: |
| CLIENT_ASSERT(di->size_immed2 == size, |
| "encode error: immediate has invalid size OPSZ_10"); |
| di->size_immed2 = OPSZ_NA; |
| *((ptr_int_t *)pc) = di->immed2; |
| pc += 8; |
| *((short *)pc) = (short) (di->immed); |
| pc += 2; |
| break; |
| #endif |
| default: |
| LOG(THREAD_GET, LOG_EMIT, 1, "ERROR: encode_immed: unhandled size: %d\n", size); |
| CLIENT_ASSERT(false, "encode error: immediate has unknown size"); |
| } |
| return pc; |
| } |
| |
| static void |
| encode_reg_ext_prefixes(decode_info_t *di, reg_id_t reg, uint which_rex) |
| { |
| #ifdef X64 |
| reg_set_ext_prefixes(di, reg, which_rex); |
| #endif |
| } |
| |
| #ifdef X64 |
| static void |
| encode_rel_addr(decode_info_t * di, opnd_t opnd) |
| { |
| /* Unlike TYPE_J and TYPE_I, who use immed values, can assume |
| * there are no other immeds, and have encode_immed complete |
| * the pc relativization once the final pc is known, we have |
| * to use a different mechanism as we're dealing with a disp |
| * and can have other immeds. |
| * We simply have instr_encode check for this exact modrrm |
| * and use a new field disp_abs to store our target. |
| */ |
| CLIENT_ASSERT(opnd_is_rel_addr(opnd), |
| "encode error: invalid type for pc-relativization"); |
| di->has_sib = false; |
| di->mod = 0; |
| di->rm = 5; |
| di->has_disp = true; |
| di->disp_abs = (byte *) opnd_get_addr(opnd); |
| /* PR 253327: since we have no explicit request for addr32, we |
| * deduce it here, w/ a conservative range estimate of instr length. |
| * However, we consult use_addr_prefix_on_short_disp() first, which will |
| * probably disallow for most x64 processors for performance reasons. |
| */ |
| if (use_addr_prefix_on_short_disp() && |
| (ptr_uint_t)di->disp_abs <= INT_MAX && |
| (!REL32_REACHABLE(di->final_pc + MAX_INSTR_LENGTH, di->disp_abs) || |
| !REL32_REACHABLE(di->final_pc + 4, di->disp_abs))) |
| di->prefixes |= PREFIX_ADDR; |
| } |
| #endif |
| |
| static void |
| encode_base_disp(decode_info_t * di, opnd_t opnd) |
| { |
| reg_id_t base, index; |
| int scale, disp; |
| /* in 64-bit mode, addr prefix simply truncates registers and final address */ |
| bool addr16 = !X64_MODE(di) && TEST(PREFIX_ADDR, di->prefixes); |
| |
| /* user can use opnd_create_abs_addr() but it will internally be a base-disp |
| * if its disp is 32-bit: if it's larger it has to be TYPE_O and not get here! |
| */ |
| CLIENT_ASSERT(opnd_is_base_disp(opnd), |
| "encode error: operand type mismatch (expecting base_disp type)"); |
| if (di->mod < 5) { |
| /* mod, rm, & sib have already been set, probably b/c |
| * we have a src that equals a dst. |
| * just exit. |
| */ |
| return; |
| } |
| |
| base = opnd_get_base(opnd); |
| index = opnd_get_index(opnd); |
| scale = opnd_get_scale(opnd); |
| disp = opnd_get_disp(opnd); |
| if (base == REG_NULL && index == REG_NULL) { |
| /* absolute displacement */ |
| if (!addr16 && di->seg_override != REG_NULL && |
| ((!X64_MODE(di) && disp >= INT16_MIN && disp <= INT16_MAX) || |
| (X64_MODE(di) && disp >= INT32_MIN && disp <= INT32_MAX)) && |
| !opnd_is_disp_force_full(opnd)) { |
| /* already have segment prefix, so adding addr16 prefix |
| * won't make worse (already in slow decoder on processor), so try to |
| * reduce size: unless on newer microarch: see comments in |
| * use_addr_prefix_on_short_disp(). |
| * if a client doesn't want this happening to a patch-later value, should |
| * use a large bogus value that won't trigger this, or |
| * specify force_full_disp. |
| */ |
| /* For x64 wanting addr32 to address high 2GB of low 4GB, caller |
| * should set disp_short_addr on the base-disp opnd, which is |
| * done automatically for opnd_create_abs_addr(). That sets |
| * PREFIX_ADDR earlier in the encoding process. |
| */ |
| if (!X64_MODE(di) && /* disp always 32-bit for x64 */ |
| use_addr_prefix_on_short_disp()) { |
| di->prefixes |= PREFIX_ADDR; /* for 16-bit disp */ |
| addr16 = true; |
| } |
| } |
| if (X64_MODE(di)) { |
| /* need a sib byte to do abs (not rip-relative) */ |
| di->mod = 0; |
| di->rm = 4; |
| di->has_sib = true; |
| di->scale = 0; |
| di->index = 4; |
| di->base = 5; |
| di->has_disp = true; |
| di->disp = disp; |
| /* if rex.x is set we'll have r12 instead of no base */ |
| CLIENT_ASSERT(!TEST(PREFIX_REX_X, di->prefixes), |
| "encode error: for x64 cannot encode abs addr w/ rex.x"); |
| } else { |
| di->has_sib = false; |
| di->mod = 0; |
| di->rm = (byte) ((addr16) ? 6 : 5); |
| di->has_disp = true; |
| di->disp = disp; |
| } |
| } else { |
| if (disp == 0 && |
| /* must use 8-bit disp for 0x0(%ebp) or 0x0(%r13) */ |
| ((!addr16 && base != REG_EBP /* x64 w/ addr prefix => ebp */ |
| IF_X64(&& base != REG_RBP && base != REG_R13 && base != REG_R13D)) || |
| /* must use 8-bit disp for 0x0(%bp) */ |
| (addr16 && (base != REG_BP || index != REG_NULL))) && |
| !opnd_is_disp_encode_zero(opnd)) { |
| /* no disp */ |
| di->mod = 0; |
| di->has_disp = false; |
| } else if (disp >= INT8_MIN && disp <= INT8_MAX && |
| !opnd_is_disp_force_full(opnd)) { |
| /* 8-bit disp */ |
| di->mod = 1; |
| di->has_disp = true; |
| di->disp = disp; |
| } else { |
| /* 32/16-bit disp */ |
| di->mod = 2; |
| di->has_disp = true; |
| di->disp = disp; |
| } |
| if (addr16) { |
| di->has_sib = false; |
| if (base == REG_BX && index == REG_SI) |
| di->rm = 0; |
| else if (base == REG_BX && index == REG_DI) |
| di->rm = 1; |
| else if (base == REG_BP && index == REG_SI) |
| di->rm = 2; |
| else if (base == REG_BP && index == REG_DI) |
| di->rm = 3; |
| else if (base == REG_SI && index == REG_NULL) |
| di->rm = 4; |
| else if (base == REG_DI && index == REG_NULL) |
| di->rm = 5; |
| else if (base == REG_BP && index == REG_NULL) |
| di->rm = 6; |
| else if (base == REG_BX && index == REG_NULL) |
| di->rm = 7; |
| else { |
| CLIENT_ASSERT(false, "encode error: invalid 16-bit base+index"); |
| di->rm = 0; |
| } |
| } else if (index == REG_NULL && base != REG_ESP /* x64 w/ addr prefix => esp */ |
| IF_X64(&& base != REG_RSP && base != REG_R12 && base != REG_R12D)) { |
| /* don't need SIB byte */ |
| di->has_sib = false; |
| encode_reg_ext_prefixes(di, base, PREFIX_REX_B); |
| di->rm = reg_get_bits(base); |
| } else { |
| /* need SIB byte */ |
| di->has_sib = true; |
| di->rm = 4; |
| if (index == REG_NULL) { |
| di->index = 4; |
| di->scale = 0; /* does it matter?!? */ |
| } else { |
| /* note that r13 can be an index register */ |
| CLIENT_ASSERT(index != REG_ESP IF_X64(&& index != REG_RSP), |
| "encode error: xsp cannot be an index register"); |
| CLIENT_ASSERT(reg_is_32bit(index) || |
| (X64_MODE(di) && reg_is_64bit(index)) || |
| reg_is_xmm(index) /* VSIB */, |
| "encode error: index must be general-purpose register"); |
| encode_reg_ext_prefixes(di, index, PREFIX_REX_X); |
| if (X64_MODE(di) && reg_is_32bit(index)) |
| di->prefixes |= PREFIX_ADDR; |
| di->index = reg_get_bits(index); |
| switch (scale) { |
| case 1: di->scale = 0; break; |
| case 2: di->scale = 1; break; |
| case 4: di->scale = 2; break; |
| case 8: di->scale = 3; break; |
| default: CLIENT_ASSERT(false, "encode error: invalid scale"); |
| } |
| } |
| if (base == REG_NULL) { |
| di->base = 5; |
| di->mod = 0; |
| di->has_disp = true; |
| di->disp = disp; |
| } else { |
| /* can't do nodisp(ebp) or nodisp(r13) */ |
| CLIENT_ASSERT(di->mod != 0 || |
| (base != REG_EBP |
| IF_X64(&& base != REG_RBP && |
| base != REG_R13 && base != REG_R13D)), |
| "encode error: xbp/r13 base must have disp"); |
| encode_reg_ext_prefixes(di, base, PREFIX_REX_B); |
| if (X64_MODE(di) && reg_is_32bit(base)) { |
| CLIENT_ASSERT(index == REG_NULL || |
| (reg_is_32bit(index) && TEST(PREFIX_ADDR, di->prefixes)), |
| "encode error: index and base must be same width"); |
| di->prefixes |= PREFIX_ADDR; |
| } |
| di->base = reg_get_bits(base); |
| } |
| } |
| } |
| } |
| |
| static void |
| set_immed(decode_info_t *di, ptr_int_t val, opnd_size_t opsize) |
| { |
| if (di->size_immed == OPSZ_NA) { |
| di->immed = val; |
| di->size_immed = opsize; |
| } else { |
| CLIENT_ASSERT(di->size_immed2 == OPSZ_NA, |
| "encode error: >4-byte immed encoding error"); |
| di->immed2 = val; |
| di->size_immed2 = opsize; |
| } |
| } |
| |
| static byte * |
| get_mem_instr_addr(decode_info_t *di, opnd_t opnd) |
| { |
| CLIENT_ASSERT(opnd_is_mem_instr(opnd), "internal encode error"); |
| return di->final_pc + ((ptr_int_t)opnd_get_instr(opnd)->note - di->cur_note) + |
| opnd_get_mem_instr_disp(opnd); |
| } |
| |
| static void |
| encode_operand(decode_info_t *di, int optype, opnd_size_t opsize, opnd_t opnd) |
| { |
| switch (optype) { |
| case TYPE_NONE: |
| case TYPE_REG: |
| case TYPE_XREG: |
| case TYPE_VAR_REG: |
| case TYPE_VARZ_REG: |
| case TYPE_VAR_XREG: |
| case TYPE_VAR_REGX: |
| case TYPE_VAR_ADDR_XREG: |
| case TYPE_1: |
| case TYPE_FLOATCONST: |
| case TYPE_INDIR_REG: |
| case TYPE_INDIR_VAR_XREG: |
| case TYPE_INDIR_VAR_REG: |
| case TYPE_INDIR_VAR_XIREG: |
| case TYPE_INDIR_VAR_XREG_OFFS_1: |
| case TYPE_INDIR_VAR_XREG_OFFS_8: |
| case TYPE_INDIR_VAR_XREG_OFFS_N: |
| case TYPE_INDIR_VAR_XIREG_OFFS_1: |
| case TYPE_INDIR_VAR_REG_OFFS_2: |
| case TYPE_INDIR_VAR_XREG_SIZEx8: |
| case TYPE_INDIR_VAR_REG_SIZEx2: |
| case TYPE_INDIR_VAR_REG_SIZEx3x5: |
| return; |
| case TYPE_REG_EX: |
| case TYPE_VAR_REG_EX: |
| case TYPE_VAR_XREG_EX: |
| case TYPE_VAR_REGX_EX: |
| encode_reg_ext_prefixes(di, opnd_get_reg(opnd), PREFIX_REX_B); |
| return; |
| case TYPE_VSIB: |
| CLIENT_ASSERT(opnd_is_base_disp(opnd), |
| "encode error: VSIB operand must be base-disp"); |
| /* fall through */ |
| case TYPE_FLOATMEM: |
| case TYPE_M: |
| CLIENT_ASSERT(opnd_is_memory_reference(opnd), |
| "encode error: M operand must be mem ref"); |
| /* fall through */ |
| case TYPE_INDIR_E: |
| case TYPE_E: |
| case TYPE_Q: |
| case TYPE_W: |
| case TYPE_R: /* we already ensured this is a reg, not memory */ |
| case TYPE_P_MODRM: /* we already ensured this is a reg, not memory */ |
| case TYPE_V_MODRM: /* we already ensured this is a reg, not memory */ |
| if (opnd_is_memory_reference(opnd)) { |
| if (opnd_is_far_memory_reference(opnd)) { |
| di->seg_override = opnd_get_segment(opnd); |
| /* should be just a SEG_ constant */ |
| CLIENT_ASSERT(di->seg_override >= REG_START_SEGMENT && |
| di->seg_override <= REG_STOP_SEGMENT, |
| "encode error: invalid segment override"); |
| } |
| if (opnd_is_mem_instr(opnd)) { |
| byte *addr = get_mem_instr_addr(di, opnd); |
| #ifdef X64 |
| if (X64_MODE(di)) |
| encode_rel_addr(di, opnd_create_rel_addr(addr, opnd_get_size(opnd))); |
| else |
| #endif |
| encode_base_disp(di, opnd_create_abs_addr(addr, opnd_get_size(opnd))); |
| di->has_instr_opnds = true; |
| } else { |
| #ifdef X64 |
| if (X64_MODE(di) && opnd_is_rel_addr(opnd)) |
| encode_rel_addr(di, opnd); |
| else if (X64_MODE(di) && opnd_is_abs_addr(opnd) && |
| !opnd_is_base_disp(opnd)) { |
| /* try to fit it as rip-rel */ |
| opnd.kind = REL_ADDR_kind; |
| encode_rel_addr(di, opnd); |
| } else |
| #endif |
| encode_base_disp(di, opnd); |
| } |
| } else { |
| CLIENT_ASSERT(opnd_is_reg(opnd), |
| "encode error: modrm not selecting mem but not selecting reg"); |
| if (di->mod < 5) { |
| /* already set (by a dst equal to src, probably) */ |
| CLIENT_ASSERT(di->mod == 3 && di->rm == reg_get_bits(opnd_get_reg(opnd)), |
| "encode error: modrm mismatch"); |
| return; |
| } |
| di->mod = 3; |
| encode_reg_ext_prefixes(di, opnd_get_reg(opnd), PREFIX_REX_B); |
| di->rm = reg_get_bits(opnd_get_reg(opnd)); |
| } |
| return; |
| case TYPE_G: |
| case TYPE_P: |
| case TYPE_V: |
| case TYPE_S: |
| case TYPE_C: |
| case TYPE_D: |
| { |
| CLIENT_ASSERT(opnd_is_reg(opnd), "encode error: operand must be a register"); |
| if (di->reg < 8) { |
| /* already set (by a dst equal to src, probably) */ |
| CLIENT_ASSERT(di->reg == reg_get_bits(opnd_get_reg(opnd)), |
| "encode error: modrm mismatch"); |
| return; |
| } |
| encode_reg_ext_prefixes(di, opnd_get_reg(opnd), PREFIX_REX_R); |
| di->reg = reg_get_bits(opnd_get_reg(opnd)); |
| return; |
| } |
| case TYPE_I: |
| if (opnd_is_near_instr(opnd)) { |
| /* allow instr as immed, that means we want to put in the 4/8-byte |
| * pc of target instr as the immed |
| * This only works if the instr has no other immeds! |
| */ |
| instr_t *target_instr = opnd_get_instr(opnd); |
| ptr_uint_t target = (ptr_uint_t)target_instr->note - di->cur_note; |
| /* We don't know the encode pc yet, so we put it in as pc-relative and |
| * fix it up later. |
| * The size was already checked, so just use the template size. |
| */ |
| set_immed(di, (ptr_uint_t)target, opsize); |
| /* this immed is pc-relative except it needs to have the |
| * instruction length subtracted from it -- we indicate that |
| * like this: |
| */ |
| CLIENT_ASSERT(di->size_immed2 == OPSZ_NA, |
| "encode error: immed size already set"); |
| di->size_immed = resolve_variable_size(di, opsize, false); |
| /* And now we ask to be adjusted to become an absolute pc: */ |
| di->immed_pc_rel_offs = true; /* == immed needs +pc */ |
| di->immed_shift = opnd_get_shift(opnd); |
| di->has_instr_opnds = true; |
| } else { |
| CLIENT_ASSERT(opnd_is_immed_int(opnd), "encode error: opnd not immed int"); |
| set_immed(di, opnd_get_immed_int(opnd), opsize); |
| } |
| return; |
| case TYPE_J: |
| { |
| ptr_uint_t target; |
| /* Since we don't know pc values right now, we convert |
| * from an absolute pc to a relative offset in encode_immed. |
| * Here we simply set the immed to the absolute pc target. |
| */ |
| if (opnd_is_near_instr(opnd)) { |
| /* assume the note fields have been set with relative offsets |
| * from some start pc, and that our caller put our note in |
| * di->cur_note |
| */ |
| instr_t *target_instr = opnd_get_instr(opnd); |
| target = (ptr_uint_t)target_instr->note - di->cur_note; |
| /* target is now a pc-relative target, so we can encode as is */ |
| set_immed(di, target, opsize); |
| /* this immed is pc-relative except it needs to have the |
| * instruction length subtracted from it -- we indicate that |
| * like this: |
| */ |
| CLIENT_ASSERT(di->size_immed2 == OPSZ_NA, |
| "encode error: immed size already set"); |
| di->size_immed = opsize; |
| di->immed_subtract_length = true; /* == immed needs -length */ |
| di->has_instr_opnds = true; |
| } else { |
| CLIENT_ASSERT(opnd_is_near_pc(opnd), "encode error: opnd not pc"); |
| target = (ptr_uint_t) opnd_get_pc(opnd); |
| set_immed(di, target, opsize); |
| /* TYPE_J never has other immeds in the same instruction */ |
| CLIENT_ASSERT(di->size_immed2 == OPSZ_NA, |
| "encode error: immed size already set"); |
| di->immed_pc_relativize = true; |
| di->size_immed = opsize; |
| } |
| return; |
| } |
| case TYPE_A: |
| { |
| ptr_uint_t target; |
| |
| #ifdef IA32_ON_IA64 |
| if (opsize == OPSZ_4_short2) { |
| if (opnd_is_near_instr(opnd)) { |
| /* assume the note fields have been set with relative offsets |
| * from some start pc |
| */ |
| instr_t *target_instr = opnd_get_instr(opnd); |
| target = (ptr_uint_t)target_instr->note; |
| /* target is absolute address of instr ready to go */ |
| set_immed(di, target, opsize); |
| di->has_instr_opnds = true; |
| } else { |
| CLIENT_ASSERT(opnd_is_near_pc(opnd), "encode error: opnd not pc"); |
| target = (ptr_uint_t) opnd_get_pc(opnd); |
| set_immed(di, target, opsize); |
| } |
| return; |
| } |
| #endif |
| |
| CLIENT_ASSERT(!X64_MODE(di), "x64 has no type A instructions"); |
| CLIENT_ASSERT(opsize == OPSZ_6_irex10_short4 || opsize == OPSZ_6 || |
| opsize == OPSZ_4 || |
| (opsize == OPSZ_10 && proc_get_vendor() != VENDOR_AMD), |
| "encode error: A operand size mismatch"); |
| CLIENT_ASSERT(di->size_immed == OPSZ_NA && |
| di->size_immed2 == OPSZ_NA, |
| "encode error: A operand size mismatch"); |
| if (opnd_is_far_instr(opnd)) { |
| /* caller set di.cur_note w/ the pc where we'll be encoding this */ |
| ptr_int_t source = (ptr_uint_t) di->cur_note; |
| instr_t *target_instr = opnd_get_instr(opnd); |
| ptr_int_t dest = (ptr_uint_t) target_instr->note; |
| ptr_uint_t encode_pc = (ptr_uint_t) di->final_pc; |
| /* A label shouldn't be very far away and thus we should not overflow |
| * (unless client asked to encode at very high address or sthg, |
| * which we won't support). |
| */ |
| CLIENT_ASSERT((dest >= source && |
| encode_pc + (dest - source) >= encode_pc) || |
| (dest < source && |
| encode_pc + (dest - source) < encode_pc), |
| "label is too far from targeter wrt encode pc"); |
| target = encode_pc + (dest - source); |
| CLIENT_ASSERT(opsize == OPSZ_6_irex10_short4, |
| "far instr size set to unsupported value"); |
| di->has_instr_opnds = true; |
| } else { |
| CLIENT_ASSERT(opnd_is_far_pc(opnd), |
| "encode error: A operand must be far pc or far instr"); |
| target = (ptr_uint_t) opnd_get_pc(opnd); |
| } |
| /* XXX PR 225937: allow client to specify whether data16 or not |
| * instead of auto-adding the prefix if offset is small |
| */ |
| if (target <= USHRT_MAX && |
| /* we can't use data16 on a far call as it changes the stack size */ |
| di->opcode != OP_call_far) { |
| int val = (opnd_get_segment_selector(opnd)<<16) | ((short) target); |
| di->prefixes |= PREFIX_DATA; |
| set_immed(di, val, OPSZ_4); |
| } else if (target > UINT_MAX) { |
| CLIENT_ASSERT(proc_get_vendor() == VENDOR_INTEL, |
| "cannot use 8-byte far pc on AMD processor"); |
| di->prefixes |= PREFIX_REX_W; |
| set_immed(di, opnd_get_segment_selector(opnd), OPSZ_10); |
| set_immed(di, target, OPSZ_10); |
| } else { |
| set_immed(di, opnd_get_segment_selector(opnd), OPSZ_6); |
| set_immed(di, target, OPSZ_6); |
| } |
| return; |
| } |
| case TYPE_O: |
| { |
| ptr_int_t addr; |
| CLIENT_ASSERT(opnd_is_abs_addr(opnd) || |
| /* rel addr => abs if won't reach */ |
| IF_X64(opnd_is_rel_addr(opnd) ||) |
| (!X64_MODE(di) && opnd_is_mem_instr(opnd)), |
| "encode error: O operand must be absolute mem ref"); |
| if (opnd_is_mem_instr(opnd)) { |
| addr = (ptr_int_t) get_mem_instr_addr(di, opnd); |
| di->has_instr_opnds = true; |
| } else |
| addr = (ptr_int_t) opnd_get_addr(opnd); |
| if (opnd_is_far_abs_addr(opnd)) { |
| di->seg_override = opnd_get_segment(opnd); |
| /* should be just a SEG_ constant */ |
| CLIENT_ASSERT(di->seg_override >= REG_START_SEGMENT && |
| di->seg_override <= REG_STOP_SEGMENT, |
| "encode error: invalid segment override"); |
| if ((!X64_MODE(di) && addr >= INT16_MIN && addr <= INT16_MAX) || |
| (X64_MODE(di) && addr >= INT32_MIN && addr <= INT32_MAX)) { |
| /* same optimization as in encode_base_disp -- see comments there */ |
| if (use_addr_prefix_on_short_disp()) { |
| di->prefixes |= PREFIX_ADDR; |
| } |
| } |
| } |
| set_immed(di, addr, resolve_addr_size(di)); |
| return; |
| } |
| |
| /* assume that opnd_type_ok has already been called -- |
| * nothing to do unless has an override, these are implicit operands */ |
| case TYPE_X: /* this means the memory address DS:(RE)(E)SI */ |
| case TYPE_XLAT: /* this means the memory address DS:(RE)(E)BX+AL */ |
| case TYPE_MASKMOVQ: /* this means the memory address DS:(RE)(E)DI */ |
| if (opnd_get_segment(opnd) != SEG_DS) |
| di->seg_override = opnd_get_segment(opnd); |
| return; |
| case TYPE_Y: /* this means the memory address ES:(RE)(E)DI */ |
| return; /* no override possible */ |
| |
| case TYPE_L: |
| { |
| reg_id_t reg = opnd_get_reg(opnd); |
| ptr_int_t immed = (reg_is_ymm(reg) ? (reg - REG_START_YMM) : |
| (reg - REG_START_XMM)); |
| immed = (immed << 4); |
| set_immed(di, immed, OPSZ_1); |
| return; |
| } |
| case TYPE_H: |
| { |
| reg_id_t reg = opnd_get_reg(opnd); |
| di->vex_vvvv = (byte)(reg_is_ymm(reg) ? (reg - REG_START_YMM) : |
| (reg - REG_START_XMM)); |
| di->vex_vvvv = (~di->vex_vvvv) & 0xf; |
| return; |
| } |
| case TYPE_B: |
| { |
| /* There are 4 bits in vvvv so no prefix bit is needed. */ |
| reg_id_t reg = opnd_get_reg(opnd); |
| encode_reg_ext_prefixes(di, reg, 0); |
| di->vex_vvvv = reg_get_bits(reg); |
| #ifdef X64 |
| if (reg_is_extended(reg)) /* reg_get_bits does % 8 */ |
| di->vex_vvvv |= 0x8; |
| #endif |
| di->vex_vvvv = (~di->vex_vvvv) & 0xf; |
| return; |
| } |
| |
| default: |
| CLIENT_ASSERT(false, "encode error: unknown operand type"); |
| } |
| } |
| |
| static byte |
| encode_vex_final_prefix_byte(byte cur_byte, decode_info_t *di, |
| const instr_info_t *info) |
| { |
| cur_byte |= |
| (di->vex_vvvv << 3) | |
| (TEST(PREFIX_VEX_L, di->prefixes) ? 0x04 : 0x00); |
| /* we override OPCODE_SUFFIX for vex to mean "requires vex.L" */ |
| if (TEST(OPCODE_SUFFIX, info->opcode)) |
| cur_byte |= 0x04; |
| /* OPCODE_{MODRM,SUFFIX} mean something else for vex */ |
| if (info->opcode > 0xffffff) { |
| byte prefix = (byte)(info->opcode >> 24); |
| if (prefix == 0x66) |
| cur_byte |= 0x1; |
| else if (prefix == 0xf3) |
| cur_byte |= 0x2; |
| else if (prefix == 0xf2) |
| cur_byte |= 0x3; |
| else |
| CLIENT_ASSERT(false, "unknown vex prefix"); |
| } |
| return cur_byte; |
| } |
| |
| static byte * |
| encode_vex_prefixes(byte *field_ptr, decode_info_t *di, const instr_info_t *info, |
| bool *output_initial_opcode) |
| { |
| byte val; |
| byte vex_mm = (byte)((info->opcode & 0x00ff0000) >> 16); |
| /* We're out flags for REQUIRES_XOP, so XOP instrs have REQUIRES_VEX and we |
| * rely on XOP.map_select being disjoint from VEX.m-mmmm: |
| */ |
| bool xop = (vex_mm >= 0x08 && vex_mm < 0x0f); /* XOP instead of VEX */ |
| CLIENT_ASSERT(output_initial_opcode != NULL, "required param"); |
| if (TESTANY(PREFIX_REX_X | PREFIX_REX_B | PREFIX_REX_W, di->prefixes) || |
| /* 3-byte vex shortest encoding for 0x0f 0x3[8a], and the same |
| * size but I'm assuming faster decode in processor for 0x0f |
| */ |
| TEST(OPCODE_THREEBYTES, info->opcode) || |
| /* XOP is always 3 bytes */ |
| xop || |
| /* 2-byte requires leading 0x0f */ |
| ((info->opcode & 0x00ff0000) >> 16) != 0x0f) { |
| /* need 3-byte vex */ |
| *output_initial_opcode = true; |
| /* first vex byte */ |
| if (xop) |
| *field_ptr = 0x8f; |
| else |
| *field_ptr = 0xc4; |
| field_ptr++; |
| /* second vex byte */ |
| val = /* these are negated */ |
| (TEST(PREFIX_REX_R, di->prefixes) ? 0x00 : 0x80) | |
| (TEST(PREFIX_REX_X, di->prefixes) ? 0x00 : 0x40) | |
| (TEST(PREFIX_REX_B, di->prefixes) ? 0x00 : 0x20); |
| if (xop) { |
| byte map_select = (byte)((info->opcode & 0x00ff0000) >> 16); |
| CLIENT_ASSERT(TEST(OPCODE_THREEBYTES, info->opcode), "internal invalid XOP"); |
| CLIENT_ASSERT(map_select < 0x20, "XOP.map_select only has 5 bits"); |
| val |= map_select; |
| } else { |
| if (TEST(OPCODE_THREEBYTES, info->opcode)) { |
| byte op3 = (byte)((info->opcode & 0x00ff0000) >> 16); |
| if (op3 == 0x38) |
| val |= 0x02; |
| else if (op3 == 0x3a) |
| val |= 0x03; |
| else |
| CLIENT_ASSERT(false, "unknown 3-byte opcode"); |
| } else { |
| byte op3 = (byte)((info->opcode & 0x00ff0000) >> 16); |
| if (op3 == 0x0f) |
| val |= 0x01; |
| } |
| } |
| *field_ptr = val; |
| field_ptr++; |
| /* third vex byte */ |
| val = (TEST(PREFIX_REX_W, di->prefixes) ? 0x80 : 0x00); |
| /* we override OPCODE_MODRM for vex to mean "requires vex.W" */ |
| if (TEST(OPCODE_MODRM, info->opcode)) |
| val = 0x80; |
| val = encode_vex_final_prefix_byte(val, di, info); |
| *field_ptr = val; |
| field_ptr++; |
| } else { |
| /* 2-byte vex */ |
| /* first vex byte */ |
| *field_ptr = 0xc5; |
| field_ptr++; |
| /* second vex byte */ |
| val = (TEST(PREFIX_REX_R, di->prefixes) ? 0x00 : 0x80); /* negated */ |
| val = encode_vex_final_prefix_byte(val, di, info); |
| *field_ptr = val; |
| field_ptr++; |
| /* 2-byte requires leading implied 0x0f */ |
| ASSERT(((info->opcode & 0x00ff0000) >> 16) == 0x0f); |
| *output_initial_opcode = true; |
| } |
| return field_ptr; |
| } |
| |
| /* special-case (==fast) encoder for cti instructions |
| * this routine cannot handle indirect branches or rets or far jmp/call; |
| * it can handle loop/jecxz but it does NOT check for data16! |
| */ |
| static byte * |
| encode_cti(instr_t *instr, byte *copy_pc, byte *final_pc, bool check_reachable |
| _IF_DEBUG(bool assert_reachable)) |
| { |
| byte *pc = copy_pc; |
| const instr_info_t * info = instr_get_instr_info(instr); |
| opnd_t opnd; |
| ptr_uint_t target; |
| |
| if (instr->prefixes != 0) { |
| if (TEST(PREFIX_JCC_TAKEN, instr->prefixes)) { |
| *pc = RAW_PREFIX_jcc_taken; |
| pc++; |
| } else if (TEST(PREFIX_JCC_NOT_TAKEN, instr->prefixes)) { |
| *pc = RAW_PREFIX_jcc_not_taken; |
| pc++; |
| } |
| /* assumption: no 16-bit targets */ |
| CLIENT_ASSERT(!TESTANY(~(PREFIX_JCC_TAKEN|PREFIX_JCC_NOT_TAKEN), |
| instr->prefixes), |
| "encode cti error: non-branch-hint prefixes not supported"); |
| } |
| |
| /* output opcode */ |
| /* first opcode byte */ |
| *pc = (byte)((info->opcode & 0x00ff0000) >> 16); |
| pc++; |
| /* second opcode byte, if there is one */ |
| if (TEST(OPCODE_TWOBYTES, info->opcode)) { |
| *pc = (byte)((info->opcode & 0x0000ff00) >> 8); |
| pc++; |
| } |
| ASSERT(!TEST(OPCODE_THREEBYTES, info->opcode)); /* no cti has 3 opcode bytes */ |
| |
| /* we assume only one operand: 1st src == jump target, but we do |
| * not check that, for speed |
| */ |
| opnd = instr_get_target(instr); |
| if (opnd_is_near_pc(opnd)) { |
| target = (ptr_uint_t) opnd_get_pc(opnd); |
| } else if (opnd_is_near_instr(opnd)) { |
| instr_t *in = opnd_get_instr(opnd); |
| target = (ptr_uint_t)final_pc + ((ptr_uint_t)in->note - (ptr_uint_t)instr->note); |
| } else { |
| target = 0; /* avoid compiler warning */ |
| CLIENT_ASSERT(false, "encode_cti error: opnd must be near pc or near instr"); |
| } |
| |
| if (instr_is_cti_short(instr)) { |
| /* 8-bit offset */ |
| ptr_int_t offset; |
| /* handled w/ mangled bytes */ |
| CLIENT_ASSERT(!instr_is_cti_short_rewrite(instr, NULL), |
| "encode_cti error: jecxz/loop already mangled"); |
| /* offset is from start of next instr */ |
| offset = target - ((ptr_int_t)(pc + 1 - copy_pc + final_pc)); |
| if (check_reachable && !(offset >= INT8_MIN && offset <= INT8_MAX)) { |
| CLIENT_ASSERT(!assert_reachable, |
| "encode_cti error: target beyond 8-bit reach"); |
| return NULL; |
| } |
| *((char *)pc) = (char) offset; |
| pc++; |
| } else { |
| /* 32-bit offset */ |
| /* offset is from start of next instr */ |
| ptr_int_t offset = target - ((ptr_int_t)(pc + 4 - copy_pc + final_pc)); |
| #ifdef X64 |
| if (check_reachable && !REL32_REACHABLE_OFFS(offset)) { |
| CLIENT_ASSERT(!assert_reachable, |
| "encode_cti error: target beyond 32-bit reach"); |
| return NULL; |
| } |
| #endif |
| *((int *)pc) = (int) offset; |
| pc += 4; |
| } |
| return pc; |
| } |
| |
| /* PR 251479: support general re-relativization. |
| * Takes in a level 0-3 instruction and encodes it by copying its |
| * raw bytes to dst_pc. For x64, if it is marked as having a rip-relative |
| * displacement, that displacement is re-relativized to reach |
| * its current target from the encoded location. |
| * Returns NULL on failure to encode (due to reachability). |
| */ |
| byte * |
| copy_and_re_relativize_raw_instr(dcontext_t *dcontext, instr_t *instr, |
| byte *dst_pc, byte *final_pc) |
| { |
| byte *orig_dst_pc = dst_pc; |
| ASSERT(instr_raw_bits_valid(instr)); |
| /* FIXME i#731: if want to support ctis as well, need |
| * instr->rip_rel_disp_sz and need to set both for non-x64 as well |
| * in decode_sizeof(): or only in decode_cti()? |
| */ |
| /* For PR 251646 we have special support for mangled jecxz/loop* */ |
| if (instr_is_cti_short_rewrite(instr, NULL)) { |
| app_pc target; |
| CLIENT_ASSERT(opnd_is_pc(instr_get_target(instr)), |
| "cti_short_rewrite: must have pc target"); |
| target = opnd_get_pc(instr_get_target(instr)); |
| memcpy(dst_pc, instr->bytes, instr->length - 4); |
| dst_pc += instr->length - 4; |
| if (!REL32_REACHABLE(final_pc + instr->length, target)) { |
| CLIENT_ASSERT(false, "mangled jecxz/loop*: target out of 32-bit reach"); |
| return NULL; |
| } |
| *((int *)dst_pc) = (int) (target - (final_pc + instr->length)); |
| } |
| #ifdef X64 |
| /* We test the flag directly to support cases where the raw bits are |
| * being set by private_instr_encode() */ |
| else if (instr_rip_rel_valid(instr) && instr_get_rip_rel_pos(instr) > 0) { |
| /* x64 4-byte rip-relative data address displacement */ |
| byte *target; |
| ptr_int_t new_offs; |
| bool addr32 = false; |
| uint rip_rel_pos = instr_get_rip_rel_pos(instr); /* disp offs within instr */ |
| DEBUG_DECLARE(bool ok;) |
| ASSERT(!instr_is_level_0(instr)); |
| DEBUG_DECLARE(ok =) instr_get_rel_addr_target(instr, &target); |
| ASSERT(ok); |
| new_offs = target - (final_pc + instr->length); |
| /* PR 253327: we don't record whether addr32 so we have to deduce it now */ |
| if ((ptr_uint_t)target <= INT_MAX) { |
| int num_prefixes; |
| int i; |
| IF_X64(bool old_mode = set_x86_mode(dcontext, instr_get_x86_mode(instr));) |
| decode_sizeof(dcontext, instr->bytes, &num_prefixes, NULL); |
| IF_X64(set_x86_mode(dcontext, old_mode)); |
| for (i = 0; i < num_prefixes; i++) { |
| if (*(instr->bytes+i) == ADDR_PREFIX_OPCODE) { |
| addr32 = true; |
| break; |
| } |
| } |
| } |
| if (!addr32 && !REL32_REACHABLE_OFFS(new_offs)) { |
| /* unreachable: not clear whether routing through register here |
| * is worth the complexities of the length changing, so for now |
| * we fail and rely on caller to do a conservative estimate |
| * of reachability and transform this instruction before encoding. |
| */ |
| CLIENT_ASSERT(false, "encoding failed re-relativizing rip-relative " |
| "address whose target is unreachable"); |
| return NULL; |
| } |
| memcpy(dst_pc, instr->bytes, rip_rel_pos); |
| dst_pc += rip_rel_pos; |
| *((int *)dst_pc) = (int) new_offs; |
| if (rip_rel_pos + 4U < instr->length) { |
| /* suffix byte */ |
| memcpy(dst_pc + 4, instr->bytes + rip_rel_pos + 4, |
| instr->length - (rip_rel_pos + 4)); |
| } |
| } else |
| #endif |
| memcpy(dst_pc, instr->bytes, instr->length); |
| return orig_dst_pc + instr->length; |
| } |
| |
| /* Encodes instrustion instr. The parameter copy_pc points |
| * to the address of this instruction in the fragment cache. |
| * Checks for and fixes pc-relative instructions. |
| * N.B: if instr is a jump with an instr_t target, the caller MUST set the note |
| * field in the target instr_t prior to calling instr_encode on the jump instr. |
| * |
| * Returns the pc after the encoded instr, or NULL if the instruction cannot be encoded. |
| * Note that if instr_is_label(instr) will encoded as a 0-byte instruction. |
| * If a pc-relative operand cannot reach its target: |
| * If reachable == NULL, we assert and encoding fails (returning NULL); |
| * Else, encoding continues, and *reachable is set to false. |
| * Else, if reachable != NULL, *reachable is set to true. |
| */ |
| static byte * |
| instr_encode_common(dcontext_t *dcontext, instr_t *instr, byte *copy_pc, byte *final_pc, |
| bool check_reachable, bool *has_instr_opnds/*OUT OPTIONAL*/ |
| _IF_DEBUG(bool assert_reachable)) |
| { |
| const instr_info_t * info; |
| decode_info_t di; |
| |
| /* pointer to and into the instruction binary */ |
| byte *cache_pc = copy_pc; |
| byte *field_ptr = cache_pc; |
| byte *disp_relativize_at = NULL; |
| uint opc; |
| bool output_initial_opcode = false; |
| if (has_instr_opnds != NULL) |
| *has_instr_opnds = false; |
| |
| /* first handle the already-encoded instructions */ |
| if (instr_raw_bits_valid(instr)) { |
| CLIENT_ASSERT(check_reachable, "internal encode error: cannot encode raw " |
| "bits and ignore reachability"); |
| /* copy raw bits, possibly re-relativizing */ |
| return copy_and_re_relativize_raw_instr(dcontext, instr, cache_pc, final_pc); |
| } |
| CLIENT_ASSERT(instr_operands_valid(instr), "instr_encode error: operands invalid"); |
| opc = instr_get_opcode(instr); |
| if ((instr_is_cbr(instr) && |
| (!instr_is_cti_loop(instr) || |
| /* no addr16 */ |
| reg_is_pointer_sized(opnd_get_reg(instr_get_src(instr, 1))))) || |
| /* no indirect or far */ |
| opc == OP_jmp_short || opc == OP_jmp || opc == OP_call) { |
| if (!TESTANY(~(PREFIX_JCC_TAKEN|PREFIX_JCC_NOT_TAKEN), instr->prefixes)) { |
| /* encode_cti cannot handle funny prefixes or indirect branches or rets */ |
| return encode_cti(instr, copy_pc, final_pc, check_reachable |
| _IF_DEBUG(assert_reachable)); |
| } |
| } |
| |
| /* else really encode */ |
| info = instr_get_instr_info(instr); |
| if (info == NULL) { |
| CLIENT_ASSERT(instr_is_label(instr), "instr_encode: invalid instr"); |
| return (instr_is_label(instr) ? copy_pc : NULL); |
| } |
| |
| /* first, walk through instr list to find format that matches |
| * this instr's operands |
| */ |
| DOLOG(ENC_LEVEL, LOG_EMIT, { loginst(dcontext, 1, instr, "\n--- encoding"); }); |
| |
| memset(&di, 0, sizeof(decode_info_t)); |
| di.opcode = opc; |
| IF_X64(di.x86_mode = instr_get_x86_mode(instr)); |
| /* while only PREFIX_SIGNIFICANT should be set by the user, internally |
| * we set di.prefixes to communicate size prefixes between |
| * opnd_type_ok() and here, first clearing out the size specifiers |
| * in encoding_possible(). |
| */ |
| di.prefixes = instr->prefixes; |
| di.vex_vvvv = 0xf; /* 4 1's by default */ |
| |
| /* We check predication, to help clients who are generating instrs from |
| * having incorrect analysis results on their own gencode. |
| * We assume each opcode has constant predication info. |
| */ |
| if (instr_get_predicate(instr) != decode_predicate_from_instr_info(opc, info)) { |
| if (instr_get_predicate(instr) == DR_PRED_NONE) |
| CLIENT_ASSERT(false, "instr is missing a predicate"); |
| else |
| CLIENT_ASSERT(false, "instr contains an invalid predicate for its opcode"); |
| return NULL; |
| } |
| |
| /* Used for PR 253327 addr32 rip-relative and instr_t targets, including |
| * during encoding_possible(). |
| */ |
| di.start_pc = cache_pc; |
| di.final_pc = final_pc; |
| |
| while (!encoding_possible(&di, instr, info)) { |
| LOG(THREAD, LOG_EMIT, ENC_LEVEL, "\tencoding for 0x%x no good...\n", |
| info->opcode); |
| info = get_next_instr_info(info); |
| /* stop when hit end of list or when hit extra operand tables (OP_CONTD) */ |
| if (info == NULL || info->opcode == OP_CONTD) { |
| DOLOG(1, LOG_EMIT, { |
| LOG(THREAD, LOG_EMIT, 1, "ERROR: Could not find encoding for: "); |
| instr_disassemble(dcontext, instr, THREAD); |
| LOG(THREAD, LOG_EMIT, 1, "\n"); |
| }); |
| CLIENT_ASSERT(false, "instr_encode error: no encoding found (see log)"); |
| /* FIXME: since labels (case 4468) have a legal length 0 |
| * we may want to return a separate status code for failure. |
| */ |
| return NULL; |
| } |
| } |
| |
| /* fill out the other fields of di */ |
| di.size_immed = OPSZ_NA; |
| di.size_immed2 = OPSZ_NA; |
| /* these (illegal) values indicate uninitialization */ |
| di.reg = 8; |
| di.mod = 5; |
| |
| /* prefixes */ |
| di.seg_override = REG_NULL; /* operands will fill in */ |
| |
| /* instr_t* operand support */ |
| di.cur_note = (ptr_int_t) instr->note; |
| |
| /* operands |
| * we can ignore extra operands here, since all extra operands |
| * are hardcoded |
| */ |
| if (info->dst1_type != TYPE_NONE) |
| encode_operand(&di, info->dst1_type, info->dst1_size, instr_get_dst(instr, 0)); |
| if (info->dst2_type != TYPE_NONE) |
| encode_operand(&di, info->dst2_type, info->dst2_size, instr_get_dst(instr, 1)); |
| if (info->src1_type != TYPE_NONE) |
| encode_operand(&di, info->src1_type, info->src1_size, instr_get_src(instr, 0)); |
| if (info->src2_type != TYPE_NONE) |
| encode_operand(&di, info->src2_type, info->src2_size, instr_get_src(instr, 1)); |
| if (info->src3_type != TYPE_NONE) |
| encode_operand(&di, info->src3_type, info->src3_size, instr_get_src(instr, 2)); |
| if (di.mod == 5 && di.reg < 8) { /* mod may never be set (e.g., OP_extrq) */ |
| /* follow lead of below where we set to all 1's */ |
| di.mod = 3; |
| CLIENT_ASSERT(di.rm == 0, "internal error: mod not set but rm was"); |
| di.rm = 7; |
| } |
| |
| /* finally, do the actual bit writing */ |
| |
| /* output the prefix byte(s) */ |
| if (di.prefixes != 0) { |
| if (TEST(PREFIX_LOCK, di.prefixes)) { |
| *field_ptr = RAW_PREFIX_lock; |
| field_ptr++; |
| } |
| if (TEST(PREFIX_XACQUIRE, di.prefixes)) { |
| *field_ptr = RAW_PREFIX_xacquire; |
| field_ptr++; |
| } |
| if (TEST(PREFIX_XRELEASE, di.prefixes)) { |
| *field_ptr = RAW_PREFIX_xrelease; |
| field_ptr++; |
| } |
| if (TEST(PREFIX_JCC_TAKEN, di.prefixes)) { |
| *field_ptr = RAW_PREFIX_jcc_taken; |
| field_ptr++; |
| } else if (TEST(PREFIX_JCC_NOT_TAKEN, di.prefixes)) { |
| *field_ptr = RAW_PREFIX_jcc_not_taken; |
| field_ptr++; |
| } |
| } |
| if (TEST(PREFIX_DATA, di.prefixes)) { |
| *field_ptr = DATA_PREFIX_OPCODE; |
| field_ptr++; |
| } |
| /* N.B.: we assume the order of 0x67 <seg> in coarse_is_indirect_stub() |
| * and instr_is_tls_xcx_spill() |
| */ |
| if (TEST(PREFIX_ADDR, di.prefixes)) { |
| *field_ptr = ADDR_PREFIX_OPCODE; |
| field_ptr++; |
| } |
| if (di.seg_override != REG_NULL) { |
| switch (di.seg_override) { |
| case SEG_ES: *field_ptr = 0x26; break; |
| case SEG_CS: *field_ptr = 0x2e; break; |
| case SEG_SS: *field_ptr = 0x36; break; |
| case SEG_DS: *field_ptr = 0x3e; break; |
| case SEG_FS: *field_ptr = 0x64; break; |
| case SEG_GS: *field_ptr = 0x65; break; |
| default: CLIENT_ASSERT(false, "instr_encode error: unknown segment prefix"); |
| } |
| field_ptr++; |
| } |
| |
| /* vex prefix must be last and if present includes prefix bytes, rex flags, |
| * and some opcode bytes |
| */ |
| if (TEST(REQUIRES_VEX, info->flags)) { |
| field_ptr = encode_vex_prefixes(field_ptr, &di, info, &output_initial_opcode); |
| } else { |
| CLIENT_ASSERT(!TEST(PREFIX_VEX_L, di.prefixes), "internal encode vex error"); |
| |
| /* output the opcode required prefix byte (if needed) */ |
| if (info->opcode > 0xffffff && |
| /* if OPCODE_{MODRM,SUFFIX} there can be no prefix-opcode byte */ |
| !TESTANY(OPCODE_MODRM|OPCODE_SUFFIX, info->opcode)) { |
| /* prefix byte is part of opcode */ |
| *field_ptr = (byte)(info->opcode >> 24); |
| field_ptr++; |
| } |
| |
| if (TEST(REQUIRES_REX, info->flags)) { |
| /* We could add other rex flags by overloading OPCODE_SUFFIX or |
| * possibly OPCODE_MODRM (but the latter only for instrs that |
| * aren't in mod_ext). For now this flag implies rex.w. |
| */ |
| di.prefixes |= PREFIX_REX_W; |
| } |
| |
| /* NOTE - the rex prefix must be the last prefix (even if the other prefix is |
| * part of the opcode). Xref PR 271878. */ |
| if (TESTANY(PREFIX_REX_ALL, di.prefixes)) { |
| byte rexval = REX_PREFIX_BASE_OPCODE; |
| if (TEST(PREFIX_REX_W, di.prefixes)) |
| rexval |= REX_PREFIX_W_OPFLAG; |
| if (TEST(PREFIX_REX_R, di.prefixes)) |
| rexval |= REX_PREFIX_R_OPFLAG; |
| if (TEST(PREFIX_REX_X, di.prefixes)) |
| rexval |= REX_PREFIX_X_OPFLAG; |
| if (TEST(PREFIX_REX_B, di.prefixes)) |
| rexval |= REX_PREFIX_B_OPFLAG; |
| *field_ptr = rexval; |
| field_ptr++; |
| } |
| } |
| |
| if (!output_initial_opcode) { |
| /* output the opcode byte(s) (opcode required prefixes are handled above) */ |
| if (TEST(OPCODE_THREEBYTES, info->opcode)) { |
| /* implied initial opcode byte */ |
| *field_ptr = 0x0f; |
| field_ptr++; |
| } |
| /* first opcode byte */ |
| *field_ptr = (byte)((info->opcode & 0x00ff0000) >> 16); |
| field_ptr++; |
| } |
| |
| /* second opcode byte, if there is one */ |
| if (TEST(OPCODE_TWOBYTES, info->opcode)) { |
| *field_ptr = (byte)((info->opcode & 0x0000ff00) >> 8); |
| field_ptr++; |
| } |
| /* /n: part of opcode is in reg of modrm byte */ |
| if (TEST(OPCODE_REG, info->opcode)) { |
| CLIENT_ASSERT(di.reg == 8, |
| "instr_encode error: /n opcode inconsistency"); /* unset */ |
| di.reg = (byte) (info->opcode & 0x00000007); |
| if (di.mod == 5) { |
| /* modrm only used for opcode |
| * mod and rm are arbitrary: seem to be set to all 1's by compilers |
| */ |
| di.mod = 3; |
| di.rm = 7; |
| } |
| } |
| /* opcode depends on entire modrm byte */ |
| if (!TEST(REQUIRES_VEX, info->flags) && TEST(OPCODE_MODRM, info->opcode)) { |
| /* modrm is encoded in prefix byte */ |
| *field_ptr = (byte)(info->opcode >> 24); |
| field_ptr++; |
| di.mod = 5; /* prevent modrm output from opnds below */ |
| } |
| |
| /* output modrm byte(s) */ |
| if (di.mod != 5) { |
| byte modrm; |
| if (di.reg == 8) /* if never set, set to 0 */ |
| di.reg = 0; |
| CLIENT_ASSERT(di.mod <= 0x3 && di.reg <= 0x7 && di.rm <= 0x7, |
| "encode error: invalid modrm"); |
| modrm = MODRM_BYTE(di.mod, di.reg, di.rm); |
| *field_ptr = modrm; |
| field_ptr++; |
| if (di.has_sib) { |
| byte sib = (byte) ((di.scale << 6) | (di.index << 3) | (di.base)); |
| CLIENT_ASSERT(di.scale <= 0x3 && di.index <= 0x7 && di.base <= 0x7, |
| "encode error: invalid scale/index/base"); |
| *field_ptr = sib; |
| field_ptr++; |
| } |
| if (di.has_disp) { |
| if (di.mod == 1) { |
| *field_ptr = (byte) di.disp; |
| field_ptr++; |
| } else if (!X64_MODE(&di) && TEST(PREFIX_ADDR, di.prefixes)) { |
| CLIENT_ASSERT_TRUNCATE(*((short *)field_ptr), ushort, di.disp, |
| "encode error: modrm disp too large for 16-bit"); |
| *((short *)field_ptr) = (ushort) di.disp; |
| field_ptr+=2; |
| } else { |
| if (X64_MODE(&di) && di.mod == 0 && di.rm == 5) { |
| /* pc-relative, but we don't know size of immeds yet */ |
| disp_relativize_at = field_ptr; |
| } else { |
| *((int *)field_ptr) = di.disp; |
| } |
| field_ptr+=4; |
| } |
| } |
| } |
| |
| /* output immed byte(s) */ |
| /* HACK: to tell an instr target of a control transfer instruction |
| * our length, store into di.modrm the bytes so far |
| */ |
| CLIENT_ASSERT_TRUNCATE(di.modrm, byte, (field_ptr - cache_pc), |
| "encode error: instr too long"); |
| di.modrm = (byte) (field_ptr - cache_pc); |
| if (di.size_immed != OPSZ_NA) |
| field_ptr = encode_immed(&di, field_ptr); |
| if (di.size_immed2 != OPSZ_NA) |
| field_ptr = encode_immed(&di, field_ptr); |
| |
| /* suffix opcode */ |
| if (!TEST(REQUIRES_VEX, info->flags) && TEST(OPCODE_SUFFIX, info->opcode)) { |
| /* none of these have immeds, currently (and presumably never will have) */ |
| ASSERT_CURIOSITY(di.size_immed == OPSZ_NA && di.size_immed2 == OPSZ_NA); |
| /* modrm is encoded in prefix byte */ |
| *field_ptr = (byte)(info->opcode >> 24); |
| field_ptr++; |
| } |
| |
| if (disp_relativize_at != NULL) { |
| CLIENT_ASSERT(X64_MODE(&di), "encode error: no rip-relative in x86 mode!"); |
| if (check_reachable && |
| !CHECK_TRUNCATE_TYPE_int(di.disp_abs - (field_ptr - copy_pc + final_pc)) && |
| /* PR 253327: we auto-add addr prefix for out-of-reach low tgt */ |
| (!TEST(PREFIX_ADDR, di.prefixes) || (ptr_uint_t)di.disp_abs > INT_MAX)) { |
| CLIENT_ASSERT(!assert_reachable, |
| "encode error: rip-relative reference out of 32-bit reach"); |
| return NULL; |
| } |
| *((int *)disp_relativize_at) = (int) |
| (di.disp_abs - (field_ptr - copy_pc + final_pc)); |
| /* in case caller is caching these bits (in particular, |
| * private_instr_encode()), set rip_rel_pos */ |
| CLIENT_ASSERT(CHECK_TRUNCATE_TYPE_byte(disp_relativize_at - di.start_pc), |
| "internal encode error: rip-relative instr pos too large"); |
| IF_X64(instr_set_rip_rel_pos(instr, (byte)(disp_relativize_at - di.start_pc))); |
| } |
| |
| #if DEBUG_DISABLE /* turn back on if want to debug */ |
| if (stats->loglevel >= 3) { |
| byte *pc = cache_pc; |
| LOG(THREAD, LOG_EMIT, 3, "instr_encode on: "); |
| instr_disassemble(dcontext, instr, THREAD); |
| LOG(THREAD, LOG_EMIT, 3, " : "); |
| do { |
| LOG(THREAD, LOG_EMIT, 3, " %02x", *pc); |
| pc++; |
| } while (pc < field_ptr); |
| LOG(THREAD, LOG_EMIT, 3, "\n"); |
| } |
| if (0 && instr_raw_bits_valid(instr)) { |
| /* encoding in original bits */ |
| int i; |
| int len = (field_ptr - cache_pc); |
| byte ours, orig; |
| for (i=0; i<len; i++) { |
| ours = *(cache_pc+i); |
| orig = *(((byte *)instr_get_raw_bits(instr))+i); |
| if (ours != orig) { |
| /* special cases: difference is ok, not an error: */ |
| if (i==0 && ours==0x66 && (orig==0xf2||orig==0xf3) && |
| /* allow rep/repne & data prefixes to have order swapped */ |
| *(cache_pc+i+1)==orig && |
| *(((byte *)instr_get_raw_bits(instr))+i+1)==ours) { |
| i++; /* skip next */ |
| } else if (i==0 && |
| ((ours==0x3a && orig==0x38) || |
| (ours==0x3b && orig==0x39) || |
| (ours==0x03 && orig==0x01) || |
| (ours==0x33 && orig==0x31))) { |
| /* Gv, Ev vs Ev, Gv equivalent when both opnds regs */ |
| i++; /* skip next */ |
| } else if ((i==1 || i==2) && |
| ((ours | 0x3c)==0x3c) && |
| ((orig | 0x7c)==0x7c) && |
| *(((byte *)instr_get_raw_bits( |