Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(290)

Unified Diff: runtime/vm/disassembler_x64.cc

Issue 10826248: Add X64 assembler. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/disassembler.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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, &regop, &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, &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:
+ 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, &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 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, &regop, &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, &regop, &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, &regop, &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, &regop, &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, &regop, &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, &regop, &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, &regop, &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, &regop, &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, &regop, &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, &regop, &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, &regop, &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, &regop, &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, &regop, &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, &regop, &rm);
+ AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else if (opcode == 0x7E) {
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("movq %s, ", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else {
+ UnimplementedInstruction();
+ }
+ } else if (opcode == 0x1F) {
+ // NOP
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &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, &regop, &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, &regop, &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, &regop, &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, &regop, &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, &regop, &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, &regop, &rm);
+ if (regop == 0) {
+ AppendToBuffer("pop ");
+ data += PrintRightOperand(data);
+ }
+ }
+ break;
+
+ case 0xFF: {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &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, &regop, &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, &regop, &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
« no previous file with comments | « runtime/vm/disassembler.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698