mirror of
https://github.com/lifting-bits/remill
synced 2026-06-21 13:56:07 +00:00
6322f794d1
* This branch contains support for new architectures. * Initial start to support for AArch 32 * Progress * Forgot the new files * Added all data Integer processing instructions without S + ADDS and started ANDS * Updated * Finished Integer Data Processing with three registers, added integer data processing with 2 regs + immediate, started MUL instructions * UMULL, UMULLS, UMLAL, UMLALS * Corrected condition for addend or 0 immediate for UMULL/UMLAL + SMULL/SMLAL instructions * Correct ops in Binary.cpp * UMAAL * SMULL, SMULLS, SMLAL, SMLALS + corrected acc was missing shift left in concatination * Updated decoding instructions based on top level encodings * Update returns around kDataProcessingRI and kDataProcessingI with comments to explain the correlation to the instruction rep * Added appropriate inst.category flags to Multiply and accumulate * Load/Store Word, Unsigned Byte (immediate, literal) && start of Logical Arithmetic (three register, immediate shift) * Was missing UMAAL DEF_ISEL in Binary.cpp * AddAddrRegOp * Logical Arithmetic (three register, immediate shift) without accounting for the possible PC jump * Made DecodeA32ExpandImm much much smaller * Replaced some imm ops with AddImmOp calls * Created AddShiftOp * Added interpreter for evaluating new PC value at decoding time to handle direct jumps and conditional jumps * Created EvalPCDest added PC evaluation to Logical Arithmetic Instructions * AddShiftOp -> AddShiftOp, AddShiftThenExtractOp, AddExtractThenShiftOp * Cleaned up some formatting, Renamed DecodeA32ExpandImm to ExpandTo32AddImmAddCarry and added a clarifying comment * Added comment to EvalPCDest for clarity * Cleaned up some things, updated the decoding semantics and semantics for the logical instructions * Shortened kLogArithEvaluators and fixed a bug * Updates from testing instructions * Fixed DEF_ISEL for pre/post index instructions in MEM.cpp * Integer Test and Compare (two register, immediate shift) * Logical Arithmetic (two register and immediate) * Integer Test and Compare (one register and immediate) * Added to the top level encoding infrastructure to handle the Data-processing register (register shift) set of instructions and 3 corresponding subsets * Add structs for the 3 subsets of Data-processing register (register shift) * Code status before refactoring operand types * This branch contains support for new architectures. * Initial start to support for AArch 32 * Progress * Forgot the new files * Added all data Integer processing instructions without S + ADDS and started ANDS * Updated * Finished Integer Data Processing with three registers, added integer data processing with 2 regs + immediate, started MUL instructions * UMULL, UMULLS, UMLAL, UMLALS * Corrected condition for addend or 0 immediate for UMULL/UMLAL + SMULL/SMLAL instructions * Correct ops in Binary.cpp * UMAAL * SMULL, SMULLS, SMLAL, SMLALS + corrected acc was missing shift left in concatination * Updated decoding instructions based on top level encodings * Update returns around kDataProcessingRI and kDataProcessingI with comments to explain the correlation to the instruction rep * Added appropriate inst.category flags to Multiply and accumulate * Load/Store Word, Unsigned Byte (immediate, literal) && start of Logical Arithmetic (three register, immediate shift) * Was missing UMAAL DEF_ISEL in Binary.cpp * AddAddrRegOp * Logical Arithmetic (three register, immediate shift) without accounting for the possible PC jump * Made DecodeA32ExpandImm much much smaller * Replaced some imm ops with AddImmOp calls * Created AddShiftOp * Added interpreter for evaluating new PC value at decoding time to handle direct jumps and conditional jumps * Created EvalPCDest added PC evaluation to Logical Arithmetic Instructions * AddShiftOp -> AddShiftOp, AddShiftThenExtractOp, AddExtractThenShiftOp * Cleaned up some formatting, Renamed DecodeA32ExpandImm to ExpandTo32AddImmAddCarry and added a clarifying comment * Added comment to EvalPCDest for clarity * Cleaned up some things, updated the decoding semantics and semantics for the logical instructions * Shortened kLogArithEvaluators and fixed a bug * Updates from testing instructions * Fixed DEF_ISEL for pre/post index instructions in MEM.cpp * Integer Test and Compare (two register, immediate shift) * Logical Arithmetic (two register and immediate) * Integer Test and Compare (one register and immediate) * Added to the top level encoding infrastructure to handle the Data-processing register (register shift) set of instructions and 3 corresponding subsets * Add structs for the 3 subsets of Data-processing register (register shift) * Code status before refactoring operand types * Finished updates off master * Start of operand refactor * Finished Expression Operand Support * Fix the .gitignore to add AArch32 to lib/Arch && removed all extra rrx ops from semantics * Updated .gitignore again, Added AddShiftRegRegOperand, Updated AddShiftRegImmOperand, Finished Register shift instructions for Integer Test and Compare, Logical Arithmetic, Integer Data Processing * Updated ROR in AddShiftRegRegOperand * Created ExtractAndZExtExpr * Fixed comment formatting in if else statements * Created RORExpr * Small fixes * Small fix in Logical Arithmetic (two register and immediate) * Corrected AddShiftRegRegOperand and cleaned it up. Split the carry op into a separate function. * conditional support + Start of Branch instructions * Created AddExprOp, cleaned up some expressions in reg shifted reg, and updated some occurances of ShiftThenExtractOp with ExtractAndZExtExpr * Updates from testing register shifted by register value inst * Fix to ROR in AddShiftRegCarryOperand * Corrected negation in DecodeCondition * DecodeCondition edit * DecodeCondition and AddShiftRegCarryOperand edits * Updated arch_for_decode to arch * Halfword Multiply and Accumulate * Edits from testing Halfword Multiply and Accumulate * Changed order of operands in Halfword Multiply and Accumulate to better reflect inst format + updated inst errors * Branch (Imm) & BX/BXL * Update aarch32 cmake * cmake update * CLZ * Forgot BITBYTE.cpp * MOVT * Integer Saturating Arithmetic * updated semantics in SMLAWh & SMLAh to use Select for setting PSTATE.Q * Started Load/Store Word, Unsigned Byte (register) & fixed MOV halfword * Load/Store Word, Unsigned Byte (register) * Finished testing load/Store Word, Unsigned Byte (register) * Load/Store Dual, Half, Signed Byte (register) * Rest of Extra load store: Load/Store Dual, Half, Signed Byte (immediate, literal) * Finished testing all the Load/store additions * Signed multiply, Divide * Cleaned up SExt some * Saturate Insts and Start of Load Store Multiple - STMDB and LDM (aliases which support PUSH and POP of multiple regs) * Condensed args in STMDB and LDM semantics * Rest of Multiple Load/Store that do not execute in a different mode * Bitfield Extract * Extend and Add * fix * NOP * Small fix * Simplified the bit reps in TryMoveSpecialRegisterAndHintsI * Moved Bitfield extract semantics out of BINARY and into BITBYTE * Finished correcting S/ZExt and Trunc use * Ran scripts/format-files to format * Smoke Test * Add false delay slot to kCategoryConditionalDirectFunctionCall * CI: Use single packaging job, add changelog support (#491) * CI: Add tag handler (#492) * Delay slot fixes to TraceLifter Co-authored-by: Peter Goodman <peter.goodman@gmail.com> Co-authored-by: Alessandro Gario <5714290+alessandrogario@users.noreply.github.com>
741 lines
22 KiB
C++
741 lines
22 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/Instruction.h"
|
|
|
|
#include <glog/logging.h>
|
|
#include <llvm/IR/Instruction.h>
|
|
#include <llvm/IR/Instructions.h>
|
|
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
|
|
#include "remill/Arch/Arch.h"
|
|
#include "remill/Arch/Name.h"
|
|
#include "remill/BC/Util.h"
|
|
|
|
namespace remill {
|
|
|
|
std::string OperandExpression::Serialize(void) const {
|
|
std::stringstream ss;
|
|
if (auto llvm_op = std::get_if<LLVMOpExpr>(this)) {
|
|
ss << "(" << llvm::Instruction::getOpcodeName(llvm_op->llvm_opcode) << " "
|
|
<< llvm_op->op1->Serialize();
|
|
if (llvm_op->op2) {
|
|
ss << " " << llvm_op->op2->Serialize();
|
|
} else {
|
|
ss << " to " << remill::LLVMThingToString(type);
|
|
}
|
|
ss << ")";
|
|
} else if (auto reg_op = std::get_if<const Register *>(this)) {
|
|
ss << (*reg_op)->name;
|
|
} else if (auto ci_op = std::get_if<llvm::Constant *>(this)) {
|
|
ss << remill::LLVMThingToString(*ci_op);
|
|
} else if (auto str_op = std::get_if<std::string>(this)) {
|
|
ss << *str_op;
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
Operand::Register::Register(void) : size(0) {}
|
|
|
|
Operand::ShiftRegister::ShiftRegister(void)
|
|
: shift_size(0),
|
|
extract_size(0),
|
|
shift_first(false),
|
|
shift_op(Operand::ShiftRegister::kShiftInvalid),
|
|
extend_op(Operand::ShiftRegister::kExtendInvalid) {}
|
|
|
|
Operand::Immediate::Immediate(void) : val(0), is_signed(false) {}
|
|
|
|
Operand::Address::Address(void)
|
|
: scale(0),
|
|
displacement(0),
|
|
address_size(0),
|
|
kind(kInvalid) {}
|
|
|
|
Operand::Operand(void)
|
|
: type(Operand::kTypeInvalid),
|
|
action(Operand::kActionInvalid),
|
|
size(0),
|
|
expr(nullptr) {}
|
|
|
|
namespace {
|
|
static int64_t SignedImmediate(uint64_t val, uint64_t size) {
|
|
switch (size) {
|
|
case 8: return static_cast<int64_t>(static_cast<int8_t>(val));
|
|
case 16: return static_cast<int64_t>(static_cast<int16_t>(val));
|
|
case 32: return static_cast<int64_t>(static_cast<int32_t>(val));
|
|
default: return static_cast<int64_t>(val);
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
std::string Operand::Serialize(void) const {
|
|
std::stringstream ss;
|
|
switch (action) {
|
|
case Operand::kActionInvalid: ss << "(INVALID_OP "; break;
|
|
case Operand::kActionRead: ss << "(READ_OP "; break;
|
|
case Operand::kActionWrite: ss << "(WRITE_OP "; break;
|
|
}
|
|
switch (type) {
|
|
case Operand::kTypeInvalid: ss << "(INVALID)"; break;
|
|
|
|
case Operand::kTypeRegister:
|
|
ss << "(REG_" << reg.size << " " << reg.name << ")";
|
|
break;
|
|
|
|
case Operand::kTypeShiftRegister: {
|
|
auto shift_begin = [&](void) {
|
|
switch (shift_reg.shift_op) {
|
|
case Operand::ShiftRegister::kShiftInvalid: break;
|
|
|
|
case Operand::ShiftRegister::kShiftLeftWithZeroes:
|
|
ss << "(LSL ";
|
|
break;
|
|
|
|
case Operand::ShiftRegister::kShiftLeftWithOnes: ss << "(MSL "; break;
|
|
|
|
case Operand::ShiftRegister::kShiftUnsignedRight:
|
|
ss << "(LSR ";
|
|
break;
|
|
|
|
case Operand::ShiftRegister::kShiftSignedRight: ss << "(ASR "; break;
|
|
|
|
case Operand::ShiftRegister::kShiftLeftAround: ss << "(ROL "; break;
|
|
|
|
case Operand::ShiftRegister::kShiftRightAround: ss << "(ROR "; break;
|
|
}
|
|
};
|
|
|
|
auto shift_end = [&](void) {
|
|
if (Operand::ShiftRegister::kShiftInvalid != shift_reg.shift_op) {
|
|
ss << " " << shift_reg.shift_size << ")";
|
|
}
|
|
};
|
|
|
|
auto extract_begin = [&](void) {
|
|
switch (shift_reg.extend_op) {
|
|
case Operand::ShiftRegister::kExtendInvalid: break;
|
|
|
|
case Operand::ShiftRegister::kExtendSigned:
|
|
ss << "(SEXT (TRUNC ";
|
|
break;
|
|
|
|
case Operand::ShiftRegister::kExtendUnsigned:
|
|
ss << "(ZEXT (TRUNC ";
|
|
break;
|
|
}
|
|
};
|
|
|
|
auto extract_end = [&](void) {
|
|
switch (shift_reg.extend_op) {
|
|
case Operand::ShiftRegister::kExtendInvalid: break;
|
|
|
|
case Operand::ShiftRegister::kExtendSigned:
|
|
ss << " " << shift_reg.extract_size << ") " << size << ")";
|
|
break;
|
|
|
|
case Operand::ShiftRegister::kExtendUnsigned:
|
|
ss << " " << shift_reg.extract_size << ") " << size << ")";
|
|
break;
|
|
}
|
|
};
|
|
|
|
if (shift_reg.shift_first) {
|
|
extract_begin();
|
|
shift_begin();
|
|
} else {
|
|
shift_begin();
|
|
extract_begin();
|
|
}
|
|
|
|
ss << "(REG_" << shift_reg.reg.size << " " << shift_reg.reg.name << ")";
|
|
|
|
if (shift_reg.shift_first) {
|
|
shift_end();
|
|
extract_end();
|
|
} else {
|
|
extract_end();
|
|
shift_end();
|
|
}
|
|
|
|
break;
|
|
}
|
|
case Operand::kTypeImmediate:
|
|
ss << "(";
|
|
if (imm.is_signed) {
|
|
ss << "SIGNED_IMM_" << size << " ";
|
|
auto simm = SignedImmediate(imm.val, size);
|
|
if (simm < 0) {
|
|
ss << "-0x" << std::hex << static_cast<uint64_t>(-simm) << std::dec;
|
|
} else {
|
|
ss << "0x" << std::hex << imm.val << std::dec;
|
|
}
|
|
} else {
|
|
ss << "IMM_" << size << " " << std::hex << imm.val << std::dec << ")";
|
|
}
|
|
break;
|
|
|
|
case Operand::kTypeAddress: {
|
|
ss << "(";
|
|
|
|
// Nice version of the memory size.
|
|
switch (size) {
|
|
case 8: ss << "BYTE"; break;
|
|
case 16: ss << "WORD"; break;
|
|
case 32: ss << "DWORD"; break;
|
|
case 64: ss << "QWORD"; break;
|
|
case 80: ss << "TBYTE"; break;
|
|
case 128: ss << "OWORD"; break;
|
|
case 256: ss << "DOWORD"; break;
|
|
case 512: ss << "QOWORD"; break;
|
|
default:
|
|
CHECK(!(size & 7))
|
|
<< "Memory operand size must be divisible by 8; got " << size
|
|
<< " bits.";
|
|
ss << std::dec << (size / 8) << "_BYTES";
|
|
break;
|
|
}
|
|
|
|
ss << "_PTR";
|
|
|
|
int num_components = 0;
|
|
if (addr.displacement) {
|
|
++num_components;
|
|
}
|
|
if (!addr.segment_base_reg.name.empty()) {
|
|
++num_components;
|
|
}
|
|
if (!addr.base_reg.name.empty()) {
|
|
++num_components;
|
|
}
|
|
if (!addr.index_reg.name.empty()) {
|
|
++num_components;
|
|
}
|
|
|
|
if (1 < num_components) {
|
|
ss << " (ADD";
|
|
}
|
|
|
|
if (!addr.segment_base_reg.name.empty()) {
|
|
ss << " (REG_" << addr.segment_base_reg.size << " "
|
|
<< addr.segment_base_reg.name << ")";
|
|
}
|
|
|
|
if (!addr.base_reg.name.empty()) {
|
|
ss << " (REG_" << addr.base_reg.size << " " << addr.base_reg.name
|
|
<< ")";
|
|
}
|
|
|
|
if (addr.scale) {
|
|
CHECK(!addr.index_reg.name.empty());
|
|
ss << " (MUL";
|
|
}
|
|
|
|
if (!addr.index_reg.name.empty()) {
|
|
ss << " (REG_" << addr.index_reg.size << " " << addr.index_reg.name
|
|
<< ")";
|
|
}
|
|
|
|
if (addr.scale) {
|
|
ss << " (IMM_" << addr.index_reg.size << " 0x" << std::hex << addr.scale
|
|
<< std::dec << ")";
|
|
ss << ")"; // End of `(MUL`.
|
|
}
|
|
|
|
if (addr.displacement) {
|
|
ss << " (SIGNED_IMM_" << addr.address_size << " ";
|
|
if (0 > addr.displacement) {
|
|
ss << "-0x" << std::hex << (-addr.displacement) << std::dec;
|
|
} else {
|
|
ss << "0x" << std::hex << addr.displacement << std::dec;
|
|
}
|
|
ss << ")"; // End of `(SIGNED_IMM_`.
|
|
}
|
|
if (1 < num_components) {
|
|
ss << ")"; // End of `(ADD`.
|
|
}
|
|
ss << ")"; // End of `(ADDR_`.
|
|
break;
|
|
}
|
|
case Operand::kTypeExpression: ss << expr->Serialize(); break;
|
|
}
|
|
ss << ")";
|
|
return ss.str();
|
|
}
|
|
|
|
std::string Condition::Serialize(void) const {
|
|
std::stringstream ss;
|
|
|
|
ss << "(";
|
|
switch (kind) {
|
|
case Condition::kTypeIsEqual:
|
|
ss << "(REG_" << lhs_reg.size << " " << lhs_reg.name << ") = (REG_"
|
|
<< rhs_reg.size << " " << rhs_reg.name << ")";
|
|
break;
|
|
case Condition::kTypeIsOne:
|
|
ss << "(REG_" << lhs_reg.size << " " << lhs_reg.name << ") = 1";
|
|
break;
|
|
case Condition::kTypeIsZero:
|
|
ss << "(REG_" << lhs_reg.size << " " << lhs_reg.name << ") = 0";
|
|
break;
|
|
case Condition::kTypeTrue: ss << "TRUE"; break;
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
|
|
Instruction::Instruction(void)
|
|
: pc(0),
|
|
next_pc(0),
|
|
delayed_pc(0),
|
|
branch_taken_pc(0),
|
|
branch_not_taken_pc(0),
|
|
arch_name(kArchInvalid),
|
|
arch(nullptr),
|
|
is_atomic_read_modify_write(false),
|
|
has_branch_taken_delay_slot(false),
|
|
has_branch_not_taken_delay_slot(false),
|
|
in_delay_slot(false),
|
|
category(Instruction::kCategoryInvalid) {}
|
|
|
|
void Instruction::Reset(void) {
|
|
pc = 0;
|
|
next_pc = 0;
|
|
delayed_pc = 0;
|
|
branch_taken_pc = 0;
|
|
branch_not_taken_pc = 0;
|
|
arch_name = kArchInvalid;
|
|
is_atomic_read_modify_write = false;
|
|
has_branch_taken_delay_slot = false;
|
|
has_branch_not_taken_delay_slot = false;
|
|
in_delay_slot = false;
|
|
category = Instruction::kCategoryInvalid;
|
|
arch = nullptr;
|
|
operands.clear();
|
|
function.clear();
|
|
bytes.clear();
|
|
next_expr_index = 0;
|
|
}
|
|
|
|
OperandExpression *Instruction::AllocateExpression(void) {
|
|
CHECK_LT(next_expr_index, kMaxNumExpr);
|
|
return &(exprs[next_expr_index++]);
|
|
}
|
|
|
|
OperandExpression *Instruction::EmplaceRegister(const Register *reg) {
|
|
auto expr = AllocateExpression();
|
|
expr->emplace<const Register *>(reg);
|
|
expr->type = reg->type;
|
|
return expr;
|
|
}
|
|
|
|
OperandExpression *Instruction::EmplaceRegister(std::string_view reg_name) {
|
|
return EmplaceRegister(arch->RegisterByName(reg_name));
|
|
}
|
|
|
|
OperandExpression *Instruction::EmplaceConstant(llvm::Constant *val) {
|
|
auto expr = AllocateExpression();
|
|
expr->emplace<llvm::Constant *>(val);
|
|
expr->type = val->getType();
|
|
return expr;
|
|
}
|
|
|
|
OperandExpression *Instruction::EmplaceVariable(std::string_view var_name,
|
|
llvm::Type *type) {
|
|
auto expr = AllocateExpression();
|
|
expr->emplace<std::string>(var_name.data(), var_name.size());
|
|
expr->type = type;
|
|
return expr;
|
|
}
|
|
|
|
OperandExpression *Instruction::EmplaceBinaryOp(unsigned opcode,
|
|
OperandExpression *op1,
|
|
OperandExpression *op2) {
|
|
auto expr = AllocateExpression();
|
|
expr->emplace<LLVMOpExpr>(LLVMOpExpr{opcode, op1, op2});
|
|
expr->type = op1->type;
|
|
return expr;
|
|
}
|
|
|
|
OperandExpression *Instruction::EmplaceUnaryOp(unsigned opcode,
|
|
OperandExpression *op1,
|
|
llvm::Type *type) {
|
|
auto expr = AllocateExpression();
|
|
expr->emplace<LLVMOpExpr>(LLVMOpExpr{opcode, op1, nullptr});
|
|
expr->type = type;
|
|
return expr;
|
|
}
|
|
|
|
Operand &Instruction::EmplaceOperand(const Operand::Register ®_op) {
|
|
operands.emplace_back();
|
|
auto &op = operands.back();
|
|
op.type = Operand::kTypeExpression;
|
|
op.size = reg_op.size;
|
|
if (auto reg = arch->RegisterByName(reg_op.name)) {
|
|
op.expr = EmplaceRegister(reg);
|
|
} else {
|
|
auto &context = *arch->context;
|
|
auto ty = llvm::Type::getIntNTy(context, reg_op.size);
|
|
op.expr = EmplaceVariable(reg_op.name, ty);
|
|
}
|
|
return op;
|
|
}
|
|
|
|
Operand &Instruction::EmplaceOperand(const Operand::Immediate &imm_op) {
|
|
operands.emplace_back();
|
|
auto &op = operands.back();
|
|
auto &context = *arch->context;
|
|
|
|
auto ty = llvm::Type::getIntNTy(context, arch->address_size);
|
|
op.expr =
|
|
EmplaceConstant(llvm::ConstantInt::get(ty, imm_op.val, imm_op.is_signed));
|
|
op.size = arch->address_size;
|
|
op.type = Operand::kTypeExpression;
|
|
return op;
|
|
}
|
|
|
|
Operand &Instruction::EmplaceOperand(const Operand::ShiftRegister &shift_op) {
|
|
operands.emplace_back();
|
|
auto &op = operands.back();
|
|
op.type = Operand::kTypeExpression;
|
|
op.size = arch->address_size;
|
|
auto &arch_reg = shift_op.reg;
|
|
|
|
auto &context = *arch->context;
|
|
auto reg = arch->RegisterByName(arch_reg.name);
|
|
auto reg_type = reg->type;
|
|
auto reg_size = reg->size * 8u;
|
|
auto op_type = llvm::Type::getIntNTy(context, op.size);
|
|
|
|
const uint64_t zero = 0;
|
|
const uint64_t one = 1;
|
|
const uint64_t shift_size = shift_op.shift_size;
|
|
|
|
const auto shift_val = llvm::ConstantInt::get(op_type, shift_size);
|
|
|
|
auto expr = EmplaceRegister(reg);
|
|
|
|
auto curr_size = reg_size;
|
|
|
|
auto do_extract = [&](void) {
|
|
if (Operand::ShiftRegister::kExtendInvalid != shift_op.extend_op) {
|
|
|
|
auto extract_type = llvm::Type::getIntNTy(context, shift_op.extract_size);
|
|
|
|
if (reg_size > shift_op.extract_size) {
|
|
curr_size = shift_op.extract_size;
|
|
expr = EmplaceUnaryOp(llvm::Instruction::Trunc, expr, extract_type);
|
|
|
|
} else {
|
|
CHECK(reg_size == shift_op.extract_size)
|
|
<< "Invalid extraction size. Can't extract "
|
|
<< shift_op.extract_size << " bits from a " << reg_size
|
|
<< "-bit value in operand " << op.Serialize()
|
|
<< " of instruction at " << std::hex << pc;
|
|
}
|
|
|
|
if (op.size > shift_op.extract_size) {
|
|
switch (shift_op.extend_op) {
|
|
case Operand::ShiftRegister::kExtendSigned:
|
|
expr = EmplaceUnaryOp(llvm::Instruction::SExt, expr, op_type);
|
|
curr_size = op.size;
|
|
break;
|
|
case Operand::ShiftRegister::kExtendUnsigned:
|
|
expr = EmplaceUnaryOp(llvm::Instruction::ZExt, expr, op_type);
|
|
curr_size = op.size;
|
|
break;
|
|
default:
|
|
LOG(FATAL) << "Invalid extend operation type for instruction at "
|
|
<< std::hex << pc;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
CHECK(curr_size <= op.size);
|
|
|
|
if (curr_size < op.size) {
|
|
expr = EmplaceUnaryOp(llvm::Instruction::ZExt, expr, op_type);
|
|
curr_size = op.size;
|
|
}
|
|
};
|
|
|
|
auto do_shift = [&](void) {
|
|
if (Operand::ShiftRegister::kShiftInvalid != shift_op.shift_op) {
|
|
|
|
CHECK(shift_size < op.size)
|
|
<< "Shift of size " << shift_size
|
|
<< " is wider than the base register size in shift register in "
|
|
<< Serialize();
|
|
|
|
switch (shift_op.shift_op) {
|
|
|
|
// Left shift.
|
|
case Operand::ShiftRegister::kShiftLeftWithZeroes:
|
|
expr = EmplaceBinaryOp(llvm::Instruction::Shl, expr,
|
|
EmplaceConstant(shift_val));
|
|
break;
|
|
|
|
// Masking shift left.
|
|
case Operand::ShiftRegister::kShiftLeftWithOnes: {
|
|
const auto mask_val =
|
|
llvm::ConstantInt::get(reg_type, ~((~zero) << shift_size));
|
|
expr = EmplaceBinaryOp(llvm::Instruction::Shl, expr,
|
|
EmplaceConstant(shift_val));
|
|
expr = EmplaceBinaryOp(llvm::Instruction::Or, expr,
|
|
EmplaceConstant(mask_val));
|
|
break;
|
|
}
|
|
|
|
// Logical right shift.
|
|
case Operand::ShiftRegister::kShiftUnsignedRight:
|
|
expr = EmplaceBinaryOp(llvm::Instruction::LShr, expr,
|
|
EmplaceConstant(shift_val));
|
|
break;
|
|
|
|
// Arithmetic right shift.
|
|
case Operand::ShiftRegister::kShiftSignedRight:
|
|
expr = EmplaceBinaryOp(llvm::Instruction::AShr, expr,
|
|
EmplaceConstant(shift_val));
|
|
break;
|
|
|
|
// Rotate left.
|
|
case Operand::ShiftRegister::kShiftLeftAround: {
|
|
const uint64_t shr_amount = (~shift_size + one) & (op.size - one);
|
|
const auto shr_val = llvm::ConstantInt::get(op_type, shr_amount);
|
|
auto expr1 = EmplaceBinaryOp(llvm::Instruction::LShr, expr,
|
|
EmplaceConstant(shr_val));
|
|
auto expr2 = EmplaceBinaryOp(llvm::Instruction::Shl, expr,
|
|
EmplaceConstant(shift_val));
|
|
expr = EmplaceBinaryOp(llvm::Instruction::Or, expr1, expr2);
|
|
break;
|
|
}
|
|
|
|
// Rotate right.
|
|
case Operand::ShiftRegister::kShiftRightAround: {
|
|
const uint64_t shl_amount = (~shift_size + one) & (op.size - one);
|
|
const auto shl_val = llvm::ConstantInt::get(op_type, shl_amount);
|
|
auto expr1 = EmplaceBinaryOp(llvm::Instruction::LShr, expr,
|
|
EmplaceConstant(shift_val));
|
|
auto expr2 = EmplaceBinaryOp(llvm::Instruction::Shl, expr,
|
|
EmplaceConstant(shl_val));
|
|
expr = EmplaceBinaryOp(llvm::Instruction::Or, expr1, expr2);
|
|
break;
|
|
}
|
|
|
|
case Operand::ShiftRegister::kShiftInvalid: break;
|
|
}
|
|
}
|
|
if (curr_size < op.size) {
|
|
expr = EmplaceUnaryOp(llvm::Instruction::ZExt, expr, op_type);
|
|
curr_size = op.size;
|
|
}
|
|
};
|
|
|
|
if (shift_op.shift_first) {
|
|
do_shift();
|
|
do_extract();
|
|
} else {
|
|
do_extract();
|
|
do_shift();
|
|
}
|
|
op.expr = expr;
|
|
return op;
|
|
}
|
|
|
|
Operand &Instruction::EmplaceOperand(const Operand::Address &addr_op) {
|
|
operands.emplace_back();
|
|
auto &op = operands.back();
|
|
|
|
const auto word_type = arch->AddressType();
|
|
const auto zero = llvm::ConstantInt::get(word_type, 0, false);
|
|
const auto word_size = arch->address_size;
|
|
|
|
CHECK(word_size >= addr_op.base_reg.size)
|
|
<< "Memory base register " << addr_op.base_reg.name
|
|
<< "for instruction at " << std::hex << pc
|
|
<< " is wider than the machine word size.";
|
|
|
|
CHECK(word_size >= addr_op.index_reg.size)
|
|
<< "Memory index register " << addr_op.base_reg.name
|
|
<< "for instruction at " << std::hex << pc
|
|
<< " is wider than the machine word size.";
|
|
|
|
auto reg_or_zero = [=](const Operand::Register ®) {
|
|
if (!reg.name.empty()) {
|
|
if (auto reg_pointer = arch->RegisterByName(reg.name)) {
|
|
return EmplaceRegister(reg_pointer);
|
|
} else {
|
|
return EmplaceVariable(reg.name,
|
|
llvm::Type::getIntNTy(*arch->context, reg.size));
|
|
}
|
|
} else {
|
|
return EmplaceConstant(zero);
|
|
}
|
|
};
|
|
|
|
auto addr = reg_or_zero(addr_op.base_reg);
|
|
|
|
if (!addr_op.index_reg.name.empty() && addr_op.scale) {
|
|
auto index = reg_or_zero(addr_op.index_reg);
|
|
if (addr_op.scale != 1) {
|
|
auto scale = llvm::ConstantInt::get(
|
|
word_type, static_cast<uint64_t>(addr_op.scale), true);
|
|
index = EmplaceBinaryOp(llvm::Instruction::Mul, index,
|
|
EmplaceConstant(scale));
|
|
}
|
|
addr = EmplaceBinaryOp(llvm::Instruction::Add, addr, index);
|
|
}
|
|
|
|
if (addr_op.displacement) {
|
|
if (0 < addr_op.displacement) {
|
|
auto disp = llvm::ConstantInt::get(
|
|
word_type, static_cast<uint64_t>(addr_op.displacement));
|
|
addr =
|
|
EmplaceBinaryOp(llvm::Instruction::Add, addr, EmplaceConstant(disp));
|
|
} else {
|
|
auto disp = llvm::ConstantInt::get(
|
|
word_type, static_cast<uint64_t>(-addr_op.displacement));
|
|
addr =
|
|
EmplaceBinaryOp(llvm::Instruction::Sub, addr, EmplaceConstant(disp));
|
|
}
|
|
}
|
|
|
|
// Compute the segmented address.
|
|
if (!addr_op.segment_base_reg.name.empty()) {
|
|
auto segment = reg_or_zero(addr_op.segment_base_reg);
|
|
addr = EmplaceBinaryOp(llvm::Instruction::Add, addr, segment);
|
|
}
|
|
|
|
// Memory address is smaller than the machine word size (e.g. 32-bit address
|
|
// used in 64-bit).
|
|
if (addr_op.address_size < word_size) {
|
|
auto addr_type = llvm::Type::getIntNTy(
|
|
*arch->context, static_cast<unsigned>(addr_op.address_size));
|
|
|
|
addr = EmplaceUnaryOp(llvm::Instruction::Trunc, addr, addr_type);
|
|
addr = EmplaceUnaryOp(llvm::Instruction::ZExt, addr, word_type);
|
|
}
|
|
op.expr = addr;
|
|
op.type = Operand::kTypeExpression;
|
|
return op;
|
|
}
|
|
|
|
std::string Instruction::Serialize(void) const {
|
|
std::stringstream ss;
|
|
ss << "(";
|
|
switch (arch_name) {
|
|
case kArchInvalid: break;
|
|
case kArchAMD64:
|
|
case kArchAMD64_AVX:
|
|
case kArchAMD64_AVX512: ss << "AMD64"; break;
|
|
case kArchX86:
|
|
case kArchX86_AVX:
|
|
case kArchX86_AVX512: ss << "X86"; break;
|
|
case kArchAArch32LittleEndian: ss << "AArch32"; break;
|
|
case kArchAArch64LittleEndian: ss << "AArch64"; break;
|
|
case kArchSparc32: ss << "SPARC32"; break;
|
|
case kArchSparc64: ss << "SPARC64"; break;
|
|
}
|
|
|
|
ss << " " << std::hex << pc;
|
|
|
|
if (IsValid()) {
|
|
if (bytes.empty()) {
|
|
ss << " (NO-BYTES)";
|
|
|
|
} else {
|
|
ss << " (BYTES";
|
|
for (auto byte : bytes) {
|
|
ss << " " << std::setw(2) << std::setfill('0') << std::hex
|
|
<< static_cast<unsigned>(static_cast<uint8_t>(byte));
|
|
}
|
|
ss << ")";
|
|
}
|
|
|
|
} else if (bytes.empty()) {
|
|
ss << " (NO-BYTES)";
|
|
|
|
} else {
|
|
|
|
// if the instruction is invalid print the bytes
|
|
// It will be helpful in mapping to the instruction in the absence of binary
|
|
ss << " (BYTES";
|
|
for (auto byte : bytes) {
|
|
ss << " " << std::setw(2) << std::setfill('0') << std::hex
|
|
<< static_cast<unsigned>(static_cast<uint8_t>(byte));
|
|
}
|
|
ss << ")";
|
|
}
|
|
|
|
if (function.empty()) {
|
|
ss << " !NO-FUNCTION!";
|
|
} else {
|
|
ss << " " << function;
|
|
}
|
|
|
|
for (const auto &op : operands) {
|
|
ss << " " << op.Serialize();
|
|
}
|
|
|
|
if (is_atomic_read_modify_write) {
|
|
ss << " IS_ATOMIC";
|
|
}
|
|
|
|
if (has_branch_taken_delay_slot || has_branch_not_taken_delay_slot) {
|
|
ss << " (DELAY_SLOT";
|
|
if (has_branch_taken_delay_slot) {
|
|
ss << " (TAKEN " << std::hex << delayed_pc << std::dec << ")";
|
|
}
|
|
if (has_branch_not_taken_delay_slot) {
|
|
ss << " (NOT_TAKEN " << std::hex << delayed_pc << std::dec << ")";
|
|
}
|
|
ss << ")";
|
|
}
|
|
|
|
if (in_delay_slot) {
|
|
ss << " IN_DELAY_SLOT";
|
|
}
|
|
|
|
switch (category) {
|
|
case Instruction::kCategoryDirectJump:
|
|
ss << " (BRANCH " << std::hex << branch_taken_pc << ")";
|
|
break;
|
|
case Instruction::kCategoryDirectFunctionCall:
|
|
ss << " (DIRECT_CALL (TAKEN " << std::hex << branch_taken_pc << ")"
|
|
<< " (RETURN " << branch_not_taken_pc << "))";
|
|
break;
|
|
case Instruction::kCategoryIndirectFunctionCall:
|
|
ss << " (INDIRECT_CALL (TAKEN <unknown>)"
|
|
<< " (RETURN " << branch_not_taken_pc << "))";
|
|
break;
|
|
case Instruction::kCategoryConditionalBranch:
|
|
ss << " (COND_BRANCH (TAKEN " << std::hex << branch_taken_pc << ")"
|
|
<< " (NOT_TAKEN " << branch_not_taken_pc << std::dec << "))";
|
|
break;
|
|
case kCategoryConditionalIndirectJump:
|
|
ss << " (COND_BRANCH (TAKEN <unknown>)"
|
|
<< " (NOT_TAKEN " << branch_not_taken_pc << std::dec << "))";
|
|
default: break;
|
|
}
|
|
|
|
ss << ")";
|
|
return ss.str();
|
|
}
|
|
|
|
} // namespace remill
|