| Index: runtime/vm/disassembler_x64.cc
|
| ===================================================================
|
| --- runtime/vm/disassembler_x64.cc (revision 10509)
|
| +++ runtime/vm/disassembler_x64.cc (working copy)
|
| @@ -4,124 +4,1834 @@
|
|
|
| #include "vm/disassembler.h"
|
|
|
| -#if !defined(_WIN32) // Disassembler is not yet supported under WIN32.
|
| -#include <errno.h>
|
| -#include <stdio.h>
|
| -#include <stdlib.h>
|
| -#include <unistd.h>
|
| -#endif
|
| -
|
| -#include "vm/globals.h" // Needed here to get TARGET_ARCH_X64.
|
| +#include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32.
|
| #if defined(TARGET_ARCH_X64)
|
| -#include "platform/assert.h"
|
| +#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 {
|
|
|
| -void Disassembler::Disassemble(uword start,
|
| - uword end,
|
| - DisassemblyFormatter* formatter,
|
| - const Code::Comments& comments) {
|
| - // First print the actual addresses so that we know where in memory this is
|
| - // being disassembled from.
|
| - formatter->Print("start: %p end: %p\n", start, end);
|
|
|
| -#if !defined(_WIN32) // Disassembler is not yet supported under WIN32.
|
| - // Write code block to tmp file.
|
| - char tmp[] = "/tmp/codeblock.XXXXXX";
|
| - int fd = mkstemp(tmp);
|
| - if (fd < 0) {
|
| - int errsv = errno;
|
| - formatter->Print("Could not open tmp file %s, errno=%s\n",
|
| - tmp,
|
| - strerror(errsv));
|
| - return; // failed
|
| +enum OperandType {
|
| + UNSET_OP_ORDER = 0,
|
| + // Operand size decides between 16, 32 and 64 bit operands.
|
| + REG_OPER_OP_ORDER = 1, // Register destination, operand source.
|
| + OPER_REG_OP_ORDER = 2, // Operand destination, register source.
|
| + // Fixed 8-bit operands.
|
| + BYTE_SIZE_OPERAND_FLAG = 4,
|
| + BYTE_REG_OPER_OP_ORDER = REG_OPER_OP_ORDER | BYTE_SIZE_OPERAND_FLAG,
|
| + BYTE_OPER_REG_OP_ORDER = OPER_REG_OP_ORDER | BYTE_SIZE_OPERAND_FLAG
|
| +};
|
| +
|
| +//------------------------------------------------------------------
|
| +// Tables
|
| +//------------------------------------------------------------------
|
| +struct ByteMnemonic {
|
| + int b; // -1 terminates, otherwise must be in range (0..255)
|
| + OperandType op_order_;
|
| + const char* mnem;
|
| +};
|
| +
|
| +
|
| +static const ByteMnemonic two_operands_instr[] = {
|
| + { 0x00, BYTE_OPER_REG_OP_ORDER, "add" },
|
| + { 0x01, OPER_REG_OP_ORDER, "add" },
|
| + { 0x02, BYTE_REG_OPER_OP_ORDER, "add" },
|
| + { 0x03, REG_OPER_OP_ORDER, "add" },
|
| + { 0x08, BYTE_OPER_REG_OP_ORDER, "or" },
|
| + { 0x09, OPER_REG_OP_ORDER, "or" },
|
| + { 0x0A, BYTE_REG_OPER_OP_ORDER, "or" },
|
| + { 0x0B, REG_OPER_OP_ORDER, "or" },
|
| + { 0x10, BYTE_OPER_REG_OP_ORDER, "adc" },
|
| + { 0x11, OPER_REG_OP_ORDER, "adc" },
|
| + { 0x12, BYTE_REG_OPER_OP_ORDER, "adc" },
|
| + { 0x13, REG_OPER_OP_ORDER, "adc" },
|
| + { 0x18, BYTE_OPER_REG_OP_ORDER, "sbb" },
|
| + { 0x19, OPER_REG_OP_ORDER, "sbb" },
|
| + { 0x1A, BYTE_REG_OPER_OP_ORDER, "sbb" },
|
| + { 0x1B, REG_OPER_OP_ORDER, "sbb" },
|
| + { 0x20, BYTE_OPER_REG_OP_ORDER, "and" },
|
| + { 0x21, OPER_REG_OP_ORDER, "and" },
|
| + { 0x22, BYTE_REG_OPER_OP_ORDER, "and" },
|
| + { 0x23, REG_OPER_OP_ORDER, "and" },
|
| + { 0x28, BYTE_OPER_REG_OP_ORDER, "sub" },
|
| + { 0x29, OPER_REG_OP_ORDER, "sub" },
|
| + { 0x2A, BYTE_REG_OPER_OP_ORDER, "sub" },
|
| + { 0x2B, REG_OPER_OP_ORDER, "sub" },
|
| + { 0x30, BYTE_OPER_REG_OP_ORDER, "xor" },
|
| + { 0x31, OPER_REG_OP_ORDER, "xor" },
|
| + { 0x32, BYTE_REG_OPER_OP_ORDER, "xor" },
|
| + { 0x33, REG_OPER_OP_ORDER, "xor" },
|
| + { 0x38, BYTE_OPER_REG_OP_ORDER, "cmp" },
|
| + { 0x39, OPER_REG_OP_ORDER, "cmp" },
|
| + { 0x3A, BYTE_REG_OPER_OP_ORDER, "cmp" },
|
| + { 0x3B, REG_OPER_OP_ORDER, "cmp" },
|
| + { 0x63, REG_OPER_OP_ORDER, "movsxlq" },
|
| + { 0x84, BYTE_REG_OPER_OP_ORDER, "test" },
|
| + { 0x85, REG_OPER_OP_ORDER, "test" },
|
| + { 0x86, BYTE_REG_OPER_OP_ORDER, "xchg" },
|
| + { 0x87, REG_OPER_OP_ORDER, "xchg" },
|
| + { 0x88, BYTE_OPER_REG_OP_ORDER, "mov" },
|
| + { 0x89, OPER_REG_OP_ORDER, "mov" },
|
| + { 0x8A, BYTE_REG_OPER_OP_ORDER, "mov" },
|
| + { 0x8B, REG_OPER_OP_ORDER, "mov" },
|
| + { 0x8D, REG_OPER_OP_ORDER, "lea" },
|
| + { -1, UNSET_OP_ORDER, "" }
|
| +};
|
| +
|
| +
|
| +static const ByteMnemonic zero_operands_instr[] = {
|
| + { 0xC3, UNSET_OP_ORDER, "ret" },
|
| + { 0xC9, UNSET_OP_ORDER, "leave" },
|
| + { 0xF4, UNSET_OP_ORDER, "hlt" },
|
| + { 0xFC, UNSET_OP_ORDER, "cld" },
|
| + { 0xCC, UNSET_OP_ORDER, "int3" },
|
| + { 0x60, UNSET_OP_ORDER, "pushad" },
|
| + { 0x61, UNSET_OP_ORDER, "popad" },
|
| + { 0x9C, UNSET_OP_ORDER, "pushfd" },
|
| + { 0x9D, UNSET_OP_ORDER, "popfd" },
|
| + { 0x9E, UNSET_OP_ORDER, "sahf" },
|
| + { 0x99, UNSET_OP_ORDER, "cdq" },
|
| + { 0x9B, UNSET_OP_ORDER, "fwait" },
|
| + { 0xA4, UNSET_OP_ORDER, "movs" },
|
| + { 0xA5, UNSET_OP_ORDER, "movs" },
|
| + { 0xA6, UNSET_OP_ORDER, "cmps" },
|
| + { 0xA7, UNSET_OP_ORDER, "cmps" },
|
| + { -1, UNSET_OP_ORDER, "" }
|
| +};
|
| +
|
| +
|
| +static const ByteMnemonic call_jump_instr[] = {
|
| + { 0xE8, UNSET_OP_ORDER, "call" },
|
| + { 0xE9, UNSET_OP_ORDER, "jmp" },
|
| + { -1, UNSET_OP_ORDER, "" }
|
| +};
|
| +
|
| +
|
| +static const ByteMnemonic short_immediate_instr[] = {
|
| + { 0x05, UNSET_OP_ORDER, "add" },
|
| + { 0x0D, UNSET_OP_ORDER, "or" },
|
| + { 0x15, UNSET_OP_ORDER, "adc" },
|
| + { 0x1D, UNSET_OP_ORDER, "sbb" },
|
| + { 0x25, UNSET_OP_ORDER, "and" },
|
| + { 0x2D, UNSET_OP_ORDER, "sub" },
|
| + { 0x35, UNSET_OP_ORDER, "xor" },
|
| + { 0x3D, UNSET_OP_ORDER, "cmp" },
|
| + { -1, UNSET_OP_ORDER, "" }
|
| +};
|
| +
|
| +
|
| +static const char* const conditional_code_suffix[] = {
|
| + "o", "no", "c", "nc", "z", "nz", "na", "a",
|
| + "s", "ns", "pe", "po", "l", "ge", "le", "g"
|
| +};
|
| +
|
| +
|
| +enum InstructionType {
|
| + NO_INSTR,
|
| + ZERO_OPERANDS_INSTR,
|
| + TWO_OPERANDS_INSTR,
|
| + JUMP_CONDITIONAL_SHORT_INSTR,
|
| + REGISTER_INSTR,
|
| + PUSHPOP_INSTR, // Has implicit 64-bit operand size.
|
| + MOVE_REG_INSTR,
|
| + CALL_JUMP_INSTR,
|
| + SHORT_IMMEDIATE_INSTR
|
| +};
|
| +
|
| +
|
| +enum Prefixes {
|
| + ESCAPE_PREFIX = 0x0F,
|
| + OPERAND_SIZE_OVERRIDE_PREFIX = 0x66,
|
| + ADDRESS_SIZE_OVERRIDE_PREFIX = 0x67,
|
| + REPNE_PREFIX = 0xF2,
|
| + REP_PREFIX = 0xF3,
|
| + REPEQ_PREFIX = REP_PREFIX
|
| +};
|
| +
|
| +
|
| +struct InstructionDesc {
|
| + const char* mnem;
|
| + InstructionType type;
|
| + OperandType op_order_;
|
| + bool byte_size_operation; // Fixed 8-bit operation.
|
| +};
|
| +
|
| +
|
| +class InstructionTable : public ValueObject {
|
| + public:
|
| + InstructionTable();
|
| + const InstructionDesc& Get(uint8_t x) const {
|
| + return instructions_[x];
|
| }
|
| - ssize_t size = write(fd, reinterpret_cast<const void*>(start), end - start);
|
| - if (size <= 0) {
|
| - if (size < 0) {
|
| - int errsv = errno;
|
| - formatter->Print("Could not write to tmp file %s, errno=%s\n",
|
| - tmp,
|
| - strerror(errsv));
|
| +
|
| + private:
|
| + InstructionDesc instructions_[256];
|
| + void Clear();
|
| + void Init();
|
| + void CopyTable(const ByteMnemonic bm[], InstructionType type);
|
| + void SetTableRange(InstructionType type,
|
| + uint8_t start,
|
| + uint8_t end,
|
| + bool byte_size,
|
| + 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 = "(bad)";
|
| + instructions_[i].type = NO_INSTR;
|
| + instructions_[i].op_order_ = UNSET_OP_ORDER;
|
| + instructions_[i].byte_size_operation = false;
|
| + }
|
| +}
|
| +
|
| +
|
| +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(PUSHPOP_INSTR, 0x50, 0x57, false, "push");
|
| + SetTableRange(PUSHPOP_INSTR, 0x58, 0x5F, false, "pop");
|
| + SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, false, "mov");
|
| +}
|
| +
|
| +
|
| +void InstructionTable::CopyTable(const ByteMnemonic bm[],
|
| + InstructionType type) {
|
| + for (int i = 0; bm[i].b >= 0; i++) {
|
| + InstructionDesc* id = &instructions_[bm[i].b];
|
| + id->mnem = bm[i].mnem;
|
| + OperandType op_order = bm[i].op_order_;
|
| + id->op_order_ =
|
| + static_cast<OperandType>(op_order & ~BYTE_SIZE_OPERAND_FLAG);
|
| + ASSERT(NO_INSTR == id->type); // Information not already entered
|
| + id->type = type;
|
| + id->byte_size_operation = ((op_order & BYTE_SIZE_OPERAND_FLAG) != 0);
|
| + }
|
| +}
|
| +
|
| +
|
| +void InstructionTable::SetTableRange(InstructionType type,
|
| + uint8_t start,
|
| + uint8_t end,
|
| + bool byte_size,
|
| + const char* mnem) {
|
| + for (uint8_t b = start; b <= end; b++) {
|
| + InstructionDesc* id = &instructions_[b];
|
| + ASSERT(NO_INSTR == id->type); // Information not already entered
|
| + id->mnem = mnem;
|
| + id->type = type;
|
| + id->byte_size_operation = byte_size;
|
| + }
|
| +}
|
| +
|
| +
|
| +void InstructionTable::AddJumpConditionalShort() {
|
| + for (uint8_t b = 0x70; b <= 0x7F; b++) {
|
| + InstructionDesc* id = &instructions_[b];
|
| + ASSERT(NO_INSTR == id->type); // Information not already entered
|
| + id->mnem = NULL; // Computed depending on condition code.
|
| + id->type = JUMP_CONDITIONAL_SHORT_INSTR;
|
| + }
|
| +}
|
| +
|
| +
|
| +static InstructionTable instruction_table;
|
| +
|
| +
|
| +static InstructionDesc cmov_instructions[16] = {
|
| + {"cmovo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovno", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovnc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovnz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovna", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmova", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovs", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovns", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovpe", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovpo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovl", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovge", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovle", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
|
| + {"cmovg", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}
|
| +};
|
| +
|
| +
|
| +//-------------------------------------------------
|
| +// DisassemblerX64 implementation.
|
| +
|
| +
|
| +static const int kMaxXmmRegisters = 16;
|
| +static const char* xmm_regs[kMaxXmmRegisters] = {
|
| + "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
|
| + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"
|
| +};
|
| +
|
| +class DisassemblerX64 : public ValueObject {
|
| + public:
|
| + DisassemblerX64(char* buffer, intptr_t buffer_size)
|
| + : buffer_(buffer),
|
| + buffer_size_(buffer_size),
|
| + buffer_pos_(0) {
|
| + buffer_[buffer_pos_] = '\0';
|
| + }
|
| +
|
| + virtual ~DisassemblerX64() {
|
| + }
|
| +
|
| + int InstructionDecode(uword pc);
|
| +
|
| + private:
|
| + enum OperandSize {
|
| + BYTE_SIZE = 0,
|
| + WORD_SIZE = 1,
|
| + DOUBLEWORD_SIZE = 2,
|
| + QUADWORD_SIZE = 3
|
| + };
|
| +
|
| + void setRex(uint8_t rex) {
|
| + ASSERT(0x40 == (rex & 0xF0));
|
| + rex_ = rex;
|
| + }
|
| +
|
| + bool rex() { return rex_ != 0; }
|
| +
|
| + bool rex_b() { return (rex_ & 0x01) != 0; }
|
| +
|
| + // Actual number of base register given the low bits and the rex.b state.
|
| + int base_reg(int low_bits) { return low_bits | ((rex_ & 0x01) << 3); }
|
| +
|
| + bool rex_x() { return (rex_ & 0x02) != 0; }
|
| +
|
| + bool rex_r() { return (rex_ & 0x04) != 0; }
|
| +
|
| + bool rex_w() { return (rex_ & 0x08) != 0; }
|
| +
|
| + OperandSize operand_size() {
|
| + if (byte_size_operand_) return BYTE_SIZE;
|
| + if (rex_w()) return QUADWORD_SIZE;
|
| + if (operand_size_ != 0) return WORD_SIZE;
|
| + return DOUBLEWORD_SIZE;
|
| + }
|
| +
|
| + char operand_size_code() {
|
| + return "bwlq"[operand_size()];
|
| + }
|
| +
|
| + // Disassembler helper functions.
|
| + void get_modrm(uint8_t data,
|
| + int* mod,
|
| + int* regop,
|
| + int* rm) {
|
| + *mod = (data >> 6) & 3;
|
| + *regop = ((data & 0x38) >> 3) | (rex_r() ? 8 : 0);
|
| + *rm = (data & 7) | (rex_b() ? 8 : 0);
|
| + }
|
| +
|
| + void get_sib(uint8_t data,
|
| + int* scale,
|
| + int* index,
|
| + int* base) {
|
| + *scale = (data >> 6) & 3;
|
| + *index = ((data >> 3) & 7) | (rex_x() ? 8 : 0);
|
| + *base = (data & 7) | (rex_b() ? 8 : 0);
|
| + }
|
| +
|
| + const char* NameOfCPURegister(int reg) const {
|
| + return Assembler::RegisterName(static_cast<Register>(reg));
|
| + }
|
| +
|
| + const char* NameOfByteCPURegister(int reg) const {
|
| + return NameOfCPURegister(reg);
|
| + }
|
| +
|
| + const char* NameOfXMMRegister(int reg) const {
|
| + ASSERT((0 <= reg) && (reg < kMaxXmmRegisters));
|
| + return xmm_regs[reg];
|
| + }
|
| +
|
| + void AppendToBuffer(const char* format, ...);
|
| + void AppendAddressToBuffer(uint8_t* addr);
|
| +
|
| + int PrintOperands(const char* mnem,
|
| + OperandType op_order,
|
| + uint8_t* data);
|
| +
|
| + typedef const char* (DisassemblerX64::*RegisterNameMapping)(int reg) const;
|
| +
|
| + int PrintRightOperandHelper(uint8_t* modrmp,
|
| + RegisterNameMapping register_name);
|
| + int PrintRightOperand(uint8_t* modrmp);
|
| + int PrintRightByteOperand(uint8_t* modrmp);
|
| + int PrintRightXMMOperand(uint8_t* modrmp);
|
| + int PrintImmediate(uint8_t* data, OperandSize size);
|
| + int PrintImmediateOp(uint8_t* data);
|
| + const char* TwoByteMnemonic(uint8_t opcode);
|
| + int TwoByteOpcodeInstruction(uint8_t* data);
|
| +
|
| + int F6F7Instruction(uint8_t* data);
|
| + int ShiftInstruction(uint8_t* data);
|
| + int JumpShort(uint8_t* data);
|
| + int JumpConditional(uint8_t* data);
|
| + int JumpConditionalShort(uint8_t* data);
|
| + int SetCC(uint8_t* data);
|
| + int FPUInstruction(uint8_t* data);
|
| + int MemoryFPUInstruction(int escape_opcode, int regop, uint8_t* modrm_start);
|
| + int RegisterFPUInstruction(int escape_opcode, uint8_t modrm_byte);
|
| +
|
| + bool DecodeInstructionType(const InstructionDesc& idesc, uint8_t** data);
|
| +
|
| + void UnimplementedInstruction() {
|
| + AppendToBuffer("'Unimplemented Instruction'");
|
| + }
|
| +
|
| + 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_.
|
| +
|
| + // Prefixes parsed
|
| + uint8_t rex_;
|
| + uint8_t operand_size_; // 0x66 or (if no group 3 prefix is present) 0x0.
|
| + // 0xF2, 0xF3, or (if no group 1 prefix is present) 0.
|
| + uint8_t group_1_prefix_;
|
| + // Byte size operand override.
|
| + bool byte_size_operand_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(DisassemblerX64);
|
| +};
|
| +
|
| +
|
| +// Append the str to the output buffer.
|
| +void DisassemblerX64::AppendToBuffer(const char* format, ...) {
|
| + char* buf = buffer_ + buffer_pos_;
|
| + va_list args;
|
| + va_start(args, format);
|
| + int retval = OS::VSNPrint(buf, buffer_size_, format, args);
|
| + va_end(args);
|
| + buffer_pos_ += retval;
|
| +}
|
| +
|
| +
|
| +int DisassemblerX64::PrintRightOperandHelper(
|
| + uint8_t* modrmp,
|
| + RegisterNameMapping direct_register_name) {
|
| + int mod, regop, rm;
|
| + get_modrm(*modrmp, &mod, ®op, &rm);
|
| + RegisterNameMapping register_name = (mod == 3) ? direct_register_name :
|
| + &DisassemblerX64::NameOfCPURegister;
|
| + switch (mod) {
|
| + case 0:
|
| + if ((rm & 7) == 5) {
|
| + int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 1);
|
| + AppendToBuffer("[0x%x]", disp);
|
| + return 5;
|
| + } else if ((rm & 7) == 4) {
|
| + // Codes for SIB byte.
|
| + uint8_t sib = *(modrmp + 1);
|
| + int scale, index, base;
|
| + get_sib(sib, &scale, &index, &base);
|
| + if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
|
| + // index == rsp means no index. Only use sib byte with no index for
|
| + // rsp and r12 base.
|
| + AppendToBuffer("[%s]", NameOfCPURegister(base));
|
| + return 2;
|
| + } else if (base == 5) {
|
| + // base == rbp means no base register (when mod == 0).
|
| + int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
|
| + AppendToBuffer("[%s*%d+0x%x]",
|
| + NameOfCPURegister(index),
|
| + 1 << scale, disp);
|
| + return 6;
|
| + } else if (index != 4 && base != 5) {
|
| + // [base+index*scale]
|
| + AppendToBuffer("[%s+%s*%d]",
|
| + NameOfCPURegister(base),
|
| + NameOfCPURegister(index),
|
| + 1 << scale);
|
| + return 2;
|
| + } else {
|
| + UnimplementedInstruction();
|
| + return 1;
|
| + }
|
| + } else {
|
| + AppendToBuffer("[%s]", NameOfCPURegister(rm));
|
| + return 1;
|
| + }
|
| + break;
|
| + case 1: // fall through
|
| + case 2:
|
| + if ((rm & 7) == 4) {
|
| + uint8_t sib = *(modrmp + 1);
|
| + int scale, index, base;
|
| + get_sib(sib, &scale, &index, &base);
|
| + int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 2)
|
| + : *reinterpret_cast<char*>(modrmp + 2);
|
| + if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
|
| + if (-disp > 0) {
|
| + AppendToBuffer("[%s-0x%x]", NameOfCPURegister(base), -disp);
|
| + } else {
|
| + AppendToBuffer("[%s+0x%x]", NameOfCPURegister(base), disp);
|
| + }
|
| + } else {
|
| + if (-disp > 0) {
|
| + AppendToBuffer("[%s+%s*%d-0x%x]",
|
| + NameOfCPURegister(base),
|
| + NameOfCPURegister(index),
|
| + 1 << scale,
|
| + -disp);
|
| + } else {
|
| + AppendToBuffer("[%s+%s*%d+0x%x]",
|
| + NameOfCPURegister(base),
|
| + NameOfCPURegister(index),
|
| + 1 << scale,
|
| + disp);
|
| + }
|
| + }
|
| + return mod == 2 ? 6 : 3;
|
| + } else {
|
| + // No sib.
|
| + int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 1)
|
| + : *reinterpret_cast<char*>(modrmp + 1);
|
| + if (-disp > 0) {
|
| + AppendToBuffer("[%s-0x%x]", NameOfCPURegister(rm), -disp);
|
| + } else {
|
| + AppendToBuffer("[%s+0x%x]", NameOfCPURegister(rm), disp);
|
| + }
|
| + return (mod == 2) ? 5 : 2;
|
| + }
|
| + break;
|
| + case 3:
|
| + AppendToBuffer("%s", (this->*register_name)(rm));
|
| + return 1;
|
| + default:
|
| + UnimplementedInstruction();
|
| + return 1;
|
| + }
|
| + UNREACHABLE();
|
| +}
|
| +
|
| +
|
| +int DisassemblerX64::PrintImmediate(uint8_t* data, OperandSize size) {
|
| + int64_t value;
|
| + int count;
|
| + switch (size) {
|
| + case BYTE_SIZE:
|
| + value = *data;
|
| + count = 1;
|
| + break;
|
| + case WORD_SIZE:
|
| + value = *reinterpret_cast<int16_t*>(data);
|
| + count = 2;
|
| + break;
|
| + case DOUBLEWORD_SIZE:
|
| + value = *reinterpret_cast<uint32_t*>(data);
|
| + count = 4;
|
| + break;
|
| + case QUADWORD_SIZE:
|
| + value = *reinterpret_cast<int32_t*>(data);
|
| + count = 4;
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + value = 0; // Initialize variables on all paths to satisfy the compiler.
|
| + count = 0;
|
| + }
|
| + AppendToBuffer("%" PRIxPTR "", value);
|
| + return count;
|
| +}
|
| +
|
| +
|
| +// Returns number of bytes used by machine instruction, including *data byte.
|
| +// Writes immediate instructions to 'tmp_buffer_'.
|
| +int DisassemblerX64::PrintImmediateOp(uint8_t* data) {
|
| + bool byte_size_immediate = (*data & 0x02) != 0;
|
| + uint8_t modrm = *(data + 1);
|
| + int mod, regop, rm;
|
| + get_modrm(modrm, &mod, ®op, &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:
|
| + UnimplementedInstruction();
|
| + }
|
| + AppendToBuffer("%s%c ", mnem, operand_size_code());
|
| + int count = PrintRightOperand(data + 1);
|
| + AppendToBuffer(",0x");
|
| + OperandSize immediate_size = byte_size_immediate ? BYTE_SIZE : operand_size();
|
| + count += PrintImmediate(data + 1 + count, immediate_size);
|
| + return 1 + count;
|
| +}
|
| +
|
| +
|
| +// Returns number of bytes used, including *data.
|
| +int DisassemblerX64::F6F7Instruction(uint8_t* data) {
|
| + ASSERT(*data == 0xF7 || *data == 0xF6);
|
| + uint8_t modrm = *(data + 1);
|
| + int mod, regop, rm;
|
| + get_modrm(modrm, &mod, ®op, &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 7:
|
| + mnem = "idiv";
|
| + break;
|
| + default:
|
| + UnimplementedInstruction();
|
| }
|
| - close(fd);
|
| - remove(tmp);
|
| - return;
|
| + AppendToBuffer("%s%c %s",
|
| + mnem,
|
| + operand_size_code(),
|
| + NameOfCPURegister(rm));
|
| + return 2;
|
| + } else if (regop == 0) {
|
| + AppendToBuffer("test%c ", operand_size_code());
|
| + int count = PrintRightOperand(data + 1); // Use name of 64-bit register.
|
| + AppendToBuffer(",0x");
|
| + count += PrintImmediate(data + 1 + count, operand_size());
|
| + return 1 + count;
|
| + } else {
|
| + UnimplementedInstruction();
|
| + return 2;
|
| }
|
| - close(fd);
|
| +}
|
|
|
| - // Disassemble tmp file to stdout.
|
| - char cmd[256];
|
| -#if defined(__APPLE__)
|
| - snprintf(cmd, sizeof(cmd),
|
| - "( cat %1$s | "
|
| - " hexdump -v -e '\".byte \" 1/1 \"0x%%02x\" \"\\n\"' | "
|
| - " as - -arch x86_64 -o %1$s.o ; otool -tV %1$s.o"
|
| - ") </dev/null 2>&1", tmp);
|
| -#else
|
| - snprintf(cmd, sizeof(cmd), "( /usr/bin/objdump -b binary -m i386:x86-64 -D %s"
|
| - " ) </dev/null 2>&1", tmp);
|
| -#endif
|
| - FILE* output = popen(cmd, "r");
|
| - if (output == NULL) {
|
| - int errsv = errno;
|
| - formatter->Print("Could not run \"%s\", errno=%s\n", cmd, strerror(errsv));
|
| - remove(tmp);
|
| - return; // failed
|
| +
|
| +int DisassemblerX64::ShiftInstruction(uint8_t* data) {
|
| + uint8_t op = *data & (~1);
|
| + if (op != 0xD0 && op != 0xD2 && op != 0xC0) {
|
| + UnimplementedInstruction();
|
| + return 1;
|
| }
|
| - const int kMaxOutputLine = 1024;
|
| - char line[kMaxOutputLine];
|
| -#if defined(__APPLE__)
|
| - const char* header = "(__TEXT,__text) section\n";
|
| -#else
|
| - const char* header = "<.data>:\n";
|
| -#endif
|
| - char* header_pos = NULL;
|
| - while (header_pos == NULL && fgets(line, sizeof(line), output) != NULL) {
|
| - header_pos = strstr(line, header);
|
| - if (header_pos != NULL) {
|
| - formatter->Print("%s", header_pos + strlen(header));
|
| + uint8_t modrm = *(data + 1);
|
| + int mod, regop, rm;
|
| + get_modrm(modrm, &mod, ®op, &rm);
|
| + regop &= 0x7; // The REX.R bit does not affect the operation.
|
| + int imm8 = -1;
|
| + int num_bytes = 2;
|
| + if (mod != 3) {
|
| + UnimplementedInstruction();
|
| + return num_bytes;
|
| + }
|
| + const char* mnem = NULL;
|
| + switch (regop) {
|
| + case 0:
|
| + mnem = "rol";
|
| + break;
|
| + case 1:
|
| + mnem = "ror";
|
| + break;
|
| + case 2:
|
| + mnem = "rcl";
|
| + break;
|
| + case 3:
|
| + mnem = "rcr";
|
| + break;
|
| + case 4:
|
| + mnem = "shl";
|
| + break;
|
| + case 5:
|
| + mnem = "shr";
|
| + break;
|
| + case 7:
|
| + mnem = "sar";
|
| + break;
|
| + default:
|
| + UnimplementedInstruction();
|
| + return num_bytes;
|
| + }
|
| + ASSERT(NULL != mnem);
|
| + if (op == 0xD0) {
|
| + imm8 = 1;
|
| + } else if (op == 0xC0) {
|
| + imm8 = *(data + 2);
|
| + num_bytes = 3;
|
| + }
|
| + AppendToBuffer("%s%c %s,",
|
| + mnem,
|
| + operand_size_code(),
|
| + byte_size_operand_ ? NameOfByteCPURegister(rm)
|
| + : NameOfCPURegister(rm));
|
| + if (op == 0xD2) {
|
| + AppendToBuffer("cl");
|
| + } else {
|
| + AppendToBuffer("%d", imm8);
|
| + }
|
| + return num_bytes;
|
| +}
|
| +
|
| +
|
| +int DisassemblerX64::PrintRightOperand(uint8_t* modrmp) {
|
| + return PrintRightOperandHelper(modrmp,
|
| + &DisassemblerX64::NameOfCPURegister);
|
| +}
|
| +
|
| +
|
| +int DisassemblerX64::PrintRightByteOperand(uint8_t* modrmp) {
|
| + return PrintRightOperandHelper(modrmp,
|
| + &DisassemblerX64::NameOfByteCPURegister);
|
| +}
|
| +
|
| +
|
| +int DisassemblerX64::PrintRightXMMOperand(uint8_t* modrmp) {
|
| + return PrintRightOperandHelper(modrmp,
|
| + &DisassemblerX64::NameOfXMMRegister);
|
| +}
|
| +
|
| +
|
| +// Returns number of bytes used including the current *data.
|
| +// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
|
| +int DisassemblerX64::PrintOperands(const char* mnem,
|
| + OperandType op_order,
|
| + uint8_t* data) {
|
| + uint8_t modrm = *data;
|
| + int mod, regop, rm;
|
| + get_modrm(modrm, &mod, ®op, &rm);
|
| + int advance = 0;
|
| + const char* register_name =
|
| + byte_size_operand_ ? NameOfByteCPURegister(regop)
|
| + : NameOfCPURegister(regop);
|
| + switch (op_order) {
|
| + case REG_OPER_OP_ORDER: {
|
| + AppendToBuffer("%s%c %s,",
|
| + mnem,
|
| + operand_size_code(),
|
| + register_name);
|
| + advance = byte_size_operand_ ? PrintRightByteOperand(data)
|
| + : PrintRightOperand(data);
|
| + break;
|
| }
|
| + case OPER_REG_OP_ORDER: {
|
| + AppendToBuffer("%s%c ", mnem, operand_size_code());
|
| + advance = byte_size_operand_ ? PrintRightByteOperand(data)
|
| + : PrintRightOperand(data);
|
| + AppendToBuffer(",%s", register_name);
|
| + break;
|
| + }
|
| + default:
|
| + UNREACHABLE();
|
| + break;
|
| }
|
| + return advance;
|
| +}
|
|
|
|
|
| - int comment_finger = 0;
|
| - while (fgets(line, sizeof(line), output) != NULL) {
|
| - char* tab = strchr(line, '\t');
|
| - if (tab != NULL) {
|
| - *tab = '\0';
|
| - intptr_t offset = 0;
|
| - sscanf(line, "%p", reinterpret_cast<void**>(&offset)); // NOLINT
|
| - while (comment_finger < comments.Length() &&
|
| - comments.PCOffsetAt(comment_finger) <= offset) {
|
| - formatter->Print(" ;; %s\n",
|
| - comments.CommentAt(comment_finger).ToCString());
|
| - comment_finger++;
|
| +void DisassemblerX64::AppendAddressToBuffer(uint8_t* addr_byte_ptr) {
|
| + NoGCScope no_gc;
|
| + uword addr = reinterpret_cast<uword>(addr_byte_ptr);
|
| + AppendToBuffer("0x%0" PRIxPTR "", addr);
|
| + // Try to print as heap object or stub name
|
| + if (!Isolate::Current()->heap()->CodeContains(addr) &&
|
| + Isolate::Current()->heap()->Contains(addr - kHeapObjectTag)) {
|
| + Object& obj = Object::Handle(reinterpret_cast<RawObject*>(addr));
|
| + if (obj.IsArray()) {
|
| + const Array& arr = Array::CheckedHandle(obj.raw());
|
| + intptr_t len = arr.Length();
|
| + if (len > 5) len = 5; // Print a max of 5 elements.
|
| + AppendToBuffer(" Array[");
|
| + int i = 0;
|
| + while (i < len) {
|
| + obj = arr.At(i);
|
| + if (i > 0) AppendToBuffer(", ");
|
| + AppendToBuffer(obj.ToCString());
|
| + i++;
|
| }
|
| + if (i < arr.Length()) AppendToBuffer(", ...");
|
| + AppendToBuffer("]");
|
| + return;
|
| + }
|
| + AppendToBuffer(" '%s'", obj.ToCString());
|
| + } else {
|
| + // 'addr' is not an object, but probably a code address.
|
| + const char* name_of_stub = StubCode::NameOfStub(addr);
|
| + if (name_of_stub != NULL) {
|
| + AppendToBuffer(" [stub: %s]", name_of_stub);
|
| + } else {
|
| + // Print only if jumping to entry point.
|
| + const Code& code = Code::Handle(Code::LookupCode(addr));
|
| + if (!code.IsNull() && (code.EntryPoint() == addr)) {
|
| + const Function& function = Function::Handle(code.function());
|
| + if (function.IsNull()) {
|
| + AppendToBuffer(" [ stub ]");
|
| + } else {
|
| + const char* name_of_function = function.ToFullyQualifiedCString();
|
| + AppendToBuffer(" [%s]", name_of_function);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
|
|
| - formatter->Print("%016p %08x %s", start + offset, offset, tab + 1);
|
| +
|
| +// Returns number of bytes used, including *data.
|
| +int DisassemblerX64::JumpShort(uint8_t* data) {
|
| + ASSERT(0xEB == *data);
|
| + uint8_t b = *(data + 1);
|
| + uint8_t* dest = data + static_cast<int8_t>(b) + 2;
|
| + AppendToBuffer("jmp ");
|
| + AppendAddressToBuffer(dest);
|
| + return 2;
|
| +}
|
| +
|
| +
|
| +// Returns number of bytes used, including *data.
|
| +int DisassemblerX64::JumpConditional(uint8_t* data) {
|
| + ASSERT(0x0F == *data);
|
| + uint8_t cond = *(data + 1) & 0x0F;
|
| + uint8_t* dest = data + *reinterpret_cast<int32_t*>(data + 2) + 6;
|
| + const char* mnem = conditional_code_suffix[cond];
|
| + AppendToBuffer("j%s ", mnem);
|
| + AppendAddressToBuffer(dest);
|
| + return 6; // includes 0x0F
|
| +}
|
| +
|
| +
|
| +// Returns number of bytes used, including *data.
|
| +int DisassemblerX64::JumpConditionalShort(uint8_t* data) {
|
| + uint8_t cond = *data & 0x0F;
|
| + uint8_t b = *(data + 1);
|
| + uint8_t* dest = data + static_cast<uint8_t>(b) + 2;
|
| + const char* mnem = conditional_code_suffix[cond];
|
| + AppendToBuffer("j%s ", mnem);
|
| + AppendAddressToBuffer(dest);
|
| + return 2;
|
| +}
|
| +
|
| +
|
| +// Returns number of bytes used, including *data.
|
| +int DisassemblerX64::SetCC(uint8_t* data) {
|
| + ASSERT(0x0F == *data);
|
| + uint8_t cond = *(data + 1) & 0x0F;
|
| + const char* mnem = conditional_code_suffix[cond];
|
| + AppendToBuffer("set%s%c ", mnem, operand_size_code());
|
| + PrintRightByteOperand(data + 2);
|
| + return 3; // includes 0x0F
|
| +}
|
| +
|
| +
|
| +// Returns number of bytes used, including *data.
|
| +int DisassemblerX64::FPUInstruction(uint8_t* data) {
|
| + uint8_t escape_opcode = *data;
|
| + ASSERT(0xD8 == (escape_opcode & 0xF8));
|
| + uint8_t modrm_byte = *(data+1);
|
| +
|
| + if (modrm_byte >= 0xC0) {
|
| + return RegisterFPUInstruction(escape_opcode, modrm_byte);
|
| + } else {
|
| + return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1);
|
| + }
|
| +}
|
| +
|
| +
|
| +int DisassemblerX64::MemoryFPUInstruction(int escape_opcode,
|
| + int modrm_byte,
|
| + uint8_t* modrm_start) {
|
| + const char* mnem = "?";
|
| + int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte.
|
| + switch (escape_opcode) {
|
| + case 0xD9: switch (regop) {
|
| + case 0: mnem = "fld_s"; break;
|
| + case 3: mnem = "fstp_s"; break;
|
| + case 7: mnem = "fstcw"; break;
|
| + default: UnimplementedInstruction();
|
| + }
|
| + break;
|
| +
|
| + case 0xDB: switch (regop) {
|
| + case 0: mnem = "fild_s"; break;
|
| + case 1: mnem = "fisttp_s"; break;
|
| + case 2: mnem = "fist_s"; break;
|
| + case 3: mnem = "fistp_s"; break;
|
| + default: UnimplementedInstruction();
|
| + }
|
| + break;
|
| +
|
| + case 0xDD: switch (regop) {
|
| + case 0: mnem = "fld_d"; break;
|
| + case 3: mnem = "fstp_d"; break;
|
| + default: UnimplementedInstruction();
|
| + }
|
| + break;
|
| +
|
| + case 0xDF: switch (regop) {
|
| + case 5: mnem = "fild_d"; break;
|
| + case 7: mnem = "fistp_d"; break;
|
| + default: UnimplementedInstruction();
|
| + }
|
| + break;
|
| +
|
| + default: UnimplementedInstruction();
|
| + }
|
| + AppendToBuffer("%s ", mnem);
|
| + int count = PrintRightOperand(modrm_start);
|
| + return count + 1;
|
| +}
|
| +
|
| +int DisassemblerX64::RegisterFPUInstruction(int escape_opcode,
|
| + uint8_t modrm_byte) {
|
| + bool has_register = false; // Is the FPU register encoded in modrm_byte?
|
| + const char* mnem = "?";
|
| +
|
| + switch (escape_opcode) {
|
| + case 0xD8:
|
| + UnimplementedInstruction();
|
| + break;
|
| +
|
| + case 0xD9:
|
| + switch (modrm_byte & 0xF8) {
|
| + case 0xC0:
|
| + mnem = "fld";
|
| + has_register = true;
|
| + break;
|
| + case 0xC8:
|
| + mnem = "fxch";
|
| + has_register = true;
|
| + break;
|
| + default:
|
| + switch (modrm_byte) {
|
| + case 0xE0: mnem = "fchs"; break;
|
| + case 0xE1: mnem = "fabs"; break;
|
| + case 0xE3: mnem = "fninit"; break;
|
| + case 0xE4: mnem = "ftst"; break;
|
| + case 0xE8: mnem = "fld1"; break;
|
| + case 0xEB: mnem = "fldpi"; break;
|
| + case 0xED: mnem = "fldln2"; break;
|
| + case 0xEE: mnem = "fldz"; break;
|
| + case 0xF0: mnem = "f2xm1"; break;
|
| + case 0xF1: mnem = "fyl2x"; break;
|
| + case 0xF2: mnem = "fptan"; break;
|
| + case 0xF5: mnem = "fprem1"; break;
|
| + case 0xF7: mnem = "fincstp"; break;
|
| + case 0xF8: mnem = "fprem"; break;
|
| + case 0xFD: mnem = "fscale"; break;
|
| + case 0xFE: mnem = "fsin"; break;
|
| + case 0xFF: mnem = "fcos"; break;
|
| + default: UnimplementedInstruction();
|
| + }
|
| + }
|
| + break;
|
| +
|
| + case 0xDA:
|
| + if (modrm_byte == 0xE9) {
|
| + mnem = "fucompp";
|
| + } else {
|
| + UnimplementedInstruction();
|
| + }
|
| + break;
|
| +
|
| + case 0xDB:
|
| + if ((modrm_byte & 0xF8) == 0xE8) {
|
| + mnem = "fucomi";
|
| + has_register = true;
|
| + } else if (modrm_byte == 0xE2) {
|
| + mnem = "fclex";
|
| + } else {
|
| + UnimplementedInstruction();
|
| + }
|
| + break;
|
| +
|
| + case 0xDC:
|
| + has_register = true;
|
| + switch (modrm_byte & 0xF8) {
|
| + case 0xC0: mnem = "fadd"; break;
|
| + case 0xE8: mnem = "fsub"; break;
|
| + case 0xC8: mnem = "fmul"; break;
|
| + case 0xF8: mnem = "fdiv"; break;
|
| + default: UnimplementedInstruction();
|
| + }
|
| + break;
|
| +
|
| + case 0xDD:
|
| + has_register = true;
|
| + switch (modrm_byte & 0xF8) {
|
| + case 0xC0: mnem = "ffree"; break;
|
| + case 0xD8: mnem = "fstp"; break;
|
| + default: UnimplementedInstruction();
|
| + }
|
| + break;
|
| +
|
| + case 0xDE:
|
| + if (modrm_byte == 0xD9) {
|
| + mnem = "fcompp";
|
| + } else {
|
| + has_register = true;
|
| + switch (modrm_byte & 0xF8) {
|
| + case 0xC0: mnem = "faddp"; break;
|
| + case 0xE8: mnem = "fsubp"; break;
|
| + case 0xC8: mnem = "fmulp"; break;
|
| + case 0xF8: mnem = "fdivp"; break;
|
| + default: UnimplementedInstruction();
|
| + }
|
| + }
|
| + break;
|
| +
|
| + case 0xDF:
|
| + if (modrm_byte == 0xE0) {
|
| + mnem = "fnstsw_ax";
|
| + } else if ((modrm_byte & 0xF8) == 0xE8) {
|
| + mnem = "fucomip";
|
| + has_register = true;
|
| + }
|
| + break;
|
| +
|
| + default: UnimplementedInstruction();
|
| + }
|
| +
|
| + if (has_register) {
|
| + AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
|
| + } else {
|
| + AppendToBuffer("%s", mnem);
|
| + }
|
| + return 2;
|
| +}
|
| +
|
| +
|
| +// TODO(srdjan): Should we add a branch hint argument?
|
| +bool DisassemblerX64::DecodeInstructionType(const InstructionDesc& idesc,
|
| + uint8_t** data) {
|
| + uint8_t current = **data;
|
| + switch (idesc.type) {
|
| + case ZERO_OPERANDS_INSTR:
|
| + if (current >= 0xA4 && current <= 0xA7) {
|
| + // String move or compare operations.
|
| + if (group_1_prefix_ == REP_PREFIX) {
|
| + // REP.
|
| + AppendToBuffer("rep ");
|
| + }
|
| + // TODO(srdjan): Should we enable printing of REX.W?
|
| + // if (rex_w()) AppendToBuffer("REX.W ");
|
| + AppendToBuffer("%s%c", idesc.mnem, operand_size_code());
|
| + } else {
|
| + AppendToBuffer("%s", idesc.mnem, operand_size_code());
|
| + }
|
| + (*data)++;
|
| + break;
|
| +
|
| + case TWO_OPERANDS_INSTR:
|
| + (*data)++;
|
| + (*data) += PrintOperands(idesc.mnem, idesc.op_order_, *data);
|
| + break;
|
| +
|
| + case JUMP_CONDITIONAL_SHORT_INSTR:
|
| + (*data) += JumpConditionalShort(*data);
|
| + break;
|
| +
|
| + case REGISTER_INSTR:
|
| + AppendToBuffer("%s%c %s",
|
| + idesc.mnem,
|
| + operand_size_code(),
|
| + NameOfCPURegister(base_reg(current & 0x07)));
|
| + (*data)++;
|
| + break;
|
| + case PUSHPOP_INSTR:
|
| + AppendToBuffer("%s %s",
|
| + idesc.mnem,
|
| + NameOfCPURegister(base_reg(current & 0x07)));
|
| + (*data)++;
|
| + break;
|
| + case MOVE_REG_INSTR: {
|
| + uint8_t* addr = NULL;
|
| + switch (operand_size()) {
|
| + case WORD_SIZE:
|
| + addr = reinterpret_cast<uint8_t*>(
|
| + *reinterpret_cast<int16_t*>(*data + 1));
|
| + (*data) += 3;
|
| + break;
|
| + case DOUBLEWORD_SIZE:
|
| + addr = reinterpret_cast<uint8_t*>(
|
| + *reinterpret_cast<int32_t*>(*data + 1));
|
| + (*data) += 5;
|
| + break;
|
| + case QUADWORD_SIZE:
|
| + addr = reinterpret_cast<uint8_t*>(
|
| + *reinterpret_cast<int64_t*>(*data + 1));
|
| + (*data) += 9;
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| + AppendToBuffer("mov%c %s,",
|
| + operand_size_code(),
|
| + NameOfCPURegister(base_reg(current & 0x07)));
|
| + AppendAddressToBuffer(addr);
|
| + break;
|
| }
|
| +
|
| + case CALL_JUMP_INSTR: {
|
| + uint8_t* addr = *data + *reinterpret_cast<int32_t*>(*data + 1) + 5;
|
| + AppendToBuffer("%s ", idesc.mnem);
|
| + AppendAddressToBuffer(addr);
|
| + (*data) += 5;
|
| + break;
|
| + }
|
| +
|
| + case SHORT_IMMEDIATE_INSTR: {
|
| + uint8_t* addr =
|
| + reinterpret_cast<uint8_t*>(*reinterpret_cast<int32_t*>(*data + 1));
|
| + AppendToBuffer("%s rax, ", idesc.mnem);
|
| + AppendAddressToBuffer(addr);
|
| + (*data) += 5;
|
| + break;
|
| + }
|
| +
|
| + case NO_INSTR:
|
| + return false;
|
| +
|
| + default:
|
| + UNIMPLEMENTED(); // This type is not implemented.
|
| }
|
| - pclose(output);
|
| + return true;
|
| +}
|
|
|
| - // Delete tmp files.
|
| - remove(tmp);
|
| -#if defined(__APPLE__)
|
| - char tmp_o[32];
|
| - snprintf(tmp_o, sizeof(tmp_o), "%s.o", tmp);
|
| - remove(tmp_o);
|
| -#endif
|
| -#endif // !defined(_WIN32)
|
| +
|
| +// Handle all two-byte opcodes, which start with 0x0F.
|
| +// These instructions may be affected by an 0x66, 0xF2, or 0xF3 prefix.
|
| +// We do not use any three-byte opcodes, which start with 0x0F38 or 0x0F3A.
|
| +int DisassemblerX64::TwoByteOpcodeInstruction(uint8_t* data) {
|
| + uint8_t opcode = *(data + 1);
|
| + uint8_t* current = data + 2;
|
| + // At return, "current" points to the start of the next instruction.
|
| + const char* mnemonic = TwoByteMnemonic(opcode);
|
| + if (operand_size_ == 0x66) {
|
| + // 0x66 0x0F prefix.
|
| + int mod, regop, rm;
|
| + if (opcode == 0x3A) {
|
| + uint8_t third_byte = *current;
|
| + current = data + 3;
|
| + if (third_byte == 0x17) {
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + AppendToBuffer("extractps "); // reg/m32, xmm, imm8
|
| + current += PrintRightOperand(current);
|
| + AppendToBuffer(", %s, %d", NameOfCPURegister(regop), (*current) & 3);
|
| + current += 1;
|
| + } else if (third_byte == 0x0b) {
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + // roundsd xmm, xmm/m64, imm8
|
| + AppendToBuffer("roundsd %s, ", NameOfCPURegister(regop));
|
| + current += PrintRightOperand(current);
|
| + AppendToBuffer(", %d", (*current) & 3);
|
| + current += 1;
|
| + } else {
|
| + UnimplementedInstruction();
|
| + }
|
| + } else {
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + if (opcode == 0x1f) {
|
| + current++;
|
| + if (rm == 4) { // SIB byte present.
|
| + current++;
|
| + }
|
| + if (mod == 1) { // Byte displacement.
|
| + current += 1;
|
| + } else if (mod == 2) { // 32-bit displacement.
|
| + current += 4;
|
| + } // else no immediate displacement.
|
| + AppendToBuffer("nop");
|
| + } else if (opcode == 0x28) {
|
| + AppendToBuffer("movapd %s, ", NameOfXMMRegister(regop));
|
| + current += PrintRightXMMOperand(current);
|
| + } else if (opcode == 0x29) {
|
| + AppendToBuffer("movapd ");
|
| + current += PrintRightXMMOperand(current);
|
| + AppendToBuffer(", %s", NameOfXMMRegister(regop));
|
| + } else if (opcode == 0x6E) {
|
| + AppendToBuffer("mov%c %s,",
|
| + rex_w() ? 'q' : 'd',
|
| + NameOfXMMRegister(regop));
|
| + current += PrintRightOperand(current);
|
| + } else if (opcode == 0x6F) {
|
| + AppendToBuffer("movdqa %s,",
|
| + NameOfXMMRegister(regop));
|
| + current += PrintRightXMMOperand(current);
|
| + } else if (opcode == 0x7E) {
|
| + AppendToBuffer("mov%c ",
|
| + rex_w() ? 'q' : 'd');
|
| + current += PrintRightOperand(current);
|
| + AppendToBuffer(", %s", NameOfXMMRegister(regop));
|
| + } else if (opcode == 0x7F) {
|
| + AppendToBuffer("movdqa ");
|
| + current += PrintRightXMMOperand(current);
|
| + AppendToBuffer(", %s", NameOfXMMRegister(regop));
|
| + } else if (opcode == 0xD6) {
|
| + AppendToBuffer("movq ");
|
| + current += PrintRightXMMOperand(current);
|
| + AppendToBuffer(", %s", NameOfXMMRegister(regop));
|
| + } else if (opcode == 0x50) {
|
| + AppendToBuffer("movmskpd %s,", NameOfCPURegister(regop));
|
| + current += PrintRightXMMOperand(current);
|
| + } else {
|
| + const char* mnemonic = "?";
|
| + if (opcode == 0x54) {
|
| + mnemonic = "andpd";
|
| + } else if (opcode == 0x56) {
|
| + mnemonic = "orpd";
|
| + } else if (opcode == 0x57) {
|
| + mnemonic = "xorpd";
|
| + } else if (opcode == 0x2E) {
|
| + mnemonic = "ucomisd";
|
| + } else if (opcode == 0x2F) {
|
| + mnemonic = "comisd";
|
| + } else {
|
| + UnimplementedInstruction();
|
| + }
|
| + AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
|
| + current += PrintRightXMMOperand(current);
|
| + }
|
| + }
|
| + } else if (group_1_prefix_ == 0xF2) {
|
| + // Beginning of instructions with prefix 0xF2.
|
| +
|
| + if (opcode == 0x11 || opcode == 0x10) {
|
| + // MOVSD: Move scalar double-precision fp to/from/between XMM registers.
|
| + AppendToBuffer("movsd ");
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + if (opcode == 0x11) {
|
| + current += PrintRightXMMOperand(current);
|
| + AppendToBuffer(",%s", NameOfXMMRegister(regop));
|
| + } else {
|
| + AppendToBuffer("%s,", NameOfXMMRegister(regop));
|
| + current += PrintRightXMMOperand(current);
|
| + }
|
| + } else if (opcode == 0x2A) {
|
| + // CVTSI2SD: integer to XMM double conversion.
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + AppendToBuffer("%sd %s,", mnemonic, NameOfXMMRegister(regop));
|
| + current += PrintRightOperand(current);
|
| + } else if (opcode == 0x2C) {
|
| + // CVTTSD2SI:
|
| + // Convert with truncation scalar double-precision FP to integer.
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + AppendToBuffer("cvttsd2si%c %s,",
|
| + operand_size_code(), NameOfCPURegister(regop));
|
| + current += PrintRightXMMOperand(current);
|
| + } else if (opcode == 0x2D) {
|
| + // CVTSD2SI: Convert scalar double-precision FP to integer.
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + AppendToBuffer("cvtsd2si%c %s,",
|
| + operand_size_code(), NameOfCPURegister(regop));
|
| + current += PrintRightXMMOperand(current);
|
| + } else if ((opcode & 0xF8) == 0x58 || opcode == 0x51) {
|
| + // XMM arithmetic. Mnemonic was retrieved at the start of this function.
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
|
| + current += PrintRightXMMOperand(current);
|
| + } else {
|
| + UnimplementedInstruction();
|
| + }
|
| + } else if (group_1_prefix_ == 0xF3) {
|
| + // Instructions with prefix 0xF3.
|
| + if (opcode == 0x11 || opcode == 0x10) {
|
| + // MOVSS: Move scalar double-precision fp to/from/between XMM registers.
|
| + AppendToBuffer("movss ");
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + if (opcode == 0x11) {
|
| + current += PrintRightOperand(current);
|
| + AppendToBuffer(",%s", NameOfXMMRegister(regop));
|
| + } else {
|
| + AppendToBuffer("%s,", NameOfXMMRegister(regop));
|
| + current += PrintRightOperand(current);
|
| + }
|
| + } else if (opcode == 0x2A) {
|
| + // CVTSI2SS: integer to XMM single conversion.
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + AppendToBuffer("%ss %s,", mnemonic, NameOfXMMRegister(regop));
|
| + current += PrintRightOperand(current);
|
| + } else if (opcode == 0x2C) {
|
| + // CVTTSS2SI:
|
| + // Convert with truncation scalar single-precision FP to dword integer.
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + AppendToBuffer("cvttss2si%c %s,",
|
| + operand_size_code(), NameOfCPURegister(regop));
|
| + current += PrintRightXMMOperand(current);
|
| + } else if (opcode == 0x5A) {
|
| + // CVTSS2SD:
|
| + // Convert scalar single-precision FP to scalar double-precision FP.
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop));
|
| + current += PrintRightXMMOperand(current);
|
| + } else if (opcode == 0x7E) {
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + AppendToBuffer("movq %s, ", NameOfXMMRegister(regop));
|
| + current += PrintRightXMMOperand(current);
|
| + } else {
|
| + UnimplementedInstruction();
|
| + }
|
| + } else if (opcode == 0x1F) {
|
| + // NOP
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + current++;
|
| + if (rm == 4) { // SIB byte present.
|
| + current++;
|
| + }
|
| + if (mod == 1) { // Byte displacement.
|
| + current += 1;
|
| + } else if (mod == 2) { // 32-bit displacement.
|
| + current += 4;
|
| + } // else no immediate displacement.
|
| + AppendToBuffer("nop");
|
| +
|
| + } else if (opcode == 0x28) {
|
| + // movaps xmm, xmm/m128
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + AppendToBuffer("movaps %s, ", NameOfXMMRegister(regop));
|
| + current += PrintRightXMMOperand(current);
|
| +
|
| + } else if (opcode == 0x29) {
|
| + // movaps xmm/m128, xmm
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + AppendToBuffer("movaps ");
|
| + current += PrintRightXMMOperand(current);
|
| + AppendToBuffer(", %s", NameOfXMMRegister(regop));
|
| +
|
| + } else if (opcode == 0xA2 || opcode == 0x31) {
|
| + // RDTSC or CPUID
|
| + AppendToBuffer("%s", mnemonic);
|
| +
|
| + } else if ((opcode & 0xF0) == 0x40) {
|
| + // CMOVcc: conditional move.
|
| + int condition = opcode & 0x0F;
|
| + const InstructionDesc& idesc = cmov_instructions[condition];
|
| + byte_size_operand_ = idesc.byte_size_operation;
|
| + current += PrintOperands(idesc.mnem, idesc.op_order_, current);
|
| +
|
| + } else if (opcode == 0x57) {
|
| + // xorps xmm, xmm/m128
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + AppendToBuffer("xorps %s, ", NameOfXMMRegister(regop));
|
| + current += PrintRightXMMOperand(current);
|
| +
|
| + } else if ((opcode & 0xF0) == 0x80) {
|
| + // Jcc: Conditional jump (branch).
|
| + current = data + JumpConditional(data);
|
| +
|
| + } else if (opcode == 0xBE || opcode == 0xBF || opcode == 0xB6 ||
|
| + opcode == 0xB7 || opcode == 0xAF) {
|
| + // Size-extending moves, IMUL.
|
| + current += PrintOperands(mnemonic, REG_OPER_OP_ORDER, current);
|
| +
|
| + } else if ((opcode & 0xF0) == 0x90) {
|
| + // SETcc: Set byte on condition. Needs pointer to beginning of instruction.
|
| + current = data + SetCC(data);
|
| +
|
| + } else if (opcode == 0xAB || opcode == 0xA5 || opcode == 0xAD) {
|
| + // SHLD, SHRD (double-precision shift), BTS (bit set).
|
| + AppendToBuffer("%s ", mnemonic);
|
| + int mod, regop, rm;
|
| + get_modrm(*current, &mod, ®op, &rm);
|
| + current += PrintRightOperand(current);
|
| + if (opcode == 0xAB) {
|
| + AppendToBuffer(",%s", NameOfCPURegister(regop));
|
| + } else {
|
| + AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
|
| + }
|
| + } else {
|
| + UnimplementedInstruction();
|
| + }
|
| + return static_cast<int>(current - data);
|
| }
|
|
|
|
|
| -int Disassembler::DecodeInstruction(char* hexa_buffer, intptr_t hexa_size,
|
| +// Mnemonics for two-byte opcode instructions starting with 0x0F.
|
| +// The argument is the second byte of the two-byte opcode.
|
| +// Returns NULL if the instruction is not handled here.
|
| +const char* DisassemblerX64::TwoByteMnemonic(uint8_t opcode) {
|
| + switch (opcode) {
|
| + case 0x1F:
|
| + return "nop";
|
| + case 0x2A: // F2/F3 prefix.
|
| + return "cvtsi2s";
|
| + case 0x31:
|
| + return "rdtsc";
|
| + case 0x51: // F2 prefix.
|
| + return "sqrtsd";
|
| + case 0x58: // F2 prefix.
|
| + return "addsd";
|
| + case 0x59: // F2 prefix.
|
| + return "mulsd";
|
| + case 0x5C: // F2 prefix.
|
| + return "subsd";
|
| + case 0x5E: // F2 prefix.
|
| + return "divsd";
|
| + case 0xA2:
|
| + return "cpuid";
|
| + case 0xA5:
|
| + return "shld";
|
| + case 0xAB:
|
| + return "bts";
|
| + case 0xAD:
|
| + return "shrd";
|
| + case 0xAF:
|
| + return "imul";
|
| + case 0xB6:
|
| + return "movzxb";
|
| + case 0xB7:
|
| + return "movzxw";
|
| + case 0xBE:
|
| + return "movsxb";
|
| + case 0xBF:
|
| + return "movsxw";
|
| + default:
|
| + return NULL;
|
| + }
|
| +}
|
| +
|
| +
|
| +int DisassemblerX64::InstructionDecode(uword pc) {
|
| + uint8_t* data = reinterpret_cast<uint8_t*>(pc);
|
| + uint8_t current;
|
| +
|
| + // Scan for prefixes.
|
| + while (true) {
|
| + current = *data;
|
| + if (current == OPERAND_SIZE_OVERRIDE_PREFIX) { // Group 3 prefix.
|
| + operand_size_ = current;
|
| + } else if ((current & 0xF0) == 0x40) { // REX prefix.
|
| + setRex(current);
|
| + // TODO(srdjan): Should we enable printing of REX.W?
|
| + // if (rex_w()) AppendToBuffer("REX.W ");
|
| + } else if ((current & 0xFE) == 0xF2) { // Group 1 prefix (0xF2 or 0xF3).
|
| + group_1_prefix_ = current;
|
| + } else { // Not a prefix - an opcode.
|
| + break;
|
| + }
|
| + data++;
|
| + }
|
| +
|
| + const InstructionDesc& idesc = instruction_table.Get(current);
|
| + byte_size_operand_ = idesc.byte_size_operation;
|
| + bool processed = DecodeInstructionType(idesc, &data);
|
| +
|
| + if (!processed) {
|
| + switch (*data) {
|
| + case 0xC2:
|
| + AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data + 1));
|
| + data += 3;
|
| + break;
|
| +
|
| + case 0x69: // fall through
|
| + case 0x6B: {
|
| + int mod, regop, rm;
|
| + get_modrm(*(data + 1), &mod, ®op, &rm);
|
| + int32_t imm = *data == 0x6B ? *(data + 2)
|
| + : *reinterpret_cast<int32_t*>(data + 2);
|
| + AppendToBuffer("imul%c %s,%s,0x%x",
|
| + operand_size_code(),
|
| + NameOfCPURegister(regop),
|
| + NameOfCPURegister(rm), imm);
|
| + data += 2 + (*data == 0x6B ? 1 : 4);
|
| + break;
|
| + }
|
| +
|
| + case 0x81: // fall through
|
| + case 0x83: // 0x81 with sign extension bit set
|
| + data += PrintImmediateOp(data);
|
| + break;
|
| +
|
| + case 0x0F:
|
| + data += TwoByteOpcodeInstruction(data);
|
| + break;
|
| +
|
| + case 0x8F: {
|
| + data++;
|
| + int mod, regop, rm;
|
| + get_modrm(*data, &mod, ®op, &rm);
|
| + if (regop == 0) {
|
| + AppendToBuffer("pop ");
|
| + data += PrintRightOperand(data);
|
| + }
|
| + }
|
| + break;
|
| +
|
| + case 0xFF: {
|
| + data++;
|
| + int mod, regop, rm;
|
| + get_modrm(*data, &mod, ®op, &rm);
|
| + const char* mnem = NULL;
|
| + switch (regop) {
|
| + case 0:
|
| + mnem = "inc";
|
| + break;
|
| + case 1:
|
| + mnem = "dec";
|
| + break;
|
| + case 2:
|
| + mnem = "call";
|
| + break;
|
| + case 4:
|
| + mnem = "jmp";
|
| + break;
|
| + case 6:
|
| + mnem = "push";
|
| + break;
|
| + default:
|
| + mnem = "???";
|
| + }
|
| + AppendToBuffer(((regop <= 1) ? "%s%c " : "%s "),
|
| + mnem,
|
| + operand_size_code());
|
| + data += PrintRightOperand(data);
|
| + }
|
| + break;
|
| +
|
| + case 0xC7: // imm32, fall through
|
| + case 0xC6: // imm8
|
| + {
|
| + bool is_byte = *data == 0xC6;
|
| + data++;
|
| + if (is_byte) {
|
| + AppendToBuffer("movb ");
|
| + data += PrintRightByteOperand(data);
|
| + int32_t imm = *data;
|
| + AppendToBuffer(",0x%x", imm);
|
| + data++;
|
| + } else {
|
| + AppendToBuffer("mov%c ", operand_size_code());
|
| + data += PrintRightOperand(data);
|
| + int32_t imm = *reinterpret_cast<int32_t*>(data);
|
| + AppendToBuffer(",0x%x", imm);
|
| + data += 4;
|
| + }
|
| + }
|
| + break;
|
| +
|
| + case 0x80: {
|
| + data++;
|
| + AppendToBuffer("cmpb ");
|
| + data += PrintRightByteOperand(data);
|
| + int32_t imm = *data;
|
| + AppendToBuffer(",0x%x", imm);
|
| + data++;
|
| + }
|
| + break;
|
| +
|
| + case 0x88: // 8bit, fall through
|
| + case 0x89: // 32bit
|
| + {
|
| + bool is_byte = *data == 0x88;
|
| + int mod, regop, rm;
|
| + data++;
|
| + get_modrm(*data, &mod, ®op, &rm);
|
| + if (is_byte) {
|
| + AppendToBuffer("movb ");
|
| + data += PrintRightByteOperand(data);
|
| + AppendToBuffer(",%s", NameOfByteCPURegister(regop));
|
| + } else {
|
| + AppendToBuffer("mov%c ", operand_size_code());
|
| + data += PrintRightOperand(data);
|
| + AppendToBuffer(",%s", NameOfCPURegister(regop));
|
| + }
|
| + }
|
| + break;
|
| +
|
| + case 0x90:
|
| + case 0x91:
|
| + case 0x92:
|
| + case 0x93:
|
| + case 0x94:
|
| + case 0x95:
|
| + case 0x96:
|
| + case 0x97: {
|
| + int reg = (*data & 0x7) | (rex_b() ? 8 : 0);
|
| + if (reg == 0) {
|
| + AppendToBuffer("nop"); // Common name for xchg rax,rax.
|
| + } else {
|
| + AppendToBuffer("xchg%c rax, %s",
|
| + operand_size_code(),
|
| + NameOfCPURegister(reg));
|
| + }
|
| + data++;
|
| + }
|
| + break;
|
| + case 0xB0:
|
| + case 0xB1:
|
| + case 0xB2:
|
| + case 0xB3:
|
| + case 0xB4:
|
| + case 0xB5:
|
| + case 0xB6:
|
| + case 0xB7:
|
| + case 0xB8:
|
| + case 0xB9:
|
| + case 0xBA:
|
| + case 0xBB:
|
| + case 0xBC:
|
| + case 0xBD:
|
| + case 0xBE:
|
| + case 0xBF: {
|
| + // mov reg8,imm8 or mov reg32,imm32
|
| + uint8_t opcode = *data;
|
| + data++;
|
| + uint8_t is_32bit = (opcode >= 0xB8);
|
| + int reg = (opcode & 0x7) | (rex_b() ? 8 : 0);
|
| + if (is_32bit) {
|
| + AppendToBuffer("mov%c %s, ",
|
| + operand_size_code(),
|
| + NameOfCPURegister(reg));
|
| + data += PrintImmediate(data, DOUBLEWORD_SIZE);
|
| + } else {
|
| + AppendToBuffer("movb %s, ",
|
| + NameOfByteCPURegister(reg));
|
| + data += PrintImmediate(data, BYTE_SIZE);
|
| + }
|
| + break;
|
| + }
|
| + case 0xFE: {
|
| + data++;
|
| + int mod, regop, rm;
|
| + get_modrm(*data, &mod, ®op, &rm);
|
| + if (regop == 1) {
|
| + AppendToBuffer("decb ");
|
| + data += PrintRightByteOperand(data);
|
| + } else {
|
| + UnimplementedInstruction();
|
| + }
|
| + break;
|
| + }
|
| + case 0x68:
|
| + AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data + 1));
|
| + data += 5;
|
| + break;
|
| +
|
| + case 0x6A:
|
| + AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
|
| + data += 2;
|
| + break;
|
| +
|
| + case 0xA1: // Fall through.
|
| + case 0xA3:
|
| + switch (operand_size()) {
|
| + case DOUBLEWORD_SIZE: {
|
| + AppendAddressToBuffer(
|
| + reinterpret_cast<uint8_t*>(
|
| + *reinterpret_cast<int32_t*>(data + 1)));
|
| + if (*data == 0xA1) { // Opcode 0xA1
|
| + AppendToBuffer("movzxlq rax,(");
|
| + AppendAddressToBuffer(
|
| + reinterpret_cast<uint8_t*>(
|
| + *reinterpret_cast<int32_t*>(data + 1)));
|
| + AppendToBuffer(")");
|
| + } else { // Opcode 0xA3
|
| + AppendToBuffer("movzxlq (");
|
| + AppendAddressToBuffer(
|
| + reinterpret_cast<uint8_t*>(
|
| + *reinterpret_cast<int32_t*>(data + 1)));
|
| + AppendToBuffer("),rax");
|
| + }
|
| + data += 5;
|
| + break;
|
| + }
|
| + case QUADWORD_SIZE: {
|
| + // New x64 instruction mov rax,(imm_64).
|
| + if (*data == 0xA1) { // Opcode 0xA1
|
| + AppendToBuffer("movq rax,(");
|
| + AppendAddressToBuffer(*reinterpret_cast<uint8_t**>(data + 1));
|
| + AppendToBuffer(")");
|
| + } else { // Opcode 0xA3
|
| + AppendToBuffer("movq (");
|
| + AppendAddressToBuffer(*reinterpret_cast<uint8_t**>(data + 1));
|
| + AppendToBuffer("),rax");
|
| + }
|
| + data += 9;
|
| + break;
|
| + }
|
| + default:
|
| + UnimplementedInstruction();
|
| + data += 2;
|
| + }
|
| + break;
|
| +
|
| + case 0xA8:
|
| + AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data + 1));
|
| + data += 2;
|
| + break;
|
| +
|
| + case 0xA9: {
|
| + int64_t value = 0;
|
| + switch (operand_size()) {
|
| + case WORD_SIZE:
|
| + value = *reinterpret_cast<uint16_t*>(data + 1);
|
| + data += 3;
|
| + break;
|
| + case DOUBLEWORD_SIZE:
|
| + value = *reinterpret_cast<uint32_t*>(data + 1);
|
| + data += 5;
|
| + break;
|
| + case QUADWORD_SIZE:
|
| + value = *reinterpret_cast<int32_t*>(data + 1);
|
| + data += 5;
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| + AppendToBuffer("test%c rax,0x%0" PRIxPTR "",
|
| + operand_size_code(),
|
| + value);
|
| + break;
|
| + }
|
| + case 0xD1: // fall through
|
| + case 0xD3: // fall through
|
| + case 0xC1:
|
| + data += ShiftInstruction(data);
|
| + break;
|
| + case 0xD0: // fall through
|
| + case 0xD2: // fall through
|
| + case 0xC0:
|
| + byte_size_operand_ = true;
|
| + data += ShiftInstruction(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 0xF6:
|
| + byte_size_operand_ = true; // fall through
|
| + case 0xF7:
|
| + data += F6F7Instruction(data);
|
| + break;
|
| +
|
| + default:
|
| + UnimplementedInstruction();
|
| + data += 1;
|
| + }
|
| + } // !processed
|
| +
|
| + if (buffer_pos_ < buffer_size_) {
|
| + buffer_[buffer_pos_] = '\0';
|
| + }
|
| +
|
| + int instr_len = data - reinterpret_cast<uint8_t*>(pc);
|
| + ASSERT(instr_len > 0); // Ensure progress.
|
| +
|
| + return instr_len;
|
| +}
|
| +
|
| +
|
| +int Disassembler::DecodeInstruction(char* hex_buffer, intptr_t hex_size,
|
| char* human_buffer, intptr_t human_size,
|
| uword pc) {
|
| - UNIMPLEMENTED();
|
| - return 0;
|
| + ASSERT(hex_size > 0);
|
| + ASSERT(human_size > 0);
|
| + DisassemblerX64 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';
|
| + return instruction_length;
|
| }
|
|
|
| +
|
| +void Disassembler::Disassemble(uword start,
|
| + uword end,
|
| + DisassemblyFormatter* formatter,
|
| + const Code::Comments& comments) {
|
| + ASSERT(formatter != NULL);
|
| + char hex_buffer[kHexadecimalBufferSize]; // Instruction in hexadecimal form.
|
| + char human_buffer[kUserReadableBufferSize]; // Human-readable instruction.
|
| + uword pc = start;
|
| + intptr_t comment_finger = 0;
|
| + while (pc < end) {
|
| + const intptr_t offset = pc - start;
|
| + while (comment_finger < comments.Length() &&
|
| + comments.PCOffsetAt(comment_finger) <= offset) {
|
| + formatter->Print(" ;; %s\n",
|
| + comments.CommentAt(comment_finger).ToCString());
|
| + comment_finger++;
|
| + }
|
| + int instruction_length = DecodeInstruction(hex_buffer,
|
| + sizeof(hex_buffer),
|
| + human_buffer,
|
| + sizeof(human_buffer),
|
| + pc);
|
| + formatter->ConsumeInstruction(hex_buffer,
|
| + sizeof(hex_buffer),
|
| + human_buffer,
|
| + sizeof(human_buffer),
|
| + pc);
|
| + pc += instruction_length;
|
| + }
|
| +}
|
| +
|
| } // namespace dart
|
|
|
| #endif // defined TARGET_ARCH_X64
|
|
|