| /* |
| * Copyright (c) 2013-2016, Intel Corporation |
| * |
| * 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 Intel Corporation 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 THE COPYRIGHT OWNER 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 "pti-defs.h" |
| #include "pti-ild.h" |
| #include "pti-enums.h" |
| |
| #include <assert.h> |
| #include <stdlib.h> |
| |
| PTI_INLINE PTI_NORETURN void |
| pti_abort () |
| { |
| /* this would be be a programming error. */ |
| assert (0); |
| |
| /* make sure to exit also for release builds. */ |
| exit(1); |
| } |
| |
| /* SET UP 3 TABLES */ |
| |
| static pti_uint8_t has_disp_regular[3][4][8]; |
| |
| static void |
| init_has_disp_regular_table (void) |
| { |
| pti_uint8_t eamode; |
| pti_uint8_t rm; |
| pti_uint8_t mod; |
| |
| /* zero whole table */ |
| for (eamode = 0; eamode < 3; eamode++) |
| for (mod = 0; mod < 4; mod++) |
| for (rm = 0; rm < 8; rm++) |
| has_disp_regular[eamode][mod][rm] = 0; |
| |
| /*fill the eamode16 */ |
| has_disp_regular[0][0][6] = 2; |
| for (rm = 0; rm < 8; rm++) |
| { |
| for (mod = 1; mod <= 2; mod++) |
| has_disp_regular[0][mod][rm] = mod; |
| } |
| |
| /*fill eamode32/64 */ |
| for (eamode = 1; eamode <= 2; eamode++) |
| { |
| for (rm = 0; rm < 8; rm++) |
| { |
| has_disp_regular[eamode][1][rm] = 1; |
| has_disp_regular[eamode][2][rm] = 4; |
| }; |
| has_disp_regular[eamode][0][5] = 4; |
| } |
| } |
| |
| |
| |
| static pti_uint8_t eamode_table[2][PTI_MODE_LAST]; |
| |
| static void |
| init_eamode_table (void) |
| { |
| pti_uint8_t mode; |
| pti_uint8_t asz; |
| |
| /* zero out the table. */ |
| for (asz = 0; asz < 2; asz++) |
| for (mode = 0; mode < PTI_MODE_LAST; mode++) |
| eamode_table[asz][mode] = 0; |
| |
| for (mode = PTI_MODE_16; mode <= PTI_MODE_64; mode++) |
| eamode_table[0][mode] = mode; |
| |
| eamode_table[1][PTI_MODE_16] = PTI_MODE_32; |
| eamode_table[1][PTI_MODE_32] = PTI_MODE_16; |
| eamode_table[1][PTI_MODE_64] = PTI_MODE_32; |
| } |
| |
| static pti_uint8_t has_sib_table[3][4][8]; |
| |
| static void |
| init_has_sib_table (void) |
| { |
| pti_uint8_t eamode; |
| pti_uint8_t mod; |
| pti_uint8_t rm; |
| |
| /* zero whole table */ |
| for (eamode = 0; eamode < 3; eamode++) |
| for (mod = 0; mod < 4; mod++) |
| for (rm = 0; rm < 8; rm++) |
| has_sib_table[eamode][mod][rm] = 0; |
| |
| /*for eamode32/64 there is sib byte for mod!=3 and rm==4 */ |
| for (eamode = 1; eamode <= 2; eamode++) |
| { |
| for (mod = 0; mod <= 2; mod++) |
| { |
| has_sib_table[eamode][mod][4] = 1; |
| } |
| } |
| } |
| |
| /* SOME ACCESSORS */ |
| |
| PTI_INLINE pti_uint8_t |
| get_byte (pti_ild_t * ild, pti_uint_t i) |
| { |
| return ild->itext[i]; |
| } |
| |
| PTI_INLINE pti_uint8_t const * |
| get_byte_ptr (pti_ild_t * ild, pti_uint_t i) |
| { |
| return ild->itext + i; |
| } |
| |
| PTI_INLINE pti_bool_t |
| mode_64b (pti_ild_t * ild) |
| { |
| return ild->mode == PTI_MODE_64; |
| } |
| |
| PTI_INLINE pti_bool_t |
| mode_32b (pti_ild_t * ild) |
| { |
| return ild->mode == PTI_MODE_32; |
| } |
| |
| PTI_INLINE pti_bool_t |
| bits_match (pti_uint8_t x, pti_uint8_t mask, pti_uint8_t target) |
| { |
| return (x & mask) == target; |
| } |
| |
| PTI_INLINE void |
| set_error (pti_ild_t * ild) |
| { |
| ild->u.s.error = 1; |
| } |
| |
| /* accessors for REX.R/VEX R */ |
| PTI_INLINE pti_uint_t |
| pti_get_rex_vex_r (pti_ild_t * ild) |
| { |
| if (ild->u.s.vexc5) |
| return (ild->c5byte1 >> 7) & 1; |
| else if (ild->u.s.vexc4) |
| return (ild->c4byte1 >> 7) & 1; |
| else if (ild->rex) |
| return (ild->rex >> 2) & 1; |
| return 0; |
| } |
| |
| PTI_INLINE pti_uint_t |
| pti_get_rex_vex_w (pti_ild_t * ild) |
| { |
| if (ild->u.s.vexc5) |
| return 0; |
| else if (ild->u.s.vexc4) |
| return (ild->c4byte2 >> 7) & 1; |
| else if (ild->rex) |
| return (ild->rex >> 3) & 1; |
| return 0; |
| } |
| |
| PTI_INLINE |
| pti_machine_mode_enum_t pti_get_nominal_eosz_non64 (pti_ild_t * ild) |
| { |
| if (mode_32b (ild)) |
| { |
| if (ild->u.s.osz) |
| return PTI_MODE_16; |
| return PTI_MODE_32; |
| } |
| if (ild->u.s.osz) |
| return PTI_MODE_32; |
| return PTI_MODE_16; |
| } |
| |
| PTI_INLINE pti_machine_mode_enum_t |
| pti_get_nominal_eosz (pti_ild_t * ild) |
| { |
| if (mode_64b (ild)) |
| { |
| if (pti_get_rex_vex_w (ild)) |
| return PTI_MODE_64; |
| if (ild->u.s.osz) |
| return PTI_MODE_16; |
| return PTI_MODE_32; |
| } |
| return pti_get_nominal_eosz_non64 (ild); |
| } |
| |
| PTI_INLINE pti_machine_mode_enum_t |
| pti_get_nominal_eosz_df64 (pti_ild_t * ild) |
| { |
| if (mode_64b (ild)) |
| { |
| if (pti_get_rex_vex_w (ild)) |
| return PTI_MODE_64; |
| if (ild->u.s.osz) |
| return PTI_MODE_16; |
| /* only this next line of code is different relative |
| to pti_get_nominal_eosz(), above */ |
| return PTI_MODE_64; |
| } |
| return pti_get_nominal_eosz_non64 (ild); |
| } |
| |
| PTI_INLINE |
| pti_machine_mode_enum_t pti_get_nominal_easz_non64 (pti_ild_t * ild) |
| { |
| if (mode_32b (ild)) |
| { |
| if (ild->u.s.asz) |
| return PTI_MODE_16; |
| return PTI_MODE_32; |
| } |
| if (ild->u.s.asz) |
| return PTI_MODE_32; |
| return PTI_MODE_16; |
| } |
| |
| PTI_INLINE pti_machine_mode_enum_t |
| pti_get_nominal_easz (pti_ild_t * ild) |
| { |
| if (mode_64b (ild)) |
| { |
| if (ild->u.s.asz) |
| return PTI_MODE_32; |
| return PTI_MODE_64; |
| } |
| return pti_get_nominal_easz_non64 (ild); |
| } |
| |
| PTI_INLINE pti_uint_t |
| resolve_z (pti_machine_mode_enum_t eosz) |
| { |
| static const pti_uint_t bytes[] = { 2, 4, 4 }; |
| if (eosz < PTI_MODE_LAST) |
| return bytes[eosz]; |
| pti_abort (); |
| } |
| |
| PTI_INLINE pti_uint_t |
| resolve_v (pti_machine_mode_enum_t eosz) |
| { |
| static const pti_uint_t bytes[] = { 2, 4, 8 }; |
| if (eosz < PTI_MODE_LAST) |
| return bytes[eosz]; |
| pti_abort (); |
| } |
| |
| /* DECODERS */ |
| |
| static void |
| prefix_rex_dec (pti_ild_t * ild) |
| { |
| pti_uint_t max_bytes = ild->max_bytes; |
| pti_uint_t length = 0; |
| pti_uint_t rex = 0; |
| pti_uint_t nprefixes = 0; |
| |
| while (length < max_bytes) |
| { |
| pti_uint8_t b = get_byte (ild, length); |
| switch (b) |
| { |
| case 0x66: |
| ild->u.s.osz = 1; |
| /*ignore possible REX prefix encoutered earlier */ |
| rex = 0; |
| break; |
| |
| case 0x67: |
| ild->u.s.asz = 1; |
| rex = 0; |
| break; |
| |
| /* segment prefixes */ |
| case 0x2E: |
| case 0x3E: |
| case 0x26: |
| case 0x36: |
| if (mode_64b (ild) == 0) |
| ild->seg = b; |
| /* ignore possible REX prefix encountered earlier */ |
| rex = 0; |
| |
| break; |
| case 0x64: |
| case 0x65: |
| /* for 64b mode we are ignoring non valid segment prefixes |
| only FS=0x64 and GS=0x64 are valid for 64b mode */ |
| ild->seg = b; |
| /* ignore possible REX prefix encountered earlier */ |
| rex = 0; |
| break; |
| |
| case 0xF0: |
| ild->u.s.lock = 1; |
| rex = 0; |
| break; |
| |
| case 0xF3: |
| ild->u.s.f3 = 1; |
| ild->u.s.last_f2f3 = 3; |
| rex = 0; |
| break; |
| |
| case 0xF2: |
| ild->u.s.f2 = 1; |
| ild->u.s.last_f2f3 = 2; |
| rex = 0; |
| break; |
| |
| default: |
| /*Take care of REX prefix */ |
| if (mode_64b (ild) && (b & 0xf0) == 0x40) |
| { |
| rex = b; |
| } |
| else |
| goto out; |
| } |
| length++; |
| nprefixes++; |
| } |
| out: |
| ild->length = length; |
| ild->nprefixes = (pti_uint8_t) nprefixes; |
| ild->rex = (pti_uint8_t) rex; |
| if (length >= max_bytes) |
| { |
| /* all available length was taken by prefixes, but we for sure need at |
| * least one additional byte for an opcode, hence we are out of bytes. |
| */ |
| set_error (ild); |
| return; |
| } |
| } |
| |
| static void |
| vex_opcode_dec (pti_ild_t * ild) |
| { |
| pti_uint_t length = ild->length; |
| ild->nominal_opcode = get_byte (ild, length); |
| ild->nominal_opcode_pos = (pti_uint8_t) length; /*FIXME: needed? */ |
| ild->length = length + 1; |
| } |
| |
| static void |
| vex_c5_dec (pti_ild_t * ild) |
| { |
| pti_uint_t max_bytes = ild->max_bytes; |
| pti_uint_t length = ild->length; |
| if (mode_64b (ild)) |
| { |
| ild->u.s.vexc5 = 1; |
| length++; /* eat the c5 */ |
| } |
| else if (length + 1 < max_bytes) |
| { /* non64b mode */ |
| pti_uint8_t n = get_byte (ild, length + 1); |
| if (bits_match (n, 0xC0, 0xC0)) |
| { |
| ild->u.s.vexc5 = 1; |
| length++; /* eat the c5 */ |
| } |
| else |
| { |
| /* not c5 vex, keep going */ |
| return; |
| } |
| } |
| else |
| { |
| set_error (ild); |
| return; |
| } |
| |
| /* vex payload processing */ |
| |
| /* we want to make sure, that we have additional 2 bytes |
| * available for reading - for vex payload byte and opcode */ |
| if ((length + 2) <= max_bytes) |
| { |
| ild->c5byte1 = get_byte (ild, length); |
| pti_set_map (ild, PTI_MAP_1); |
| length++; /* eat the vex payload byte */ |
| ild->length = length; |
| vex_opcode_dec (ild); |
| } |
| else |
| { |
| set_error (ild); |
| } |
| } |
| |
| static void |
| vex_c4_dec (pti_ild_t * ild) |
| { |
| pti_uint_t max_bytes = ild->max_bytes; |
| pti_uint_t length = ild->length; |
| if (mode_64b (ild)) |
| { |
| ild->u.s.vexc4 = 1; |
| length++; /* eat the c4 */ |
| } |
| else if (length + 1 < max_bytes) |
| { /* non64b mode */ |
| pti_uint8_t n = get_byte (ild, length + 1); |
| if (bits_match (n, 0xC0, 0xC0)) |
| { |
| ild->u.s.vexc4 = 1; |
| length++; /* eat the c4 */ |
| } |
| else |
| { |
| /* not c4 vex, keep going */ |
| return; |
| } |
| } |
| else |
| { |
| set_error (ild); |
| return; |
| } |
| |
| /* vex payload processing */ |
| |
| /* we want to make sure, that we have additional 2 bytes |
| * available for reading - for vex payload byte and opcode */ |
| if ((length + 3) <= max_bytes) |
| { |
| ild->c4byte1 = get_byte (ild, length); |
| ild->c4byte2 = get_byte (ild, length + 1); |
| |
| pti_set_map (ild, (pti_map_enum_t) (ild->c4byte1 & 0x1F)); |
| if (pti_get_map (ild) == PTI_MAP_3) |
| ild->imm1_bytes = 1; |
| |
| length += 2; /* eat the 2byte vex payload */ |
| ild->length = length; |
| vex_opcode_dec (ild); |
| } |
| else |
| { |
| set_error (ild); |
| } |
| } |
| |
| static void |
| vex_dec (pti_ild_t * ild) |
| { |
| /* prefix scanner checked length for us so we know at least 1B is left. */ |
| pti_uint8_t b = get_byte (ild, ild->length); |
| if (b == 0xC5) |
| vex_c5_dec (ild); |
| else if (b == 0xC4) |
| vex_c4_dec (ild); |
| } |
| |
| |
| static void |
| get_next_as_opcode (pti_ild_t * ild) |
| { |
| pti_uint_t length = ild->length; |
| if (length < ild->max_bytes) |
| { |
| ild->nominal_opcode = get_byte (ild, length); |
| ild->nominal_opcode_pos = (pti_uint8_t) length; |
| ild->length = length + 1; |
| } |
| else |
| { |
| set_error (ild); |
| } |
| } |
| |
| |
| static void |
| opcode_dec (pti_ild_t * ild) |
| { |
| pti_uint_t length = ild->length; |
| pti_uint8_t b = get_byte (ild, length); |
| /*no need to check max_bytes - it was checked in previous scanners */ |
| |
| if (b != 0x0F) |
| { /* 1B opcodes, map 0 */ |
| pti_set_map (ild, PTI_MAP_0); |
| ild->nominal_opcode = b; |
| ild->nominal_opcode_pos = (pti_uint8_t) length; |
| ild->length = length + 1; |
| return; |
| } |
| |
| length++; /* eat the 0x0F */ |
| ild->nominal_opcode_pos = (pti_uint8_t) length; |
| |
| /* 0x0F opcodes MAPS 1,2,3 */ |
| if (length < ild->max_bytes) |
| { |
| pti_uint8_t m = get_byte (ild, length); |
| if (m == 0x38) |
| { |
| length++; /* eat the 0x38 */ |
| pti_set_map (ild, PTI_MAP_2); |
| ild->length = length; |
| get_next_as_opcode (ild); |
| return; |
| } |
| else if (m == 0x3A) |
| { |
| length++; /* eat the 0x3A */ |
| pti_set_map (ild, PTI_MAP_3); |
| ild->length = length; |
| ild->imm1_bytes = 1; |
| get_next_as_opcode (ild); |
| return; |
| } |
| else if (m == 0x3B) |
| { |
| length++; /* eat the 0x3B */ |
| pti_set_map (ild, PTI_MAP_INVALID); |
| ild->length = length; |
| get_next_as_opcode (ild); |
| return; |
| } |
| else if (m > 0x38 && m <= 0x3F) |
| { |
| length++; /* eat the 0x39...0x3F (minus 3A and 3B) */ |
| pti_set_map (ild, PTI_MAP_INVALID); |
| ild->length = length; |
| get_next_as_opcode (ild); |
| return; |
| } |
| else if (m == 0x0F) |
| { /* 3dNow */ |
| pti_set_map (ild, PTI_MAP_AMD3DNOW); |
| ild->imm1_bytes = 1; |
| /* real opcode is in immediate later on, but we need an |
| * opcode now. */ |
| ild->nominal_opcode = 0x0F; |
| ild->length = length + 1; /*eat the second 0F */ |
| } |
| else |
| { /* map 2 (simple two byte opcodes) */ |
| length++; /* eat the 2nd opcode byte */ |
| ild->nominal_opcode = m; |
| ild->nominal_opcode_pos = (pti_uint8_t) length; |
| pti_set_map (ild, PTI_MAP_1); |
| ild->length = length; |
| } |
| } |
| else |
| { |
| set_error (ild); |
| } |
| } |
| |
| |
| #include "pti-modrm.h" |
| #include "pti-disp-defs.h" |
| #include "pti-disp.h" |
| |
| |
| static void |
| modrm_dec (pti_ild_t * ild) |
| { |
| static pti_uint8_t const *const has_modrm_2d[2] = { |
| has_modrm_map_0x0, |
| has_modrm_map_0x0F |
| }; |
| pti_uint_t has_modrm = PTI_MODRM_FALSE; |
| |
| pti_map_enum_t map = pti_get_map (ild); |
| if (map >= PTI_MAP_2) |
| has_modrm = PTI_MODRM_TRUE; |
| else |
| has_modrm = has_modrm_2d[map][ild->nominal_opcode]; |
| if (has_modrm == PTI_MODRM_FALSE) |
| return; |
| if (has_modrm == PTI_MODRM_UNDEF) /*FIXME: what to do about these? */ |
| return; |
| ild->has_modrm = (pti_uint8_t) has_modrm; |
| if (ild->length >= ild->max_bytes) |
| { |
| /* really >= here because we have not eaten the byte yet */ |
| set_error (ild); |
| return; |
| } |
| ild->modrm_byte = get_byte (ild, ild->length); |
| ild->length++; /* eat modrm */ |
| if (has_modrm != PTI_MODRM_IGNORE_MOD) |
| { |
| /* set disp_bytes and sib using simple tables */ |
| |
| pti_uint8_t eamode = eamode_table[ild->u.s.asz][ild->mode]; |
| pti_uint8_t mod = (pti_uint8_t) pti_get_modrm_mod(ild); |
| pti_uint8_t rm = (pti_uint8_t) pti_get_modrm_rm(ild); |
| |
| ild->disp_bytes = has_disp_regular[eamode][mod][rm]; |
| ild->u.s.sib = has_sib_table[eamode][mod][rm]; |
| } |
| |
| } |
| |
| static void |
| sib_dec (pti_ild_t * ild) |
| { |
| if (ild->u.s.sib) |
| { |
| pti_uint_t length = ild->length; |
| if (length < ild->max_bytes) |
| { |
| ild->sib_byte = get_byte (ild, length); |
| ild->length = length + 1; |
| if (pti_get_sib_base (ild) == 5 && pti_get_modrm_mod (ild) == 0) |
| ild->disp_bytes = 4; |
| } |
| else |
| { |
| set_error (ild); |
| } |
| } |
| } |
| |
| static void |
| compute_disp_dec (pti_ild_t * ild) |
| { |
| /* set ild->disp_bytes for maps 0 and 1. */ |
| static pti_uint8_t const *const map_map[] = { |
| /* map 0 */ disp_bytes_map_0x0, |
| /* map 1 */ disp_bytes_map_0x0F, |
| /* map 2 */ 0, |
| /* map 3 */ 0, |
| /* amd3dnow */ 0, |
| /* invalid */ 0 |
| }; |
| |
| pti_uint8_t const *const disp_table = map_map[ild->map]; |
| pti_uint_t disp_kind; |
| |
| if (disp_table == 0) |
| return; |
| disp_kind = disp_table[ild->nominal_opcode]; |
| switch (disp_kind) |
| { |
| case PTI_DISP_NONE: |
| ild->disp_bytes = 0; |
| break; |
| case PTI_PRESERVE_DEFAULT: |
| /* nothing to do */ |
| break; |
| case PTI_BRDISP8: |
| ild->disp_bytes = 1; |
| break; |
| case PTI_DISP_BUCKET_0_l1: |
| /* BRDISPz(eosz) for 16/32 modes, and BRDISP32 for 64b mode */ |
| if (mode_64b (ild)) |
| ild->disp_bytes = 4; |
| else |
| { |
| pti_machine_mode_enum_t eosz = pti_get_nominal_eosz (ild); |
| ild->disp_bytes = (pti_uint8_t) resolve_z(eosz); |
| } |
| break; |
| case PTI_MEMDISPv_DISP_WIDTH_ASZ_NONTERM_EASZ_l2: |
| /* MEMDISPv(easz) */ |
| { |
| pti_machine_mode_enum_t easz = pti_get_nominal_easz (ild); |
| ild->disp_bytes = (pti_uint8_t) resolve_v(easz); |
| } |
| break; |
| case PTI_BRDISPz_BRDISP_WIDTH_OSZ_NONTERM_EOSZ_l2: |
| /* BRDISPz(eosz) for 16/32/64 modes */ |
| { |
| pti_machine_mode_enum_t eosz = pti_get_nominal_eosz (ild); |
| ild->disp_bytes = (pti_uint8_t) resolve_z(eosz); |
| } |
| break; |
| case PTI_RESOLVE_BYREG_DISP_map0x0_op0xc7_l1: |
| /* reg=0 -> preserve, reg=7 -> BRDISPz(eosz) */ |
| if (ild->map == PTI_MAP_0 && pti_get_modrm_reg (ild) == 7) |
| { |
| pti_machine_mode_enum_t eosz = pti_get_nominal_eosz (ild); |
| ild->disp_bytes = (pti_uint8_t) resolve_z(eosz); |
| } |
| break; |
| default: |
| pti_abort (); |
| } |
| } |
| |
| static void |
| disp_dec (pti_ild_t * ild) |
| { |
| pti_uint_t disp_bytes; |
| if (ild->disp_bytes == 0 && pti_get_map (ild) < PTI_MAP_2) |
| { |
| compute_disp_dec (ild); |
| } |
| disp_bytes = ild->disp_bytes; |
| if (disp_bytes == 0) |
| return; |
| if (ild->length + disp_bytes > ild->max_bytes) |
| { |
| set_error (ild); |
| return; |
| } |
| |
| /*Record only position; must be able to re-read itext bytes for actual |
| value. (SMC/CMC issue). */ |
| ild->disp_pos = (pti_uint8_t) ild->length; |
| ild->length += disp_bytes; |
| } |
| |
| |
| #include "pti-imm-defs.h" |
| #include "pti-imm.h" |
| |
| static void |
| set_imm_bytes (pti_ild_t * ild) |
| { |
| /*: set ild->imm1_bytes and ild->imm2_bytes for maps 0/1 */ |
| static pti_uint8_t const *const map_map[] = { |
| /* map 0 */ imm_bytes_map_0x0, |
| /* map 1 */ imm_bytes_map_0x0F, |
| /* map 2 */ 0, |
| /* map 3 */ 0, |
| /* amd3dnow */ 0, |
| /* invalid */ 0 |
| }; |
| pti_uint8_t const *const map_imm = map_map[ild->map]; |
| pti_uint_t imm_code; |
| |
| if (map_imm == 0) |
| return; |
| imm_code = map_imm[ild->nominal_opcode]; |
| switch (imm_code) |
| { |
| case PTI_IMM_NONE: |
| case PTI_0_IMM_WIDTH_CONST_l2: |
| /* nothing for either case */ |
| break; |
| case PTI_UIMM8_IMM_WIDTH_CONST_l2: |
| ild->imm1_bytes = 1; |
| break; |
| case PTI_SIMM8_IMM_WIDTH_CONST_l2: |
| ild->imm1_bytes = 1; |
| break; |
| |
| case PTI_SIMMz_IMM_WIDTH_OSZ_NONTERM_EOSZ_l2: |
| /* SIMMz(eosz) */ |
| { |
| pti_machine_mode_enum_t eosz = pti_get_nominal_eosz (ild); |
| ild->imm1_bytes = (pti_uint8_t) resolve_z(eosz); |
| } |
| break; |
| case PTI_UIMMv_IMM_WIDTH_OSZ_NONTERM_EOSZ_l2: |
| /* UIMMv(eosz) */ |
| { |
| pti_machine_mode_enum_t eosz = pti_get_nominal_eosz (ild); |
| ild->imm1_bytes = (pti_uint8_t) resolve_v(eosz); |
| } |
| |
| break; |
| case PTI_UIMM16_IMM_WIDTH_CONST_l2: |
| ild->imm1_bytes = 2; |
| break; |
| case PTI_SIMMz_IMM_WIDTH_OSZ_NONTERM_DF64_EOSZ_l2: |
| /* push defaults to eosz64 in 64b mode, then uses SIMMz */ |
| { |
| pti_machine_mode_enum_t eosz = pti_get_nominal_eosz_df64 (ild); |
| ild->imm1_bytes = (pti_uint8_t) resolve_z(eosz); |
| } |
| break; |
| case PTI_RESOLVE_BYREG_IMM_WIDTH_map0x0_op0xf7_l1: |
| if (ild->map == PTI_MAP_0 && pti_get_modrm_reg (ild) < 2) |
| { |
| pti_machine_mode_enum_t eosz = pti_get_nominal_eosz (ild); |
| ild->imm1_bytes = (pti_uint8_t) resolve_z(eosz); |
| } |
| break; |
| case PTI_RESOLVE_BYREG_IMM_WIDTH_map0x0_op0xc7_l1: |
| if (ild->map == PTI_MAP_0 && pti_get_modrm_reg (ild) == 0) |
| { |
| pti_machine_mode_enum_t eosz = pti_get_nominal_eosz (ild); |
| ild->imm1_bytes = (pti_uint8_t) resolve_z(eosz); |
| } |
| break; |
| case PTI_RESOLVE_BYREG_IMM_WIDTH_map0x0_op0xf6_l1: |
| if (ild->map == PTI_MAP_0 && pti_get_modrm_reg (ild) < 2) |
| { |
| ild->imm1_bytes = 1; |
| } |
| break; |
| case PTI_IMM_hasimm_map0x0_op0xc8_l1: |
| if (ild->map == PTI_MAP_0) |
| { |
| /*enter -> imm1=2, imm2=1 */ |
| ild->imm1_bytes = 2; |
| ild->imm2_bytes = 1; |
| } |
| break; |
| case PTI_IMM_hasimm_map0x0F_op0x78_l1: |
| /* AMD SSE4a (insertq/extrq use osz/f2) vs vmread (no prefixes) */ |
| if (ild->map == PTI_MAP_1) |
| { |
| if (ild->u.s.osz || ild->u.s.last_f2f3 == 2) |
| { |
| ild->imm1_bytes = 1; |
| ild->imm2_bytes = 1; |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void |
| imm_dec (pti_ild_t * ild) |
| { |
| if (ild->map == PTI_MAP_AMD3DNOW) |
| { |
| if (ild->length < ild->max_bytes) |
| { |
| ild->nominal_opcode = get_byte (ild, ild->length); |
| ild->length++; |
| } |
| else |
| set_error (ild); |
| return; |
| } |
| set_imm_bytes (ild); |
| if (ild->imm1_bytes == 0) |
| return; |
| |
| if (ild->length + ild->imm1_bytes > ild->max_bytes) |
| { |
| set_error (ild); |
| return; |
| } |
| /*FIXME: could record immediate position if ever needed... */ |
| ild->length += ild->imm1_bytes; |
| |
| if (ild->imm2_bytes == 0) |
| return; |
| |
| if (ild->length + ild->imm2_bytes > ild->max_bytes) |
| { |
| set_error (ild); |
| return; |
| } |
| ild->length += ild->imm2_bytes; |
| } |
| |
| static void |
| decode (pti_ild_t * ild) |
| { |
| prefix_rex_dec (ild); |
| vex_dec (ild); |
| if (ild->nominal_opcode_pos == 0) |
| opcode_dec (ild); |
| modrm_dec (ild); |
| sib_dec (ild); |
| disp_dec (ild); |
| imm_dec (ild); |
| } |
| |
| PTI_INLINE pti_int64_t |
| sign_extend_bq (pti_int8_t x) |
| { |
| return x; |
| } |
| |
| PTI_INLINE pti_int64_t |
| sign_extend_wq (pti_int16_t x) |
| { |
| return x; |
| } |
| |
| PTI_INLINE pti_int64_t |
| sign_extend_dq (pti_int32_t x) |
| { |
| return x; |
| } |
| |
| static void |
| set_branch_target (pti_ild_t * ild) |
| { |
| pti_int64_t npc; |
| pti_uint64_t sign_extended_disp = 0; |
| if (ild->disp_bytes == 1) |
| sign_extended_disp = sign_extend_bq (get_byte (ild, ild->disp_pos)); |
| else if (ild->disp_bytes == 2) |
| { |
| pti_int16_t *w = (pti_int16_t *) (get_byte_ptr (ild, ild->disp_pos)); |
| sign_extended_disp = sign_extend_wq (*w); |
| } |
| else if (ild->disp_bytes == 4) |
| { |
| pti_int32_t *d = (pti_int32_t *) (get_byte_ptr (ild, ild->disp_pos)); |
| sign_extended_disp = sign_extend_dq (*d); |
| } |
| else |
| pti_abort (); |
| npc = (pti_int64_t) (ild->runtime_address + ild->length); |
| ild->direct_target = (pti_uint64_t) (npc + sign_extended_disp); |
| } |
| |
| /* MAIN ENTRY POINTS */ |
| |
| PTI_DLL_EXPORT void |
| pti_ild_init (void) |
| { /* initialization */ |
| init_has_disp_regular_table (); |
| init_has_sib_table (); |
| init_eamode_table (); |
| } |
| |
| PTI_DLL_EXPORT pti_bool_t |
| pti_instruction_length_decode (pti_ild_t * ild) |
| { |
| /*FIXME: could remove this. rely on user memset. */ |
| ild->iclass = PTI_INST_INVALID; |
| ild->u.i = 0; |
| ild->direct_target = 0; |
| decode (ild); |
| return ild->u.s.error == 0; |
| } |
| |
| PTI_DLL_EXPORT pti_bool_t |
| pti_instruction_decode (pti_ild_t * ild) |
| { |
| pti_uint8_t opcode = ild->nominal_opcode; |
| pti_uint8_t map = pti_get_map (ild); |
| |
| /*FIXME: finish pti_instruction_decode, validate prefixes or absense of. */ |
| |
| if (ild->map > PTI_MAP_1) |
| return 0; /* uninteresting */ |
| if (ild->u.s.vexc4 || ild->u.s.vexc5) |
| return 0; /* uninteresting */ |
| |
| /* PTI_INST_JCC, 70...7F, 0F (0x80...0x8F) */ |
| if (opcode >= 0x70 && opcode <= 0x7F) |
| { /*Jcc MAP0 */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_JCC; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_direct = 1; |
| ild->u.s.cond = 1; |
| set_branch_target (ild); |
| return 1; |
| } |
| return 0; |
| } |
| if (opcode >= 0x80 && opcode <= 0x8F) |
| { /*Jcc MAP1 */ |
| if (map == PTI_MAP_1) |
| { |
| ild->iclass = PTI_INST_JCC; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_direct = 1; |
| ild->u.s.cond = 1; |
| set_branch_target (ild); |
| return 1; |
| } |
| return 0; |
| } |
| |
| switch (ild->nominal_opcode) |
| { |
| case 0x9A: /* CALL far direct */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_CALL_9A; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| ild->u.s.call = 1; |
| return 1; |
| } |
| return 0; |
| |
| case 0xFF: /* CALL with /r = /2 and /3, JMP with /4 and /5 */ |
| if (map == PTI_MAP_0) |
| { |
| pti_uint_t reg = pti_get_modrm_reg (ild); |
| if (reg == 2) |
| { |
| ild->iclass = PTI_INST_CALL_FFr2; |
| ild->u.s.branch = 1; |
| ild->u.s.call = 1; |
| return 1; |
| } |
| else if (reg == 3) |
| { |
| ild->iclass = PTI_INST_CALL_FFr3; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| ild->u.s.call = 1; |
| return 1; |
| } |
| else if (reg == 4) |
| { |
| ild->iclass = PTI_INST_JMP_FFr4; |
| ild->u.s.branch = 1; |
| return 1; |
| } |
| else if (reg == 5) |
| { |
| ild->iclass = PTI_INST_JMP_FFr5; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| return 1; |
| } |
| } |
| return 0; |
| case 0xE8: /* CALL */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_CALL_E8; |
| ild->u.s.branch = 1; |
| ild->u.s.call = 1; |
| ild->u.s.branch_direct = 1; |
| set_branch_target (ild); |
| return 1; |
| } |
| return 0; |
| |
| case 0xCD: /* INT */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_INT; |
| return 1; |
| } |
| return 0; |
| |
| case 0xCC: /* INT3 */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_INT3; |
| return 1; |
| } |
| return 0; |
| case 0xCE: /* INTO */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_INTO; |
| return 1; |
| } |
| return 0; |
| case 0xF1: /* INT1 */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_INT1; |
| return 1; |
| } |
| return 0; |
| |
| case 0xCF: /* PTI_INST_IRET (includes IRETD/IRETQ) */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_IRET; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| ild->u.s.ret = 1; |
| return 1; |
| } |
| return 0; |
| |
| case 0xE9: /* JMP */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_JMP_E9; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_direct = 1; |
| set_branch_target (ild); |
| return 1; |
| } |
| return 0; |
| |
| case 0xEA: /* JMP - far jump -- FIXME: do we compute target? */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_JMP_EA; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| return 1; |
| } |
| return 0; |
| case 0xEB: /* JMP */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_JMP_EB; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_direct = 1; |
| set_branch_target (ild); |
| return 1; |
| } |
| return 0; |
| |
| case 0xE3: /* JRCXZ */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_JrCXZ; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_direct = 1; |
| ild->u.s.cond = 1; |
| set_branch_target (ild); |
| return 1; |
| } |
| return 0; |
| |
| case 0xE0: /* LOOPNE */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_LOOPNE; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_direct = 1; |
| ild->u.s.cond = 1; |
| set_branch_target (ild); |
| return 1; |
| } |
| return 0; |
| case 0xE1: /* LOOPE */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_LOOPE; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_direct = 1; |
| ild->u.s.cond = 1; |
| set_branch_target (ild); |
| return 1; |
| } |
| return 0; |
| case 0xE2: /* LOOP */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_LOOP; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_direct = 1; |
| ild->u.s.cond = 1; |
| set_branch_target (ild); |
| return 1; |
| } |
| return 0; |
| |
| case 0x22: /* MOV to CR in map 1 , check for reg 3 */ |
| if (map == PTI_MAP_1) |
| if (pti_get_modrm_reg (ild) == 3) |
| if (pti_get_rex_vex_r (ild) == 0) { |
| ild->iclass = PTI_INST_MOV_CR3; |
| return 1; |
| } |
| return 0; |
| |
| case 0xC3: /* PTI_INST_RET_C3, */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_RET_C3; |
| ild->u.s.branch = 1; |
| ild->u.s.ret = 1; |
| return 1; |
| } |
| return 0; |
| |
| case 0xC2: /* PTI_INST_RET_C2, */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_RET_C2; |
| ild->u.s.branch = 1; |
| ild->u.s.ret = 1; |
| return 1; |
| } |
| return 0; |
| case 0xCB: /* PTI_INST_RET_CB, */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_RET_CB; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| ild->u.s.ret = 1; |
| return 1; |
| } |
| return 0; |
| case 0xCA: /* PTI_INST_RET_CA, */ |
| if (map == PTI_MAP_0) |
| { |
| ild->iclass = PTI_INST_RET_CA; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| ild->u.s.ret = 1; |
| return 1; |
| } |
| return 0; |
| |
| case 0x05: /* map 1 PTI_INST_SYSCALL, */ |
| if (map == PTI_MAP_1) |
| { |
| ild->iclass = PTI_INST_SYSCALL; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| ild->u.s.call = 1; |
| return 1; |
| } |
| return 0; |
| case 0x34: /* map 1 PTI_INST_SYSENTER, */ |
| if (map == PTI_MAP_1) |
| { |
| ild->iclass = PTI_INST_SYSENTER; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| ild->u.s.call = 1; |
| return 1; |
| } |
| return 0; |
| case 0x35: /* map 1 PTI_INST_SYSEXIT, */ |
| if (map == PTI_MAP_1) |
| { |
| ild->iclass = PTI_INST_SYSEXIT; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| ild->u.s.ret = 1; |
| return 1; |
| } |
| return 0; |
| case 0x07: /* map 1 PTI_INST_SYSRET, */ |
| if (map == PTI_MAP_1) |
| { |
| ild->iclass = PTI_INST_SYSRET; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| ild->u.s.ret = 1; |
| return 1; |
| } |
| return 0; |
| |
| case 0x01: /* map 1 PTI_INST_VMLAUNCH/RESUME/CALL, */ |
| if (map == PTI_MAP_1) |
| { |
| switch (ild->modrm_byte) |
| { |
| case 0xc1: |
| ild->iclass = PTI_INST_VMCALL; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| ild->u.s.ret = 1; |
| return 1; |
| |
| case 0xc2: |
| ild->iclass = PTI_INST_VMLAUNCH; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| ild->u.s.call = 1; |
| return 1; |
| |
| case 0xc3: |
| ild->iclass = PTI_INST_VMRESUME; |
| ild->u.s.branch = 1; |
| ild->u.s.branch_far = 1; |
| ild->u.s.call = 1; |
| return 1; |
| |
| default: |
| return 0; |
| } |
| } |
| return 0; |
| |
| case 0xc7: /* map 1 PTI_INST_VMPTRLD, */ |
| if (map == PTI_MAP_1 && |
| pti_get_modrm_mod (ild) != 3 && |
| pti_get_modrm_reg (ild) == 6) |
| { |
| ild->iclass = PTI_INST_VMPTRLD; |
| return 1; |
| } |
| return 0; |
| |
| default: |
| break; |
| } |
| |
| return 0; |
| |
| } |