blob: 90957ce1886a1134b0b78b2b108765bef69d7148 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/disassembler.h"
#include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32.
#if defined(TARGET_ARCH_IA32)
#include "platform/utils.h"
#include "vm/allocation.h"
#include "vm/heap.h"
#include "vm/os.h"
#include "vm/stack_frame.h"
#include "vm/stub_code.h"
namespace dart {
#ifndef PRODUCT
// Tables used for decoding of x86 instructions.
enum OperandOrder { UNSET_OP_ORDER = 0, REG_OPER_OP_ORDER, OPER_REG_OP_ORDER };
struct ByteMnemonic {
int b; // -1 terminates, otherwise must be in range (0..255)
const char* mnem;
OperandOrder op_order_;
};
static ByteMnemonic two_operands_instr[] = {
{0x01, "add", OPER_REG_OP_ORDER}, {0x03, "add", REG_OPER_OP_ORDER},
{0x09, "or", OPER_REG_OP_ORDER}, {0x0B, "or", REG_OPER_OP_ORDER},
{0x11, "adc", OPER_REG_OP_ORDER}, {0x13, "adc", REG_OPER_OP_ORDER},
{0x19, "sbb", OPER_REG_OP_ORDER}, {0x1B, "sbb", REG_OPER_OP_ORDER},
{0x21, "and", OPER_REG_OP_ORDER}, {0x23, "and", REG_OPER_OP_ORDER},
{0x29, "sub", OPER_REG_OP_ORDER}, {0x2B, "sub", REG_OPER_OP_ORDER},
{0x31, "xor", OPER_REG_OP_ORDER}, {0x33, "xor", REG_OPER_OP_ORDER},
{0x39, "cmp", OPER_REG_OP_ORDER}, {0x3B, "cmp", REG_OPER_OP_ORDER},
{0x85, "test", REG_OPER_OP_ORDER}, {0x87, "xchg", REG_OPER_OP_ORDER},
{0x8A, "mov_b", REG_OPER_OP_ORDER}, {0x8B, "mov", REG_OPER_OP_ORDER},
{0x8D, "lea", REG_OPER_OP_ORDER}, {-1, "", UNSET_OP_ORDER}};
static ByteMnemonic zero_operands_instr[] = {
{0xC3, "ret", UNSET_OP_ORDER}, {0xC9, "leave", UNSET_OP_ORDER},
{0x90, "nop", UNSET_OP_ORDER}, {0xF4, "hlt", UNSET_OP_ORDER},
{0xCC, "int3", UNSET_OP_ORDER}, {0x60, "pushad", UNSET_OP_ORDER},
{0x61, "popad", UNSET_OP_ORDER}, {0x9C, "pushfd", UNSET_OP_ORDER},
{0x9D, "popfd", UNSET_OP_ORDER}, {0x9E, "sahf", UNSET_OP_ORDER},
{0x99, "cdq", UNSET_OP_ORDER}, {0x9B, "fwait", UNSET_OP_ORDER},
{-1, "", UNSET_OP_ORDER}};
static ByteMnemonic call_jump_instr[] = {{0xE8, "call", UNSET_OP_ORDER},
{0xE9, "jmp", UNSET_OP_ORDER},
{-1, "", UNSET_OP_ORDER}};
static ByteMnemonic short_immediate_instr[] = {
{0x05, "add", UNSET_OP_ORDER}, {0x0D, "or", UNSET_OP_ORDER},
{0x15, "adc", UNSET_OP_ORDER}, {0x25, "and", UNSET_OP_ORDER},
{0x2D, "sub", UNSET_OP_ORDER}, {0x35, "xor", UNSET_OP_ORDER},
{0x3D, "cmp", UNSET_OP_ORDER}, {-1, "", UNSET_OP_ORDER}};
static const char* jump_conditional_mnem[] = {
/*0*/ "jo", "jno", "jc", "jnc",
/*4*/ "jz", "jnz", "jna", "ja",
/*8*/ "js", "jns", "jpe", "jpo",
/*12*/ "jl", "jnl", "jng", "jg"};
static const char* set_conditional_mnem[] = {
/*0*/ "seto", "setno", "setc", "setnc",
/*4*/ "setz", "setnz", "setna", "seta",
/*8*/ "sets", "setns", "setpe", "setpo",
/*12*/ "setl", "setnl", "setng", "setg"};
static const char* conditional_move_mnem[] = {
/*0*/ "cmovo", "cmovno", "cmovc", "cmovnc",
/*4*/ "cmovz", "cmovnz", "cmovna", "cmova",
/*8*/ "cmovs", "cmovns", "cmovpe", "cmovpo",
/*12*/ "cmovl", "cmovnl", "cmovng", "cmovg"};
enum InstructionType {
NO_INSTR,
ZERO_OPERANDS_INSTR,
TWO_OPERANDS_INSTR,
JUMP_CONDITIONAL_SHORT_INSTR,
REGISTER_INSTR,
MOVE_REG_INSTR,
CALL_JUMP_INSTR,
SHORT_IMMEDIATE_INSTR
};
struct InstructionDesc {
const char* mnem;
InstructionType type;
OperandOrder op_order_;
};
class InstructionTable : public ValueObject {
public:
InstructionTable();
const InstructionDesc& Get(uint8_t x) const { return instructions_[x]; }
private:
InstructionDesc instructions_[256];
void Clear();
void Init();
void CopyTable(ByteMnemonic bm[], InstructionType type);
void SetTableRange(InstructionType type,
uint8_t start,
uint8_t end,
const char* mnem);
void AddJumpConditionalShort();
DISALLOW_COPY_AND_ASSIGN(InstructionTable);
};
InstructionTable::InstructionTable() {
Clear();
Init();
}
void InstructionTable::Clear() {
for (int i = 0; i < 256; i++) {
instructions_[i].mnem = "";
instructions_[i].type = NO_INSTR;
instructions_[i].op_order_ = UNSET_OP_ORDER;
}
}
void InstructionTable::Init() {
CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR);
CopyTable(call_jump_instr, CALL_JUMP_INSTR);
CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR);
AddJumpConditionalShort();
SetTableRange(REGISTER_INSTR, 0x40, 0x47, "inc");
SetTableRange(REGISTER_INSTR, 0x48, 0x4F, "dec");
SetTableRange(REGISTER_INSTR, 0x50, 0x57, "push");
SetTableRange(REGISTER_INSTR, 0x58, 0x5F, "pop");
SetTableRange(REGISTER_INSTR, 0x91, 0x97, "xchg eax,"); // 0x90 is nop.
SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, "mov");
}
void InstructionTable::CopyTable(ByteMnemonic bm[], InstructionType type) {
for (int i = 0; bm[i].b >= 0; i++) {
InstructionDesc* id = &instructions_[bm[i].b];
id->mnem = bm[i].mnem;
id->op_order_ = bm[i].op_order_;
ASSERT(id->type == NO_INSTR); // Information already entered
id->type = type;
}
}
void InstructionTable::SetTableRange(InstructionType type,
uint8_t start,
uint8_t end,
const char* mnem) {
for (uint8_t b = start; b <= end; b++) {
InstructionDesc* id = &instructions_[b];
ASSERT(id->type == NO_INSTR); // Information already entered
id->mnem = mnem;
id->type = type;
}
}
void InstructionTable::AddJumpConditionalShort() {
for (uint8_t b = 0x70; b <= 0x7F; b++) {
InstructionDesc* id = &instructions_[b];
ASSERT(id->type == NO_INSTR); // Information already entered
id->mnem = jump_conditional_mnem[b & 0x0F];
id->type = JUMP_CONDITIONAL_SHORT_INSTR;
}
}
static InstructionTable instruction_table;
// Mnemonics for instructions 0xF0 byte.
// Returns NULL if the instruction is not handled here.
static const char* F0Mnem(uint8_t f0byte) {
switch (f0byte) {
case 0x12:
return "movhlps";
case 0x14:
return "unpcklps";
case 0x15:
return "unpckhps";
case 0x16:
return "movlhps";
case 0xA2:
return "cpuid";
case 0x31:
return "rdtsc";
case 0xBE:
return "movsx_b";
case 0xBF:
return "movsx_w";
case 0xB6:
return "movzx_b";
case 0xB7:
return "movzx_w";
case 0xAF:
return "imul";
case 0xA4: // Fall through.
case 0xA5:
return "shld";
case 0xAC: // Fall through.
case 0xAD:
return "shrd";
case 0xA3:
return "bt";
case 0xAB:
return "bts";
case 0xBD:
return "bsr";
case 0xB1:
return "cmpxchg";
case 0x50:
return "movmskps";
case 0x51:
return "sqrtps";
case 0x52:
return "rqstps";
case 0x53:
return "rcpps";
case 0x54:
return "andps";
case 0x56:
return "orps";
case 0x57:
return "xorps";
case 0x58:
return "addps";
case 0x59:
return "mulps";
case 0x5A:
return "cvtps2pd";
case 0x5C:
return "subps";
case 0x5D:
return "minps";
case 0x5E:
return "divps";
case 0x5F:
return "maxps";
case 0x28:
return "movaps";
case 0x10:
return "movups";
case 0x11:
return "movups";
default:
return NULL;
}
}
static const char* PackedDoubleMnemonic(uint8_t data) {
const char* mnemonic = NULL;
if (data == 0xFE) mnemonic = "paddd ";
if (data == 0xFA) mnemonic = "psubd ";
if (data == 0x2F) mnemonic = "comisd ";
if (data == 0x58) mnemonic = "addpd ";
if (data == 0x5C) mnemonic = "subpd ";
if (data == 0x59) mnemonic = "mulpd ";
if (data == 0x5E) mnemonic = "divpd ";
if (data == 0x5D) mnemonic = "minpd ";
if (data == 0x5F) mnemonic = "maxpd ";
if (data == 0x51) mnemonic = "sqrtpd ";
if (data == 0x5A) mnemonic = "cvtpd2ps ";
ASSERT(mnemonic != NULL);
return mnemonic;
}
static bool IsTwoXmmRegInstruction(uint8_t f0byte) {
return f0byte == 0x28 || f0byte == 0x11 || f0byte == 0x12 || f0byte == 0x14 ||
f0byte == 0x15 || f0byte == 0x16 || f0byte == 0x51 || f0byte == 0x52 ||
f0byte == 0x53 || f0byte == 0x54 || f0byte == 0x56 || f0byte == 0x58 ||
f0byte == 0x59 || f0byte == 0x5C || f0byte == 0x5D || f0byte == 0x5E ||
f0byte == 0x5F || f0byte == 0x5A;
}
// The implementation of x86 decoding based on the above tables.
class X86Decoder : public ValueObject {
public:
X86Decoder(char* buffer, intptr_t buffer_size)
: buffer_(buffer), buffer_size_(buffer_size), buffer_pos_(0) {
buffer_[buffer_pos_] = '\0';
}
~X86Decoder() {}
// Writes one disassembled instruction into the buffer (0-terminated).
// Returns the length of the disassembled machine instruction in bytes.
int InstructionDecode(uword pc);
private:
enum {
eax = 0,
ecx = 1,
edx = 2,
ebx = 3,
esp = 4,
ebp = 5,
esi = 6,
edi = 7
};
// Bottleneck functions to print into the out_buffer.
void PrintInt(int value);
void PrintHex(int value, bool signed_value = false);
void Print(const char* str);
const char* GetBranchPrefix(uint8_t** data);
bool DecodeInstructionType(const InstructionDesc& idesc,
const char* branch_hint,
uint8_t** data);
// Printing of common values.
void PrintCPURegister(int reg);
void PrintCPUByteRegister(int reg);
void PrintXmmRegister(int reg);
void PrintXmmComparison(int comparison);
void PrintAddress(uword addr);
typedef void (X86Decoder::*RegisterNamePrinter)(int reg);
int PrintRightOperandHelper(uint8_t* modrmp,
RegisterNamePrinter register_printer);
int PrintRightOperand(uint8_t* modrmp);
int PrintRightXmmOperand(uint8_t* modrmp);
int PrintRightByteOperand(uint8_t* modrmp);
int PrintOperands(const char* mnem, OperandOrder op_order, uint8_t* data);
int PrintImmediateOp(uint8_t* data, bool size_override = false);
// Handle special encodings.
int JumpShort(uint8_t* data);
int JumpConditional(uint8_t* data, const char* comment);
int JumpConditionalShort(uint8_t* data, const char* comment);
int SetCC(uint8_t* data);
int CMov(uint8_t* data);
int D1D3C1Instruction(uint8_t* data);
uint8_t* F3Instruction(uint8_t* data);
int F7Instruction(uint8_t* data);
int FPUInstruction(uint8_t* data);
uint8_t* SSEInstruction(uint8_t prefix, uint8_t primary, uint8_t* data);
int BitwisePDInstruction(uint8_t* data);
int Packed660F38Instruction(uint8_t* data);
int DecodeEnter(uint8_t* data);
void CheckPrintStop(uint8_t* data);
// Disassembler helper functions.
static void GetModRm(uint8_t data, int* mod, int* regop, int* rm) {
*mod = (data >> 6) & 3;
*regop = (data & 0x38) >> 3;
*rm = data & 7;
}
static void GetSib(uint8_t data, int* scale, int* index, int* base) {
*scale = (data >> 6) & 3;
*index = (data >> 3) & 7;
*base = data & 7;
}
// Convenience functions.
char* get_buffer() const { return buffer_; }
char* current_position_in_buffer() { return buffer_ + buffer_pos_; }
intptr_t remaining_size_in_buffer() { return buffer_size_ - buffer_pos_; }
char* buffer_; // Decode instructions into this buffer.
intptr_t buffer_size_; // The size of the buffer_.
intptr_t buffer_pos_; // Current character position in the buffer_.
DISALLOW_COPY_AND_ASSIGN(X86Decoder);
};
void X86Decoder::PrintInt(int value) {
char int_buffer[16];
OS::SNPrint(int_buffer, sizeof(int_buffer), "%#x", value);
Print(int_buffer);
}
// Append the int value (printed in hex) to the output buffer.
void X86Decoder::PrintHex(int value, bool signed_value) {
char hex_buffer[16];
if (signed_value && value < 0) {
OS::SNPrint(hex_buffer, sizeof(hex_buffer), "-%#x", -value);
} else {
OS::SNPrint(hex_buffer, sizeof(hex_buffer), "%#x", value);
}
Print(hex_buffer);
}
// Append the str to the output buffer.
void X86Decoder::Print(const char* str) {
char cur = *str++;
while (cur != '\0' && (buffer_pos_ < (buffer_size_ - 1))) {
buffer_[buffer_pos_++] = cur;
cur = *str++;
}
buffer_[buffer_pos_] = '\0';
}
static const int kMaxCPURegisters = 8;
static const char* cpu_regs[kMaxCPURegisters] = {"eax", "ecx", "edx", "ebx",
"esp", "ebp", "esi", "edi"};
static const int kMaxByteCPURegisters = 8;
static const char* byte_cpu_regs[kMaxByteCPURegisters] = {
"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"};
static const int kMaxXmmRegisters = 8;
static const char* xmm_regs[kMaxXmmRegisters] = {
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"};
void X86Decoder::PrintCPURegister(int reg) {
ASSERT(0 <= reg);
ASSERT(reg < kMaxCPURegisters);
Print(cpu_regs[reg]);
}
void X86Decoder::PrintCPUByteRegister(int reg) {
ASSERT(0 <= reg);
ASSERT(reg < kMaxByteCPURegisters);
Print(byte_cpu_regs[reg]);
}
void X86Decoder::PrintXmmRegister(int reg) {
ASSERT(0 <= reg);
ASSERT(reg < kMaxXmmRegisters);
Print(xmm_regs[reg]);
}
void X86Decoder::PrintXmmComparison(int comparison) {
ASSERT(0 <= comparison);
ASSERT(comparison < 8);
static const char* comparisons[8] = {
"eq", "lt", "le", "unordered", "not eq", "not lt", "not le", "ordered"};
Print(comparisons[comparison]);
}
void X86Decoder::PrintAddress(uword addr) {
char addr_buffer[32];
OS::SNPrint(addr_buffer, sizeof(addr_buffer), "%#" Px "", addr);
Print(addr_buffer);
// Try to print as stub name.
const char* name_of_stub = StubCode::NameOfStub(addr);
if (name_of_stub != NULL) {
Print(" [stub: ");
Print(name_of_stub);
Print("]");
}
}
int X86Decoder::PrintRightOperandHelper(uint8_t* modrmp,
RegisterNamePrinter register_printer) {
int mod, regop, rm;
GetModRm(*modrmp, &mod, &regop, &rm);
switch (mod) {
case 0:
if (rm == ebp) {
int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 1);
Print("[");
PrintHex(disp);
Print("]");
return 5;
} else if (rm == esp) {
uint8_t sib = *(modrmp + 1);
int scale, index, base;
GetSib(sib, &scale, &index, &base);
if (index == esp && base == esp && scale == 0 /*times_1*/) {
Print("[");
PrintCPURegister(rm);
Print("]");
return 2;
} else if (base == ebp) {
int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
Print("[");
PrintCPURegister(index);
Print("*");
PrintInt(1 << scale);
if (disp < 0) {
Print("-");
disp = -disp;
} else {
Print("+");
}
PrintHex(disp);
Print("]");
return 6;
} else if (index != esp && base != ebp) {
// [base+index*scale]
Print("[");
PrintCPURegister(base);
Print("+");
PrintCPURegister(index);
Print("*");
PrintInt(1 << scale);
Print("]");
return 2;
} else {
UNIMPLEMENTED();
return 1;
}
} else {
Print("[");
PrintCPURegister(rm);
Print("]");
return 1;
}
break;
case 1: // fall through
case 2:
if (rm == esp) {
uint8_t sib = *(modrmp + 1);
int scale, index, base;
GetSib(sib, &scale, &index, &base);
int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 2)
: *reinterpret_cast<int8_t*>(modrmp + 2);
if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) {
Print("[");
PrintCPURegister(rm);
if (disp < 0) {
Print("-");
disp = -disp;
} else {
Print("+");
}
PrintHex(disp);
Print("]");
} else {
Print("[");
PrintCPURegister(base);
Print("+");
PrintCPURegister(index);
Print("*");
PrintInt(1 << scale);
if (disp < 0) {
Print("-");
disp = -disp;
} else {
Print("+");
}
PrintHex(disp);
Print("]");
}
return mod == 2 ? 6 : 3;
} else {
// No sib.
int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 1)
: *reinterpret_cast<int8_t*>(modrmp + 1);
Print("[");
PrintCPURegister(rm);
if (disp < 0) {
Print("-");
disp = -disp;
} else {
Print("+");
}
PrintHex(disp);
Print("]");
return mod == 2 ? 5 : 2;
}
break;
case 3:
(this->*register_printer)(rm);
return 1;
default:
UNIMPLEMENTED();
return 1;
}
UNREACHABLE();
}
int X86Decoder::PrintRightOperand(uint8_t* modrmp) {
return PrintRightOperandHelper(modrmp, &X86Decoder::PrintCPURegister);
}
int X86Decoder::PrintRightXmmOperand(uint8_t* modrmp) {
return PrintRightOperandHelper(modrmp, &X86Decoder::PrintXmmRegister);
}
int X86Decoder::PrintRightByteOperand(uint8_t* modrmp) {
return PrintRightOperandHelper(modrmp, &X86Decoder::PrintCPUByteRegister);
}
int X86Decoder::PrintOperands(const char* mnem,
OperandOrder op_order,
uint8_t* data) {
uint8_t modrm = *data;
int mod, regop, rm;
GetModRm(modrm, &mod, &regop, &rm);
int advance = 0;
switch (op_order) {
case REG_OPER_OP_ORDER: {
Print(mnem);
Print(" ");
PrintCPURegister(regop);
Print(",");
advance = PrintRightOperand(data);
break;
}
case OPER_REG_OP_ORDER: {
Print(mnem);
Print(" ");
advance = PrintRightOperand(data);
Print(",");
PrintCPURegister(regop);
break;
}
default:
UNREACHABLE();
break;
}
return advance;
}
int X86Decoder::PrintImmediateOp(uint8_t* data, bool size_override) {
bool sign_extension_bit = (*data & 0x02) != 0;
uint8_t modrm = *(data + 1);
int mod, regop, rm;
GetModRm(modrm, &mod, &regop, &rm);
const char* mnem = "Imm???";
switch (regop) {
case 0:
mnem = "add";
break;
case 1:
mnem = "or";
break;
case 2:
mnem = "adc";
break;
case 3:
mnem = "sbb";
break;
case 4:
mnem = "and";
break;
case 5:
mnem = "sub";
break;
case 6:
mnem = "xor";
break;
case 7:
mnem = "cmp";
break;
default:
UNIMPLEMENTED();
}
Print(mnem);
Print(" ");
int count = PrintRightOperand(data + 1);
Print(",");
if (size_override) {
PrintHex(*reinterpret_cast<int16_t*>(data + 1 + count));
return 1 + count + 2 /*int16_t*/;
} else if (sign_extension_bit) {
PrintHex(*reinterpret_cast<int8_t*>(data + 1 + count), sign_extension_bit);
return 1 + count + 1 /*int8_t*/;
} else {
PrintHex(*reinterpret_cast<int32_t*>(data + 1 + count));
return 1 + count + 4 /*int32_t*/;
}
}
int X86Decoder::DecodeEnter(uint8_t* data) {
uint16_t size = *reinterpret_cast<uint16_t*>(data + 1);
uint8_t level = *reinterpret_cast<uint8_t*>(data + 3);
Print("enter ");
PrintInt(size);
Print(", ");
PrintInt(level);
return 4;
}
// Returns number of bytes used, including *data.
int X86Decoder::JumpShort(uint8_t* data) {
ASSERT(*data == 0xEB);
uint8_t b = *(data + 1);
uword dest = reinterpret_cast<uword>(data) + static_cast<int8_t>(b) + 2;
Print("jmp ");
PrintAddress(dest);
return 2;
}
// Returns number of bytes used, including *data.
int X86Decoder::JumpConditional(uint8_t* data, const char* comment) {
ASSERT(*data == 0x0F);
uint8_t cond = *(data + 1) & 0x0F;
uword dest =
reinterpret_cast<uword>(data) + *reinterpret_cast<int32_t*>(data + 2) + 6;
const char* mnem = jump_conditional_mnem[cond];
Print(mnem);
Print(" ");
PrintAddress(dest);
if (comment != NULL) {
Print(", ");
Print(comment);
}
return 6; // includes 0x0F
}
// Returns number of bytes used, including *data.
int X86Decoder::JumpConditionalShort(uint8_t* data, const char* comment) {
uint8_t cond = *data & 0x0F;
uint8_t b = *(data + 1);
uword dest = reinterpret_cast<uword>(data) + static_cast<int8_t>(b) + 2;
const char* mnem = jump_conditional_mnem[cond];
Print(mnem);
Print(" ");
PrintAddress(dest);
if (comment != NULL) {
Print(", ");
Print(comment);
}
return 2;
}
// Returns number of bytes used, including *data.
int X86Decoder::SetCC(uint8_t* data) {
ASSERT(*data == 0x0F);
uint8_t cond = *(data + 1) & 0x0F;
const char* mnem = set_conditional_mnem[cond];
Print(mnem);
Print(" ");
PrintRightByteOperand(data + 2);
return 3; // includes 0x0F
}
// Returns number of bytes used, including *data.
int X86Decoder::CMov(uint8_t* data) {
ASSERT(*data == 0x0F);
uint8_t cond = *(data + 1) & 0x0F;
const char* mnem = conditional_move_mnem[cond];
int op_size = PrintOperands(mnem, REG_OPER_OP_ORDER, data + 2);
return 2 + op_size; // includes 0x0F
}
int X86Decoder::D1D3C1Instruction(uint8_t* data) {
uint8_t op = *data;
ASSERT(op == 0xD1 || op == 0xD3 || op == 0xC1);
int mod, regop, rm;
GetModRm(*(data + 1), &mod, &regop, &rm);
int num_bytes = 1;
const char* mnem = NULL;
switch (regop) {
case 2:
mnem = "rcl";
break;
case 4:
mnem = "shl";
break;
case 5:
mnem = "shr";
break;
case 7:
mnem = "sar";
break;
default:
UNIMPLEMENTED();
}
ASSERT(mnem != NULL);
Print(mnem);
Print(" ");
if (op == 0xD1) {
num_bytes += PrintRightOperand(data + 1);
Print(", 1");
} else if (op == 0xC1) {
num_bytes += PrintRightOperand(data + 1);
Print(", ");
PrintInt(*(data + 2));
num_bytes++;
} else {
ASSERT(op == 0xD3);
num_bytes += PrintRightOperand(data + 1);
Print(", cl");
}
return num_bytes;
}
uint8_t* X86Decoder::F3Instruction(uint8_t* data) {
if (*(data + 1) == 0x0F) {
uint8_t b2 = *(data + 2);
switch (b2) {
case 0x2C: {
data += 3;
data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data);
break;
}
case 0x2A: {
data += 3;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print("cvtsi2ss ");
PrintXmmRegister(regop);
Print(",");
data += PrintRightOperand(data);
break;
}
case 0x2D: {
data += 3;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print("cvtss2si ");
PrintCPURegister(regop);
Print(",");
data += PrintRightXmmOperand(data);
break;
}
case 0x11: {
// movss xmm <- address
Print("movss ");
data += 3;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
data += PrintRightXmmOperand(data);
Print(",");
PrintXmmRegister(regop);
break;
}
case 0x10: {
// movss address <- xmm
data += 3;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print("movss ");
PrintXmmRegister(regop);
Print(",");
data += PrintRightOperand(data);
break;
}
case 0x51: // Fall through.
case 0x58: // Fall through.
case 0x59: // Fall through.
case 0x5A: // Fall through.
case 0x5C: // Fall through.
case 0x5E: // Fall through.
case 0xE6: {
data += 3;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
const char* mnem = "?? 0xF3";
switch (b2) {
case 0x51:
mnem = "sqrtss";
break;
case 0x58:
mnem = "addss";
break;
case 0x59:
mnem = "mulss";
break;
case 0x5A:
mnem = "cvtss2sd";
break;
case 0x5C:
mnem = "subss";
break;
case 0x5E:
mnem = "divss";
break;
case 0xE6:
mnem = "cvtdq2pd";
break;
default:
UNIMPLEMENTED();
}
Print(mnem);
Print(" ");
PrintXmmRegister(regop);
Print(",");
data += PrintRightXmmOperand(data);
break;
}
case 0x7E: {
data += 3;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print("movq ");
PrintXmmRegister(regop);
Print(",");
data += PrintRightOperand(data);
break;
}
default:
UNIMPLEMENTED();
}
} else if (*(data + 1) == 0xA4) {
Print("rep_movsb");
data += 2;
} else {
UNIMPLEMENTED();
}
return data;
}
// Returns number of bytes used, including *data.
int X86Decoder::F7Instruction(uint8_t* data) {
ASSERT(*data == 0xF7);
uint8_t modrm = *(data + 1);
int mod, regop, rm;
GetModRm(modrm, &mod, &regop, &rm);
if (mod == 3 && regop != 0) {
const char* mnem = NULL;
switch (regop) {
case 2:
mnem = "not";
break;
case 3:
mnem = "neg";
break;
case 4:
mnem = "mul";
break;
case 5:
mnem = "imul";
break;
case 6:
mnem = "div";
break;
case 7:
mnem = "idiv";
break;
default:
UNIMPLEMENTED();
}
Print(mnem);
Print(" ");
PrintCPURegister(rm);
return 2;
} else if (mod == 3 && regop == eax) {
int32_t imm = *reinterpret_cast<int32_t*>(data + 2);
Print("test ");
PrintCPURegister(rm);
Print(",");
PrintHex(imm);
return 6;
} else if (regop == eax) {
Print("test ");
int count = PrintRightOperand(data + 1);
int32_t imm = *reinterpret_cast<int32_t*>(data + 1 + count);
Print(",");
PrintHex(imm);
return 1 + count + 4 /*int32_t*/;
} else if (regop == 5) {
Print("imul ");
int count = PrintRightOperand(data + 1);
return 1 + count;
} else if (regop == 4) {
Print("mul ");
int count = PrintRightOperand(data + 1);
return 1 + count;
} else {
OS::Print("F7 Instr regop %d\n", regop);
UNIMPLEMENTED();
return 2;
}
}
// Returns number of bytes used, including *data.
int X86Decoder::FPUInstruction(uint8_t* data) {
uint8_t b1 = *data;
uint8_t b2 = *(data + 1);
if (b1 == 0xD9) {
const char* mnem = NULL;
switch (b2) {
case 0xE0:
mnem = "fchs";
break;
case 0xE1:
mnem = "fabs";
break;
case 0xE4:
mnem = "ftst";
break;
case 0xE8:
mnem = "fld1";
break;
case 0xEE:
mnem = "fldz";
break;
case 0xF2:
mnem = "fptan";
break;
case 0xF5:
mnem = "fprem1";
break;
case 0xF8:
mnem = "fprem";
break;
case 0xF7:
mnem = "fincstp";
break;
case 0xFB:
mnem = "fsincos";
break;
case 0xFE:
mnem = "fsin";
break;
case 0xFF:
mnem = "fcos";
break;
}
if (mnem != NULL) {
Print(mnem);
return 2;
} else if ((b2 & 0xF8) == 0xC8) {
Print("fxch st");
PrintInt(b2 & 0x7);
return 2;
} else {
int mod, regop, rm;
GetModRm(*(data + 1), &mod, &regop, &rm);
const char* mnem = "? FPU 0xD9";
switch (regop) {
case 0:
mnem = "fld_s";
break;
case 3:
mnem = "fstp_s";
break;
case 5:
mnem = "fldcw";
break;
case 7:
mnem = "fnstcw";
break;
default:
UNIMPLEMENTED();
}
Print(mnem);
Print(" ");
int count = PrintRightOperand(data + 1);
return count + 1;
}
} else if (b1 == 0xDD) {
if ((b2 & 0xF8) == 0xC0) {
Print("ffree st");
PrintInt(b2 & 0x7);
return 2;
} else {
int mod, regop, rm;
GetModRm(*(data + 1), &mod, &regop, &rm);
const char* mnem = "? FPU 0xDD";
switch (regop) {
case 0:
mnem = "fld_d";
break;
case 3:
mnem = "fstp_d";
break;
default:
UNIMPLEMENTED();
}
Print(mnem);
Print(" ");
int count = PrintRightOperand(data + 1);
return count + 1;
}
} else if (b1 == 0xDB) {
int mod, regop, rm;
GetModRm(*(data + 1), &mod, &regop, &rm);
const char* mnem = "? FPU 0xDB";
switch (regop) {
case 0:
mnem = "fild_s";
break;
case 2:
mnem = "fist_s";
break;
case 3:
mnem = "fistp_s";
break;
default:
UNIMPLEMENTED();
}
Print(mnem);
Print(" ");
int count = PrintRightOperand(data + 1);
return count + 1;
} else if (b1 == 0xDF) {
if (b2 == 0xE0) {
Print("fnstsw_ax");
return 2;
}
int mod, regop, rm;
GetModRm(*(data + 1), &mod, &regop, &rm);
const char* mnem = "? FPU 0xDF";
switch (regop) {
case 5:
mnem = "fild_d";
break;
case 7:
mnem = "fistp_d";
break;
default:
UNIMPLEMENTED();
}
Print(mnem);
Print(" ");
int count = PrintRightOperand(data + 1);
return count + 1;
} else if (b1 == 0xDC || b1 == 0xDE) {
bool is_pop = (b1 == 0xDE);
if (is_pop && b2 == 0xD9) {
Print("fcompp");
return 2;
}
const char* mnem = "FP0xDC";
switch (b2 & 0xF8) {
case 0xC0:
mnem = "fadd";
break;
case 0xE8:
mnem = "fsub";
break;
case 0xC8:
mnem = "fmul";
break;
case 0xF8:
mnem = "fdiv";
break;
default:
UNIMPLEMENTED();
}
Print(mnem);
Print(is_pop ? "p" : "");
Print(" st");
PrintInt(b2 & 0x7);
return 2;
} else if (b1 == 0xDA && b2 == 0xE9) {
const char* mnem = "fucompp";
Print(mnem);
return 2;
}
Print("Unknown FP instruction");
return 2;
}
uint8_t* X86Decoder::SSEInstruction(uint8_t prefix,
uint8_t primary,
uint8_t* data) {
ASSERT(prefix == 0x0F);
int mod, regop, rm;
if (primary == 0x10) {
GetModRm(*data, &mod, &regop, &rm);
Print("movups ");
PrintXmmRegister(regop);
Print(",");
data += PrintRightOperand(data);
} else if (primary == 0x11) {
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print("movups ");
data += PrintRightXmmOperand(data);
Print(",");
PrintXmmRegister(regop);
} else if (IsTwoXmmRegInstruction(primary)) {
const char* f0mnem = F0Mnem(primary);
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print(f0mnem);
Print(" ");
PrintXmmRegister(regop);
Print(",");
data += PrintRightXmmOperand(data);
}
return data;
}
int X86Decoder::BitwisePDInstruction(uint8_t* data) {
const char* mnem =
(*data == 0x57) ? "xorpd" : (*data == 0x56) ? "orpd" : "andpd";
int mod, regop, rm;
GetModRm(*(data + 1), &mod, &regop, &rm);
Print(mnem);
Print(" ");
PrintXmmRegister(regop);
Print(",");
return 1 + PrintRightXmmOperand(data + 1);
}
int X86Decoder::Packed660F38Instruction(uint8_t* data) {
if (*(data + 1) == 0x25) {
Print("pmovsxdq ");
int mod, regop, rm;
GetModRm(*(data + 2), &mod, &regop, &rm);
PrintXmmRegister(regop);
Print(",");
return 2 + PrintRightXmmOperand(data + 2);
} else if (*(data + 1) == 0x29) {
Print("pcmpeqq ");
int mod, regop, rm;
GetModRm(*(data + 2), &mod, &regop, &rm);
PrintXmmRegister(regop);
Print(",");
return 2 + PrintRightXmmOperand(data + 2);
}
UNREACHABLE();
return 1;
}
// Called when disassembling test eax, 0xXXXXX.
void X86Decoder::CheckPrintStop(uint8_t* data) {
// Recognize stop pattern.
if (*reinterpret_cast<uint8_t*>(data + 5) == 0xCC) {
Print(" STOP:'");
const char* text = *reinterpret_cast<const char**>(data + 1);
Print(text);
Print("'");
}
}
const char* X86Decoder::GetBranchPrefix(uint8_t** data) {
// We use these two prefixes only with branch prediction
switch (**data) {
case 0x3E: // ds
(*data)++;
return "predicted taken";
case 0x2E: // cs
(*data)++;
return "predicted not taken";
case 0xF0: // lock
Print("lock ");
(*data)++;
return NULL;
default: // Ignore all other instructions.
return NULL;
}
}
bool X86Decoder::DecodeInstructionType(const InstructionDesc& idesc,
const char* branch_hint,
uint8_t** data) {
switch (idesc.type) {
case ZERO_OPERANDS_INSTR:
Print(idesc.mnem);
(*data)++;
return true;
case TWO_OPERANDS_INSTR:
(*data)++;
(*data) += PrintOperands(idesc.mnem, idesc.op_order_, *data);
return true;
case JUMP_CONDITIONAL_SHORT_INSTR:
(*data) += JumpConditionalShort(*data, branch_hint);
return true;
case REGISTER_INSTR:
Print(idesc.mnem);
Print(" ");
PrintCPURegister(**data & 0x07);
(*data)++;
return true;
case MOVE_REG_INSTR: {
uword addr = *reinterpret_cast<uword*>(*data + 1);
Print("mov ");
PrintCPURegister(**data & 0x07), Print(",");
PrintAddress(addr);
(*data) += 5;
return true;
}
case CALL_JUMP_INSTR: {
uword addr = reinterpret_cast<uword>(*data) +
*reinterpret_cast<uword*>(*data + 1) + 5;
Print(idesc.mnem);
Print(" ");
PrintAddress(addr);
(*data) += 5;
return true;
}
case SHORT_IMMEDIATE_INSTR: {
uword addr = *reinterpret_cast<uword*>(*data + 1);
Print(idesc.mnem);
Print(" eax, ");
PrintAddress(addr);
(*data) += 5;
return true;
}
case NO_INSTR:
return false;
default:
UNIMPLEMENTED(); // This type is not implemented.
return false;
}
}
int X86Decoder::InstructionDecode(uword pc) {
uint8_t* data = reinterpret_cast<uint8_t*>(pc);
// Check for hints.
const char* branch_hint = GetBranchPrefix(&data);
const InstructionDesc& idesc = instruction_table.Get(*data);
// Will be set to false if the current instruction
// is not in 'instructions' table.
bool processed = DecodeInstructionType(idesc, branch_hint, &data);
//----------------------------
if (!processed) {
switch (*data) {
case 0xC2:
Print("ret ");
PrintHex(*reinterpret_cast<uint16_t*>(data + 1));
data += 3;
break;
case 0x69: // fall through
case 0x6B: {
int mod, regop, rm;
GetModRm(*(data + 1), &mod, &regop, &rm);
int32_t imm =
*data == 0x6B ? *(data + 2) : *reinterpret_cast<int32_t*>(data + 2);
Print("imul ");
PrintCPURegister(regop);
Print(",");
PrintCPURegister(rm);
Print(",");
PrintHex(imm);
data += 2 + (*data == 0x6B ? 1 : 4);
} break;
case 0xF6: {
int mod, regop, rm;
GetModRm(*(data + 1), &mod, &regop, &rm);
if ((mod == 3) && (regop == eax)) {
Print("test_b ");
PrintCPURegister(rm);
Print(",");
PrintHex(*(data + 2));
data += 3;
} else {
data++;
Print("test_b ");
data += PrintRightOperand(data);
int32_t imm = *data;
Print(",");
PrintHex(imm);
data++;
}
} break;
case 0x81: // fall through
case 0x83: // 0x81 with sign extension bit set
data += PrintImmediateOp(data);
break;
case 0x0F: {
uint8_t f0byte = *(data + 1);
const char* f0mnem = F0Mnem(f0byte);
if (f0byte == 0xA2 || f0byte == 0x31) {
Print(f0mnem);
data += 2;
} else if ((f0byte & 0xF0) == 0x80) {
data += JumpConditional(data, branch_hint);
} else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
f0byte == 0xB7 || f0byte == 0xAF || f0byte == 0xBD) {
data += 2;
data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data);
} else if (f0byte == 0x57) {
data += 2;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print(f0mnem);
Print(" ");
PrintXmmRegister(regop);
Print(",");
data += PrintRightXmmOperand(data);
} else if (f0byte == 0xB1) {
data += 2;
data += PrintOperands(f0mnem, OPER_REG_OP_ORDER, data);
} else if ((f0byte & 0xF0) == 0x90) {
data += SetCC(data);
} else if ((f0byte & 0xF0) == 0x40) {
data += CMov(data);
} else if (f0byte == 0x2F) {
data += 2;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print("comiss ");
PrintXmmRegister(regop);
Print(",");
PrintXmmRegister(rm);
data++;
} else if (f0byte == 0x1F) {
if (*(data + 2) == 0x00) {
Print("nop");
data += 3;
} else if (*(data + 2) == 0x40 && *(data + 3) == 0x00) {
Print("nop");
data += 4;
} else if (*(data + 2) == 0x44 && *(data + 3) == 0x00 &&
*(data + 4) == 0x00) {
Print("nop");
data += 5;
} else if (*(data + 2) == 0x80 && *(data + 3) == 0x00 &&
*(data + 4) == 0x00 && *(data + 5) == 0x00 &&
*(data + 6) == 0x00) {
Print("nop");
data += 7;
} else if (*(data + 2) == 0x84 && *(data + 3) == 0x00 &&
*(data + 4) == 0x00 && *(data + 5) == 0x00 &&
*(data + 6) == 0x00 && *(data + 7) == 0x00) {
Print("nop");
data += 8;
} else {
UNIMPLEMENTED();
}
} else {
data += 2;
if (f0byte == 0xAB || f0byte == 0xA4 || f0byte == 0xA5 ||
f0byte == 0xAC || f0byte == 0xAD || f0byte == 0xA3) {
// shrd, shld, bts, bt
Print(f0mnem);
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print(" ");
data += PrintRightOperand(data);
Print(",");
PrintCPURegister(regop);
if (f0byte == 0xAB || f0byte == 0xA3) {
// Done.
} else if (f0byte == 0xA5 || f0byte == 0xAD) {
Print(",cl");
} else {
Print(", ");
PrintInt(*(data++));
}
} else if ((f0byte == 0x10) || (f0byte == 0x11) ||
IsTwoXmmRegInstruction(f0byte)) {
data = SSEInstruction(0x0F, f0byte, data);
} else if (f0byte == 0x50) {
Print("movmskps ");
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
PrintCPURegister(regop);
Print(",");
data += PrintRightXmmOperand(data);
} else if (f0byte == 0xC2 || f0byte == 0xC6) {
if (f0byte == 0xC2)
Print("cmpps ");
else
Print("shufps ");
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print(" ");
PrintXmmRegister(regop);
Print(",");
data += PrintRightXmmOperand(data);
int comparison = *data;
Print(" [");
PrintHex(comparison);
Print("]");
data++;
} else {
UNIMPLEMENTED();
}
}
} break;
case 0x8F: {
data++;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
if (regop == eax) {
Print("pop ");
data += PrintRightOperand(data);
}
} break;
case 0xFF: {
data++;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
const char* mnem = NULL;
switch (regop) {
case esi:
mnem = "push";
break;
case eax:
mnem = "inc";
break;
case ecx:
mnem = "dec";
break;
case edx:
mnem = "call";
break;
case esp:
mnem = "jmp";
break;
default:
mnem = "??? 0xFF";
}
Print(mnem);
Print(" ");
data += PrintRightOperand(data);
} break;
case 0xC7: // imm32, fall through
case 0xC6: // imm8
{
bool is_byte = *data == 0xC6;
data++;
Print(is_byte ? "mov_b" : "mov");
Print(" ");
data += PrintRightOperand(data);
int32_t imm = is_byte ? *data : *reinterpret_cast<int32_t*>(data);
Print(",");
PrintHex(imm);
data += is_byte ? 1 : 4;
} break;
case 0x80: {
data++;
Print("cmpb ");
data += PrintRightOperand(data);
int32_t imm = *data;
Print(",");
PrintHex(imm);
data++;
} break;
case 0x88: // 8bit, fall through
case 0x89: // 32bit
{
bool is_byte = *data == 0x88;
int mod, regop, rm;
data++;
GetModRm(*data, &mod, &regop, &rm);
Print(is_byte ? "mov_b" : "mov");
Print(" ");
data += PrintRightOperand(data);
Print(",");
PrintCPURegister(regop);
} break;
case 0x66: // prefix
data++;
if (*data == 0x8B) {
data++;
data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data);
} else if (*data == 0x89) {
data++;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print("mov_w ");
data += PrintRightOperand(data);
Print(",");
PrintCPURegister(regop);
} else if (*data == 0x0F) {
data++;
if (*data == 0X6E) {
data++;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print("movd ");
PrintXmmRegister(regop);
Print(",");
PrintCPURegister(rm);
data++;
} else if (*data == 0X7E) {
data++;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print("movd ");
PrintCPURegister(rm);
Print(",");
PrintXmmRegister(regop);
data++;
} else if (*data == 0xD6) {
data++;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print("movq ");
data += PrintRightOperand(data);
Print(",");
PrintXmmRegister(regop);
} else if (*data == 0x57 || *data == 0x56 || *data == 0x54) {
data += BitwisePDInstruction(data);
} else if (*data == 0x1F && *(data + 1) == 0x44 &&
*(data + 2) == 0x00 && *(data + 3) == 0x00) {
data += 4;
Print("nop");
} else if (*data == 0x50) {
Print("movmskpd ");
data++;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
PrintCPURegister(regop);
Print(",");
data += PrintRightXmmOperand(data);
} else if (*data == 0x3A && *(data + 1) == 0x16) {
Print("pextrd ");
data += 2;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
PrintCPURegister(rm);
Print(",");
PrintXmmRegister(regop);
Print(",");
PrintHex(*(data + 1));
data += 2;
} else if (*data == 0x38) {
data += Packed660F38Instruction(data);
} else if (*data == 0xEF) {
int mod, regop, rm;
GetModRm(*(data + 1), &mod, &regop, &rm);
Print("pxor ");
PrintXmmRegister(regop);
Print(",");
PrintXmmRegister(rm);
data += 2;
} else if (*data == 0x3A) {
data++;
if (*data == 0x0B) {
data++;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print("roundsd ");
PrintXmmRegister(regop);
Print(", ");
PrintXmmRegister(rm);
Print(", ");
PrintInt(data[1] & 3);
data += 2;
} else {
UNIMPLEMENTED();
}
} else if (*data == 0x14) {
int mod, regop, rm;
GetModRm(*(data + 1), &mod, &regop, &rm);
Print("unpcklpd ");
PrintXmmRegister(regop);
Print(",");
PrintXmmRegister(rm);
data += 2;
} else if (*data == 0x15) {
int mod, regop, rm;
GetModRm(*(data + 1), &mod, &regop, &rm);
Print("unpckhpd ");
PrintXmmRegister(regop);
Print(",");
PrintXmmRegister(rm);
data += 2;
} else if ((*data == 0xFE) || (*data == 0xFA) || (*data == 0x2F) ||
(*data == 0x58) || (*data == 0x5C) || (*data == 0x59) ||
(*data == 0x5E) || (*data == 0x5D) || (*data == 0x5F) ||
(*data == 0x51) || (*data == 0x5A)) {
const char* mnemonic = PackedDoubleMnemonic(*data);
int mod, regop, rm;
GetModRm(*(data + 1), &mod, &regop, &rm);
Print(mnemonic);
PrintXmmRegister(regop);
Print(",");
PrintXmmRegister(rm);
data += 2;
} else if (*data == 0xC6) {
int mod, regop, rm;
data++;
GetModRm(*data, &mod, &regop, &rm);
Print("shufpd ");
PrintXmmRegister(regop);
Print(",");
data += PrintRightXmmOperand(data);
int comparison = *data;
Print(" [");
PrintHex(comparison);
Print("]");
data++;
} else {
UNIMPLEMENTED();
}
} else if (*data == 0x3B) {
data++;
Print("cmp_w ");
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
PrintCPURegister(regop);
Print(",");
data += PrintRightOperand(data);
} else if ((*data == 0x81) || (*data == 0x83)) {
data += PrintImmediateOp(data, true /* size_override */);
} else if (*data == 0xC7) {
data++;
Print("mov_w ");
data += PrintRightOperand(data);
int16_t imm = *reinterpret_cast<int16_t*>(data);
Print(",");
PrintHex(imm);
data += 2;
} else if (*data == 0x90) {
data++;
Print("nop");
} else {
UNIMPLEMENTED();
}
break;
case 0xFE: {
data++;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
if (mod == 3 && regop == ecx) {
Print("dec_b ");
PrintCPURegister(rm);
} else {
UNIMPLEMENTED();
}
data++;
} break;
case 0x68:
Print("push ");
PrintHex(*reinterpret_cast<int32_t*>(data + 1));
data += 5;
break;
case 0x6A:
Print("push ");
PrintHex(*reinterpret_cast<int8_t*>(data + 1));
data += 2;
break;
case 0xA8:
Print("test al,");
PrintHex(*reinterpret_cast<uint8_t*>(data + 1));
data += 2;
break;
case 0xA9:
Print("test eax,");
PrintHex(*reinterpret_cast<int32_t*>(data + 1));
CheckPrintStop(data);
data += 5;
break;
case 0xD1: // fall through
case 0xD3: // fall through
case 0xC1:
data += D1D3C1Instruction(data);
break;
case 0xD9: // fall through
case 0xDA: // fall through
case 0xDB: // fall through
case 0xDC: // fall through
case 0xDD: // fall through
case 0xDE: // fall through
case 0xDF:
data += FPUInstruction(data);
break;
case 0xEB:
data += JumpShort(data);
break;
case 0xF3:
data = F3Instruction(data);
break;
case 0xF2: {
if (*(data + 1) == 0x0F) {
uint8_t b2 = *(data + 2);
if (b2 == 0x11) {
Print("movsd ");
data += 3;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
data += PrintRightXmmOperand(data);
Print(",");
PrintXmmRegister(regop);
} else if (b2 == 0x10) {
data += 3;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print("movsd ");
PrintXmmRegister(regop);
Print(",");
data += PrintRightOperand(data);
} else {
const char* mnem = "? 0xF2";
switch (b2) {
case 0x2A:
mnem = "cvtsi2sd";
break;
case 0x2C:
mnem = "cvttsd2si";
break;
case 0x2D:
mnem = "cvtsd2i";
break;
case 0x51:
mnem = "sqrtsd";
break;
case 0x58:
mnem = "addsd";
break;
case 0x59:
mnem = "mulsd";
break;
case 0x5A:
mnem = "cvtsd2ss";
break;
case 0x5C:
mnem = "subsd";
break;
case 0x5E:
mnem = "divsd";
break;
default:
UNIMPLEMENTED();
}
data += 3;
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
if (b2 == 0x2A) {
Print(mnem);
Print(" ");
PrintXmmRegister(regop);
Print(",");
data += PrintRightOperand(data);
} else if ((b2 == 0x2D) || (b2 == 0x2C)) {
Print(mnem);
Print(" ");
PrintCPURegister(regop);
Print(",");
PrintXmmRegister(rm);
data++;
} else {
Print(mnem);
Print(" ");
PrintXmmRegister(regop);
Print(",");
data += PrintRightXmmOperand(data);
}
}
} else {
UNIMPLEMENTED();
}
break;
}
case 0xF7:
data += F7Instruction(data);
break;
case 0xC8:
data += DecodeEnter(data);
break;
default:
OS::Print("Unknown case %#x\n", *data);
UNIMPLEMENTED();
}
}
int instr_len = data - reinterpret_cast<uint8_t*>(pc);
ASSERT(instr_len > 0); // Ensure progress.
return instr_len;
} // NOLINT
void Disassembler::DecodeInstruction(char* hex_buffer,
intptr_t hex_size,
char* human_buffer,
intptr_t human_size,
int* out_instr_len,
const Code& code,
Object** object,
uword pc) {
ASSERT(hex_size > 0);
ASSERT(human_size > 0);
X86Decoder decoder(human_buffer, human_size);
int instruction_length = decoder.InstructionDecode(pc);
uint8_t* pc_ptr = reinterpret_cast<uint8_t*>(pc);
int hex_index = 0;
int remaining_size = hex_size - hex_index;
for (int i = 0; (i < instruction_length) && (remaining_size > 2); ++i) {
OS::SNPrint(&hex_buffer[hex_index], remaining_size, "%02x", pc_ptr[i]);
hex_index += 2;
remaining_size -= 2;
}
hex_buffer[hex_index] = '\0';
if (out_instr_len) {
*out_instr_len = instruction_length;
}
*object = NULL;
if (!code.IsNull() && code.is_alive()) {
intptr_t offsets_length = code.pointer_offsets_length();
for (intptr_t i = 0; i < offsets_length; i++) {
uword addr = code.GetPointerOffsetAt(i) + code.PayloadStart();
if ((pc <= addr) && (addr < (pc + instruction_length))) {
*object = &Object::Handle(*reinterpret_cast<RawObject**>(addr));
break;
}
}
}
}
#endif // !PRODUCT
} // namespace dart
#endif // defined TARGET_ARCH_IA32