mirror of
https://github.com/lifting-bits/remill
synced 2026-06-21 13:56:07 +00:00
338 lines
8.4 KiB
C++
338 lines
8.4 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 <glog/logging.h>
|
|
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
|
|
#include "remill/Arch/Arch.h"
|
|
#include "remill/Arch/Instruction.h"
|
|
#include "remill/Arch/Name.h"
|
|
|
|
namespace remill {
|
|
|
|
Operand::Register::Register(void)
|
|
: size(0) {}
|
|
|
|
Operand::ShiftRegister::ShiftRegister(void)
|
|
: shift_size(0),
|
|
extract_size(0),
|
|
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) {}
|
|
|
|
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:
|
|
|
|
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;
|
|
}
|
|
|
|
switch (shift_reg.extend_op) {
|
|
case Operand::ShiftRegister::kExtendInvalid:
|
|
ss << "(REG_" << shift_reg.reg.size << " "
|
|
<< shift_reg.reg.name << ")";
|
|
break;
|
|
|
|
case Operand::ShiftRegister::kExtendSigned:
|
|
ss << "(SEXT (TRUNC (REG_" << shift_reg.reg.size
|
|
<< " " << shift_reg.reg.name << ") " << shift_reg.extract_size
|
|
<< ") " << size << ")";
|
|
break;
|
|
|
|
case Operand::ShiftRegister::kExtendUnsigned:
|
|
ss << "(ZEXT (TRUNC (REG_" << shift_reg.reg.size
|
|
<< " " << shift_reg.reg.name << ") " << shift_reg.extract_size
|
|
<< ") " << size << ")";
|
|
break;
|
|
}
|
|
|
|
if (Operand::ShiftRegister::kShiftInvalid != shift_reg.shift_op) {
|
|
ss << " " << shift_reg.shift_size << ")";
|
|
}
|
|
|
|
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;
|
|
}
|
|
ss << ")";
|
|
return ss.str();
|
|
}
|
|
|
|
Instruction::Instruction(void)
|
|
: pc(0),
|
|
next_pc(0),
|
|
branch_taken_pc(0),
|
|
branch_not_taken_pc(0),
|
|
arch_name(kArchInvalid),
|
|
operand_size(0),
|
|
arch_for_decode(nullptr),
|
|
is_atomic_read_modify_write(false),
|
|
category(Instruction::kCategoryInvalid) {}
|
|
|
|
void Instruction::Reset(void) {
|
|
pc = 0;
|
|
next_pc = 0;
|
|
branch_taken_pc = 0;
|
|
branch_not_taken_pc = 0;
|
|
arch_name = kArchInvalid;
|
|
operand_size = 0;
|
|
is_atomic_read_modify_write = false;
|
|
category = Instruction::kCategoryInvalid;
|
|
arch_for_decode = nullptr;
|
|
operands.clear();
|
|
function.clear();
|
|
bytes.clear();
|
|
}
|
|
|
|
bool Instruction::FinalizeDecode(void) {
|
|
if (!IsValid()) {
|
|
return false;
|
|
} else if (!arch_for_decode) {
|
|
return true;
|
|
} else {
|
|
auto ret = arch_for_decode->DecodeInstruction(pc, bytes, *this);
|
|
arch_for_decode = nullptr;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
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 kArchMips32:
|
|
ss << "MIPS32";
|
|
break;
|
|
case kArchMips64:
|
|
ss << "MIPS64";
|
|
break;
|
|
case kArchAArch64LittleEndian:
|
|
ss << "AArch64";
|
|
break;
|
|
}
|
|
|
|
ss << " " << std::hex << pc;
|
|
|
|
if (IsValid()) {
|
|
ss << " " << std::dec << (next_pc - pc) << " ";
|
|
|
|
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 (is_atomic_read_modify_write) {
|
|
ss << "ATOMIC ";
|
|
}
|
|
} else {
|
|
ss << " ";
|
|
}
|
|
|
|
ss << function;
|
|
for (const auto &op : operands) {
|
|
ss << " " << op.Serialize();
|
|
}
|
|
ss << ")";
|
|
return ss.str();
|
|
}
|
|
|
|
} // namespace remill
|