| /* ********************************************************** |
| * Copyright (c) 2014 Google, 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 Google, 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. |
| */ |
| |
| #include "../globals.h" |
| #include "instr.h" |
| #include "decode.h" |
| #include "decode_private.h" |
| #include <string.h> /* for memcpy */ |
| |
| /* ARM decoder. |
| * General strategy: |
| * + We use a data-driven table-based approach, as we need to both encode and |
| * decode and a central source of data lets us move in both directions. |
| */ |
| /* FIXME i#1551: add Thumb support: for now just A32 */ |
| /* FIXME i#1551: add A64 support: for now just A32 */ |
| |
| /* With register lists we can see quite long operand lists */ |
| #define MAX_OPNDS IF_X64_ELSE(8, 22) |
| |
| bool |
| is_isa_mode_legal(dr_isa_mode_t mode) |
| { |
| #ifdef X64 |
| return (mode == DR_ISA_ARM_A64); |
| #else |
| return (mode == DR_ISA_ARM_THUMB || DR_ISA_ARM_A32); |
| #endif |
| } |
| |
| static bool |
| reg_is_past_last_simd(reg_id_t reg, uint add) |
| { |
| if (reg >= DR_REG_Q0 && reg <= DR_REG_Q31) |
| return reg+add > IF_X64_ELSE(DR_REG_Q31, DR_REG_Q15); |
| if (reg >= DR_REG_D0 && reg <= DR_REG_D31) |
| return reg+add > DR_REG_D31; |
| if (reg >= DR_REG_S0 && reg <= DR_REG_S31) |
| return reg+add > DR_REG_S31; |
| if (reg >= DR_REG_H0 && reg <= DR_REG_H31) |
| return reg+add > DR_REG_H31; |
| if (reg >= DR_REG_B0 && reg <= DR_REG_B31) |
| return reg+add > DR_REG_B31; |
| ASSERT_NOT_REACHED(); |
| return true; |
| } |
| |
| /* We assume little-endian */ |
| static inline int |
| decode_predicate(uint instr_word) |
| { |
| return instr_word >> 28; /* bits 31:28 */ |
| } |
| |
| /* We often take bits 27:20 as an 8-bit opcode */ |
| static inline int |
| decode_opc8(uint instr_word) |
| { |
| return (instr_word >> 20) & 0xff; |
| } |
| |
| /* We often take bits 7:4 as a 4-bit auxiliary opcode */ |
| static inline int |
| decode_opc4(uint instr_word) |
| { |
| return (instr_word >> 4) & 0xf; |
| } |
| |
| static reg_id_t |
| decode_regA(decode_info_t *di) |
| { |
| /* A32 = 19:16 */ |
| return DR_REG_START_GPR + ((di->instr_word >> 16) & 0xf); |
| } |
| |
| static reg_id_t |
| decode_regB(decode_info_t *di) |
| { |
| /* A32 = 15:12 */ |
| return DR_REG_START_GPR + ((di->instr_word >> 12) & 0xf); |
| } |
| |
| static reg_id_t |
| decode_regC(decode_info_t *di) |
| { |
| /* A32 = 11:8 */ |
| return DR_REG_START_GPR + ((di->instr_word >> 8) & 0xf); |
| } |
| |
| static reg_id_t |
| decode_regD(decode_info_t *di) |
| { |
| /* A32 = 3:0 */ |
| return DR_REG_START_GPR + (di->instr_word & 0xf); |
| } |
| |
| static inline reg_id_t |
| decode_simd_start(opnd_size_t opsize) |
| { |
| if (opsize == OPSZ_1) |
| return DR_REG_B0; |
| else if (opsize == OPSZ_2) |
| return DR_REG_H0; |
| else if (opsize == OPSZ_4) |
| return DR_REG_S0; |
| else if (opsize == OPSZ_8) |
| return DR_REG_D0; |
| else if (opsize == OPSZ_16) |
| return DR_REG_Q0; |
| else |
| CLIENT_ASSERT(false, "invalid SIMD reg size"); |
| return DR_REG_D0; |
| } |
| |
| static reg_id_t |
| decode_vregA(decode_info_t *di, opnd_size_t opsize) |
| { |
| /* A32 = 7,19:16 */ |
| return decode_simd_start(opsize) + |
| (((di->instr_word & 0x00000080) >> 3) | ((di->instr_word >> 16) & 0xf)); |
| } |
| |
| static reg_id_t |
| decode_vregB(decode_info_t *di, opnd_size_t opsize) |
| { |
| /* A32 = 22,15:12 */ |
| return decode_simd_start(opsize) + |
| (((di->instr_word & 0x00400000) >> 18) | ((di->instr_word >> 12) & 0xf)); |
| } |
| |
| static reg_id_t |
| decode_vregC(decode_info_t *di, opnd_size_t opsize) |
| { |
| /* A32 = 5,3:0 */ |
| return decode_simd_start(opsize) + |
| (((di->instr_word & 0x00000020) >> 1) | (di->instr_word & 0xf)); |
| } |
| |
| static reg_id_t |
| decode_wregA(decode_info_t *di, opnd_size_t opsize) |
| { |
| /* A32 = 19:16,7 */ |
| return decode_simd_start(opsize) + |
| (((di->instr_word & 0x000f0000) >> 15) | ((di->instr_word >> 19) & 0x1)); |
| } |
| |
| static reg_id_t |
| decode_wregB(decode_info_t *di, opnd_size_t opsize) |
| { |
| /* A32 = 15:12,22 */ |
| return decode_simd_start(opsize) + |
| (((di->instr_word & 0x0000f000) >> 11) | ((di->instr_word >> 22) & 0x1)); |
| } |
| |
| static reg_id_t |
| decode_wregC(decode_info_t *di, opnd_size_t opsize) |
| { |
| /* A32 = 3:0,5 */ |
| return decode_simd_start(opsize) + |
| (((di->instr_word & 0x0000000f) << 1) | ((di->instr_word >> 5) & 0x1)); |
| } |
| |
| static ptr_int_t |
| decode_immed(decode_info_t *di, uint start_bit, opnd_size_t opsize, bool is_signed) |
| { |
| ptr_int_t val; |
| uint mask = ((1 << opnd_size_in_bits(opsize)) - 1); |
| if (is_signed) |
| val = (ptr_int_t)(int)((di->instr_word >> start_bit) & mask); |
| else |
| val = (ptr_int_t)(ptr_uint_t)((di->instr_word >> start_bit) & mask); |
| return val; |
| } |
| |
| static bool |
| decode_float_reglist(decode_info_t *di, opnd_size_t downsz, opnd_size_t upsz, |
| opnd_t *array, uint *counter INOUT) |
| { |
| uint i; |
| uint count = (uint) decode_immed(di, 0, OPSZ_1, false/*unsigned*/); |
| reg_id_t first_reg; |
| if (upsz == OPSZ_8) { |
| /* XXX i#1551: if immed is odd, supposed to be (deprecated) OP_fldmx */ |
| count /= 2; |
| } else |
| CLIENT_ASSERT(upsz == OPSZ_4, "invalid opsz for TYPE_L_CONSEC"); |
| /* There must be an immediately prior simd reg */ |
| CLIENT_ASSERT(*counter > 0 && opnd_is_reg(array[*counter-1]), |
| "invalid instr template"); |
| count--; /* The prior was already added */ |
| first_reg = opnd_get_reg(array[*counter-1]); |
| for (i = 0; i < count; i++) { |
| print_file(STDERR, "reglist: first=%s, new=%s\n", reg_names[first_reg], |
| reg_names[first_reg + i]); |
| if ((upsz == OPSZ_8 && first_reg + i > DR_REG_D31) || |
| (upsz == OPSZ_4 && first_reg + i > DR_REG_S31)) |
| return false; /* invalid */ |
| array[(*counter)++] = opnd_create_reg_ex(first_reg + i, downsz, 0); |
| di->reglist_sz += opnd_size_in_bytes(downsz); |
| } |
| return true; |
| } |
| |
| static dr_shift_type_t |
| decode_index_shift(decode_info_t *di, uint *amount OUT) |
| { |
| ptr_int_t sh2 = decode_immed(di, DECODE_INDEX_SHIFT_TYPE_BITPOS, |
| DECODE_INDEX_SHIFT_TYPE_SIZE, false); |
| ptr_int_t val = decode_immed(di, DECODE_INDEX_SHIFT_AMOUNT_BITPOS, |
| DECODE_INDEX_SHIFT_AMOUNT_SIZE, false); |
| if (sh2 == 0 && val == 0) { |
| *amount = 0; |
| return DR_SHIFT_NONE; |
| } else if (sh2 == SHIFT_ENCODING_LSL) { |
| *amount = val; |
| return DR_SHIFT_LSL; |
| } else if (sh2 == SHIFT_ENCODING_LSR) { |
| *amount = (val == 0) ? 32 : val; |
| return DR_SHIFT_LSR; |
| } else if (sh2 == SHIFT_ENCODING_ASR) { |
| *amount = (val == 0) ? 32 : val; |
| return DR_SHIFT_ASR; |
| } else if (sh2 == SHIFT_ENCODING_RRX && val == 0) { |
| *amount = 1; |
| return DR_SHIFT_RRX; |
| } else { |
| *amount = val; |
| return DR_SHIFT_ROR; |
| } |
| } |
| |
| static bool |
| decode_operand(decode_info_t *di, byte optype, opnd_size_t opsize, opnd_t *array, |
| uint *counter INOUT) |
| { |
| uint i; |
| ptr_int_t val = 0; |
| opnd_size_t downsz = resolve_size_downward(opsize); |
| opnd_size_t upsz = resolve_size_upward(opsize); |
| |
| switch (optype) { |
| case TYPE_NONE: |
| array[(*counter)++] = opnd_create_null(); |
| return true; |
| |
| /* Registers */ |
| case TYPE_R_A: |
| case TYPE_R_A_TOP: /* we aren't storing whether top in our IR */ |
| array[(*counter)++] = opnd_create_reg_ex(decode_regA(di), downsz, 0); |
| return true; |
| case TYPE_R_B: |
| case TYPE_R_B_TOP: /* we aren't storing whether top in our IR */ |
| array[(*counter)++] = opnd_create_reg_ex(decode_regB(di), downsz, 0); |
| return true; |
| case TYPE_R_C: |
| case TYPE_R_C_TOP: /* we aren't storing whether top in our IR */ |
| array[(*counter)++] = opnd_create_reg_ex(decode_regC(di), downsz, 0); |
| return true; |
| case TYPE_R_D: |
| case TYPE_R_D_TOP: /* we aren't storing whether top in our IR */ |
| array[(*counter)++] = opnd_create_reg_ex(decode_regD(di), downsz, 0); |
| return true; |
| case TYPE_R_D_NEGATED: |
| array[(*counter)++] = opnd_create_reg_ex(decode_regD(di), downsz, |
| DR_OPND_NEGATED); |
| return true; |
| case TYPE_R_B_EVEN: |
| case TYPE_R_D_EVEN: { |
| reg_id_t reg = (optype == TYPE_R_B_EVEN) ? decode_regB(di) : decode_regD(di); |
| if ((reg - DR_REG_START_GPR) % 2 == 1) |
| return false; |
| array[(*counter)++] = opnd_create_reg_ex(reg, downsz, 0); |
| return true; |
| } |
| case TYPE_R_B_PLUS1: |
| case TYPE_R_D_PLUS1: { |
| reg_id_t reg; |
| if (*counter <= 0 || !opnd_is_reg(array[(*counter)-1])) |
| return false; |
| reg = opnd_get_reg(array[(*counter)-1]); |
| if (reg == DR_REG_STOP_32 || reg == DR_REG_STOP_64) |
| return false; |
| array[(*counter)++] = opnd_create_reg_ex(reg + 1, downsz, 0); |
| return true; |
| } |
| case TYPE_CR_A: |
| array[(*counter)++] = opnd_create_reg_ex(decode_regA(di) - DR_REG_START_GPR + |
| DR_REG_CR0, downsz, 0); |
| return true; |
| case TYPE_CR_B: |
| array[(*counter)++] = opnd_create_reg_ex(decode_regB(di) - DR_REG_START_GPR + |
| DR_REG_CR0, downsz, 0); |
| return true; |
| case TYPE_CR_C: |
| array[(*counter)++] = opnd_create_reg_ex(decode_regC(di) - DR_REG_START_GPR + |
| DR_REG_CR0, downsz, 0); |
| return true; |
| case TYPE_CR_D: |
| array[(*counter)++] = opnd_create_reg_ex(decode_regD(di) - DR_REG_START_GPR + |
| DR_REG_CR0, downsz, 0); |
| return true; |
| case TYPE_V_A: |
| array[(*counter)++] = opnd_create_reg_ex(decode_vregA(di, upsz), downsz, 0); |
| return true; |
| case TYPE_V_B: |
| array[(*counter)++] = opnd_create_reg_ex(decode_vregB(di, upsz), downsz, 0); |
| return true; |
| case TYPE_V_C: |
| array[(*counter)++] = opnd_create_reg_ex(decode_vregC(di, upsz), downsz, 0); |
| return true; |
| case TYPE_W_A: |
| array[(*counter)++] = opnd_create_reg_ex(decode_wregA(di, upsz), downsz, 0); |
| return true; |
| case TYPE_W_B: |
| array[(*counter)++] = opnd_create_reg_ex(decode_wregB(di, upsz), downsz, 0); |
| return true; |
| case TYPE_W_C: |
| array[(*counter)++] = opnd_create_reg_ex(decode_wregC(di, upsz), downsz, 0); |
| return true; |
| case TYPE_V_C_3b: { |
| reg_id_t reg = decode_simd_start(upsz) + (di->instr_word & 0x00000007); |
| array[(*counter)++] = opnd_create_reg_ex(reg, downsz, 0); |
| return true; |
| } |
| case TYPE_V_C_4b: { |
| reg_id_t reg = decode_simd_start(upsz) + (di->instr_word & 0x0000000f); |
| array[(*counter)++] = opnd_create_reg_ex(reg, downsz, 0); |
| return true; |
| } |
| case TYPE_W_C_PLUS1: { |
| reg_id_t reg; |
| if (*counter <= 0 || !opnd_is_reg(array[(*counter)-1])) |
| return false; |
| reg = opnd_get_reg(array[(*counter)-1]); |
| if (reg_is_past_last_simd(reg, 1)) |
| return false; |
| array[(*counter)++] = opnd_create_reg_ex(reg + 1, downsz, 0); |
| return true; |
| } |
| case TYPE_SPSR: |
| array[(*counter)++] = opnd_create_reg_ex(DR_REG_SPSR, downsz, 0); |
| return true; |
| case TYPE_CPSR: |
| array[(*counter)++] = opnd_create_reg_ex(DR_REG_CPSR, downsz, 0); |
| return true; |
| case TYPE_FPSCR: |
| array[(*counter)++] = opnd_create_reg_ex(DR_REG_FPSCR, downsz, 0); |
| return true; |
| case TYPE_LR: |
| array[(*counter)++] = opnd_create_reg_ex(DR_REG_LR, downsz, 0); |
| return true; |
| case TYPE_SP: |
| array[(*counter)++] = opnd_create_reg_ex(DR_REG_SP, downsz, 0); |
| return true; |
| |
| /* Register lists */ |
| case TYPE_L_8b: |
| case TYPE_L_13b: |
| case TYPE_L_16b: { |
| uint num = (optype == TYPE_L_8b ? 8 : (optype == TYPE_L_13b ? 13 : 16)); |
| for (i = 0; i < num; i++) { |
| if ((di->instr_word & (1 << i)) != 0) { |
| array[(*counter)++] = opnd_create_reg_ex(DR_REG_START_GPR + i, downsz, 0); |
| di->reglist_sz += opnd_size_in_bytes(downsz); |
| } |
| } |
| return true; |
| } |
| case TYPE_L_CONSEC: |
| return decode_float_reglist(di, downsz, upsz, array, counter); |
| case TYPE_L_VBx2: |
| case TYPE_L_VBx3: |
| case TYPE_L_VBx4: |
| case TYPE_L_VBx2D: |
| case TYPE_L_VBx3D: |
| case TYPE_L_VBx4D: { |
| reg_id_t start = decode_vregB(di, upsz); |
| uint inc = 1; |
| if (optype == TYPE_L_VBx2D || optype == TYPE_L_VBx3D || optype == TYPE_L_VBx4D) |
| inc = 2; |
| array[(*counter)++] = opnd_create_reg_ex(start, downsz, 0); |
| if (reg_is_past_last_simd(start, inc)) |
| return false; |
| array[(*counter)++] = opnd_create_reg_ex(start + inc, downsz, 0); |
| if (optype == TYPE_L_VBx2 || optype == TYPE_L_VBx2D) |
| return true; |
| if (reg_is_past_last_simd(start, 2*inc)) |
| return false; |
| array[(*counter)++] = opnd_create_reg_ex(start + 2*inc, downsz, 0); |
| if (optype == TYPE_L_VBx3 || optype == TYPE_L_VBx3D) |
| return true; |
| if (reg_is_past_last_simd(start, 3*inc)) |
| return false; |
| array[(*counter)++] = opnd_create_reg_ex(start + 3*inc, downsz, 0); |
| return true; |
| } |
| |
| /* Immeds */ |
| case TYPE_I_b0: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 0, opsize, false/*unsigned*/), opsize); |
| return true; |
| case TYPE_NI_b0: |
| array[(*counter)++] = |
| opnd_create_immed_int(-decode_immed(di, 0, opsize, false/*unsigned*/), |
| opsize); |
| return true; |
| case TYPE_I_b3: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 3, opsize, false/*unsigned*/), opsize); |
| return true; |
| case TYPE_I_b4: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 4, opsize, false/*unsigned*/), opsize); |
| return true; |
| case TYPE_I_b5: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 5, opsize, false/*unsigned*/), opsize); |
| return true; |
| case TYPE_I_b6: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 6, opsize, false/*unsigned*/), opsize); |
| return true; |
| case TYPE_I_b7: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 7, opsize, false/*unsigned*/), opsize); |
| return true; |
| case TYPE_I_b8: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 8, opsize, false/*unsigned*/), opsize); |
| return true; |
| case TYPE_I_b9: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 9, opsize, false/*unsigned*/), opsize); |
| return true; |
| case TYPE_I_b10: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 10, opsize, false/*unsign*/), opsize); |
| return true; |
| case TYPE_I_b16: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 16, opsize, false/*unsign*/), opsize); |
| return true; |
| case TYPE_I_b17: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 17, opsize, false/*unsign*/), opsize); |
| return true; |
| case TYPE_I_b18: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 18, opsize, false/*unsign*/), opsize); |
| return true; |
| case TYPE_I_b19: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 19, opsize, false/*unsign*/), opsize); |
| return true; |
| case TYPE_I_b20: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 20, opsize, false/*unsign*/), opsize); |
| return true; |
| case TYPE_I_b21: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 21, opsize, false/*unsign*/), opsize); |
| return true; |
| case TYPE_NI_b8_b0: |
| case TYPE_I_b8_b0: { |
| if (opsize == OPSZ_2) { |
| val = decode_immed(di, 0, OPSZ_4b, false/*unsigned*/); |
| val |= (decode_immed(di, 8, OPSZ_12b, false/*unsigned*/) << 12); |
| } else if (opsize == OPSZ_1) { |
| val = decode_immed(di, 0, OPSZ_4b, false/*unsigned*/); |
| val |= (decode_immed(di, 8, OPSZ_4b, false/*unsigned*/) << 4); |
| } else |
| CLIENT_ASSERT(false, "unsupported 0-8 split immed size"); |
| if (optype == TYPE_NI_b8_b0) |
| val = -val; |
| array[(*counter)++] = opnd_create_immed_int(val, opsize); |
| return true; |
| } |
| case TYPE_I_b16_b0: { |
| opnd_size_t each; |
| uint sz = opnd_size_in_bits(opsize); |
| CLIENT_ASSERT(sz % 2 == 0, "split 0-16 size must be even"); |
| if (opsize == OPSZ_2) |
| each = OPSZ_1; |
| else if (opsize == OPSZ_1) |
| each = OPSZ_4b; |
| else { |
| CLIENT_ASSERT(false, "unsupported 0-16 split immed size"); |
| each = OPSZ_0; |
| } |
| val = decode_immed(di, 0, each, false/*unsigned*/); |
| val |= (decode_immed(di, 16, each, false/*unsigned*/) << (sz/2)); |
| array[(*counter)++] = opnd_create_immed_int(val, opsize); |
| return true; |
| } |
| case TYPE_I_b0_b5: { |
| if (opsize == OPSZ_5b) { |
| val = decode_immed(di, 5, OPSZ_1b, false/*unsigned*/); |
| val |= (decode_immed(di, 0, OPSZ_4b, false/*unsigned*/) << 1); |
| } else |
| CLIENT_ASSERT(false, "unsupported 5-0 split immed size"); |
| array[(*counter)++] = opnd_create_immed_int(val, opsize); |
| return true; |
| } |
| case TYPE_I_b0_b24: { /* OP_blx imm24:H:0 */ |
| if (opsize == OPSZ_25b) { |
| val = decode_immed(di, 24, OPSZ_1b, false/*unsigned*/) << 1; |
| val |= (decode_immed(di, 0, OPSZ_3, false/*unsigned*/) << 2); |
| } else |
| CLIENT_ASSERT(false, "unsupported 24-0 split immed size"); |
| array[(*counter)++] = opnd_create_immed_int(val, opsize); |
| return true; |
| } |
| case TYPE_I_b5_b3: { /* OP_vmla scalar: M:Vm<3> */ |
| if (opsize == OPSZ_2b) { |
| val = decode_immed(di, 3, OPSZ_1b, false/*unsigned*/); |
| val |= (decode_immed(di, 5, OPSZ_1b, false/*unsigned*/) << 1); |
| } else |
| CLIENT_ASSERT(false, "unsupported 5-0 split immed size"); |
| array[(*counter)++] = opnd_create_immed_int(val, opsize); |
| return true; |
| } |
| case TYPE_I_b8_b16: { /* OP_msr */ |
| if (opsize == OPSZ_5b) { |
| val = decode_immed(di, 16, OPSZ_4b, false/*unsigned*/); |
| val |= (decode_immed(di, 8, OPSZ_1b, false/*unsigned*/) << 4); |
| } else |
| CLIENT_ASSERT(false, "unsupported 5-0 split immed size"); |
| array[(*counter)++] = opnd_create_immed_int(val, opsize); |
| return true; |
| } |
| case TYPE_I_b21_b5: { /* OP_vmov: 21,6:5 */ |
| if (opsize == OPSZ_3b) { |
| val = decode_immed(di, 5, OPSZ_2b, false/*unsigned*/); |
| val |= (decode_immed(di, 21, OPSZ_1b, false/*unsigned*/) << 2); |
| } else |
| CLIENT_ASSERT(false, "unsupported 5-0 split immed size"); |
| array[(*counter)++] = opnd_create_immed_int(val, opsize); |
| return true; |
| } |
| case TYPE_I_b21_b6: { /* OP_vmov: 21,6 */ |
| if (opsize == OPSZ_2b) { |
| val = decode_immed(di, 6, OPSZ_1b, false/*unsigned*/); |
| val |= (decode_immed(di, 21, OPSZ_1b, false/*unsigned*/) << 1); |
| } else |
| CLIENT_ASSERT(false, "unsupported 5-0 split immed size"); |
| array[(*counter)++] = opnd_create_immed_int(val, opsize); |
| return true; |
| } |
| case TYPE_I_b24_b16_b0: { /* OP_vbic, OP_vmov: 24,18:16,3:0 */ |
| if (opsize == OPSZ_1) { |
| val = decode_immed(di, 0, OPSZ_4b, false/*unsigned*/); |
| val |= (decode_immed(di, 16, OPSZ_3b, false/*unsigned*/) << 4); |
| val |= (decode_immed(di, 24, OPSZ_1b, false/*unsigned*/) << 7); |
| } else |
| CLIENT_ASSERT(false, "unsupported 5-0 split immed size"); |
| array[(*counter)++] = opnd_create_immed_int(val, opsize); |
| return true; |
| } |
| case TYPE_SHIFT_b5: |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 5, opsize, false/*unsigned*/), |
| opsize); |
| return true; |
| case TYPE_SHIFT_b6: /* value is :0 */ |
| array[(*counter)++] = |
| opnd_create_immed_int(decode_immed(di, 5, opsize, false/*unsigned*/) << 1, |
| opsize); |
| return true; |
| case TYPE_SHIFT_LSL: |
| array[(*counter)++] = opnd_create_immed_int(SHIFT_ENCODING_LSL, opsize); |
| return true; |
| case TYPE_SHIFT_ASR: |
| array[(*counter)++] = opnd_create_immed_int(SHIFT_ENCODING_ASR, opsize); |
| return true; |
| case TYPE_K: |
| array[(*counter)++] = opnd_create_immed_int(opsize, OPSZ_0); |
| return true; |
| |
| /* Memory */ |
| case TYPE_M: |
| if (opsize == OPSZ_VAR_REGLIST) { |
| opsize = opnd_size_from_bytes(di->reglist_sz); |
| } |
| array[(*counter)++] = |
| opnd_create_base_disp(decode_regA(di), REG_NULL, 0, 0, opsize); |
| return true; |
| case TYPE_M_POS_I12: |
| array[(*counter)++] = |
| opnd_create_base_disp(decode_regA(di), REG_NULL, 0, |
| decode_immed(di, 0, OPSZ_12b, false/*unsigned*/), |
| opsize); |
| return true; |
| case TYPE_M_NEG_I12: |
| array[(*counter)++] = |
| opnd_create_base_disp(decode_regA(di), REG_NULL, 0, |
| -decode_immed(di, 0, OPSZ_12b, false/*unsigned*/), |
| opsize); |
| return true; |
| case TYPE_M_POS_SHREG: |
| case TYPE_M_NEG_SHREG: { |
| uint amount; |
| dr_shift_type_t shift = decode_index_shift(di, &amount); |
| array[*counter] = |
| opnd_create_base_disp_arm(decode_regA(di), decode_regD(di), shift, amount, |
| 0, optype == TYPE_M_NEG_SHREG ? |
| DR_OPND_NEGATED : 0, opsize); |
| (*counter)++; |
| return true; |
| } |
| |
| /* FIXME i#1551: add decoding of the other operand type */ |
| default: |
| array[(*counter)++] = opnd_create_null(); |
| /* ok to assert, types coming only from instr_info_t */ |
| SYSLOG_INTERNAL_ERROR("unknown operand type %s\n", type_names[optype]); |
| CLIENT_ASSERT(false, "decode error: unknown operand type"); |
| } |
| return false; |
| } |
| |
| /* Disassembles the instruction at pc into the data structures ret_info |
| * and di. Returns a pointer to the pc of the next instruction. |
| * Returns NULL on an invalid instruction. |
| * Caller should set di->isa_mode. |
| */ |
| static byte * |
| read_instruction(byte *pc, byte *orig_pc, |
| const instr_info_t **ret_info, decode_info_t *di |
| _IF_DEBUG(bool report_invalid)) |
| { |
| uint instr_word; |
| const instr_info_t *info; |
| uint idx; |
| |
| /* Read instr bytes and initialize di */ |
| di->start_pc = pc; |
| di->orig_pc = orig_pc; |
| instr_word = *(uint *)pc; |
| pc += sizeof(instr_word); |
| di->instr_word = instr_word; |
| di->reglist_sz = 0; |
| |
| di->predicate = decode_predicate(instr_word) + DR_PRED_EQ; |
| if (di->predicate == DR_PRED_OP) { |
| uint opc7 = /* remove bit 22 */ |
| ((instr_word >> 21) & 0x7c) | ((instr_word >> 20) & 0x3); |
| info = &A32_unpred_opc7[opc7]; |
| } else { |
| uint opc8 = decode_opc8(instr_word); |
| info = &A32_pred_opc8[opc8]; |
| } |
| |
| /* If an extension, discard the old info and get a new one */ |
| while (info->type > INVALID) { |
| if (info->type == EXT_OPC4X) { |
| if ((instr_word & 0x10 /*bit 4*/) == 0) |
| idx = 0; |
| else if ((instr_word & 0x80 /*bit 7*/) == 0) |
| idx = 1; |
| else |
| idx = 2 + ((instr_word >> 5) & 0x3) /*bits 6:5*/; |
| info = &A32_ext_opc4x[info->code][idx]; |
| } else if (info->type == EXT_OPC4Y) { |
| if ((instr_word & 0x10 /*bit 4*/) == 0) |
| idx = 0; |
| else |
| idx = 1 + ((instr_word >> 5) & 0x7) /*bits 7:5*/; |
| info = &A32_ext_opc4y[info->code][idx]; |
| } else if (info->type == EXT_OPC4) { |
| idx = decode_opc4(instr_word); |
| info = &A32_ext_opc4[info->code][idx]; |
| } else if (info->type == EXT_IMM1916) { |
| idx = (((instr_word >> 16) & 0xf) /*bits 19:16*/ == 0) ? 0 : 1; |
| info = &A32_ext_imm1916[info->code][idx]; |
| } else if (info->type == EXT_BIT4) { |
| idx = (instr_word >> 4) & 0x1 /*bit 4*/; |
| info = &A32_ext_bit4[info->code][idx]; |
| } else if (info->type == EXT_BIT5) { |
| idx = (instr_word >> 5) & 0x1 /*bit 5*/; |
| info = &A32_ext_bit5[info->code][idx]; |
| } else if (info->type == EXT_BIT9) { |
| idx = (instr_word >> 9) & 0x1 /*bit 9*/; |
| info = &A32_ext_bit9[info->code][idx]; |
| } else if (info->type == EXT_BITS8) { |
| idx = (instr_word >> 8) & 0x3 /*bits 9:8*/; |
| info = &A32_ext_bits8[info->code][idx]; |
| } else if (info->type == EXT_BITS0) { |
| idx = instr_word & 0x7 /*bits 2:0*/; |
| info = &A32_ext_bits0[info->code][idx]; |
| } else if (info->type == EXT_IMM5) { |
| idx = (((instr_word >> 7) & 0x1f) /*bits 11:7*/ == 0) ? 0 : 1; |
| info = &A32_ext_imm5[info->code][idx]; |
| } else if (info->type == EXT_FP) { |
| idx = ((instr_word >> 8) & 0xf) /*bits 11:8*/; |
| idx = (idx == 0xa ? 0 : (idx == 0xb ? 1 : 2)); |
| info = &A32_ext_fp[info->code][idx]; |
| } else if (info->type == EXT_FPA) { |
| idx = ((instr_word >> 4) & 0x7) /*bits 6:4*/; |
| idx = (idx == 0 ? 0 : (idx == 1 ? 1 : (idx == 4 ? 2 : 3))); |
| if (idx == 3) |
| info = &invalid_instr; |
| else |
| info = &A32_ext_opc4fpA[info->code][idx]; |
| } else if (info->type == EXT_FPB) { |
| idx = ((instr_word >> 4) & 0x7) /*bits 6:4*/; |
| info = &A32_ext_opc4fpB[info->code][idx]; |
| } else if (info->type == EXT_BITS16) { |
| idx = ((instr_word >> 16) & 0xf) /*bits 19:16*/; |
| info = &A32_ext_bits16[info->code][idx]; |
| } else if (info->type == EXT_RBPC) { |
| idx = (((instr_word >> 12) & 0xf) /*bits 19:16*/ != 0xf) ? 0 : 1; |
| info = &A32_ext_RBPC[info->code][idx]; |
| } else if (info->type == EXT_RDPC) { |
| idx = ((instr_word & 0xf) /*bits 3:0*/ == 0xf) ? 1 : 0; |
| info = &A32_ext_RDPC[info->code][idx]; |
| } else if (info->type == EXT_BIT6) { |
| idx = ((instr_word >> 6) & 0x1) /*bit 6 */; |
| info = &A32_ext_bit6[info->code][idx]; |
| } else if (info->type == EXT_BIT7) { |
| idx = ((instr_word >> 7) & 0x1) /*bit 7 */; |
| info = &A32_ext_bit7[info->code][idx]; |
| } else if (info->type == EXT_BIT19) { |
| idx = ((instr_word >> 19) & 0x1) /*bit 19 */; |
| info = &A32_ext_bit19[info->code][idx]; |
| } else if (info->type == EXT_BIT22) { |
| idx = ((instr_word >> 22) & 0x1) /*bit 22 */; |
| info = &A32_ext_bit22[info->code][idx]; |
| } else if (info->type == EXT_BITS20) { |
| idx = ((instr_word >> 20) & 0xf) /*bits 23:20 */; |
| info = &A32_ext_bits20[info->code][idx]; |
| } else if (info->type == EXT_IMM1816) { |
| idx = (((instr_word >> 16) & 0x7) /*bits 18:16*/ == 0) ? 0 : 1; |
| info = &A32_ext_imm1816[info->code][idx]; |
| } else if (info->type == EXT_IMM2016) { |
| idx = (((instr_word >> 16) & 0x1f) /*bits 20:16*/ == 0) ? 0 : 1; |
| info = &A32_ext_imm2016[info->code][idx]; |
| } else if (info->type == EXT_SIMD6) { |
| idx = /*6 bits 11:8,6,4 */ |
| ((instr_word >> 6) & 0x3c) | |
| ((instr_word >> 5) & 0x2) | |
| ((instr_word >> 4) & 0x1); |
| info = &A32_ext_simd6[info->code][idx]; |
| } else if (info->type == EXT_SIMD5) { |
| idx = /*5 bits 11:8,5 */ |
| ((instr_word >> 7) & 0x1e) | ((instr_word >> 5) & 0x1); |
| info = &A32_ext_simd5[info->code][idx]; |
| } else if (info->type == EXT_SIMD5B) { |
| idx = /*bits 18:16,8:7 */ |
| ((instr_word >> 14) & 0x1c) | ((instr_word >> 7) & 0x3); |
| info = &A32_ext_simd5b[info->code][idx]; |
| } else if (info->type == EXT_SIMD8) { |
| /* Odds + 0 == 9 entries each */ |
| idx = 9 * ((instr_word >> 8) & 0xf) /*bits 11:8*/; |
| if (((instr_word >> 4) & 0x1) != 0) |
| idx += 1 + ((instr_word >> 5) & 0x7) /*bits 7:5*/; |
| info = &A32_ext_simd8[info->code][idx]; |
| } else if (info->type == EXT_SIMD6B) { |
| idx = /*bits 11:8,7:6 */ |
| ((instr_word >> 6) & 0x3c) | ((instr_word >> 6) & 0x3); |
| info = &A32_ext_simd6b[info->code][idx]; |
| } else if (info->type == EXT_SIMD6C) { |
| /* bits 10:8,7:6 + extra set of 7:6 for bit 11 being set */ |
| if (((instr_word >> 11) & 0x1) != 0) |
| idx = 32 + ((instr_word >> 6) & 0x3); |
| else |
| idx = ((instr_word >> 6) & 0x1c) | ((instr_word >> 6) & 0x3); |
| info = &A32_ext_simd6c[info->code][idx]; |
| } else if (info->type == EXT_SIMD2) { |
| idx = /*11,6 */ |
| ((instr_word >> 10) & 0x2) | ((instr_word >> 6) & 0x1); |
| info = &A32_ext_simd2[info->code][idx]; |
| } else if (info->type == EXT_VLDA) { |
| int reg = (instr_word & 0xf); |
| idx = /*bits (11:8,7:6)*3+X where X based on value of 3:0 */ |
| 3 * (((instr_word >> 6) & 0x3c) | ((instr_word >> 6) & 0x3)); |
| idx += (reg == 0xd ? 0 : (reg == 0xf ? 1 : 2)); |
| /* this table stops at 0xa in top bits, to save space */ |
| if (((instr_word >> 8) & 0xf) > 0xa) |
| info = &invalid_instr; |
| else |
| info = &A32_ext_vldA[info->code][idx]; |
| } else if (info->type == EXT_VLDB) { |
| int reg = (instr_word & 0xf); |
| idx = /*bits (11:8,Y)*3+X where X based on value of 3:0 */ |
| 3 * ((instr_word >> 7) & 0x1e); |
| /* Y is bit 6 if bit 11 is set; else, bit 5 */ |
| if (((instr_word >> 11) & 0x1) != 0) |
| idx |= ((instr_word >> 6) & 0x1); |
| else |
| idx |= ((instr_word >> 5) & 0x1); |
| idx += (reg == 0xd ? 0 : (reg == 0xf ? 1 : 2)); |
| info = &A32_ext_vldB[info->code][idx]; |
| } else if (info->type == EXT_VLDC) { |
| int reg = (instr_word & 0xf); |
| idx = /*bits (9:8,7:5)*3+X where X based on value of 3:0 */ |
| 3 * (((instr_word >> 5) & 0x18) | ((instr_word >> 5) & 0x7)); |
| idx += (reg == 0xd ? 0 : (reg == 0xf ? 1 : 2)); |
| info = &A32_ext_vldC[info->code][idx]; |
| } |
| } |
| CLIENT_ASSERT(info->type <= INVALID, "decoding table error"); |
| |
| /* All required bits should be set */ |
| if ((instr_word & info->opcode) != info->opcode && info->type != INVALID) |
| info = &invalid_instr; |
| |
| /* We should now have either a valid OP_ opcode or an invalid opcode */ |
| if (info == &invalid_instr || info->type < OP_FIRST || info->type > OP_LAST) { |
| DODEBUG({ |
| /* PR 605161: don't report on DR addresses */ |
| if (report_invalid && !is_dynamo_address(di->start_pc)) { |
| SYSLOG_INTERNAL_WARNING_ONCE("Invalid opcode encountered"); |
| LOG(THREAD_GET, LOG_ALL, 1, "Invalid opcode @"PFX": 0x%016x\n", |
| di->start_pc, instr_word); |
| } |
| }); |
| *ret_info = &invalid_instr; |
| return NULL; |
| } |
| |
| /* Unlike x86, we have a fixed size, so we're done */ |
| *ret_info = info; |
| return pc; |
| } |
| |
| byte * |
| decode_eflags_usage(dcontext_t *dcontext, byte *pc, uint *usage, |
| dr_opnd_query_flags_t flags) |
| { |
| const instr_info_t *info; |
| decode_info_t di; |
| di.isa_mode = dr_get_isa_mode(dcontext); |
| pc = read_instruction(pc, pc, &info, &di _IF_DEBUG(true)); |
| *usage = instr_eflags_conditionally(info->eflags, di.predicate, flags); |
| /* we're fine returning NULL on failure */ |
| return pc; |
| } |
| |
| byte * |
| decode_opcode(dcontext_t *dcontext, byte *pc, instr_t *instr) |
| { |
| const instr_info_t *info; |
| decode_info_t di; |
| di.isa_mode = dr_get_isa_mode(dcontext); |
| pc = read_instruction(pc, pc, &info, &di _IF_DEBUG(true)); |
| instr_set_isa_mode(instr, di.isa_mode); |
| instr_set_opcode(instr, info->type); |
| if (!instr_valid(instr)) { |
| CLIENT_ASSERT(!instr_valid(instr), "decode_opcode: invalid instr"); |
| return NULL; |
| } |
| instr->eflags = info->eflags; |
| instr_set_eflags_valid(instr, true); |
| instr_set_operands_valid(instr, false); |
| instr_set_raw_bits(instr, pc, pc - di.orig_pc); |
| return pc; |
| } |
| |
| /* XXX: some of this code could be shared with x86/decode.c */ |
| static byte * |
| decode_common(dcontext_t *dcontext, byte *pc, byte *orig_pc, instr_t *instr) |
| { |
| const instr_info_t *info; |
| decode_info_t di; |
| byte *next_pc; |
| uint num_dsts = 0, num_srcs = 0; |
| opnd_t dsts[MAX_OPNDS]; |
| opnd_t srcs[MAX_OPNDS]; |
| |
| CLIENT_ASSERT(instr->opcode == OP_INVALID || instr->opcode == OP_UNDECODED, |
| "decode: instr is already decoded, may need to call instr_reset()"); |
| |
| di.isa_mode = dr_get_isa_mode(dcontext); |
| next_pc = read_instruction(pc, orig_pc, &info, &di |
| _IF_DEBUG(!TEST(INSTR_IGNORE_INVALID, instr->flags))); |
| instr_set_isa_mode(instr, di.isa_mode); |
| instr_set_opcode(instr, info->type); |
| /* failure up to this point handled fine -- we set opcode to OP_INVALID */ |
| if (next_pc == NULL) { |
| LOG(THREAD, LOG_INTERP, 3, "decode: invalid instr at "PFX"\n", pc); |
| CLIENT_ASSERT(!instr_valid(instr), "decode: invalid instr"); |
| return NULL; |
| } |
| instr->eflags = info->eflags; |
| instr_set_eflags_valid(instr, true); |
| /* since we don't use set_src/set_dst we must explicitly say they're valid */ |
| instr_set_operands_valid(instr, true); |
| |
| if (di.predicate != DR_PRED_OP) { |
| /* XXX: not bothering to mark invalid for DECODE_PREDICATE_AL */ |
| instr_set_predicate(instr, di.predicate); |
| } |
| |
| /* operands */ |
| do { |
| if (info->dst1_type != TYPE_NONE) { |
| if (!decode_operand(&di, info->dst1_type, info->dst1_size, dsts, &num_dsts)) |
| goto decode_invalid; |
| } |
| if (info->dst2_type != TYPE_NONE) { |
| if (!decode_operand(&di, info->dst2_type, info->dst2_size, |
| TEST(DECODE_4_SRCS, info->flags) ? srcs : dsts, |
| TEST(DECODE_4_SRCS, info->flags) ? &num_srcs : &num_dsts)) |
| goto decode_invalid; |
| } |
| if (info->src1_type != TYPE_NONE) { |
| if (!decode_operand(&di, info->src1_type, info->src1_size, |
| TEST(DECODE_3_DSTS, info->flags) ? dsts : srcs, |
| TEST(DECODE_3_DSTS, info->flags) ? &num_dsts : &num_srcs)) |
| goto decode_invalid; |
| } |
| if (info->src2_type != TYPE_NONE) { |
| if (!decode_operand(&di, info->src2_type, info->src2_size, srcs, &num_srcs)) |
| goto decode_invalid; |
| } |
| if (info->src3_type != TYPE_NONE) { |
| if (!decode_operand(&di, info->src3_type, info->src3_size, srcs, &num_srcs)) |
| goto decode_invalid; |
| } |
| info = instr_info_extra_opnds(info); |
| } while (info != NULL); |
| |
| CLIENT_ASSERT(num_srcs < sizeof(srcs)/sizeof(srcs[0]), "internal decode error"); |
| CLIENT_ASSERT(num_dsts < sizeof(dsts)/sizeof(dsts[0]), "internal decode error"); |
| |
| /* now copy operands into their real slots */ |
| instr_set_num_opnds(dcontext, instr, num_dsts, num_srcs); |
| if (num_dsts > 0) { |
| memcpy(instr->dsts, dsts, num_dsts*sizeof(opnd_t)); |
| } |
| if (num_srcs > 0) { |
| instr->src0 = srcs[0]; |
| if (num_srcs > 1) { |
| memcpy(instr->srcs, &(srcs[1]), (num_srcs-1)*sizeof(opnd_t)); |
| } |
| } |
| |
| if (orig_pc != pc) { |
| /* We do not want to copy when encoding and condone an invalid |
| * relative target |
| */ |
| instr_set_raw_bits_valid(instr, false); |
| instr_set_translation(instr, orig_pc); |
| } else { |
| /* we set raw bits AFTER setting all srcs and dsts b/c setting |
| * a src or dst marks instr as having invalid raw bits |
| */ |
| IF_X64(ASSERT(CHECK_TRUNCATE_TYPE_uint(next_pc - pc))); |
| instr_set_raw_bits(instr, pc, (uint)(next_pc - pc)); |
| } |
| |
| return next_pc; |
| |
| decode_invalid: |
| instr_set_operands_valid(instr, false); |
| instr_set_opcode(instr, OP_INVALID); |
| return NULL; |
| } |
| |
| byte * |
| decode(dcontext_t *dcontext, byte *pc, instr_t *instr) |
| { |
| return decode_common(dcontext, pc, pc, instr); |
| } |
| |
| byte * |
| decode_from_copy(dcontext_t *dcontext, byte *copy_pc, byte *orig_pc, instr_t *instr) |
| { |
| return decode_common(dcontext, copy_pc, orig_pc, instr); |
| } |
| |
| byte * |
| decode_cti(dcontext_t *dcontext, byte *pc, instr_t *instr) |
| { |
| /* XXX i#1551: build a fast decoder for branches -- though it may not make |
| * sense for 32-bit where many instrs can write to the pc. |
| */ |
| return decode(dcontext, pc, instr); |
| } |
| |
| byte * |
| decode_next_pc(dcontext_t *dcontext, byte *pc) |
| { |
| /* FIXME i#1551: check for invalid opcodes */ |
| /* FIXME i#1551: add Thumb support */ |
| return pc + 4; |
| } |
| |
| int |
| decode_sizeof(dcontext_t *dcontext, byte *pc, int *num_prefixes |
| _IF_X64(uint *rip_rel_pos)) |
| { |
| /* FIXME i#1551: check for invalid opcodes */ |
| /* FIXME i#1551: add Thumb support */ |
| return 4; |
| } |
| |
| /* XXX: share this with x86 */ |
| byte * |
| decode_raw(dcontext_t *dcontext, byte *pc, instr_t *instr) |
| { |
| /* XXX i#1551: set isa_mode of instr once we add that feature */ |
| int sz = decode_sizeof(dcontext, pc, NULL _IF_X64(NULL)); |
| if (sz == 0) { |
| /* invalid instruction! */ |
| instr_set_opcode(instr, OP_INVALID); |
| return NULL; |
| } |
| instr_set_opcode(instr, OP_UNDECODED); |
| instr_set_raw_bits(instr, pc, sz); |
| /* assumption: operands are already marked invalid (instr was reset) */ |
| return (pc + sz); |
| } |
| |
| const instr_info_t * |
| instr_info_extra_opnds(const instr_info_t *info) |
| { |
| /* XXX i#1551: pick proper *_extra_operands table */ |
| if (TEST(DECODE_EXTRA_SHIFT, info->flags)) |
| return &A32_extra_operands[0]; |
| else if (TEST(DECODE_EXTRA_WRITEBACK, info->flags)) |
| return &A32_extra_operands[1]; |
| else if (TEST(DECODE_EXTRA_WRITEBACK2, info->flags)) |
| return &A32_extra_operands[2]; |
| else if (TEST(DECODE_EXTRA_OPERANDS, info->flags)) |
| return (const instr_info_t *)(info->code); |
| else |
| return NULL; |
| } |
| |
| /* num is 0-based */ |
| byte |
| instr_info_opnd_type(const instr_info_t *info, bool src, int num) |
| { |
| int i = 0; |
| while (info != NULL) { |
| if (!src && i++ == num) |
| return info->dst1_type; |
| if (TEST(DECODE_4_SRCS, info->flags)) { |
| if (src && i++ == num) |
| return info->dst2_type; |
| } else { |
| if (!src && i++ == num) |
| return info->dst2_type; |
| } |
| if (TEST(DECODE_3_DSTS, info->flags)) { |
| if (!src && i++ == num) |
| return info->src1_type; |
| } else { |
| if (src && i++ == num) |
| return info->src1_type; |
| } |
| if (src && i++ == num) |
| return info->src2_type; |
| if (src && i++ == num) |
| return info->src3_type; |
| info = instr_info_extra_opnds(info); |
| } |
| CLIENT_ASSERT(false, "internal decode error"); |
| return TYPE_NONE; |
| } |
| |
| const instr_info_t * |
| get_next_instr_info(const instr_info_t * info) |
| { |
| return (const instr_info_t *)(info->code); |
| } |
| |
| byte |
| decode_first_opcode_byte(int opcode) |
| { |
| CLIENT_ASSERT(false, "should not be used on ARM"); |
| return 0; |
| } |
| |
| const instr_info_t * |
| opcode_to_encoding_info(uint opc, dr_isa_mode_t isa_mode) |
| { |
| if (isa_mode == DR_ISA_ARM_A32) |
| return op_instr_A32[opc]; |
| CLIENT_ASSERT(false, "NYI i#1551"); |
| return &invalid_instr; |
| } |
| |
| DR_API |
| const char * |
| decode_opcode_name(int opcode) |
| { |
| const instr_info_t * info = |
| opcode_to_encoding_info(opcode, dr_get_isa_mode(get_thread_private_dcontext())); |
| if (info != NULL) |
| return info->name; |
| else |
| return "<unknown>"; |
| } |
| |
| opnd_size_t |
| resolve_variable_size(decode_info_t *di, opnd_size_t sz, bool is_reg) |
| { |
| return sz; |
| } |
| |
| bool |
| optype_is_indir_reg(int optype) |
| { |
| return false; |
| } |
| |
| |
| #ifdef DECODE_UNIT_TEST |
| /* FIXME i#1551: add unit tests here. How divide vs suite/tests/api/ tests? */ |
| # include "instr_create.h" |
| |
| int main() |
| { |
| bool res = true; |
| standalone_init(); |
| return res; |
| } |
| |
| #endif /* DECODE_UNIT_TEST */ |