Files
lifting-bits-remill/remill/Arch/X86/Arch.cpp
T
Peter Goodman 99df2e19d4 Running clang-format on files with some additional custom scripts for… (#444)
* Running clang-format on files with some additional custom scripts for my style

* Fix missing unique_ptr in remill/BC/Optimizer.h

* Fixes and selective disabling of clang-format
2020-08-05 15:42:25 -04:00

1212 lines
42 KiB
C++

/*
* Copyright (c) 2017 Trail of Bits, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "remill/Arch/Arch.h"
#include <glog/logging.h>
#include <llvm/ADT/Triple.h>
#include <llvm/IR/Attributes.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Module.h>
#include <iomanip>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include "remill/Arch/Instruction.h"
#include "remill/Arch/Name.h"
#include "remill/Arch/X86/XED.h"
#include "remill/BC/Version.h"
#include "remill/OS/OS.h"
namespace remill {
namespace {
static const xed_state_t kXEDState32 = {XED_MACHINE_MODE_LONG_COMPAT_32,
XED_ADDRESS_WIDTH_32b};
static const xed_state_t kXEDState64 = {XED_MACHINE_MODE_LONG_64,
XED_ADDRESS_WIDTH_64b};
static bool Is64Bit(ArchName arch_name) {
return kArchAMD64 == arch_name || kArchAMD64_AVX == arch_name ||
kArchAMD64_AVX512 == arch_name;
}
static bool IsFunctionReturn(const xed_decoded_inst_t *xedd) {
auto iclass = xed_decoded_inst_get_iclass(xedd);
return XED_ICLASS_RET_NEAR == iclass || XED_ICLASS_RET_FAR == iclass;
}
// TODO(pag): Should far calls be treated as syscalls or indirect calls?
static bool IsSystemCall(const xed_decoded_inst_t *xedd) {
auto iclass = xed_decoded_inst_get_iclass(xedd);
return XED_ICLASS_SYSCALL == iclass || XED_ICLASS_SYSCALL_AMD == iclass ||
XED_ICLASS_SYSENTER == iclass;
}
static bool IsSystemReturn(const xed_decoded_inst_t *xedd) {
auto iclass = xed_decoded_inst_get_iclass(xedd);
return XED_ICLASS_SYSRET == iclass || XED_ICLASS_SYSRET_AMD == iclass ||
XED_ICLASS_SYSEXIT == iclass;
}
static bool IsInterruptCall(const xed_decoded_inst_t *xedd) {
auto iclass = xed_decoded_inst_get_iclass(xedd);
return XED_ICLASS_INT == iclass || XED_ICLASS_INT1 == iclass ||
XED_ICLASS_INT3 == iclass;
}
static bool IsConditionalInterruptCall(const xed_decoded_inst_t *xedd) {
auto iclass = xed_decoded_inst_get_iclass(xedd);
return XED_ICLASS_INTO == iclass || XED_ICLASS_BOUND == iclass;
}
static bool IsInterruptReturn(const xed_decoded_inst_t *xedd) {
auto iclass = xed_decoded_inst_get_iclass(xedd);
return XED_ICLASS_IRET <= iclass && XED_ICLASS_IRETQ >= iclass;
}
// This includes `JRCXZ`.
static bool IsConditionalBranch(const xed_decoded_inst_t *xedd) {
return XED_CATEGORY_COND_BR == xed_decoded_inst_get_category(xedd);
}
static bool IsDirectFunctionCall(const xed_decoded_inst_t *xedd) {
auto xedi = xed_decoded_inst_inst(xedd);
auto xedo = xed_inst_operand(xedi, 0);
auto op_name = xed_operand_name(xedo);
auto iclass = xed_decoded_inst_get_iclass(xedd);
return XED_ICLASS_CALL_NEAR == iclass && XED_OPERAND_RELBR == op_name;
}
static bool IsDirectFunctionCallFar(const xed_decoded_inst_t *xedd) {
auto xedi = xed_decoded_inst_inst(xedd);
auto xedo = xed_inst_operand(xedi, 0);
auto op_name = xed_operand_name(xedo);
auto iclass = xed_decoded_inst_get_iclass(xedd);
return XED_ICLASS_CALL_FAR == iclass && XED_OPERAND_PTR == op_name;
}
static bool IsIndirectFunctionCall(const xed_decoded_inst_t *xedd) {
auto xedi = xed_decoded_inst_inst(xedd);
auto xedo = xed_inst_operand(xedi, 0);
auto op_name = xed_operand_name(xedo);
auto iclass = xed_decoded_inst_get_iclass(xedd);
return XED_ICLASS_CALL_NEAR == iclass && XED_OPERAND_RELBR != op_name;
}
static bool IsIndirectFunctionCallFar(const xed_decoded_inst_t *xedd) {
auto xedi = xed_decoded_inst_inst(xedd);
auto xedo = xed_inst_operand(xedi, 0);
auto op_name = xed_operand_name(xedo);
auto iclass = xed_decoded_inst_get_iclass(xedd);
return XED_ICLASS_CALL_FAR == iclass && XED_OPERAND_MEM0 == op_name;
}
static bool IsDirectJump(const xed_decoded_inst_t *xedd) {
auto xedi = xed_decoded_inst_inst(xedd);
auto xedo = xed_inst_operand(xedi, 0);
auto op_name = xed_operand_name(xedo);
auto iclass = xed_decoded_inst_get_iclass(xedd);
return XED_ICLASS_JMP == iclass && XED_OPERAND_RELBR == op_name;
}
static bool IsDirectJumpFar(const xed_decoded_inst_t *xedd) {
auto xedi = xed_decoded_inst_inst(xedd);
auto xedo = xed_inst_operand(xedi, 0);
auto op_name = xed_operand_name(xedo);
auto iclass = xed_decoded_inst_get_iclass(xedd);
return XED_ICLASS_JMP_FAR == iclass && XED_OPERAND_PTR == op_name;
}
static bool IsIndirectJump(const xed_decoded_inst_t *xedd) {
auto xedi = xed_decoded_inst_inst(xedd);
auto xedo = xed_inst_operand(xedi, 0);
auto op_name = xed_operand_name(xedo);
auto iclass = xed_decoded_inst_get_iclass(xedd);
return (XED_ICLASS_JMP == iclass && XED_OPERAND_RELBR != op_name) ||
XED_ICLASS_XEND == iclass || XED_ICLASS_XABORT == iclass;
}
static bool IsIndirectJumpFar(const xed_decoded_inst_t *xedd) {
auto xedi = xed_decoded_inst_inst(xedd);
auto xedo = xed_inst_operand(xedi, 0);
auto op_name = xed_operand_name(xedo);
auto iclass = xed_decoded_inst_get_iclass(xedd);
return XED_ICLASS_JMP_FAR == iclass && XED_OPERAND_MEM0 == op_name;
}
//It checks if the instruction might fault and uses StopFailure to recover
static bool UsesStopFailure(const xed_decoded_inst_t *xedd) {
switch (xed_decoded_inst_get_iclass(xedd)) {
case XED_ICLASS_DIV:
case XED_ICLASS_IDIV:
case XED_ICLASS_XEND:
case XED_ICLASS_XGETBV: return true;
default: return false;
}
}
static bool IsNoOp(const xed_decoded_inst_t *xedd) {
switch (xed_decoded_inst_get_category(xedd)) {
case XED_CATEGORY_NOP:
case XED_CATEGORY_WIDENOP: return true;
default: return false;
}
}
static bool IsError(const xed_decoded_inst_t *xedd) {
switch (xed_decoded_inst_get_iclass(xedd)) {
case XED_ICLASS_HLT:
case XED_ICLASS_UD0:
case XED_ICLASS_UD1:
case XED_ICLASS_UD2: return true;
default: return false;
}
}
static bool IsInvalid(const xed_decoded_inst_t *xedd) {
return XED_ICLASS_INVALID == xed_decoded_inst_get_iclass(xedd);
}
// Return the category of this instuction.
static Instruction::Category CreateCategory(const xed_decoded_inst_t *xedd) {
if (IsInvalid(xedd)) {
return Instruction::kCategoryInvalid;
} else if (IsError(xedd)) {
return Instruction::kCategoryError;
} else if (IsDirectJump(xedd)) {
return Instruction::kCategoryDirectJump;
} else if (IsIndirectJump(xedd)) {
return Instruction::kCategoryIndirectJump;
} else if (IsDirectFunctionCall(xedd)) {
return Instruction::kCategoryDirectFunctionCall;
} else if (IsIndirectFunctionCall(xedd)) {
return Instruction::kCategoryIndirectFunctionCall;
} else if (IsFunctionReturn(xedd)) {
return Instruction::kCategoryFunctionReturn;
} else if (IsConditionalBranch(xedd)) {
return Instruction::kCategoryConditionalBranch;
// Instruction implementation handles syscall emulation.
} else if (IsSystemCall(xedd)) {
return Instruction::kCategoryAsyncHyperCall;
} else if (IsSystemReturn(xedd)) {
return Instruction::kCategoryAsyncHyperCall;
// Instruction implementation handles syscall (x86, x32) emulation. This is
// invoked even for conditional interrupt, where a special flag is used to
// denote that the interrupt should happen.
} else if (IsInterruptCall(xedd)) {
return Instruction::kCategoryAsyncHyperCall;
} else if (IsConditionalInterruptCall(xedd)) {
return Instruction::kCategoryConditionalAsyncHyperCall;
} else if (IsInterruptReturn(xedd)) {
return Instruction::kCategoryAsyncHyperCall;
} else if (IsDirectJumpFar(xedd) || IsIndirectJumpFar(xedd) ||
IsDirectFunctionCallFar(xedd) || IsIndirectFunctionCallFar(xedd)) {
return Instruction::kCategoryAsyncHyperCall;
} else if (IsNoOp(xedd)) {
return Instruction::kCategoryNoOp;
} else {
return Instruction::kCategoryNormal;
}
}
std::map<xed_iform_enum_t, xed_iform_enum_t> kUnlockedIform = {
{XED_IFORM_ADC_LOCK_MEMb_IMMb_80r2, XED_IFORM_ADC_MEMb_IMMb_80r2},
{XED_IFORM_ADC_LOCK_MEMv_IMMz, XED_IFORM_ADC_MEMv_IMMz},
{XED_IFORM_ADC_LOCK_MEMb_IMMb_82r2, XED_IFORM_ADC_MEMb_IMMb_82r2},
{XED_IFORM_ADC_LOCK_MEMv_IMMb, XED_IFORM_ADC_MEMv_IMMb},
{XED_IFORM_ADC_LOCK_MEMb_GPR8, XED_IFORM_ADC_MEMb_GPR8},
{XED_IFORM_ADC_LOCK_MEMv_GPRv, XED_IFORM_ADC_MEMv_GPRv},
{XED_IFORM_DEC_LOCK_MEMb, XED_IFORM_DEC_MEMb},
{XED_IFORM_DEC_LOCK_MEMv, XED_IFORM_DEC_MEMv},
{XED_IFORM_NOT_LOCK_MEMb, XED_IFORM_NOT_MEMb},
{XED_IFORM_NOT_LOCK_MEMv, XED_IFORM_NOT_MEMv},
{XED_IFORM_SUB_LOCK_MEMb_IMMb_80r5, XED_IFORM_SUB_MEMb_IMMb_80r5},
{XED_IFORM_SUB_LOCK_MEMv_IMMz, XED_IFORM_SUB_MEMv_IMMz},
{XED_IFORM_SUB_LOCK_MEMb_IMMb_82r5, XED_IFORM_SUB_MEMb_IMMb_82r5},
{XED_IFORM_SUB_LOCK_MEMv_IMMb, XED_IFORM_SUB_MEMv_IMMb},
{XED_IFORM_SUB_LOCK_MEMb_GPR8, XED_IFORM_SUB_MEMb_GPR8},
{XED_IFORM_SUB_LOCK_MEMv_GPRv, XED_IFORM_SUB_MEMv_GPRv},
{XED_IFORM_BTC_LOCK_MEMv_IMMb, XED_IFORM_BTC_MEMv_IMMb},
{XED_IFORM_BTC_LOCK_MEMv_GPRv, XED_IFORM_BTC_MEMv_GPRv},
{XED_IFORM_AND_LOCK_MEMb_IMMb_80r4, XED_IFORM_AND_MEMb_IMMb_80r4},
{XED_IFORM_AND_LOCK_MEMv_IMMz, XED_IFORM_AND_MEMv_IMMz},
{XED_IFORM_AND_LOCK_MEMb_IMMb_82r4, XED_IFORM_AND_MEMb_IMMb_82r4},
{XED_IFORM_AND_LOCK_MEMv_IMMb, XED_IFORM_AND_MEMv_IMMb},
{XED_IFORM_AND_LOCK_MEMb_GPR8, XED_IFORM_AND_MEMb_GPR8},
{XED_IFORM_AND_LOCK_MEMv_GPRv, XED_IFORM_AND_MEMv_GPRv},
{XED_IFORM_CMPXCHG_LOCK_MEMb_GPR8, XED_IFORM_CMPXCHG_MEMb_GPR8},
{XED_IFORM_CMPXCHG_LOCK_MEMv_GPRv, XED_IFORM_CMPXCHG_MEMv_GPRv},
{XED_IFORM_INC_LOCK_MEMb, XED_IFORM_INC_MEMb},
{XED_IFORM_INC_LOCK_MEMv, XED_IFORM_INC_MEMv},
{XED_IFORM_OR_LOCK_MEMb_IMMb_80r1, XED_IFORM_OR_MEMb_IMMb_80r1},
{XED_IFORM_OR_LOCK_MEMv_IMMz, XED_IFORM_OR_MEMv_IMMz},
{XED_IFORM_OR_LOCK_MEMb_IMMb_82r1, XED_IFORM_OR_MEMb_IMMb_82r1},
{XED_IFORM_OR_LOCK_MEMv_IMMb, XED_IFORM_OR_MEMv_IMMb},
{XED_IFORM_OR_LOCK_MEMb_GPR8, XED_IFORM_OR_MEMb_GPR8},
{XED_IFORM_OR_LOCK_MEMv_GPRv, XED_IFORM_OR_MEMv_GPRv},
{XED_IFORM_XADD_LOCK_MEMb_GPR8, XED_IFORM_XADD_MEMb_GPR8},
{XED_IFORM_XADD_LOCK_MEMv_GPRv, XED_IFORM_XADD_MEMv_GPRv},
{XED_IFORM_ADD_LOCK_MEMb_IMMb_80r0, XED_IFORM_ADD_MEMb_IMMb_80r0},
{XED_IFORM_ADD_LOCK_MEMv_IMMz, XED_IFORM_ADD_MEMv_IMMz},
{XED_IFORM_ADD_LOCK_MEMb_IMMb_82r0, XED_IFORM_ADD_MEMb_IMMb_82r0},
{XED_IFORM_ADD_LOCK_MEMv_IMMb, XED_IFORM_ADD_MEMv_IMMb},
{XED_IFORM_ADD_LOCK_MEMb_GPR8, XED_IFORM_ADD_MEMb_GPR8},
{XED_IFORM_ADD_LOCK_MEMv_GPRv, XED_IFORM_ADD_MEMv_GPRv},
{XED_IFORM_SBB_LOCK_MEMb_IMMb_80r3, XED_IFORM_SBB_MEMb_IMMb_80r3},
{XED_IFORM_SBB_LOCK_MEMv_IMMz, XED_IFORM_SBB_MEMv_IMMz},
{XED_IFORM_SBB_LOCK_MEMb_IMMb_82r3, XED_IFORM_SBB_MEMb_IMMb_82r3},
{XED_IFORM_SBB_LOCK_MEMv_IMMb, XED_IFORM_SBB_MEMv_IMMb},
{XED_IFORM_SBB_LOCK_MEMb_GPR8, XED_IFORM_SBB_MEMb_GPR8},
{XED_IFORM_SBB_LOCK_MEMv_GPRv, XED_IFORM_SBB_MEMv_GPRv},
{XED_IFORM_BTS_LOCK_MEMv_IMMb, XED_IFORM_BTS_MEMv_IMMb},
{XED_IFORM_BTS_LOCK_MEMv_GPRv, XED_IFORM_BTS_MEMv_GPRv},
{XED_IFORM_XOR_LOCK_MEMb_IMMb_80r6, XED_IFORM_XOR_MEMb_IMMb_80r6},
{XED_IFORM_XOR_LOCK_MEMv_IMMz, XED_IFORM_XOR_MEMv_IMMz},
{XED_IFORM_XOR_LOCK_MEMb_IMMb_82r6, XED_IFORM_XOR_MEMb_IMMb_82r6},
{XED_IFORM_XOR_LOCK_MEMv_IMMb, XED_IFORM_XOR_MEMv_IMMb},
{XED_IFORM_XOR_LOCK_MEMb_GPR8, XED_IFORM_XOR_MEMb_GPR8},
{XED_IFORM_XOR_LOCK_MEMv_GPRv, XED_IFORM_XOR_MEMv_GPRv},
{XED_IFORM_BTR_LOCK_MEMv_IMMb, XED_IFORM_BTR_MEMv_IMMb},
{XED_IFORM_BTR_LOCK_MEMv_GPRv, XED_IFORM_BTR_MEMv_GPRv},
{XED_IFORM_CMPXCHG8B_LOCK_MEMq, XED_IFORM_CMPXCHG8B_MEMq},
{XED_IFORM_CMPXCHG8B_LOCK_MEMq, XED_IFORM_CMPXCHG8B_MEMq},
{XED_IFORM_CMPXCHG16B_LOCK_MEMdq, XED_IFORM_CMPXCHG16B_MEMdq},
{XED_IFORM_NEG_LOCK_MEMb, XED_IFORM_NEG_MEMb},
{XED_IFORM_NEG_LOCK_MEMv, XED_IFORM_NEG_MEMv},
};
// Name of this instruction function.
static std::string InstructionFunctionName(const xed_decoded_inst_t *xedd) {
// If this instuction is marked as atomic via the `LOCK` prefix then we want
// to remove it because we will already be surrounding the call to the
// semantics function with the atomic begin/end intrinsics.
auto iform = xed_decoded_inst_get_iform_enum(xedd);
if (xed_operand_values_has_lock_prefix(xedd)) {
CHECK(kUnlockedIform.count(iform))
<< xed_iform_enum_t2str(iform) << " has no unlocked iform mapping.";
iform = kUnlockedIform[iform];
}
std::stringstream ss;
std::string iform_name = xed_iform_enum_t2str(iform);
ss << iform_name;
// Some instructions are "scalable", i.e. there are variants of the
// instruction for each effective operand size. We represent these in
// the semantics files with `_<size>`, so we need to look up the correct
// selection.
if (xed_decoded_inst_get_attribute(xedd, XED_ATTRIBUTE_SCALABLE)) {
ss << "_";
ss << xed_decoded_inst_get_operand_width(xedd);
}
// Suffix the ISEL function name with the segment or control register names,
// as a runtime may need to perform complex actions that are specific to
// the register used.
if (XED_IFORM_MOV_SEG_MEMw == iform || XED_IFORM_MOV_SEG_GPR16 == iform ||
XED_IFORM_MOV_CR_CR_GPR32 == iform ||
XED_IFORM_MOV_CR_CR_GPR64 == iform) {
ss << "_";
ss << xed_reg_enum_t2str(xed_decoded_inst_get_reg(xedd, XED_OPERAND_REG0));
}
return ss.str();
}
// Decode an instruction into the XED instuction format.
static bool DecodeXED(xed_decoded_inst_t *xedd, const xed_state_t *mode,
std::string_view inst_bytes, uint64_t address) {
auto num_bytes = inst_bytes.size();
auto bytes = reinterpret_cast<const uint8_t *>(inst_bytes.data());
xed_decoded_inst_zero_set_mode(xedd, mode);
xed_decoded_inst_set_input_chip(xedd, XED_CHIP_INVALID);
auto err = xed_decode(xedd, bytes, static_cast<uint32_t>(num_bytes));
if (XED_ERROR_NONE != err) {
std::stringstream ss;
for (auto b : inst_bytes) {
ss << ' ' << std::hex << std::setw(2) << std::setfill('0')
<< (static_cast<unsigned>(b) & 0xFFu);
}
LOG(ERROR) << "Unable to decode instruction at " << std::hex << address
<< " with bytes" << ss.str()
<< " and error: " << xed_error_enum_t2str(err) << std::dec;
return false;
}
return true;
}
// Variable operand for a read register.
static Operand::Register RegOp(xed_reg_enum_t reg) {
Operand::Register reg_op;
if (XED_REG_INVALID != reg) {
switch (reg) {
case XED_REG_ST0: reg_op.name = "ST0"; break;
case XED_REG_ST1: reg_op.name = "ST1"; break;
case XED_REG_ST2: reg_op.name = "ST2"; break;
case XED_REG_ST3: reg_op.name = "ST3"; break;
case XED_REG_ST4: reg_op.name = "ST4"; break;
case XED_REG_ST5: reg_op.name = "ST5"; break;
case XED_REG_ST6: reg_op.name = "ST6"; break;
case XED_REG_ST7: reg_op.name = "ST7"; break;
default: reg_op.name = xed_reg_enum_t2str(reg); break;
}
if (XED_REG_X87_FIRST <= reg && XED_REG_X87_LAST >= reg) {
reg_op.size = 64;
} else {
reg_op.size = xed_get_register_width_bits64(reg);
}
}
return reg_op;
}
static Operand::Register SegBaseRegOp(xed_reg_enum_t reg, unsigned addr_size) {
auto op = RegOp(reg);
if (XED_REG_INVALID != reg) {
op.name += "_BASE";
op.size = addr_size;
}
return op;
}
// Decode a memory operand.
static void DecodeMemory(Instruction &inst, const xed_decoded_inst_t *xedd,
const xed_operand_t *xedo, int mem_index) {
auto iform = xed_decoded_inst_get_iform_enum(xedd);
auto iclass = xed_decoded_inst_get_iclass(xedd);
auto op_name = xed_operand_name(xedo);
auto segment = xed_decoded_inst_get_seg_reg(xedd, mem_index);
auto base = xed_decoded_inst_get_base_reg(xedd, mem_index);
auto index = xed_decoded_inst_get_index_reg(xedd, mem_index);
auto disp = xed_decoded_inst_get_memory_displacement(xedd, mem_index);
auto scale = xed_decoded_inst_get_scale(xedd, mem_index);
auto base_wide = xed_get_largest_enclosing_register(base);
// NOTE(pag): This isn't quite right (eg. it's for SCALABALE only), but works
// mostly right most of the time.
auto size = xed_decoded_inst_get_operand_width(xedd);
if (XED_IFORM_MOV_MEMw_SEG == iform) {
size = 16;
}
// Deduce the implicit segment register if it is absent.
if (XED_REG_INVALID == segment) {
segment = XED_REG_DS;
if (XED_REG_RSP == base_wide || XED_REG_RBP == base_wide) {
segment = XED_REG_SS;
}
}
// On AMD64, only the `FS` and `GS` segments are non-zero.
if (Is64Bit(inst.arch_name) && XED_REG_FS != segment &&
XED_REG_GS != segment) {
segment = XED_REG_INVALID;
// AGEN operands, e.g. for the `LEA` instuction, can be marked with an
// explicit segment, but it is ignored.
} else if (XED_OPERAND_AGEN == op_name) {
segment = XED_REG_INVALID;
}
// Special case: `POP [xSP + ...] uses the value of `xSP` after incrementing
// it by the stack width.
if (XED_ICLASS_POP == iclass && XED_REG_RSP == base_wide) {
disp += static_cast<int64_t>(size / 8);
}
Operand op = {};
op.size = size;
op.type = Operand::kTypeAddress;
op.addr.address_size =
xed_decoded_inst_get_memop_address_width(xedd, mem_index);
op.addr.segment_base_reg = SegBaseRegOp(segment, op.addr.address_size);
op.addr.base_reg = RegOp(base);
op.addr.index_reg = RegOp(index);
op.addr.scale = XED_REG_INVALID != index ? static_cast<int64_t>(scale) : 0;
op.addr.displacement = disp;
// PC-relative memory accesses are relative to the next PC.
if (XED_REG_RIP == base_wide) {
op.addr.base_reg.name = "NEXT_PC";
}
// We always pass destination operands first, then sources. Memory operands
// are represented by their addresses, and in the instuction implementations,
// accessed via intrinsics.
if (xed_operand_written(xedo)) {
op.action = Operand::kActionWrite;
op.addr.kind = Operand::Address::kMemoryWrite;
inst.operands.push_back(op);
}
if (xed_operand_read(xedo)) {
op.action = Operand::kActionRead;
if (XED_OPERAND_AGEN == op_name) {
op.addr.kind = Operand::Address::kAddressCalculation;
} else {
op.addr.kind = Operand::Address::kMemoryRead;
}
inst.operands.push_back(op);
}
}
// Decode an immediate constant.
static void DecodeImmediate(Instruction &inst, const xed_decoded_inst_t *xedd,
xed_operand_enum_t op_name) {
auto val = 0ULL;
auto is_signed = false;
auto operand_size = xed_decoded_inst_get_operand_width(xedd);
Operand op = {};
op.type = Operand::kTypeImmediate;
op.action = Operand::kActionRead;
if (XED_OPERAND_PTR == op_name) {
auto ptr_size = xed_decoded_inst_get_branch_displacement_width_bits(xedd);
CHECK(ptr_size <= operand_size)
<< "Pointer size is greater than effective operand size at " << std::hex
<< inst.pc << ".";
op.size = ptr_size;
val = static_cast<uint64_t>(xed_decoded_inst_get_branch_displacement(xedd));
} else {
auto imm_size = xed_decoded_inst_get_immediate_width_bits(xedd);
CHECK(imm_size <= operand_size)
<< "Immediate size is greater than effective operand size at "
<< std::hex << inst.pc << ".";
op.size = imm_size;
if (XED_OPERAND_IMM0SIGNED == op_name ||
xed_operand_values_get_immediate_is_signed(xedd)) {
val = static_cast<uint64_t>(
static_cast<int64_t>(xed_decoded_inst_get_signed_immediate(xedd)));
is_signed = true;
} else if (XED_OPERAND_IMM0 == op_name) {
val =
static_cast<uint64_t>(xed_decoded_inst_get_unsigned_immediate(xedd));
} else if (XED_OPERAND_IMM1_BYTES == op_name ||
XED_OPERAND_IMM1 == op_name) {
val = static_cast<uint64_t>(xed_decoded_inst_get_second_immediate(xedd));
} else {
CHECK(false) << "Unexpected immediate type "
<< xed_operand_enum_t2str(op_name) << ".";
}
}
op.imm.is_signed = is_signed;
op.imm.val = val;
inst.operands.push_back(op);
}
// Decode a register operand.
static void DecodeRegister(Instruction &inst, const xed_decoded_inst_t *xedd,
const xed_operand_t *xedo,
xed_operand_enum_t op_name) {
auto reg = xed_decoded_inst_get_reg(xedd, op_name);
CHECK(XED_REG_INVALID != reg) << "Cannot get name of invalid register.";
Operand op = {};
op.type = Operand::kTypeRegister;
op.reg = RegOp(reg);
op.size = op.reg.size;
auto read_op = op;
// Pass the register by reference.
if (xed_operand_written(xedo)) {
op.action = Operand::kActionWrite;
if (Is64Bit(inst.arch_name)) {
if (XED_REG_GPR32_FIRST <= reg && XED_REG_GPR32_LAST > reg) {
op.reg.name[0] = 'R'; // Convert things like `EAX` into `RAX`.
op.size = 64;
op.reg.size = 64;
} else if (XED_REG_XMM_FIRST <= reg && XED_REG_ZMM_LAST >= reg) {
if (kArchAMD64_AVX512 == inst.arch_name) {
op.reg.name[0] = 'Z'; // Convert things like `XMM` into `ZMM`.
op.reg.size = 512;
op.size = 512;
} else if (kArchAMD64_AVX == inst.arch_name) {
op.reg.name[0] = 'Y'; // Convert things like `XMM` into `YMM`.
op.reg.size = 256;
op.size = 256;
}
}
}
inst.operands.push_back(op);
}
if (xed_operand_read(xedo)) {
read_op.action = Operand::kActionRead;
inst.operands.push_back(read_op);
}
}
// Condition variable.
static void DecodeConditionalInterrupt(Instruction &inst) {
inst.operands.emplace_back();
auto &cond_op = inst.operands.back();
cond_op.action = Operand::kActionWrite;
cond_op.type = Operand::kTypeRegister;
cond_op.reg.name = "BRANCH_TAKEN";
cond_op.reg.size = 8;
cond_op.size = 8;
}
// Operand representing the fall-through PC, which is the not-taken branch of
// a conditional jump, or the return address for a function call.
static void DecodeFallThroughPC(Instruction &inst,
const xed_decoded_inst_t *xedd) {
auto pc_reg = Is64Bit(inst.arch_name) ? XED_REG_RIP : XED_REG_EIP;
auto pc_width = xed_get_register_width_bits64(pc_reg);
Operand not_taken_op = {};
not_taken_op.action = Operand::kActionRead;
not_taken_op.type = Operand::kTypeAddress;
not_taken_op.size = pc_width;
not_taken_op.addr.address_size = pc_width;
not_taken_op.addr.base_reg.name = "NEXT_PC";
not_taken_op.addr.base_reg.size = pc_width;
not_taken_op.addr.displacement = 0;
not_taken_op.addr.kind = Operand::Address::kControlFlowTarget;
inst.operands.push_back(not_taken_op);
inst.branch_not_taken_pc = inst.next_pc;
}
// Decode a relative branch target.
static void DecodeConditionalBranch(Instruction &inst,
const xed_decoded_inst_t *xedd) {
auto pc_reg = Is64Bit(inst.arch_name) ? XED_REG_RIP : XED_REG_EIP;
auto pc_width = xed_get_register_width_bits64(pc_reg);
auto disp =
static_cast<int64_t>(xed_decoded_inst_get_branch_displacement(xedd));
// Condition variable.
Operand cond_op = {};
cond_op.action = Operand::kActionWrite;
cond_op.type = Operand::kTypeRegister;
cond_op.reg.name = "BRANCH_TAKEN";
cond_op.reg.size = 8;
cond_op.size = 8;
inst.operands.push_back(cond_op);
// Taken branch.
Operand taken_op = {};
taken_op.action = Operand::kActionRead;
taken_op.type = Operand::kTypeAddress;
taken_op.size = pc_width;
taken_op.addr.address_size = pc_width;
taken_op.addr.base_reg.name = "NEXT_PC";
taken_op.addr.base_reg.size = pc_width;
taken_op.addr.displacement = disp;
taken_op.addr.kind = Operand::Address::kControlFlowTarget;
inst.operands.push_back(taken_op);
inst.branch_taken_pc =
static_cast<uint64_t>(static_cast<int64_t>(inst.next_pc) + disp);
DecodeFallThroughPC(inst, xedd);
}
// Decode a relative branch target.
static void DecodeRelativeBranch(Instruction &inst,
const xed_decoded_inst_t *xedd) {
auto pc_reg = Is64Bit(inst.arch_name) ? XED_REG_RIP : XED_REG_EIP;
auto pc_width = xed_get_register_width_bits64(pc_reg);
auto disp =
static_cast<int64_t>(xed_decoded_inst_get_branch_displacement(xedd));
// Taken branch.
Operand taken_op = {};
taken_op.action = Operand::kActionRead;
taken_op.type = Operand::kTypeAddress;
taken_op.size = pc_width;
taken_op.addr.address_size = pc_width;
taken_op.addr.base_reg.name = "NEXT_PC";
taken_op.addr.base_reg.size = pc_width;
taken_op.addr.displacement = disp;
taken_op.addr.kind = Operand::Address::kControlFlowTarget;
inst.operands.push_back(taken_op);
inst.branch_taken_pc =
static_cast<uint64_t>(static_cast<int64_t>(inst.next_pc) + disp);
inst.branch_not_taken_pc = inst.next_pc;
}
// Decodes the opcode byte of this FPU instruction. This is the unique part of
// the first two opcode bytes, and skips over prefix bytes. The FPU opcode is
// the 11 `x`s of the first two non-prefix bytes: `11011xxx xxxxxxxx`.
static uint16_t DecodeFpuOpcode(Instruction &inst) {
unsigned i = 0;
auto found_first_opcode_byte = false;
uint8_t bytes[15] = {};
for (auto b : inst.bytes) {
if (0xD8 == (0xF8 & b)) {
found_first_opcode_byte = true;
}
if (found_first_opcode_byte) {
bytes[i++] = static_cast<uint8_t>(b);
}
}
CHECK(i >= 2) << "Failed to find FPU opcode byte for instruction "
<< inst.Serialize();
uint16_t opcode = 0;
opcode |= static_cast<uint16_t>(bytes[0] & 3) << 8;
opcode |= static_cast<uint16_t>(bytes[1]);
return opcode;
}
// Add to the instruction operands that will let us get at the last program
// counter and opcode for non-control x87 instructions.
static void DecodeX87LastIpDp(Instruction &inst) {
auto pc_width = Is64Bit(inst.arch_name) ? 64 : 32;
Operand pc = {};
pc.action = Operand::kActionRead;
pc.type = Operand::kTypeRegister;
pc.size = pc_width;
pc.reg.name = "PC";
pc.reg.size = pc_width;
inst.operands.push_back(pc);
Operand fop;
fop.action = Operand::kActionRead;
fop.type = Operand::kTypeImmediate;
fop.size = 16;
fop.imm.is_signed = false;
fop.imm.val = static_cast<uint64_t>(DecodeFpuOpcode(inst));
inst.operands.push_back(fop);
}
// Decode an operand.
static void DecodeOperand(Instruction &inst, const xed_decoded_inst_t *xedd,
const xed_operand_t *xedo) {
switch (auto op_name = xed_operand_name(xedo)) {
case XED_OPERAND_AGEN:
case XED_OPERAND_MEM0: DecodeMemory(inst, xedd, xedo, 0); break;
case XED_OPERAND_MEM1: DecodeMemory(inst, xedd, xedo, 1); break;
case XED_OPERAND_IMM0SIGNED:
case XED_OPERAND_IMM0:
case XED_OPERAND_IMM1_BYTES:
case XED_OPERAND_IMM1:
case XED_OPERAND_PTR: DecodeImmediate(inst, xedd, op_name); break;
case XED_OPERAND_REG:
case XED_OPERAND_REG0:
case XED_OPERAND_REG1:
case XED_OPERAND_REG2:
case XED_OPERAND_REG3:
case XED_OPERAND_REG4:
case XED_OPERAND_REG5:
case XED_OPERAND_REG6:
case XED_OPERAND_REG7:
case XED_OPERAND_REG8: DecodeRegister(inst, xedd, xedo, op_name); break;
case XED_OPERAND_RELBR:
if (Instruction::kCategoryConditionalBranch == inst.category) {
DecodeConditionalBranch(inst, xedd);
} else {
DecodeRelativeBranch(inst, xedd);
}
break;
default:
LOG(FATAL) << "Unexpected operand type "
<< xed_operand_enum_t2str(op_name) << ".";
return;
}
}
class X86Arch final : public Arch {
public:
X86Arch(llvm::LLVMContext *context_, OSName os_name_, ArchName arch_name_);
virtual ~X86Arch(void);
// Returns the name of the stack pointer register.
const char *StackPointerRegisterName(void) const final;
// Returns the name of the program counter register.
const char *ProgramCounterRegisterName(void) const final;
// Decode an instuction.
bool DecodeInstruction(uint64_t address, std::string_view inst_bytes,
Instruction &inst) const final;
// Fully decode any control-flow transfer instructions, but only partially
// decode other instructions. To complete the decoding, call
// `Instruction::FinalizeDecode`.
bool LazyDecodeInstruction(uint64_t address, std::string_view inst_bytes,
Instruction &inst) const final;
// Maximum number of bytes in an instruction.
uint64_t MaxInstructionSize(void) const final;
llvm::Triple Triple(void) const final;
llvm::DataLayout DataLayout(void) const final;
// Default calling convention for this architecture.
llvm::CallingConv::ID DefaultCallingConv(void) const final;
private:
// Decode an instuction.
bool DecodeInstruction(uint64_t address, std::string_view inst_bytes,
Instruction &inst, bool is_lazy) const;
X86Arch(void) = delete;
};
X86Arch::X86Arch(llvm::LLVMContext *context_, OSName os_name_,
ArchName arch_name_)
: Arch(context_, os_name_, arch_name_) {
static bool xed_is_initialized = false;
if (!xed_is_initialized) {
DLOG(INFO) << "Initializing XED tables";
xed_tables_init();
xed_is_initialized = true;
}
}
X86Arch::~X86Arch(void) {}
// Maximum number of bytes in an instruction for this particular architecture.
uint64_t X86Arch::MaxInstructionSize(void) const {
return 15;
}
// Default calling convention for this architecture.
llvm::CallingConv::ID X86Arch::DefaultCallingConv(void) const {
if (IsX86()) {
switch (os_name) {
case kOSInvalid:
case kOSmacOS:
case kOSLinux:
case kOSWindows:
case kOSSolaris: return llvm::CallingConv::C; // cdecl.
}
} else {
switch (os_name) {
case kOSInvalid:
case kOSmacOS:
case kOSLinux:
case kOSSolaris: return llvm::CallingConv::X86_64_SysV;
case kOSWindows: return llvm::CallingConv::Win64;
}
}
}
// Get the LLVM triple for this architecture.
llvm::Triple X86Arch::Triple(void) const {
auto triple = BasicTriple();
switch (arch_name) {
case kArchAMD64:
case kArchAMD64_AVX:
case kArchAMD64_AVX512: triple.setArch(llvm::Triple::x86_64); break;
case kArchX86:
case kArchX86_AVX:
case kArchX86_AVX512: triple.setArch(llvm::Triple::x86); break;
default:
LOG(FATAL) << "Cannot get triple for non-x86 architecture "
<< GetArchName(arch_name);
}
return triple;
}
// Get the LLVM DataLayout for a module.
llvm::DataLayout X86Arch::DataLayout(void) const {
std::string dl;
switch (os_name) {
case kOSInvalid:
LOG(FATAL) << "Cannot convert module for an unrecognized OS.";
break;
case kOSLinux:
case kOSSolaris: // Probably.
switch (arch_name) {
case kArchAMD64:
case kArchAMD64_AVX:
case kArchAMD64_AVX512:
dl = "e-m:e-i64:64-f80:128-n8:16:32:64-S128";
break;
case kArchX86:
case kArchX86_AVX:
case kArchX86_AVX512:
dl = "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128";
break;
default:
LOG(FATAL) << "Cannot get data layout non-x86 architecture "
<< GetArchName(arch_name);
break;
}
break;
case kOSmacOS:
switch (arch_name) {
case kArchAMD64:
case kArchAMD64_AVX:
case kArchAMD64_AVX512:
dl = "e-m:o-i64:64-f80:128-n8:16:32:64-S128";
break;
case kArchX86:
case kArchX86_AVX:
case kArchX86_AVX512:
dl = "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128";
break;
default:
LOG(FATAL) << "Cannot get data layout for non-x86 architecture "
<< GetArchName(arch_name);
}
break;
case kOSWindows:
switch (arch_name) {
case kArchAMD64:
case kArchAMD64_AVX:
case kArchAMD64_AVX512:
dl = "e-m:w-i64:64-f80:128-n8:16:32:64-S128";
break;
case kArchX86:
case kArchX86_AVX:
case kArchX86_AVX512:
dl = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32";
break;
default:
LOG(FATAL) << "Cannot get data layout for non-x86 architecture "
<< GetArchName(arch_name);
}
break;
}
return llvm::DataLayout(dl);
}
// Decode an instuction.
bool X86Arch::DecodeInstruction(uint64_t address, std::string_view inst_bytes,
Instruction &inst, bool is_lazy) const {
inst.pc = address;
inst.arch_name = arch_name;
inst.category = Instruction::kCategoryInvalid;
xed_decoded_inst_t xedd_;
xed_decoded_inst_t *xedd = &xedd_;
auto mode = 32 == address_size ? &kXEDState32 : &kXEDState64;
if (!DecodeXED(xedd, mode, inst_bytes, address)) {
LOG(ERROR) << "DecodeXED() could not decode the following opcodes: "
<< inst.Serialize();
return false;
}
const auto len = xed_decoded_inst_get_length(xedd);
if (!inst.bytes.empty() && inst.bytes.data() == inst_bytes.data()) {
CHECK_LE(len, inst.bytes.size());
inst.bytes.resize(len);
} else {
inst.bytes = inst_bytes.substr(0, len);
}
inst.category = CreateCategory(xedd);
inst.next_pc = address + len;
// Wrap an instruction in atomic begin/end if it accesses memory with RMW
// semantics or with a LOCK prefix.
if (xed_operand_values_get_atomic(xedd) ||
xed_operand_values_has_lock_prefix(xedd) ||
XED_CATEGORY_SEMAPHORE == xed_decoded_inst_get_category(xedd)) {
inst.is_atomic_read_modify_write = true;
}
if (Instruction::kCategoryConditionalAsyncHyperCall == inst.category) {
DecodeConditionalInterrupt(inst);
}
auto iform = xed_decoded_inst_get_iform_enum(xedd);
if (!is_lazy || inst.IsControlFlow()) {
inst.function = InstructionFunctionName(xedd);
// Lift the operands. This creates the arguments for us to call the
// instuction implementation.
auto xedi = xed_decoded_inst_inst(xedd);
auto num_operands = xed_decoded_inst_noperands(xedd);
for (auto i = 0U; i < num_operands; ++i) {
auto xedo = xed_inst_operand(xedi, i);
if (XED_OPVIS_SUPPRESSED != xed_operand_operand_visibility(xedo)) {
DecodeOperand(inst, xedd, xedo);
}
}
// Control flow operands update the next program counter.
if (inst.IsControlFlow()) {
inst.operands.emplace_back();
auto &dst_ret_pc = inst.operands.back();
dst_ret_pc.type = Operand::kTypeRegister;
dst_ret_pc.action = Operand::kActionWrite;
dst_ret_pc.size = address_size;
dst_ret_pc.reg.name = "NEXT_PC";
dst_ret_pc.reg.size = address_size;
}
if (inst.IsFunctionCall()) {
DecodeFallThroughPC(inst, xedd);
// The semantics will store the return address in `RETURN_PC`. This is to
// help synchronize program counters when lifting instructions on an ISA
// with delay slots.
inst.operands.emplace_back();
auto &dst_ret_pc = inst.operands.back();
dst_ret_pc.type = Operand::kTypeRegister;
dst_ret_pc.action = Operand::kActionWrite;
dst_ret_pc.size = address_size;
dst_ret_pc.reg.name = "RETURN_PC";
dst_ret_pc.reg.size = address_size;
}
if (UsesStopFailure(xedd)) {
// These instructions might fault and uses the StopFailure to recover.
// The new operand `next_pc` is added and the REG_PC is set to next_pc
// before calling the StopFailure
inst.operands.emplace_back();
auto &next_pc = inst.operands.back();
next_pc.type = Operand::kTypeRegister;
next_pc.action = Operand::kActionRead;
next_pc.size = address_size;
next_pc.reg.name = "NEXT_PC";
next_pc.reg.size = address_size;
}
// All non-control FPU instructions update the last instruction pointer
// and opcode.
if (XED_ISA_SET_X87 == xed_decoded_inst_get_isa_set(xedd) ||
XED_ISA_SET_FCMOV == xed_decoded_inst_get_isa_set(xedd) ||
XED_CATEGORY_X87_ALU == xed_decoded_inst_get_category(xedd)) {
auto set_ip_dp = false;
const auto get_attr = xed_decoded_inst_get_attribute;
switch (iform) {
case XED_IFORM_FNOP:
case XED_IFORM_FINCSTP:
case XED_IFORM_FDECSTP:
case XED_IFORM_FFREE_X87:
case XED_IFORM_FFREEP_X87: set_ip_dp = true; break;
default:
set_ip_dp = !get_attr(xedd, XED_ATTRIBUTE_X87_CONTROL) &&
!get_attr(xedd, XED_ATTRIBUTE_X87_MMX_STATE_CW) &&
!get_attr(xedd, XED_ATTRIBUTE_X87_MMX_STATE_R) &&
!get_attr(xedd, XED_ATTRIBUTE_X87_MMX_STATE_W) &&
!get_attr(xedd, XED_ATTRIBUTE_X87_NOWAIT);
break;
}
if (set_ip_dp) {
DecodeX87LastIpDp(inst);
}
}
if (xed_decoded_inst_is_xacquire(xedd) ||
xed_decoded_inst_is_xrelease(xedd)) {
LOG(ERROR) << "Ignoring XACQUIRE/XRELEASE prefix at " << std::hex
<< inst.pc << std::dec;
}
}
// Make sure we disallow decoding of AVX instructions when running with non-
// AVX arch specified. Same thing for AVX512 instructions.
switch (xed_decoded_inst_get_isa_set(xedd)) {
case XED_ISA_SET_INVALID:
case XED_ISA_SET_LAST:
LOG(ERROR) << "Instruction decode of " << xed_iform_enum_t2str(iform)
<< " failed because XED_ISA_SET_LAST.";
return false;
case XED_ISA_SET_AVX:
case XED_ISA_SET_AVX2:
case XED_ISA_SET_AVX2GATHER:
case XED_ISA_SET_AVXAES:
case XED_ISA_SET_AVX_GFNI: {
auto supp = kArchAMD64 != inst.arch_name && kArchX86 != inst.arch_name;
LOG_IF(ERROR, !supp) << "Instruction decode of "
<< xed_iform_enum_t2str(iform)
<< " failed because the current arch is specified "
<< "as " << GetArchName(inst.arch_name)
<< " but what is needed is "
<< "the _avx or _avx512 variant.";
return supp;
}
case XED_ISA_SET_AVX512BW_128:
case XED_ISA_SET_AVX512BW_128N:
case XED_ISA_SET_AVX512BW_256:
case XED_ISA_SET_AVX512BW_512:
case XED_ISA_SET_AVX512BW_KOP:
case XED_ISA_SET_AVX512CD_128:
case XED_ISA_SET_AVX512CD_256:
case XED_ISA_SET_AVX512CD_512:
case XED_ISA_SET_AVX512DQ_128:
case XED_ISA_SET_AVX512DQ_128N:
case XED_ISA_SET_AVX512DQ_256:
case XED_ISA_SET_AVX512DQ_512:
case XED_ISA_SET_AVX512DQ_KOP:
case XED_ISA_SET_AVX512DQ_SCALAR:
case XED_ISA_SET_AVX512ER_512:
case XED_ISA_SET_AVX512ER_SCALAR:
case XED_ISA_SET_AVX512F_128:
case XED_ISA_SET_AVX512F_128N:
case XED_ISA_SET_AVX512F_256:
case XED_ISA_SET_AVX512F_512:
case XED_ISA_SET_AVX512F_KOP:
case XED_ISA_SET_AVX512F_SCALAR:
case XED_ISA_SET_AVX512PF_512:
case XED_ISA_SET_AVX512_4FMAPS_512:
case XED_ISA_SET_AVX512_4FMAPS_SCALAR:
case XED_ISA_SET_AVX512_4VNNIW_512:
case XED_ISA_SET_AVX512_BITALG_128:
case XED_ISA_SET_AVX512_BITALG_256:
case XED_ISA_SET_AVX512_BITALG_512:
case XED_ISA_SET_AVX512_GFNI_128:
case XED_ISA_SET_AVX512_GFNI_256:
case XED_ISA_SET_AVX512_GFNI_512:
case XED_ISA_SET_AVX512_IFMA_128:
case XED_ISA_SET_AVX512_IFMA_256:
case XED_ISA_SET_AVX512_IFMA_512:
case XED_ISA_SET_AVX512_VAES_128:
case XED_ISA_SET_AVX512_VAES_256:
case XED_ISA_SET_AVX512_VAES_512:
case XED_ISA_SET_AVX512_VBMI2_128:
case XED_ISA_SET_AVX512_VBMI2_256:
case XED_ISA_SET_AVX512_VBMI2_512:
case XED_ISA_SET_AVX512_VBMI_128:
case XED_ISA_SET_AVX512_VBMI_256:
case XED_ISA_SET_AVX512_VBMI_512:
case XED_ISA_SET_AVX512_VNNI_128:
case XED_ISA_SET_AVX512_VNNI_256:
case XED_ISA_SET_AVX512_VNNI_512:
case XED_ISA_SET_AVX512_VPCLMULQDQ_128:
case XED_ISA_SET_AVX512_VPCLMULQDQ_256:
case XED_ISA_SET_AVX512_VPCLMULQDQ_512:
case XED_ISA_SET_AVX512_VPOPCNTDQ_128:
case XED_ISA_SET_AVX512_VPOPCNTDQ_256:
case XED_ISA_SET_AVX512_VPOPCNTDQ_512: {
const auto supp = kArchAMD64_AVX512 == inst.arch_name ||
kArchX86_AVX512 == inst.arch_name;
if (!supp) {
LOG(ERROR) << "Instruction decode of " << xed_iform_enum_t2str(iform)
<< " failed because the current arch is specified "
<< "as " << GetArchName(inst.arch_name)
<< " but what is needed is "
<< "the _avx512 variant.";
inst.Reset();
inst.category = Instruction::kCategoryInvalid;
return false;
}
break;
}
default: break;
}
return true;
}
// Returns the name of the stack pointer register.
const char *X86Arch::StackPointerRegisterName(void) const {
if (IsX86()) {
return "ESP";
} else {
return "RSP";
}
}
// Returns the name of the program counter register.
const char *X86Arch::ProgramCounterRegisterName(void) const {
if (IsX86()) {
return "EIP";
} else {
return "RIP";
}
}
bool X86Arch::DecodeInstruction(uint64_t address, std::string_view inst_bytes,
Instruction &inst) const {
inst.arch_for_decode = nullptr;
return DecodeInstruction(address, inst_bytes, inst, false);
}
// Fully decode any control-flow transfer instructions, but only partially
// decode other instructions.
bool X86Arch::LazyDecodeInstruction(uint64_t address,
std::string_view inst_bytes,
Instruction &inst) const {
inst.arch_for_decode = nullptr;
if (DecodeInstruction(address, inst_bytes, inst, true)) {
if (!inst.IsControlFlow()) {
inst.arch_for_decode = this;
}
return true;
} else {
return false;
}
}
} // namespace
// TODO(pag): We pretend that these are singletons, but they aren't really!
Arch::ArchPtr Arch::GetX86(llvm::LLVMContext *context_, OSName os_name_,
ArchName arch_name_) {
return std::make_unique<X86Arch>(context_, os_name_, arch_name_);
}
} // namespace remill