Files
TheRock/rocm-systems/projects/rocprofiler-systems/external/dyninst/instructionAPI/src/InstructionDecoder-x86.C
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

1942 lines
79 KiB
C++

/*
* See the dyninst/COPYRIGHT file for copyright information.
*
* We provide the Paradyn Tools (below described as "Paradyn")
* on an AS IS basis, and do not warrant its validity or performance.
* We reserve the right to update, modify, or discontinue this
* software at any time. We shall have no obligation to supply such
* updates or modifications or any other form of support to you.
*
* By your use of Paradyn, you understand and agree that we (or any
* other person or entity with proprietary rights in Paradyn) are
* under no obligation to provide either maintenance services,
* update services, notices of latent defects, or correction of
* defects for Paradyn.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define INSIDE_INSTRUCTION_API
#include "InstructionDecoder-x86.h"
#include "Expression.h"
#include "common/src/arch-x86.h"
#include "Register.h"
#include "Dereference.h"
#include "Immediate.h"
#include "BinaryFunction.h"
#include "common/src/singleton_object_pool.h"
#include "unaligned_memory_access.h"
#include "registers/x86_regs.h"
#include "registers/x86_64_regs.h"
#include "registers/abstract_regs.h"
// #define VEX_DEBUG
using namespace std;
using namespace NS_x86;
namespace Dyninst
{
namespace InstructionAPI
{
bool readsOperand(unsigned int opsema, unsigned int i)
{
switch(opsema) {
case s1R2R:
return (i == 0 || i == 1);
case s1R:
case s1RW:
return i == 0;
case s1W:
return false;
case s1W2RW:
case s1W2R: // second operand read, first operand written (e.g. mov)
return i == 1;
case s1RW2R: // two operands read, first written (e.g. add)
case s1RW2RW: // e.g. xchg
case s1R2RW:
return i == 0 || i == 1;
case s1W2R3R: // e.g. imul
case s1W2RW3R: // some mul
case s1W2R3RW: // (stack) push & pop
return i == 1 || i == 2;
case s1W2W3R: // e.g. les
return i == 2;
case s1RW2R3RW:
case s1RW2R3R: // shld/shrd
case s1RW2RW3R: // [i]div, cmpxch8b
case s1R2R3R:
return i == 0 || i == 1 || i == 2;
case s1W2R3R4R:
return i == 1 || i == 2 || i == 3;
case s1RW2R3R4R:
return i == 0 || i == 1 || i == 2 || i == 3;
case sNONE:
return false;
default:
return false;
// printf("OPSEMA: %d\n", opsema);
assert(!"Unknown opsema!");
return false;
}
}
bool writesOperand(unsigned int opsema, unsigned int i)
{
switch(opsema) {
case s1R2R:
case s1R:
return false;
case s1RW:
case s1W:
case s1W2R: // second operand read, first operand written (e.g. mov)
case s1RW2R: // two operands read, first written (e.g. add)
case s1W2R3R: // e.g. imul
case s1RW2R3R: // shld/shrd
case s1RW2R3R4R:
return i == 0;
case s1R2RW:
return i == 1;
case s1W2RW:
case s1RW2RW: // e.g. xchg
case s1W2RW3R: // some mul
case s1W2W3R: // e.g. les
case s1RW2RW3R: // [i]div, cmpxch8b
return i == 0 || i == 1;
case s1W2R3RW: // (stack) push & pop
return i == 0 || i == 2;
case s1RW2R3RW:
return i == 0 || i == 2;
case sNONE:
default:
return false;
// printf("OPSEMA: %d\n", opsema);
assert(!"Unknown opsema!");
return false;
}
}
bool implicitOperand(unsigned int implicit_operands, unsigned int i)
{
return sGetImplicitOP(implicit_operands, i) != 0x0;
}
INSTRUCTION_EXPORT InstructionDecoder_x86::InstructionDecoder_x86(Architecture a) :
InstructionDecoderImpl(a),
locs(NULL),
decodedInstruction(NULL),
sizePrefixPresent(false),
addrSizePrefixPresent(false)
{
if(a == Arch_x86_64) InstructionDecoder_x86::setMode(true);
}
INSTRUCTION_EXPORT InstructionDecoder_x86::~InstructionDecoder_x86()
{
free(decodedInstruction);
free(locs);
}
static const unsigned char modrm_use_sib = 4;
INSTRUCTION_EXPORT void InstructionDecoder_x86::setMode(bool is64)
{
InstructionDecoder_x86::is64BitMode = is64;
}
Expression::Ptr InstructionDecoder_x86::makeSIBExpression(const InstructionDecoder::buffer& b)
{
unsigned scale;
Register index;
Register base;
Result_Type registerType = is64BitMode ? u64 : u32;
int op_type = is64BitMode ? op_q : op_d;
decode_SIB(locs->sib_byte, scale, index, base);
Expression::Ptr scaleAST(make_shared(singleton_object_pool<Immediate>::construct(Result(u8, dword_t(scale)))));
Expression::Ptr indexAST(make_shared(singleton_object_pool<RegisterAST>::construct(makeRegisterID(index, op_type,
locs->rex_x))));
Expression::Ptr baseAST;
if(base == 0x05)
{
switch(locs->modrm_mod)
{
case 0x00:
baseAST = decodeImmediate(op_d, b.start + locs->sib_position + 1, true);
break;
case 0x01:
case 0x02:
baseAST = make_shared(singleton_object_pool<RegisterAST>::construct(makeRegisterID(base,
op_type,
locs->rex_b)));
break;
case 0x03:
default:
assert(0);
break;
};
}
else
{
baseAST = make_shared(singleton_object_pool<RegisterAST>::construct(makeRegisterID(base,
op_type,
locs->rex_b)));
}
if(index == 0x04 && (!(is64BitMode) || !(locs->rex_x)))
{
return baseAST;
}
return makeAddExpression(baseAST, makeMultiplyExpression(indexAST, scaleAST, registerType), registerType);
}
Expression::Ptr InstructionDecoder_x86::makeModRMExpression(const InstructionDecoder::buffer& b,
unsigned int opType)
{
unsigned int regType = op_d;
Result_Type aw;
if(is64BitMode)
{
if(addrSizePrefixPresent)
{
aw = u32;
} else {
aw = u64;
regType = op_q;
}
} else {
if(!addrSizePrefixPresent)
{
aw = u32;
} else {
aw = u16;
regType = op_w;
}
}
if (opType == op_lea)
{
// For an LEA, aw (address width) is insufficient, use makeSizeType
aw = makeSizeType(opType);
}
Expression::Ptr e =
makeRegisterExpression(makeRegisterID(locs->modrm_rm, regType, locs->rex_b));
switch(locs->modrm_mod)
{
case 0:
/* modrm_rm == 0x4 is use SIB */
if(locs->modrm_rm == modrm_use_sib)
e = makeSIBExpression(b);
else if(locs->modrm_rm == 0x5 && !addrSizePrefixPresent)
{
/* modrm_rm 00 0x5 is use 32 bit displacement only */
assert(locs->opcode_position > -1);
if(is64BitMode)
{
e = makeAddExpression(makeRegisterExpression(x86_64::rip),
getModRMDisplacement(b), aw);
} else {
e = getModRMDisplacement(b);
}
} else{
e = makeRegisterExpression(makeRegisterID(locs->modrm_rm, regType, locs->rex_b));
}
if(opType == op_lea)
return e;
return makeDereferenceExpression(e, makeSizeType(opType));
case 1:
case 2:
// Expression::Ptr disp_e = makeAddExpression(e, getModRMDisplacement(b), aw);
/* Both MOD values 0b01 and 0b10 can have sibs */
if(locs->modrm_rm == modrm_use_sib)
e = makeSIBExpression(b);
e = makeAddExpression(e, getModRMDisplacement(b), aw);
if(opType == op_lea)
return e;
return makeDereferenceExpression(e, makeSizeType(opType));
case 3:
return makeRegisterExpression(makeRegisterID(locs->modrm_rm, opType, locs->rex_b));
default:
/* This should never happen */
assert(0);
return Expression::Ptr();
};
// can't get here, but make the compiler happy...
assert(0);
return Expression::Ptr();
}
Expression::Ptr InstructionDecoder_x86::decodeImmediate(unsigned int opType, const unsigned char* immStart,
bool isSigned)
{
// rex_w indicates we need to sign-extend also.
isSigned = isSigned || locs->rex_w;
switch(opType)
{
case op_b:
return Immediate::makeImmediate(Result(isSigned ? s8 : u8 ,*(const byte_t*)(immStart)));
break;
case op_d:
return Immediate::makeImmediate(Result(isSigned ? s32 : u32,Dyninst::read_memory_as<dword_t>(immStart)));
case op_w:
return Immediate::makeImmediate(Result(isSigned ? s16 : u16,Dyninst::read_memory_as<word_t>(immStart)));
break;
case op_q:
return Immediate::makeImmediate(Result(isSigned ? s64 : u64,Dyninst::read_memory_as<int64_t>(immStart)));
break;
case op_v:
if (locs->rex_w || isDefault64Insn()) {
return Immediate::makeImmediate(Result(isSigned ? s64 : u64,Dyninst::read_memory_as<int64_t>(immStart)));
}
//if(!sizePrefixPresent)
//{
return Immediate::makeImmediate(Result(isSigned ? s32 : u32,Dyninst::read_memory_as<dword_t>(immStart)));
//}
//else
//{
//return Immediate::makeImmediate(Result(isSigned ? s16 : u16,*(const word_t*)(immStart)));
//}
break;
case op_z:
// 32 bit mode & no prefix, or 16 bit mode & prefix => 32 bit
// 16 bit mode, no prefix or 32 bit mode, prefix => 16 bit
//if(!addrSizePrefixPresent)
//{
return Immediate::makeImmediate(Result(isSigned ? s32 : u32,Dyninst::read_memory_as<dword_t>(immStart)));
//}
//else
//{
//return Immediate::makeImmediate(Result(isSigned ? s16 : u16,*(const word_t*)(immStart)));
//}
break;
case op_p:
// 32 bit mode & no prefix, or 16 bit mode & prefix => 48 bit
// 16 bit mode, no prefix or 32 bit mode, prefix => 32 bit
if(!sizePrefixPresent)
{
return Immediate::makeImmediate(Result(isSigned ? s48 : u48,Dyninst::read_memory_as<int64_t>(immStart)));
}
else
{
return Immediate::makeImmediate(Result(isSigned ? s32 : u32,Dyninst::read_memory_as<dword_t>(immStart)));
}
break;
case op_a:
case op_dq:
case op_pd:
case op_ps:
case op_s:
case op_si:
case op_lea:
case op_allgprs:
case op_512:
case op_c:
assert(!"Can't happen: opType unexpected for valid ways to decode an immediate");
return Expression::Ptr();
default:
assert(!"Can't happen: opType out of range");
return Expression::Ptr();
}
}
Expression::Ptr InstructionDecoder_x86::getModRMDisplacement(const InstructionDecoder::buffer& b)
{
int disp_pos;
if(locs->sib_position != -1)
disp_pos = locs->sib_position + 1;
else disp_pos = locs->modrm_position + 1;
switch(locs->modrm_mod)
{
case 1:
return make_shared(singleton_object_pool<Immediate>::construct(Result(s8, Dyninst::read_memory_as<byte_t>(b.start +
disp_pos))));
break;
case 2:
if(0 && sizePrefixPresent)
{
return make_shared(singleton_object_pool<Immediate>::construct(Result(s16, Dyninst::read_memory_as<word_t>(b.start +
disp_pos))));
}
else
{
return make_shared(singleton_object_pool<Immediate>::construct(Result(s32, Dyninst::read_memory_as<dword_t>(b.start +
disp_pos))));
}
break;
case 0:
// In 16-bit mode, the word displacement is modrm r/m 6
if(sizePrefixPresent && !is64BitMode)
{
if(locs->modrm_rm == 6)
{
return make_shared(singleton_object_pool<Immediate>::construct(Result(s16,
Dyninst::read_memory_as<dword_t>(b.start + disp_pos))));
}
// TODO FIXME; this was decoding wrong, but I'm not sure
// why...
else if (locs->modrm_rm == 5) {
assert(b.start + disp_pos + 4 <= b.end);
return make_shared(singleton_object_pool<Immediate>::construct(Result(s32,
Dyninst::read_memory_as<dword_t>(b.start + disp_pos))));
} else {
assert(b.start + disp_pos + 1 <= b.end);
return make_shared(singleton_object_pool<Immediate>::construct(Result(s8, 0)));
}
break;
}
// ...and in 32-bit mode, the dword displacement is modrm r/m 5
else
{
if(locs->modrm_rm == 5)
{
if (b.start + disp_pos + 4 <= b.end)
return make_shared(singleton_object_pool<Immediate>::construct(Result(s32,
Dyninst::read_memory_as<dword_t>(b.start + disp_pos))));
else
return make_shared(singleton_object_pool<Immediate>::construct(Result()));
}
else
{
if (b.start + disp_pos + 1 <= b.end)
return make_shared(singleton_object_pool<Immediate>::construct(Result(s8, 0)));
else
{
return make_shared(singleton_object_pool<Immediate>::construct(Result()));
}
}
break;
}
default:
assert(b.start + disp_pos + 1 <= b.end);
return make_shared(singleton_object_pool<Immediate>::construct(Result(s8, 0)));
}
}
enum intelRegBanks
{
b_8bitNoREX = 0,
b_16bit,
b_32bit,
b_segment,
b_64bit,
b_xmm_set0, /* XMM0 -> XMM 7 */
b_xmm_set1, /* XMM8 -> XMM 15 */
b_xmm_set2, /* XMM16 -> XMM 23 */
b_xmm_set3, /* XMM24 -> XMM 31 */
b_ymm_set0, /* YMM0 -> YMM 7 */
b_ymm_set1, /* YMM8 -> YMM 15 */
b_ymm_set2, /* YMM16 -> YMM 23 */
b_ymm_set3, /* YMM24 -> YMM 31 */
b_zmm_set0, /* ZMM0 -> ZMM 7 */
b_zmm_set1, /* ZMM8 -> ZMM 15 */
b_zmm_set2, /* ZMM16 -> ZMM 23 */
b_zmm_set3, /* ZMM24 -> ZMM 31 */
b_kmask,
b_mm,
b_cr,
b_dr,
b_tr,
b_amd64ext,
b_8bitWithREX,
b_fpstack,
amd64_ext_8,
amd64_ext_16,
amd64_ext_32,
b_invalid /* should remain the final entry */
};
static MachRegister IntelRegTable32[][8] = {
{ x86::al, x86::cl, x86::dl, x86::bl, x86::ah, x86::ch, x86::dh, x86::bh }, /* b_8bitNoREX */
{ x86::ax, x86::cx, x86::dx, x86::bx, x86::sp, x86::bp, x86::si, x86::di }, /* b_16bit */
{ x86::eax, x86::ecx, x86::edx, x86::ebx, x86::esp, x86::ebp, x86::esi, x86::edi }, /* b_32bit */
{ x86::es, x86::cs, x86::ss, x86::ds, x86::fs, x86::gs, InvalidReg, InvalidReg }, /* b_segment */
{ x86_64::rax, x86_64::rcx, x86_64::rdx, x86_64::rbx, x86_64::rsp, x86_64::rbp, x86_64::rsi, x86_64::rdi }, /* b_64bit */
{ x86::xmm0, x86::xmm1, x86::xmm2, x86::xmm3, x86::xmm4, x86::xmm5, x86::xmm6, x86::xmm7 }, /* b_xmm_set0 */
{ x86_64::xmm8, x86_64::xmm9, x86_64::xmm10, x86_64::xmm11, x86_64::xmm12, x86_64::xmm13, x86_64::xmm14, x86_64::xmm15 }, /* b_xmm_set1 */
{ x86_64::xmm16, x86_64::xmm17, x86_64::xmm18, x86_64::xmm19, x86_64::xmm20, x86_64::xmm21, x86_64::xmm22, x86_64::xmm23 }, /* b_xmm_set2 */
{ x86_64::xmm24, x86_64::xmm25, x86_64::xmm26, x86_64::xmm27, x86_64::xmm28, x86_64::xmm29, x86_64::xmm30, x86_64::xmm31 }, /* b_xmm_set3 */
{ x86_64::ymm0, x86_64::ymm1, x86_64::ymm2, x86_64::ymm3, x86_64::ymm4, x86_64::ymm5, x86_64::ymm6, x86_64::ymm7 }, /* b_ymm_set0 */
{ x86_64::ymm8, x86_64::ymm9, x86_64::ymm10, x86_64::ymm11, x86_64::ymm12, x86_64::ymm13, x86_64::ymm14, x86_64::ymm15 }, /* b_ymm_set1 */
{ x86_64::ymm16, x86_64::ymm17, x86_64::ymm18, x86_64::ymm19, x86_64::ymm20, x86_64::ymm21, x86_64::ymm22, x86_64::ymm23 }, /* b_ymm_set2 */
{ x86_64::ymm24, x86_64::ymm25, x86_64::ymm26, x86_64::ymm27, x86_64::ymm28, x86_64::ymm29, x86_64::ymm30, x86_64::ymm31 }, /* b_ymm_set3 */
{ x86_64::zmm0, x86_64::zmm1, x86_64::zmm2, x86_64::zmm3, x86_64::zmm4, x86_64::zmm5, x86_64::zmm6, x86_64::zmm7 }, /* b_zmm_set0 */
{ x86_64::zmm8, x86_64::zmm9, x86_64::zmm10, x86_64::zmm11, x86_64::zmm12, x86_64::zmm13, x86_64::zmm14, x86_64::zmm15 }, /* b_zmm_set1 */
{ x86_64::zmm16, x86_64::zmm17, x86_64::zmm18, x86_64::zmm19, x86_64::zmm20, x86_64::zmm21, x86_64::zmm22, x86_64::zmm23 }, /* b_zmm_set2 */
{ x86_64::zmm24, x86_64::zmm25, x86_64::zmm26, x86_64::zmm27, x86_64::zmm28, x86_64::zmm29, x86_64::zmm30, x86_64::zmm31 }, /* b_zmm_set3 */
{ x86_64::k0, x86_64::k1, x86_64::k2, x86_64::k3, x86_64::k4, x86_64::k5, x86_64::k6, x86_64::k7 },
{ x86::mm0, x86::mm1, x86::mm2, x86::mm3, x86::mm4, x86::mm5, x86::mm6, x86::mm7 },
{ x86::cr0, x86::cr1, x86::cr2, x86::cr3, x86::cr4, x86::cr5, x86::cr6, x86::cr7 },
{ x86::dr0, x86::dr1, x86::dr2, x86::dr3, x86::dr4, x86::dr5, x86::dr6, x86::dr7 },
{ x86::tr0, x86::tr1, x86::tr2, x86::tr3, x86::tr4, x86::tr5, x86::tr6, x86::tr7 },
{ x86_64::r8, x86_64::r9, x86_64::r10, x86_64::r11, x86_64::r12, x86_64::r13, x86_64::r14, x86_64::r15 },
{ x86_64::al, x86_64::cl, x86_64::dl, x86_64::bl, x86_64::spl, x86_64::bpl, x86_64::sil, x86_64::dil },
{ x86::st0, x86::st1, x86::st2, x86::st3, x86::st4, x86::st5, x86::st6, x86::st7 }
};
static MachRegister IntelRegTable64[][8] = {
{ x86_64::al, x86_64::cl, x86_64::dl, x86_64::bl, x86_64::ah, x86_64::ch, x86_64::dh, x86_64::bh },
{ x86_64::ax, x86_64::cx, x86_64::dx, x86_64::bx, x86_64::sp, x86_64::bp, x86_64::si, x86_64::di },
{ x86_64::eax, x86_64::ecx, x86_64::edx, x86_64::ebx, x86_64::esp, x86_64::ebp, x86_64::esi, x86_64::edi },
{ x86_64::es, x86_64::cs, x86_64::ss, x86_64::ds, x86_64::fs, x86_64::gs, InvalidReg, InvalidReg },
{ x86_64::rax, x86_64::rcx, x86_64::rdx, x86_64::rbx, x86_64::rsp, x86_64::rbp, x86_64::rsi, x86_64::rdi },
{ x86_64::xmm0, x86_64::xmm1, x86_64::xmm2, x86_64::xmm3, x86_64::xmm4, x86_64::xmm5, x86_64::xmm6, x86_64::xmm7 }, /* b_xmm_set0 */
{ x86_64::xmm8, x86_64::xmm9, x86_64::xmm10, x86_64::xmm11, x86_64::xmm12, x86_64::xmm13, x86_64::xmm14, x86_64::xmm15 }, /* b_xmm_set1 */
{ x86_64::xmm16, x86_64::xmm17, x86_64::xmm18, x86_64::xmm19, x86_64::xmm20, x86_64::xmm21, x86_64::xmm22, x86_64::xmm23 }, /* b_xmm_set2 */
{ x86_64::xmm24, x86_64::xmm25, x86_64::xmm26, x86_64::xmm27, x86_64::xmm28, x86_64::xmm29, x86_64::xmm30, x86_64::xmm31 }, /* b_xmm_set3 */
{ x86_64::ymm0, x86_64::ymm1, x86_64::ymm2, x86_64::ymm3, x86_64::ymm4, x86_64::ymm5, x86_64::ymm6, x86_64::ymm7 }, /* b_ymm_set0 */
{ x86_64::ymm8, x86_64::ymm9, x86_64::ymm10, x86_64::ymm11, x86_64::ymm12, x86_64::ymm13, x86_64::ymm14, x86_64::ymm15 }, /* b_ymm_set1 */
{ x86_64::ymm16, x86_64::ymm17, x86_64::ymm18, x86_64::ymm19, x86_64::ymm20, x86_64::ymm21, x86_64::ymm22, x86_64::ymm23 }, /* b_ymm_set2 */
{ x86_64::ymm24, x86_64::ymm25, x86_64::ymm26, x86_64::ymm27, x86_64::ymm28, x86_64::ymm29, x86_64::ymm30, x86_64::ymm31 }, /* b_ymm_set3 */
{ x86_64::zmm0, x86_64::zmm1, x86_64::zmm2, x86_64::zmm3, x86_64::zmm4, x86_64::zmm5, x86_64::zmm6, x86_64::zmm7 }, /* b_zmm_set0 */
{ x86_64::zmm8, x86_64::zmm9, x86_64::zmm10, x86_64::zmm11, x86_64::zmm12, x86_64::zmm13, x86_64::zmm14, x86_64::zmm15 }, /* b_zmm_set1 */
{ x86_64::zmm16, x86_64::zmm17, x86_64::zmm18, x86_64::zmm19, x86_64::zmm20, x86_64::zmm21, x86_64::zmm22, x86_64::zmm23 }, /* b_zmm_set2 */
{ x86_64::zmm24, x86_64::zmm25, x86_64::zmm26, x86_64::zmm27, x86_64::zmm28, x86_64::zmm29, x86_64::zmm30, x86_64::zmm31 }, /* b_zmm_set3 */
{ x86_64::k0, x86_64::k1, x86_64::k2, x86_64::k3, x86_64::k4, x86_64::k5, x86_64::k6, x86_64::k7 },
{ x86_64::mm0, x86_64::mm1, x86_64::mm2, x86_64::mm3, x86_64::mm4, x86_64::mm5, x86_64::mm6, x86_64::mm7 },
{ x86_64::cr0, x86_64::cr1, x86_64::cr2, x86_64::cr3, x86_64::cr4, x86_64::cr5, x86_64::cr6, x86_64::cr7 },
{ x86_64::dr0, x86_64::dr1, x86_64::dr2, x86_64::dr3, x86_64::dr4, x86_64::dr5, x86_64::dr6, x86_64::dr7 },
{ x86_64::tr0, x86_64::tr1, x86_64::tr2, x86_64::tr3, x86_64::tr4, x86_64::tr5, x86_64::tr6, x86_64::tr7 },
{ x86_64::r8, x86_64::r9, x86_64::r10, x86_64::r11, x86_64::r12, x86_64::r13, x86_64::r14, x86_64::r15 },
{ x86_64::al, x86_64::cl, x86_64::dl, x86_64::bl, x86_64::spl, x86_64::bpl, x86_64::sil, x86_64::dil },
{ x86_64::st0, x86_64::st1, x86_64::st2, x86_64::st3, x86_64::st4, x86_64::st5, x86_64::st6, x86_64::st7 },
{ x86_64::r8b, x86_64::r9b, x86_64::r10b, x86_64::r11b, x86_64::r12b, x86_64::r13b, x86_64::r14b, x86_64::r15b },
{ x86_64::r8w, x86_64::r9w, x86_64::r10w, x86_64::r11w, x86_64::r12w, x86_64::r13w, x86_64::r14w, x86_64::r15w },
{ x86_64::r8d, x86_64::r9d, x86_64::r10d, x86_64::r11d, x86_64::r12d, x86_64::r13d, x86_64::r14d, x86_64::r15d },
};
/* Uses the appropriate lookup table based on the
decoder architecture */
class IntelRegTable_access {
public:
inline MachRegister operator()(Architecture arch,
intelRegBanks bank,
int index)
{
assert(index >= 0 && index < 8);
if(arch == Arch_x86_64)
return IntelRegTable64[bank][index];
else if(arch == Arch_x86)
{
if(bank > b_fpstack) return InvalidReg;
return IntelRegTable32[bank][index];
}
assert(0);
return InvalidReg;
}
};
static IntelRegTable_access IntelRegTable;
bool InstructionDecoder_x86::isDefault64Insn()
{
switch(m_Operation.getID())
{
case e_jmp:
case e_pop:
case e_push:
case e_call:
return true;
default:
return false;
}
}
MachRegister InstructionDecoder_x86::makeRegisterID(unsigned int intelReg, unsigned int opType,
bool isExtendedReg)
{
MachRegister retVal;
if(isExtendedReg)
{
switch(opType)
{
case op_q:
retVal = IntelRegTable(m_Arch,b_amd64ext,intelReg);
break;
case op_d:
retVal = IntelRegTable(m_Arch,amd64_ext_32,intelReg);
break;
case op_w:
retVal = IntelRegTable(m_Arch,amd64_ext_16,intelReg);
break;
case op_b:
retVal = IntelRegTable(m_Arch,amd64_ext_8,intelReg);
break;
case op_v:
if (locs->rex_w || isDefault64Insn())
retVal = IntelRegTable(m_Arch, b_amd64ext, intelReg);
else if (!sizePrefixPresent)
retVal = IntelRegTable(m_Arch, amd64_ext_32, intelReg);
//else
// retVal = IntelRegTable(m_Arch, amd64_ext_16, intelReg);
break;
case op_p:
case op_z:
// if (!sizePrefixPresent)
retVal = IntelRegTable(m_Arch, amd64_ext_32, intelReg);
// else
// retVal = IntelRegTable(m_Arch, amd64_ext_16, intelReg);
break;
case op_f:
case op_dbl:
// extended reg ignored on FP regs
retVal = IntelRegTable(m_Arch, b_fpstack,intelReg);
break;
default:
retVal = InvalidReg;
}
}
/* Promotion to 64-bit only applies to the operand types
that are varible (c,v,z). Ignoring c and z because they
do the right thing on 32- and 64-bit code.
else if(locs->rex_w)
{
// AMD64 with 64-bit operands
retVal = IntelRegTable[b_64bit][intelReg];
}
*/
else
{
switch(opType)
{
case op_v:
if(locs->rex_w || isDefault64Insn())
retVal = IntelRegTable(m_Arch,b_64bit,intelReg);
else
retVal = IntelRegTable(m_Arch,b_32bit,intelReg);
break;
case op_b:
if (locs->rex_byte & 0x40) {
retVal = IntelRegTable(m_Arch,b_8bitWithREX,intelReg);
} else {
retVal = IntelRegTable(m_Arch,b_8bitNoREX,intelReg);
}
break;
case op_q:
retVal = IntelRegTable(m_Arch,b_64bit,intelReg);
break;
case op_w:
retVal = IntelRegTable(m_Arch,b_16bit,intelReg);
break;
case op_f:
case op_dbl:
retVal = IntelRegTable(m_Arch,b_fpstack,intelReg);
break;
case op_d:
case op_si:
retVal = IntelRegTable(m_Arch,b_32bit,intelReg);
break;
default:
retVal = IntelRegTable(m_Arch,b_32bit,intelReg);
break;
}
}
if (!is64BitMode) {
if ((retVal.val() & 0x00ffffff) == 0x0001000c)
assert(0);
}
return MachRegister((retVal.val() & ~retVal.getArchitecture()) | m_Arch);
}
Result_Type InstructionDecoder_x86::makeSizeType(unsigned int opType)
{
switch(opType)
{
case op_b:
case op_c:
return u8;
case op_d:
case op_ss:
case op_allgprs:
case op_si:
return u32;
case op_w:
case op_a:
return u16;
case op_q:
case op_sd:
return u64;
case op_v:
case op_lea:
case op_z:
if (locs->rex_w)
{
return u64;
}
//if(is64BitMode || !sizePrefixPresent)
//{
return u32;
//}
//else
//{
//return u16;
//}
break;
case op_y:
if(is64BitMode)
return u64;
else
return u32;
break;
case op_p:
// book says operand size; arch-x86 says word + word * operand size
if(!is64BitMode ^ sizePrefixPresent)
{
return u48;
}
else
{
return u32;
}
case op_dq:
case op_qq:
return u64;
case op_512:
return m512;
case op_pi:
case op_ps:
case op_pd:
return dbl128;
case op_s:
return u48;
case op_f:
return sp_float;
case op_dbl:
return dp_float;
case op_14:
return m14;
default:
assert(!"Can't happen!");
return u8;
}
}
enum AVX_Regtype { AVX_XMM = 0, AVX_YMM, AVX_ZMM, AVX_NONE };
#define AVX_TYPE_OKAY(type) ((type) >= AVX_XMM && (type) <= AVX_ZMM)
/**
* Decode an avx register based on the type of prefix. Returns true if the
* given configuration is invalid and should be rejected.
*/
bool decodeAVX(intelRegBanks& bank, int* bank_index, int regnum, AVX_Regtype type, ia32_prefixes& pref, unsigned int admet)
{
/* Check to see if this is just a normal MMX register access */
if(type >= AVX_NONE || type < 0)
{
#ifdef VEX_DEBUG
printf("VEX OPERAND: REGNUM: %d ", regnum);
printf("REG_TYPE: AVX_NONE (%d)\n", type);
#endif
/* Only registers XMM0 - XMM15 are usable */
/* The register must be valid */
if(regnum < 0)
return true;
if(regnum < 8)
{
bank = b_xmm_set0;
*bank_index = regnum;
} else if(regnum < 16)
{
bank = b_xmm_set1;
*bank_index = regnum - 8;
} else {
/* Value is out of the valid range */
return true;
}
/* Return success */
return false;
}
switch(admet)
{
case am_V: case am_YV: case am_XV:
switch(pref.vex_type)
{
case VEX_TYPE_EVEX:
regnum |= pref.vex_R << 4;
regnum |= pref.vex_r << 3;
break;
case VEX_TYPE_VEX2:
case VEX_TYPE_VEX3:
regnum |= pref.vex_r << 3;
break;
default:break;
}
break;
case am_U: case am_YU: case am_XU:
case am_W: case am_YW: case am_XW:
switch(pref.vex_type)
{
case VEX_TYPE_EVEX:
regnum |= pref.vex_x << 4;
regnum |= pref.vex_b << 3;
break;
case VEX_TYPE_VEX3:
regnum |= pref.vex_x << 4;
regnum |= pref.vex_b << 3;
break;
default: break;
}
break;
case am_HK: case am_VK: case am_WK:
bank = b_kmask;
*bank_index = regnum;
if(*bank_index > 7)
*bank_index = 7;
else if(*bank_index < 0)
*bank_index = 0;
return false; /* Return success */
case am_B:
bank = b_64bit;
if(regnum > 7)
{
regnum -= 8;
bank = b_amd64ext;
}
if(regnum < 0)
regnum = 0;
if(regnum > 7)
regnum = 7;
*bank_index = regnum;
break;
default: break;/** SSE instruction */
}
#ifdef VEX_DEBUG
printf("VEX OPERAND: REGNUM: %d ", regnum);
#endif
/* Operand is potentially XMM, YMM or ZMM */
int setnum = 0;
if(regnum < 8)
{
setnum = 0;
*bank_index = regnum;
} else if(regnum < 16)
{
setnum = 1;
*bank_index = regnum - 8;
} else if(regnum < 24)
{
setnum = 2;
*bank_index = regnum - 16;
} else if(regnum < 32){
setnum = 3;
*bank_index = regnum - 24;
} else {
#ifdef VEX_DEBUG
printf("AVX REGISTER NUMBER: %d is invalid!!\n", regnum);
#endif
return false;
}
switch(type)
{
case AVX_XMM:
#ifdef VEX_DEBUG
printf("REG_TYPE: AVX_XMM (%d)\n", type);
#endif
if(setnum == 0)
bank = b_xmm_set0;
else if(setnum == 1)
bank = b_xmm_set1;
else if(setnum == 2)
bank = b_xmm_set2;
else if(setnum == 3)
bank = b_xmm_set3;
else return true;
break;
case AVX_YMM:
#ifdef VEX_DEBUG
printf("REG_TYPE: AVX_YMM (%d)\n", type);
#endif
if(setnum == 0)
bank = b_ymm_set0;
else if(setnum == 1)
bank = b_ymm_set1;
else if(setnum == 2)
bank = b_ymm_set2;
else if(setnum == 3)
bank = b_ymm_set3;
else return true;
break;
case AVX_ZMM:
#ifdef VEX_DEBUG
printf("REG_TYPE: AVX_ZMM (%d)\n", type);
#endif
if(setnum == 0)
bank = b_zmm_set0;
else if(setnum == 1)
bank = b_zmm_set1;
else if(setnum == 2)
bank = b_zmm_set2;
else if(setnum == 3)
bank = b_zmm_set3;
else return true;
break;
default:
return true;
}
/* Return Success */
return false;
}
bool InstructionDecoder_x86::decodeOneOperand(const InstructionDecoder::buffer& b,
const ia32_operand& operand,
int & imm_index, /* immediate operand index */
const Instruction* insn_to_complete,
bool isRead, bool isWritten, bool isImplicit)
{
bool isCFT = false;
bool isCall = false;
bool isConditional = false;
InsnCategory cat = insn_to_complete->getCategory();
if(cat == c_BranchInsn || cat == c_CallInsn)
{
isCFT = true;
if(cat == c_CallInsn)
{
isCall = true;
}
}
if(cat == c_BranchInsn && insn_to_complete->getOperation().getID() != e_jmp)
{
isConditional = true;
}
/* There must be a decoded instruction */
if(!decodedInstruction)
assert(!"No decoded instruction!\n");
unsigned int optype = operand.optype;
AVX_Regtype avx_type = AVX_NONE; /* The AVX register type (if VEX prefixed) */
intelRegBanks bank = b_invalid; /* Specifies an AVX bank to use for register decoding */
int bank_index = -1; /* Specifies a bank index for an AVX register */
ia32_prefixes& pref = *decodedInstruction->getPrefix();
int regnum; /* Used to keep track of some register positions */
if (sizePrefixPresent
&& ((optype == op_v) || (optype == op_z))
&& (operand.admet != am_J))
{
optype = op_w;
}
if(pref.vex_present)
{
/* Get the AVX type from the prefix */
avx_type = (AVX_Regtype)pref.vex_ll;
}
if (sizePrefixPresent && ((optype == op_v)
|| (optype == op_z)) && (operand.admet != am_J))
{
optype = op_w;
}
if(optype == op_y)
{
if(is64BitMode && locs->rex_w)
{
optype = op_q;
} else {
optype = op_d;
}
}
switch(operand.admet)
{
case 0:
// No operand
{
// fprintf(stderr, "ERROR: Instruction with mismatched operands. Raw bytes: ");
// for(unsigned int i = 0; i < decodedInstruction->getSize(); i++) {
// fprintf(stderr, "%x ", b.start[i]);
// }
// fprintf(stderr, "\n");*/
assert(!"Mismatched number of operands--check tables");
return false;
}
return false;
case am_A:
{
// am_A only shows up as a far call/jump. Position 1 should be universally safe.
Expression::Ptr addr(decodeImmediate(optype, b.start + 1));
insn_to_complete->addSuccessor(addr, isCall, false, false, false);
}
break;
case am_B:
{
// Selects a general purpose register from VEX.vvvv (VEX3 or EVEX)
if(!pref.vex_present)
{
// assert(!"Non VEX3 or EVEX instruction with am_B addressing mode!");
return false;
}
/* Grab the correct bank and bank index for this type of register */
if(decodeAVX(bank, &bank_index, pref.vex_vvvv_reg,
avx_type, pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
// Expression::Ptr op(makeRegisterExpression(
// makeRegisterID(pref.vex_vvvv_reg, optype, locs->rex_r)));
// insn_to_complete->appendOperand(op, isRead, isWritten);
}
break;
case am_C:
{
Expression::Ptr op(makeRegisterExpression(
IntelRegTable(m_Arch,b_cr,locs->modrm_reg)));
insn_to_complete->appendOperand(op, isRead, isWritten, isImplicit);
}
break;
case am_D:
{
Expression::Ptr op(makeRegisterExpression(
IntelRegTable(m_Arch,b_dr,locs->modrm_reg)));
insn_to_complete->appendOperand(op, isRead, isWritten, isImplicit);
}
break;
case am_E:
// am_M is like am_E, except that mod of 0x03 should
// never occur (am_M specified memory,
// mod of 0x03 specifies direct register access).
case am_M:
// am_R is the inverse of am_M; it should only have a mod of 3
case am_R:
// can be am_R or am_M
case am_RM:
if(isCFT)
{
insn_to_complete->addSuccessor(
makeModRMExpression(b, optype),
isCall, true, false, false);
} else {
insn_to_complete->appendOperand(
makeModRMExpression(b, optype),
isRead, isWritten, isImplicit);
}
break;
case am_F:
{
Expression::Ptr op(makeRegisterExpression(x86::flags));
insn_to_complete->appendOperand(op, isRead, isWritten, isImplicit);
}
break;
case am_G:
{
Expression::Ptr op(makeRegisterExpression(
makeRegisterID(locs->modrm_reg, optype, locs->rex_r)));
insn_to_complete->appendOperand(op, isRead, isWritten, isImplicit);
}
break;
case am_L:
/* Use Imm byte to encode XMM. Seen in FMA4*/
if(decodeAVX(bank, &bank_index,
(*(const uint8_t*)(b.start + locs->imm_position[imm_index++])) >> 4,
avx_type, pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
case am_H: /* Could be XMM, YMM or ZMM */
/* Make sure this register class is valid for VEX */
if(!AVX_TYPE_OKAY(avx_type) || !pref.vex_present)
return false;
/* Grab the correct bank and bank index for this type of register */
if(decodeAVX(bank, &bank_index, pref.vex_vvvv_reg, avx_type, pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
case am_HK: /* Could be XMM, YMM or ZMM */
/* Make sure this register class is valid for VEX */
if(!AVX_TYPE_OKAY(avx_type) || !pref.vex_present)
return false;
/* Grab the correct bank and bank index for this type of register */
if(decodeAVX(bank, &bank_index, pref.vex_vvvv_reg, avx_type, pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
case am_I:
insn_to_complete->appendOperand(decodeImmediate(optype, b.start +
locs->imm_position[imm_index++]),
isRead, isWritten, isImplicit);
break;
case am_J:
{
Expression::Ptr Offset(decodeImmediate(optype,
b.start + locs->imm_position[imm_index++],
true));
Expression::Ptr EIP(makeRegisterExpression(MachRegister::getPC(m_Arch)));
Expression::Ptr InsnSize(
make_shared(singleton_object_pool<Immediate>::construct(Result(u8,
decodedInstruction->getSize()))));
Expression::Ptr postEIP(makeAddExpression(EIP, InsnSize, u32));
Expression::Ptr op(makeAddExpression(Offset, postEIP, u32));
insn_to_complete->addSuccessor(op, isCall, false, isConditional, false);
if (isConditional)
insn_to_complete->addSuccessor(postEIP, false, false, true, true);
}
break;
case am_O:
{
// Address/offset width, which is *not* what's encoded by the optype...
// The deref's width is what's actually encoded here.
int pseudoOpType;
switch(locs->address_size)
{
case 1:
pseudoOpType = op_b;
break;
case 2:
pseudoOpType = op_w;
break;
case 4:
pseudoOpType = op_d;
break;
case 0:
if(m_Arch == Arch_x86_64) {
if(!addrSizePrefixPresent)
pseudoOpType = op_q;
else
pseudoOpType = op_d;
} else {
pseudoOpType = op_v;
}
break;
default:
assert(!"Bad address size, should be 0, 1, 2, or 4!");
pseudoOpType = op_b;
break;
}
int offset_position = locs->opcode_position;
if(locs->modrm_position > offset_position && locs->modrm_operand <
(int)(insn_to_complete->m_Operands.size()))
{
offset_position = locs->modrm_position;
}
if(locs->sib_position > offset_position)
{
offset_position = locs->sib_position;
}
offset_position++;
insn_to_complete->appendOperand(makeDereferenceExpression(
decodeImmediate(pseudoOpType, b.start + offset_position),
makeSizeType(optype)), isRead, isWritten, isImplicit);
}
break;
case am_P:
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch,b_mm,locs->modrm_reg)),
isRead, isWritten, isImplicit);
break;
case am_Q:
switch(locs->modrm_mod)
{
// direct dereference
case 0x00:
case 0x01:
case 0x02:
insn_to_complete->appendOperand(makeModRMExpression(b, optype),
isRead, isWritten, isImplicit);
break;
case 0x03:
// use of actual register
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch,b_mm,locs->modrm_rm)),
isRead, isWritten, isImplicit);
break;
default:
assert(!"2-bit value modrm_mod out of range");
break;
};
break;
case am_S:
// Segment register in modrm reg field.
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch,b_segment,locs->modrm_reg)),
isRead, isWritten, isImplicit);
break;
case am_T:
// test register in modrm reg; should only be tr6/tr7, but we'll decode any of them
// NOTE: this only appears in deprecated opcodes
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch,b_tr,locs->modrm_reg)),
isRead, isWritten, isImplicit);
break;
case am_UM:
switch(locs->modrm_mod)
{
// direct dereference
case 0x00:
case 0x01:
case 0x02:
insn_to_complete->appendOperand(
makeModRMExpression(b, makeSizeType(optype)),
isRead, isWritten, isImplicit);
break;
case 0x03:
// use of actual register (am_U)
{
/* Is this a vex prefixed instruction? */
if(pref.vex_present)
{
if(!AVX_TYPE_OKAY(avx_type))
return false;
}
/* Grab the register bank and index */
if(decodeAVX(bank, &bank_index, locs->modrm_rm, AVX_XMM,
pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
}
default:
assert(!"2-bit value modrm_mod out of range");
break;
};
break;
case am_XH: /* Must be XMM */
/* Make sure we are using a valid VEX register class */
if(!AVX_TYPE_OKAY(avx_type) || !pref.vex_present)
return false;
/* Constrain register type to only the XMM banks */
avx_type = AVX_XMM;
/* Grab the correct bank and bank index for this type of register */
if(decodeAVX(bank, &bank_index, pref.vex_vvvv_reg, avx_type, pref, operand.admet))
return false;
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
case am_YH: /* Could be XMM or YMM */
/* Make sure we are using a valid VEX register class */
if(!AVX_TYPE_OKAY(avx_type) || !pref.vex_present)
return false;
/* Constrain to only XMM or YMM registers */
if(avx_type != AVX_XMM && avx_type != AVX_YMM)
avx_type = AVX_YMM;
/* Grab the correct bank and bank index for this type of register */
if(decodeAVX(bank, &bank_index, pref.vex_vvvv_reg, avx_type, pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
case am_U: /* Could be XMM, YMM, or ZMM (or possibly non VEX)*/
/* Is this a vex prefixed instruction? */
if(pref.vex_present)
{
if(!AVX_TYPE_OKAY(avx_type))
return false;
}
/* Grab the register bank and index */
if(decodeAVX(bank, &bank_index, locs->modrm_rm, AVX_XMM, pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
case am_XU: /* Must be XMM (must be VEX) */
/* Make sure this register class is valid */
if(!AVX_TYPE_OKAY(avx_type) || !pref.vex_present)
return false;
/* Constrain register to XMM banks only */
avx_type = AVX_XMM;
/* Get the register bank and index for this register */
if(decodeAVX(bank, &bank_index, locs->modrm_rm, avx_type, pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
case am_YU: /* Must be XMM or YMM (must be VEX) */
/* Make sure this register class is valid */
if(!AVX_TYPE_OKAY(avx_type) || !pref.vex_present)
return false;
/* Constrain to either XMM or YMM registers */
if(avx_type != AVX_XMM && avx_type != AVX_YMM)
avx_type = AVX_YMM;
/* Get the register bank and index */
if(decodeAVX(bank, &bank_index, locs->modrm_rm, avx_type, pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
case am_V: /* Could be XMM, YMM or ZMM (possibly non VEX)*/
/* Is this a vex prefixed instruction? */
if(pref.vex_present && !AVX_TYPE_OKAY(avx_type))
return false;
/* Get the base register number */
regnum = locs->modrm_reg;
/* Get the register bank and the index */
if(decodeAVX(bank, &bank_index, regnum, avx_type, pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
case am_XV: /* Must be XMM (must be VEX) */
if(!AVX_TYPE_OKAY(avx_type) || !pref.vex_present)
return false;
regnum = locs->modrm_reg;
/* Get the register bank and the index */
if(decodeAVX(bank, &bank_index, regnum, avx_type, pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(IntelRegTable(
m_Arch, bank, bank_index)), isRead, isWritten, isImplicit);
break;
case am_YV: /* Must be XMM or YMM (must be VEX) */
/* Make sure this register class is valid */
if(!AVX_TYPE_OKAY(avx_type) || !pref.vex_present)
return false;
regnum = locs->modrm_reg;
/* Constrain to either XMM or YMM registers */
if(avx_type != AVX_XMM && avx_type != AVX_YMM)
avx_type = AVX_YMM;
/* Get the register bank and index */
if(decodeAVX(bank, &bank_index, regnum, avx_type, pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
case am_VK: /* A KMasking register defined in the reg of a Mod/RM*/
/* Is this a vex prefixed instruction? */
if(pref.vex_present && !AVX_TYPE_OKAY(avx_type))
return false;
/* Get the base register number */
regnum = locs->modrm_reg;
/* Get the register bank and the index */
if(decodeAVX(bank, &bank_index, regnum, avx_type, pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
case am_WK: /* Could be a K mask register or memory address*/
case am_W: /* Could be XMM, YMM, or ZMM (or possibly not VEX) */
if(pref.vex_present)
{
if(!AVX_TYPE_OKAY(avx_type))
return false;
}
// if(operand.admet == am_WK)
// printf("modrm_mod: %d modrm_reg: %d modrm_rm: %d\n",
// locs->modrm_mod, locs->modrm_reg, locs->modrm_rm);
switch(locs->modrm_mod)
{
/* Direct dereference */
case 0x00:
case 0x01:
case 0x02:
insn_to_complete->appendOperand(
makeModRMExpression(b, makeSizeType(optype)),
isRead, isWritten, isImplicit);
break;
case 0x03:
/* Just the register is used */
if(decodeAVX(bank, &bank_index, locs->modrm_rm,
avx_type, pref, operand.admet))
return false;
insn_to_complete->appendOperand(
makeRegisterExpression(IntelRegTable(
m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
default:
assert(!"2-bit value modrm_mod out of range");
break;
}
break;
case am_XW: /* Must be XMM (must be VEX) */
/* Make sure this vex is okay */
if(!AVX_TYPE_OKAY(avx_type) || !pref.vex_present)
return false;
/* Constrain to the XMM banks */
avx_type = AVX_XMM;
switch(locs->modrm_mod)
{
/* Direct dereference */
case 0x00:
case 0x01:
case 0x02:
insn_to_complete->appendOperand(makeModRMExpression(b,
makeSizeType(optype)),
isRead, isWritten, isImplicit);
break;
case 0x03:
/* Just the register is used */
if(decodeAVX(bank, &bank_index, locs->modrm_rm, avx_type, pref, operand.admet))
return false;
insn_to_complete->appendOperand(
makeRegisterExpression(IntelRegTable
(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
default:
assert(!"2-bit value modrm_mod out of range");
break;
}
break;
case am_YW: /* Must be either YMM or XMM (must be VEX) */
/* Make sure the register class is okay and we have a vex prefix */
if(!AVX_TYPE_OKAY(avx_type) || !pref.vex_present)
return false;
/* Constrain to either XMM or YMM registers */
if(avx_type != AVX_XMM && avx_type != AVX_YMM)
avx_type = AVX_YMM;
switch(locs->modrm_mod)
{
/* Direct dereference */
case 0x00:
case 0x01:
case 0x02:
insn_to_complete->appendOperand(makeModRMExpression(
b, makeSizeType(optype)),
isRead, isWritten, isImplicit);
break;
case 0x03:
/* Just the register is used */
if(decodeAVX(bank, &bank_index, locs->modrm_rm, avx_type, pref, operand.admet))
return false;
/* Append the operand */
insn_to_complete->appendOperand(makeRegisterExpression(
IntelRegTable(m_Arch, bank, bank_index)),
isRead, isWritten, isImplicit);
break;
default:
assert(!"2-bit value modrm_mod out of range");
break;
}
break;
case am_X:
{
MachRegister si_reg;
if(m_Arch == Arch_x86)
{
if(addrSizePrefixPresent)
{
si_reg = x86::si;
} else {
si_reg = x86::esi;
}
} else {
if(addrSizePrefixPresent)
{
si_reg = x86_64::esi;
} else {
si_reg = x86_64::rsi;
}
}
Expression::Ptr ds(makeRegisterExpression(
m_Arch == Arch_x86 ? x86::ds : x86_64::ds));
Expression::Ptr si(makeRegisterExpression(si_reg));
Expression::Ptr segmentOffset(make_shared
(singleton_object_pool<Immediate>::construct(Result(u32, 0x10))));
Expression::Ptr ds_segment = makeMultiplyExpression(
ds, segmentOffset, u32);
Expression::Ptr ds_si = makeAddExpression(ds_segment, si, u32);
insn_to_complete->appendOperand(
makeDereferenceExpression(ds_si, makeSizeType(optype)),
isRead, isWritten, isImplicit);
}
break;
case am_Y:
{
MachRegister di_reg;
if(m_Arch == Arch_x86)
{
if(addrSizePrefixPresent)
{
di_reg = x86::di;
} else {
di_reg = x86::edi;
}
} else {
if(addrSizePrefixPresent)
{
di_reg = x86_64::edi;
} else {
di_reg = x86_64::rdi;
}
}
Expression::Ptr es(makeRegisterExpression(
m_Arch == Arch_x86 ? x86::es : x86_64::es));
Expression::Ptr di(makeRegisterExpression(di_reg));
Immediate::Ptr imm(make_shared(
singleton_object_pool<Immediate>::construct(Result(u32, 0x10))));
Expression::Ptr es_segment(
makeMultiplyExpression(es,imm, u32));
Expression::Ptr es_di(makeAddExpression(es_segment, di, u32));
insn_to_complete->appendOperand(
makeDereferenceExpression(es_di, makeSizeType(optype)),
isRead, isWritten, isImplicit);
}
break;
case am_tworeghack:
if(optype == op_edxeax)
{
Expression::Ptr edx(makeRegisterExpression(m_Arch == Arch_x86 ? x86::edx : x86_64::edx));
Expression::Ptr eax(makeRegisterExpression(m_Arch == Arch_x86 ? x86::eax : x86_64::eax));
Expression::Ptr highAddr = makeMultiplyExpression(edx, Immediate::makeImmediate(Result(u64, 1LL << 32)), u64);
Expression::Ptr addr = makeAddExpression(highAddr, eax, u64);
Expression::Ptr op = makeDereferenceExpression(addr, u64);
insn_to_complete->appendOperand(op, isRead, isWritten, isImplicit);
} else if (optype == op_ecxebx)
{
Expression::Ptr ecx(makeRegisterExpression(m_Arch == Arch_x86 ? x86::ecx : x86_64::ecx));
Expression::Ptr ebx(makeRegisterExpression(m_Arch == Arch_x86 ? x86::ebx : x86_64::ebx));
Expression::Ptr highAddr = makeMultiplyExpression(ecx,
Immediate::makeImmediate(Result(u64, 1LL << 32)), u64);
Expression::Ptr addr = makeAddExpression(highAddr, ebx, u64);
Expression::Ptr op = makeDereferenceExpression(addr, u64);
insn_to_complete->appendOperand(op, isRead, isWritten, isImplicit);
}
break;
case am_reg:
{
MachRegister r(optype);
int size = r.size();
if((m_Arch == Arch_x86_64) && (r.regClass() == (unsigned int)x86::GPR) && (size == 4))
{
int reg_size = isDefault64Insn() ? op_q : op_v;
if(sizePrefixPresent)
{
reg_size = op_w;
}
// implicit regs are not extended
r = makeRegisterID((r.val() & 0xFF), reg_size, false);
entryID entryid = decodedInstruction->getEntry()->getID(locs);
if(locs->rex_b && insn_to_complete->m_Operands.empty() &&
(entryid == e_push || entryid == e_pop || entryid == e_xchg || ((*(b.start + locs->opcode_position) & 0xf0) == 0xb0)))
{
r = MachRegister((r.val()) | x86_64::r8.val());
assert(r.name() != "<INVALID_REG>");
}
} else {
r = MachRegister((r.val() & ~r.getArchitecture()) | m_Arch);
entryID entryid = decodedInstruction->getEntry()->getID(locs);
if(insn_to_complete->m_Operands.empty() &&
(entryid == e_push || entryid == e_pop || entryid == e_xchg || ((*(b.start + locs->opcode_position) & 0xf0) == 0xb0) ) )
{
unsigned int opcode_byte = *(b.start+locs->opcode_position);
unsigned int reg_id = (opcode_byte & 0x07);
if(locs->rex_b)
{
// FP stack registers are not affected by the rex_b bit in AM_REG.
if(r.regClass() == (unsigned) x86::GPR)
{
int reg_op_type = op_d;
switch(size)
{
case 1:
reg_op_type = op_b;
break;
case 2:
reg_op_type = op_w;
break;
case 8:
reg_op_type = op_q;
break;
default:
break;
}
r = makeRegisterID(reg_id, reg_op_type, true);
assert(r.name() != "<INVALID_REG>");
}
} else if((r.size() == 1) && (locs->rex_byte & 0x40))
{
r = makeRegisterID(reg_id, op_b, false);
assert(r.name() != "<INVALID_REG>");
}
}
if(sizePrefixPresent && (r.regClass() == (unsigned int)x86::GPR) && r.size() >= 4)
{
r = MachRegister((r.val() & ~x86::FULL) | x86::W_REG);
assert(r.name() != "<INVALID_REG>");
}
}
Expression::Ptr op(makeRegisterExpression(r));
insn_to_complete->appendOperand(op, isRead, isWritten, isImplicit);
}
break;
case am_stackH:
case am_stackP:
// handled elsewhere
break;
case am_allgprs:
if(m_Arch == Arch_x86)
{
insn_to_complete->appendOperand(makeRegisterExpression(x86::eax), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86::ecx), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86::edx), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86::ebx), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86::esp), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86::ebp), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86::esi), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86::edi), isRead, isWritten, isImplicit);
} else {
insn_to_complete->appendOperand(makeRegisterExpression(x86_64::eax), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86_64::ecx), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86_64::edx), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86_64::ebx), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86_64::esp), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86_64::ebp), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86_64::esi), isRead, isWritten, isImplicit);
insn_to_complete->appendOperand(makeRegisterExpression(x86_64::edi), isRead, isWritten, isImplicit);
}
break;
case am_ImplImm:
insn_to_complete->appendOperand(Immediate::makeImmediate(Result(makeSizeType(optype), 1)), isRead, isWritten, isImplicit);
break;
default:
printf("decodeOneOperand() called with unknown addressing method %u\n", operand.admet);
// assert(0);
return false;
}
return true;
}
void InstructionDecoder_x86::doIA32Decode(InstructionDecoder::buffer& b)
{
if(decodedInstruction == NULL)
{
decodedInstruction = reinterpret_cast<ia32_instruction*>(malloc(sizeof(ia32_instruction)));
assert(decodedInstruction);
}
if(locs == NULL)
{
locs = reinterpret_cast<ia32_locations*>(malloc(sizeof(ia32_locations)));
assert(locs);
}
locs = new(locs) ia32_locations; //reinit();
assert(locs->sib_position == -1);
decodedInstruction = new (decodedInstruction) ia32_instruction(NULL, NULL, locs);
ia32_decode(IA32_DECODE_PREFIXES, b.start, *decodedInstruction, is64BitMode);
static ia32_entry invalid = { e_No_Entry, 0, 0, false, { {0,0}, {0,0}, {0,0} }, 0, 0, 0 };
if(decodedInstruction->getLegacyType() == ILLEGAL) {
m_Operation = Operation(&invalid, nullptr, nullptr, m_Arch);
return;
}
sizePrefixPresent = (decodedInstruction->getPrefix()->getOperSzPrefix() == 0x66);
if (decodedInstruction->getPrefix()->rexW()) {
// as per 2.2.1.2 - rex.w overrides 66h
sizePrefixPresent = false;
}
addrSizePrefixPresent = (decodedInstruction->getPrefix()->getAddrSzPrefix() == 0x67);
if(decodedInstruction->getEntry()) {
// check prefix validity
// lock prefix only allowed on certain insns.
// TODO: refine further to check memory written operand
if(decodedInstruction->getPrefix()->getPrefix(0) == PREFIX_LOCK)
{
switch(decodedInstruction->getEntry()->id)
{
case e_add:
case e_adc:
case e_and:
case e_btc:
case e_btr:
case e_bts:
case e_cmpxchg:
case e_cmpxchg8b:
case e_dec:
case e_inc:
case e_neg:
case e_not:
case e_or:
case e_sbb:
case e_sub:
case e_xor:
case e_xadd:
case e_xchg:
break;
default:
m_Operation = Operation(&invalid,
decodedInstruction->getPrefix(), locs, m_Arch);
return;
}
} else if (decodedInstruction->getPrefix()->getPrefix(0) == PREFIX_REP &&
*(b.start+1) == (unsigned char)(0x0F) && *(b.start+2) == (unsigned char)(0x1E)) {
// handling ENDBR family
if (*(b.start+3) == (unsigned char)(0xFB)) {
m_Operation = Operation(e_endbr32, entryNames_IAPI[e_endbr32], m_Arch);
return;
} else if (*(b.start+3) == (unsigned char)(0xFA)) {
m_Operation = Operation(e_endbr64, entryNames_IAPI[e_endbr64], m_Arch);
return;
}
}
m_Operation = Operation(decodedInstruction->getEntry(),
decodedInstruction->getPrefix(), locs, m_Arch);
} else {
// Gap parsing can trigger this case; in particular, when it encounters prefixes in an invalid order.
// Notably, if a REX prefix (0x40-0x48) appears followed by another prefix (0x66, 0x67, etc)
// we'll reject the instruction as invalid and send it back with no entry. Since this is a common
// byte sequence to see in, for example, ASCII strings, we want to simply accept this and move on, not
// yell at the user.
m_Operation = Operation(&invalid,
decodedInstruction->getPrefix(), locs, m_Arch);
}
}
void InstructionDecoder_x86::decodeOpcode(InstructionDecoder::buffer& b)
{
doIA32Decode(b);
// Do not move through the buffer if a bad instruction was encountered
if(m_Operation.getID() == e_No_Entry) return;
b.start += decodedInstruction->getSize();
}
bool InstructionDecoder_x86::decodeOperands(const Instruction* insn_to_complete)
{
int imm_index = 0; // handle multiple immediate operands
if(!decodedInstruction || !decodedInstruction->getEntry()) return false;
unsigned int opsema = decodedInstruction->getEntry()->opsema;
unsigned int semantics = opsema & 0xFF;
unsigned int implicit_operands =
sGetImplicitOPs(decodedInstruction->getEntry()->impl_dec);
InstructionDecoder::buffer b(insn_to_complete->ptr(), insn_to_complete->size());
if (decodedInstruction->getEntry()->getID() == e_ret_near ||
decodedInstruction->getEntry()->getID() == e_ret_far) {
Expression::Ptr ret_addr = makeDereferenceExpression(makeRegisterExpression(is64BitMode ? x86_64::rsp : x86::esp),
is64BitMode ? u64 : u32);
insn_to_complete->addSuccessor(ret_addr, false, true, false, false, true);
}
if (insn_to_complete->getOperation().getID() == e_endbr32 ||
insn_to_complete->getOperation().getID() == e_endbr64) {
insn_to_complete->m_Operands.clear();
return true;
}
for(int i = 0; i < 3; i++)
{
if(decodedInstruction->getEntry()->operands[i].admet == 0 &&
decodedInstruction->getEntry()->operands[i].optype == 0)
break;
if(!decodeOneOperand(b,
decodedInstruction->getEntry()->operands[i],
imm_index,
insn_to_complete,
readsOperand(semantics, i),
writesOperand(semantics, i),
implicitOperand(implicit_operands, i)))
{
return false;
}
}
/* Does this instruction have a 4th operand? */
if(semantics >= s4OP)
{
if (decodedInstruction->getEntry()->operands[3].admet != 0 ||
decodedInstruction->getEntry()->operands[3].optype != 0) {
// Special handling for FMA4 instructions
if(!decodeOneOperand(b,
decodedInstruction->getEntry()->operands[3],
imm_index,
insn_to_complete,
readsOperand(semantics, 3),
writesOperand(semantics, 3),
implicitOperand(implicit_operands, 3)))
{
return false;
}
} else if(!decodeOneOperand(b,
{am_I, op_b}, /* This is always an IMM8 */
imm_index,
insn_to_complete,
readsOperand(semantics, 3),
writesOperand(semantics, 3),
implicitOperand(implicit_operands, 3)))
{
return false;
}
}
ia32_prefixes& pref = *decodedInstruction->getPrefix();
/* Is this an EVEX prefixed instruction? */
if(pref.vex_type == VEX_TYPE_EVEX)
{
insn_to_complete->appendOperand(makeMaskRegisterExpression(
IntelRegTable(m_Arch, b_kmask, pref.vex_aaa)),
true, false);
}
return true;
}
INSTRUCTION_EXPORT Instruction InstructionDecoder_x86::decode(InstructionDecoder::buffer& b)
{
return InstructionDecoderImpl::decode(b);
}
void InstructionDecoder_x86::doDelayedDecode(const Instruction* insn_to_complete)
{
InstructionDecoder::buffer b(insn_to_complete->ptr(), insn_to_complete->size());
//insn_to_complete->m_Operands.reserve(4);
doIA32Decode(b);
decodeOperands(insn_to_complete);
}
}
}