Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2009 the V8 project authors. All rights reserved. | 1 // Copyright 2009 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| 11 // with the distribution. | 11 // with the distribution. |
| 12 // * Neither the name of Google Inc. nor the names of its | 12 // * Neither the name of Google Inc. nor the names of its |
| 13 // contributors may be used to endorse or promote products derived | 13 // contributors may be used to endorse or promote products derived |
| 14 // from this software without specific prior written permission. | 14 // from this software without specific prior written permission. |
| 15 // | 15 // |
| 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | 27 |
| 28 #include <assert.h> | |
| 29 #include <stdio.h> | |
| 30 #include <stdarg.h> | |
| 31 | |
| 28 #include "v8.h" | 32 #include "v8.h" |
| 29 #include "disasm.h" | 33 #include "disasm.h" |
| 30 | 34 |
| 31 namespace disasm { | 35 namespace disasm { |
| 32 | 36 |
| 33 Disassembler::Disassembler(NameConverter const& converter) | 37 enum OperandOrder { |
| 34 : converter_(converter) { | 38 UNSET_OP_ORDER = 0, REG_OPER_OP_ORDER, OPER_REG_OP_ORDER |
| 35 UNIMPLEMENTED(); | 39 }; |
| 36 } | 40 |
| 37 | 41 //------------------------------------------------------------------ |
| 38 | 42 // Tables |
| 39 Disassembler::~Disassembler() { | 43 //------------------------------------------------------------------ |
| 40 UNIMPLEMENTED(); | 44 struct ByteMnemonic { |
| 41 } | 45 int b; // -1 terminates, otherwise must be in range (0..255) |
| 42 | 46 OperandOrder op_order_; |
| 43 | 47 const char* mnem; |
| 44 const char* NameConverter::NameOfAddress(unsigned char* addr) const { | 48 }; |
| 45 UNIMPLEMENTED(); | 49 |
| 46 return NULL; | 50 |
| 51 static ByteMnemonic two_operands_instr[] = { | |
| 52 { 0x03, REG_OPER_OP_ORDER, "add" }, | |
| 53 { 0x21, OPER_REG_OP_ORDER, "and" }, | |
| 54 { 0x23, REG_OPER_OP_ORDER, "and" }, | |
| 55 { 0x3B, REG_OPER_OP_ORDER, "cmp" }, | |
| 56 { 0x8D, REG_OPER_OP_ORDER, "lea" }, | |
| 57 { 0x09, OPER_REG_OP_ORDER, "or" }, | |
| 58 { 0x0B, REG_OPER_OP_ORDER, "or" }, | |
| 59 { 0x1B, REG_OPER_OP_ORDER, "sbb" }, | |
| 60 { 0x29, OPER_REG_OP_ORDER, "sub" }, | |
| 61 { 0x2B, REG_OPER_OP_ORDER, "sub" }, | |
| 62 { 0x85, REG_OPER_OP_ORDER, "test" }, | |
| 63 { 0x31, OPER_REG_OP_ORDER, "xor" }, | |
| 64 { 0x33, REG_OPER_OP_ORDER, "xor" }, | |
| 65 { 0x87, REG_OPER_OP_ORDER, "xchg" }, | |
| 66 { 0x8A, REG_OPER_OP_ORDER, "movb" }, | |
| 67 { 0x8B, REG_OPER_OP_ORDER, "mov" }, | |
| 68 { -1, UNSET_OP_ORDER, "" } | |
| 69 }; | |
| 70 | |
| 71 | |
| 72 static ByteMnemonic zero_operands_instr[] = { | |
| 73 { 0xC3, UNSET_OP_ORDER, "ret" }, | |
| 74 { 0xC9, UNSET_OP_ORDER, "leave" }, | |
| 75 { 0x90, UNSET_OP_ORDER, "nop" }, | |
| 76 { 0xF4, UNSET_OP_ORDER, "hlt" }, | |
| 77 { 0xCC, UNSET_OP_ORDER, "int3" }, | |
| 78 { 0x60, UNSET_OP_ORDER, "pushad" }, | |
| 79 { 0x61, UNSET_OP_ORDER, "popad" }, | |
| 80 { 0x9C, UNSET_OP_ORDER, "pushfd" }, | |
| 81 { 0x9D, UNSET_OP_ORDER, "popfd" }, | |
| 82 { 0x9E, UNSET_OP_ORDER, "sahf" }, | |
| 83 { 0x99, UNSET_OP_ORDER, "cdq" }, | |
| 84 { 0x9B, UNSET_OP_ORDER, "fwait" }, | |
| 85 { -1, UNSET_OP_ORDER, "" } | |
| 86 }; | |
| 87 | |
| 88 | |
| 89 static ByteMnemonic call_jump_instr[] = { | |
| 90 { 0xE8, UNSET_OP_ORDER, "call" }, | |
| 91 { 0xE9, UNSET_OP_ORDER, "jmp" }, | |
| 92 { -1, UNSET_OP_ORDER, "" } | |
| 93 }; | |
| 94 | |
| 95 | |
| 96 static ByteMnemonic short_immediate_instr[] = { | |
| 97 { 0x05, UNSET_OP_ORDER, "add" }, | |
| 98 { 0x0D, UNSET_OP_ORDER, "or" }, | |
| 99 { 0x15, UNSET_OP_ORDER, "adc" }, | |
| 100 { 0x25, UNSET_OP_ORDER, "and" }, | |
| 101 { 0x2D, UNSET_OP_ORDER, "sub" }, | |
| 102 { 0x35, UNSET_OP_ORDER, "xor" }, | |
| 103 { 0x3D, UNSET_OP_ORDER, "cmp" }, | |
| 104 { -1, UNSET_OP_ORDER, "" } | |
| 105 }; | |
| 106 | |
| 107 | |
| 108 static const char* conditional_code_suffix[] = { | |
| 109 "o", "no", "c", "nc", "z", "nz", "a", "na", | |
| 110 "s", "ns", "pe", "po", "l", "ge", "le", "g" | |
| 111 }; | |
| 112 | |
| 113 | |
| 114 enum InstructionType { | |
| 115 NO_INSTR, | |
| 116 ZERO_OPERANDS_INSTR, | |
| 117 TWO_OPERANDS_INSTR, | |
| 118 JUMP_CONDITIONAL_SHORT_INSTR, | |
| 119 REGISTER_INSTR, | |
| 120 PUSHPOP_INSTR, // Has implicit 64-bit operand size. | |
| 121 MOVE_REG_INSTR, | |
| 122 CALL_JUMP_INSTR, | |
| 123 SHORT_IMMEDIATE_INSTR | |
| 124 }; | |
| 125 | |
| 126 | |
| 127 struct InstructionDesc { | |
| 128 const char* mnem; | |
| 129 InstructionType type; | |
| 130 OperandOrder op_order_; | |
| 131 }; | |
| 132 | |
| 133 | |
| 134 class InstructionTable { | |
| 135 public: | |
| 136 InstructionTable(); | |
| 137 const InstructionDesc& Get(byte x) const { | |
| 138 return instructions_[x]; | |
| 139 } | |
| 140 | |
| 141 private: | |
| 142 InstructionDesc instructions_[256]; | |
| 143 void Clear(); | |
| 144 void Init(); | |
| 145 void CopyTable(ByteMnemonic bm[], InstructionType type); | |
| 146 void SetTableRange(InstructionType type, byte start, byte end, | |
| 147 const char* mnem); | |
| 148 void AddJumpConditionalShort(); | |
| 149 }; | |
| 150 | |
| 151 | |
| 152 InstructionTable::InstructionTable() { | |
| 153 Clear(); | |
| 154 Init(); | |
| 155 } | |
| 156 | |
| 157 | |
| 158 void InstructionTable::Clear() { | |
| 159 for (int i = 0; i < 256; i++) { | |
| 160 instructions_[i].mnem = ""; | |
| 161 instructions_[i].type = NO_INSTR; | |
| 162 instructions_[i].op_order_ = UNSET_OP_ORDER; | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 | |
| 167 void InstructionTable::Init() { | |
| 168 CopyTable(two_operands_instr, TWO_OPERANDS_INSTR); | |
| 169 CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR); | |
| 170 CopyTable(call_jump_instr, CALL_JUMP_INSTR); | |
| 171 CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR); | |
| 172 AddJumpConditionalShort(); | |
| 173 SetTableRange(PUSHPOP_INSTR, 0x50, 0x57, "push"); | |
| 174 SetTableRange(PUSHPOP_INSTR, 0x58, 0x5F, "pop"); | |
| 175 SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, "mov"); | |
| 176 } | |
| 177 | |
| 178 | |
| 179 void InstructionTable::CopyTable(ByteMnemonic bm[], InstructionType type) { | |
| 180 for (int i = 0; bm[i].b >= 0; i++) { | |
| 181 InstructionDesc* id = &instructions_[bm[i].b]; | |
| 182 id->mnem = bm[i].mnem; | |
| 183 id->op_order_ = bm[i].op_order_; | |
| 184 assert(id->type == NO_INSTR); // Information already entered | |
| 185 id->type = type; | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 | |
| 190 void InstructionTable::SetTableRange(InstructionType type, byte start, | |
| 191 byte end, const char* mnem) { | |
| 192 for (byte b = start; b <= end; b++) { | |
| 193 InstructionDesc* id = &instructions_[b]; | |
| 194 assert(id->type == NO_INSTR); // Information already entered | |
| 195 id->mnem = mnem; | |
| 196 id->type = type; | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 | |
| 201 void InstructionTable::AddJumpConditionalShort() { | |
| 202 for (byte b = 0x70; b <= 0x7F; b++) { | |
| 203 InstructionDesc* id = &instructions_[b]; | |
| 204 assert(id->type == NO_INSTR); // Information already entered | |
| 205 id->mnem = NULL; // Computed depending on condition code. | |
| 206 id->type = JUMP_CONDITIONAL_SHORT_INSTR; | |
| 207 } | |
| 208 } | |
| 209 | |
| 210 | |
| 211 static InstructionTable instruction_table; | |
| 212 | |
| 213 | |
| 214 // The X64 disassembler implementation. | |
| 215 enum UnimplementedOpcodeAction { | |
| 216 CONTINUE_ON_UNIMPLEMENTED_OPCODE, | |
| 217 ABORT_ON_UNIMPLEMENTED_OPCODE | |
| 218 }; | |
| 219 | |
| 220 | |
| 221 class DisassemblerX64 { | |
| 222 public: | |
| 223 DisassemblerX64(const NameConverter& converter, | |
| 224 UnimplementedOpcodeAction unimplemented_action = | |
| 225 ABORT_ON_UNIMPLEMENTED_OPCODE) | |
| 226 : converter_(converter), | |
| 227 tmp_buffer_pos_(0), | |
| 228 abort_on_unimplemented_( | |
| 229 unimplemented_action == ABORT_ON_UNIMPLEMENTED_OPCODE), | |
| 230 rex_(0), | |
| 231 operand_size_(0) { | |
| 232 tmp_buffer_[0] = '\0'; | |
| 233 } | |
| 234 | |
| 235 virtual ~DisassemblerX64() { | |
| 236 } | |
| 237 | |
| 238 // Writes one disassembled instruction into 'buffer' (0-terminated). | |
| 239 // Returns the length of the disassembled machine instruction in bytes. | |
| 240 int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction); | |
| 241 | |
| 242 private: | |
| 243 | |
| 244 const NameConverter& converter_; | |
| 245 v8::internal::EmbeddedVector<char, 128> tmp_buffer_; | |
| 246 unsigned int tmp_buffer_pos_; | |
| 247 bool abort_on_unimplemented_; | |
| 248 // Prefixes parsed | |
| 249 byte rex_; | |
| 250 byte operand_size_; | |
| 251 | |
| 252 void setOperandSizePrefix(byte prefix) { | |
| 253 ASSERT_EQ(0x66, prefix); | |
| 254 operand_size_ = prefix; | |
| 255 } | |
| 256 | |
| 257 void setRex(byte rex) { | |
| 258 ASSERT_EQ(0x40, rex & 0xF0); | |
| 259 rex_ = rex; | |
| 260 } | |
| 261 | |
| 262 bool rex() { return rex_ != 0; } | |
| 263 | |
| 264 bool rex_b() { return (rex_ & 0x01) != 0; } | |
| 265 | |
| 266 // Actual number of base register given the low bits and the rex.b state. | |
| 267 int base_reg(int low_bits) { return low_bits | ((rex_ & 0x01) << 3); } | |
| 268 | |
| 269 bool rex_x() { return (rex_ & 0x02) != 0; } | |
| 270 | |
| 271 bool rex_r() { return (rex_ & 0x04) != 0; } | |
| 272 | |
| 273 bool rex_w() { return (rex_ & 0x08) != 0; } | |
| 274 | |
| 275 int operand_size() { | |
| 276 return rex_w() ? 64 : (operand_size_ != 0) ? 16 : 32; | |
| 277 } | |
| 278 | |
| 279 char operand_size_code() { | |
| 280 return rex_w() ? 'q' : (operand_size_ != 0) ? 'w' : 'l'; | |
| 281 } | |
| 282 | |
| 283 const char* NameOfCPURegister(int reg) const { | |
| 284 return converter_.NameOfCPURegister(reg); | |
| 285 } | |
| 286 | |
| 287 const char* NameOfByteCPURegister(int reg) const { | |
| 288 return converter_.NameOfByteCPURegister(reg); | |
| 289 } | |
| 290 | |
| 291 const char* NameOfXMMRegister(int reg) const { | |
| 292 return converter_.NameOfXMMRegister(reg); | |
| 293 } | |
| 294 | |
| 295 const char* NameOfAddress(byte* addr) const { | |
| 296 return converter_.NameOfAddress(addr); | |
| 297 } | |
| 298 | |
| 299 // Disassembler helper functions. | |
| 300 void get_modrm(byte data, | |
| 301 int* mod, | |
| 302 int* regop, | |
| 303 int* rm) { | |
| 304 *mod = (data >> 6) & 3; | |
| 305 *regop = ((data & 0x38) >> 3) | (rex_r() ? 8 : 0); | |
| 306 *rm = (data & 7) | (rex_b() ? 8 : 0); | |
| 307 } | |
| 308 | |
| 309 void get_sib(byte data, | |
| 310 int* scale, | |
| 311 int* index, | |
| 312 int* base) { | |
| 313 *scale = (data >> 6) & 3; | |
| 314 *index = ((data >> 3) & 7) | (rex_x() ? 8 : 0); | |
| 315 *base = data & 7 | (rex_b() ? 8 : 0); | |
| 316 } | |
| 317 | |
| 318 typedef const char* (DisassemblerX64::*RegisterNameMapping)(int reg) const; | |
| 319 | |
| 320 int PrintRightOperandHelper(byte* modrmp, | |
| 321 RegisterNameMapping register_name); | |
| 322 int PrintRightOperand(byte* modrmp); | |
| 323 int PrintRightByteOperand(byte* modrmp); | |
| 324 int PrintOperands(const char* mnem, | |
| 325 OperandOrder op_order, | |
| 326 byte* data); | |
| 327 int PrintImmediateOp(byte* data); | |
| 328 int F7Instruction(byte* data); | |
| 329 int D1D3C1Instruction(byte* data); | |
| 330 int JumpShort(byte* data); | |
| 331 int JumpConditional(byte* data); | |
| 332 int JumpConditionalShort(byte* data); | |
| 333 int SetCC(byte* data); | |
| 334 int FPUInstruction(byte* data); | |
| 335 void AppendToBuffer(const char* format, ...); | |
| 336 | |
| 337 void UnimplementedInstruction() { | |
| 338 if (abort_on_unimplemented_) { | |
| 339 UNIMPLEMENTED(); | |
| 340 } else { | |
| 341 AppendToBuffer("'Unimplemented Instruction'"); | |
| 342 } | |
| 343 } | |
| 344 }; | |
| 345 | |
| 346 | |
| 347 void DisassemblerX64::AppendToBuffer(const char* format, ...) { | |
| 348 v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_; | |
| 349 va_list args; | |
| 350 va_start(args, format); | |
| 351 int result = v8::internal::OS::VSNPrintF(buf, format, args); | |
| 352 va_end(args); | |
| 353 tmp_buffer_pos_ += result; | |
| 354 } | |
| 355 | |
| 356 | |
| 357 int DisassemblerX64::PrintRightOperandHelper( | |
| 358 byte* modrmp, | |
| 359 RegisterNameMapping register_name) { | |
| 360 int mod, regop, rm; | |
| 361 get_modrm(*modrmp, &mod, ®op, &rm); | |
| 362 switch (mod) { | |
| 363 case 0: | |
| 364 if ((rm & 7) == 5) { | |
| 365 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 1); | |
| 366 AppendToBuffer("[0x%x]", disp); | |
| 367 return 5; | |
| 368 } else if ((rm & 7) == 4) { | |
| 369 // Codes for SIB byte. | |
| 370 byte sib = *(modrmp + 1); | |
| 371 int scale, index, base; | |
| 372 get_sib(sib, &scale, &index, &base); | |
| 373 if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) { | |
| 374 // index == rsp means no index. Only use sib byte with no index for | |
| 375 // rsp and r12 base. | |
| 376 AppendToBuffer("[%s]", (this->*register_name)(base)); | |
| 377 return 2; | |
| 378 } else if (base == 5) { | |
| 379 // base == rbp means no base register (when mod == 0). | |
| 380 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2); | |
| 381 AppendToBuffer("[%s*%d+0x%x]", | |
| 382 (this->*register_name)(index), | |
| 383 1 << scale, disp); | |
| 384 return 6; | |
| 385 } else if (index != 4 && base != 5) { | |
| 386 // [base+index*scale] | |
| 387 AppendToBuffer("[%s+%s*%d]", | |
| 388 (this->*register_name)(base), | |
| 389 (this->*register_name)(index), | |
| 390 1 << scale); | |
| 391 return 2; | |
| 392 } else { | |
| 393 UnimplementedInstruction(); | |
| 394 return 1; | |
| 395 } | |
| 396 } else { | |
| 397 AppendToBuffer("[%s]", (this->*register_name)(rm)); | |
| 398 return 1; | |
| 399 } | |
| 400 break; | |
| 401 case 1: // fall through | |
| 402 case 2: | |
| 403 if ((rm & 7) == 4) { | |
| 404 byte sib = *(modrmp + 1); | |
| 405 int scale, index, base; | |
| 406 get_sib(sib, &scale, &index, &base); | |
| 407 int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 2) | |
| 408 : *reinterpret_cast<char*>(modrmp + 2); | |
| 409 if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) { | |
| 410 if (-disp > 0) { | |
| 411 AppendToBuffer("[%s-0x%x]", (this->*register_name)(base), -disp); | |
| 412 } else { | |
| 413 AppendToBuffer("[%s+0x%x]", (this->*register_name)(base), disp); | |
| 414 } | |
| 415 } else { | |
| 416 if (-disp > 0) { | |
| 417 AppendToBuffer("[%s+%s*%d-0x%x]", | |
| 418 (this->*register_name)(base), | |
| 419 (this->*register_name)(index), | |
| 420 1 << scale, | |
| 421 -disp); | |
| 422 } else { | |
| 423 AppendToBuffer("[%s+%s*%d+0x%x]", | |
| 424 (this->*register_name)(base), | |
| 425 (this->*register_name)(index), | |
| 426 1 << scale, | |
| 427 disp); | |
| 428 } | |
| 429 } | |
| 430 return mod == 2 ? 6 : 3; | |
| 431 } else { | |
| 432 // No sib. | |
| 433 int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 1) | |
| 434 : *reinterpret_cast<char*>(modrmp + 1); | |
| 435 if (-disp > 0) { | |
| 436 AppendToBuffer("[%s-0x%x]", (this->*register_name)(rm), -disp); | |
| 437 } else { | |
| 438 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp); | |
| 439 } | |
| 440 return (mod == 2) ? 5 : 2; | |
| 441 } | |
| 442 break; | |
| 443 case 3: | |
| 444 AppendToBuffer("%s", (this->*register_name)(rm)); | |
| 445 return 1; | |
| 446 default: | |
| 447 UnimplementedInstruction(); | |
| 448 return 1; | |
| 449 } | |
| 450 UNREACHABLE(); | |
| 451 } | |
| 452 | |
| 453 | |
| 454 int DisassemblerX64::PrintRightOperand(byte* modrmp) { | |
| 455 return PrintRightOperandHelper(modrmp, | |
| 456 &DisassemblerX64::NameOfCPURegister); | |
| 457 } | |
| 458 | |
| 459 | |
| 460 int DisassemblerX64::PrintRightByteOperand(byte* modrmp) { | |
| 461 return PrintRightOperandHelper(modrmp, | |
| 462 &DisassemblerX64::NameOfByteCPURegister); | |
| 463 } | |
| 464 | |
| 465 | |
| 466 // Returns number of bytes used including the current *data. | |
| 467 // Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'. | |
| 468 int DisassemblerX64::PrintOperands(const char* mnem, | |
| 469 OperandOrder op_order, | |
| 470 byte* data) { | |
| 471 byte modrm = *data; | |
| 472 int mod, regop, rm; | |
| 473 get_modrm(modrm, &mod, ®op, &rm); | |
| 474 int advance = 0; | |
| 475 switch (op_order) { | |
| 476 case REG_OPER_OP_ORDER: { | |
| 477 AppendToBuffer("%s%c %s,", | |
| 478 mnem, | |
| 479 operand_size_code(), | |
| 480 NameOfCPURegister(regop)); | |
| 481 advance = PrintRightOperand(data); | |
| 482 break; | |
| 483 } | |
| 484 case OPER_REG_OP_ORDER: { | |
| 485 AppendToBuffer("%s%c ", mnem, operand_size_code()); | |
| 486 advance = PrintRightOperand(data); | |
| 487 AppendToBuffer(",%s", NameOfCPURegister(regop)); | |
| 488 break; | |
| 489 } | |
| 490 default: | |
| 491 UNREACHABLE(); | |
| 492 break; | |
| 493 } | |
| 494 return advance; | |
| 495 } | |
| 496 | |
| 497 | |
| 498 // Returns number of bytes used by machine instruction, including *data byte. | |
| 499 // Writes immediate instructions to 'tmp_buffer_'. | |
| 500 int DisassemblerX64::PrintImmediateOp(byte* data) { | |
| 501 bool sign_extension_bit = (*data & 0x02) != 0; | |
| 502 byte modrm = *(data + 1); | |
| 503 int mod, regop, rm; | |
| 504 get_modrm(modrm, &mod, ®op, &rm); | |
| 505 const char* mnem = "Imm???"; | |
| 506 switch (regop) { | |
| 507 case 0: | |
| 508 mnem = "add"; | |
| 509 break; | |
| 510 case 1: | |
| 511 mnem = "or"; | |
| 512 break; | |
| 513 case 2: | |
| 514 mnem = "adc"; | |
| 515 break; | |
| 516 case 4: | |
| 517 mnem = "and"; | |
| 518 break; | |
| 519 case 5: | |
| 520 mnem = "sub"; | |
| 521 break; | |
| 522 case 6: | |
| 523 mnem = "xor"; | |
| 524 break; | |
| 525 case 7: | |
| 526 mnem = "cmp"; | |
| 527 break; | |
| 528 default: | |
| 529 UnimplementedInstruction(); | |
| 530 } | |
| 531 AppendToBuffer("%s ", mnem); | |
| 532 int count = PrintRightOperand(data + 1); | |
| 533 if (sign_extension_bit) { | |
| 534 AppendToBuffer(",0x%x", *(data + 1 + count)); | |
| 535 return 1 + count + 1 /*int8*/; | |
| 536 } else { | |
| 537 AppendToBuffer(",0x%x", *reinterpret_cast<int32_t*>(data + 1 + count)); | |
| 538 return 1 + count + 4 /*int32_t*/; | |
| 539 } | |
| 540 } | |
| 541 | |
| 542 | |
| 543 // Returns number of bytes used, including *data. | |
| 544 int DisassemblerX64::F7Instruction(byte* data) { | |
| 545 assert(*data == 0xF7); | |
| 546 byte modrm = *(data + 1); | |
| 547 int mod, regop, rm; | |
| 548 get_modrm(modrm, &mod, ®op, &rm); | |
| 549 if (mod == 3 && regop != 0) { | |
| 550 const char* mnem = NULL; | |
| 551 switch (regop) { | |
| 552 case 2: | |
| 553 mnem = "not"; | |
| 554 break; | |
| 555 case 3: | |
| 556 mnem = "neg"; | |
| 557 break; | |
| 558 case 4: | |
| 559 mnem = "mul"; | |
| 560 break; | |
| 561 case 7: | |
| 562 mnem = "idiv"; | |
| 563 break; | |
| 564 default: | |
| 565 UnimplementedInstruction(); | |
| 566 } | |
| 567 AppendToBuffer("%s%c %s", | |
| 568 mnem, | |
| 569 operand_size_code(), | |
| 570 NameOfCPURegister(rm)); | |
| 571 return 2; | |
| 572 } else if (mod == 3 && regop == 0) { | |
| 573 int32_t imm = *reinterpret_cast<int32_t*>(data + 2); | |
| 574 AppendToBuffer("test%c %s,0x%x", | |
| 575 operand_size_code(), | |
| 576 NameOfCPURegister(rm), | |
| 577 imm); | |
| 578 return 6; | |
| 579 } else if (regop == 0) { | |
| 580 AppendToBuffer("test%c ", operand_size_code()); | |
| 581 int count = PrintRightOperand(data + 1); | |
| 582 int32_t imm = *reinterpret_cast<int32_t*>(data + 1 + count); | |
| 583 AppendToBuffer(",0x%x", imm); | |
| 584 return 1 + count + 4 /*int32_t*/; | |
| 585 } else { | |
| 586 UnimplementedInstruction(); | |
| 587 return 2; | |
| 588 } | |
| 589 } | |
| 590 | |
| 591 | |
| 592 int DisassemblerX64::D1D3C1Instruction(byte* data) { | |
| 593 byte op = *data; | |
| 594 assert(op == 0xD1 || op == 0xD3 || op == 0xC1); | |
| 595 byte modrm = *(data + 1); | |
| 596 int mod, regop, rm; | |
| 597 get_modrm(modrm, &mod, ®op, &rm); | |
| 598 ASSERT(regop < 8); | |
| 599 int imm8 = -1; | |
| 600 int num_bytes = 2; | |
| 601 if (mod == 3) { | |
| 602 const char* mnem = NULL; | |
| 603 if (op == 0xD1) { | |
| 604 imm8 = 1; | |
| 605 switch (regop) { | |
| 606 case 2: | |
| 607 mnem = "rcl"; | |
| 608 break; | |
| 609 case 7: | |
| 610 mnem = "sar"; | |
| 611 break; | |
| 612 case 4: | |
| 613 mnem = "shl"; | |
| 614 break; | |
| 615 default: | |
| 616 UnimplementedInstruction(); | |
| 617 } | |
| 618 } else if (op == 0xC1) { | |
| 619 imm8 = *(data + 2); | |
| 620 num_bytes = 3; | |
| 621 switch (regop) { | |
| 622 case 2: | |
| 623 mnem = "rcl"; | |
| 624 break; | |
| 625 case 4: | |
| 626 mnem = "shl"; | |
| 627 break; | |
| 628 case 5: | |
| 629 mnem = "shr"; | |
| 630 break; | |
| 631 case 7: | |
| 632 mnem = "sar"; | |
| 633 break; | |
| 634 default: | |
| 635 UnimplementedInstruction(); | |
| 636 } | |
| 637 } else if (op == 0xD3) { | |
| 638 switch (regop) { | |
| 639 case 4: | |
| 640 mnem = "shl"; | |
| 641 break; | |
| 642 case 5: | |
| 643 mnem = "shr"; | |
| 644 break; | |
| 645 case 7: | |
| 646 mnem = "sar"; | |
| 647 break; | |
| 648 default: | |
| 649 UnimplementedInstruction(); | |
| 650 } | |
| 651 } | |
| 652 assert(mnem != NULL); | |
| 653 AppendToBuffer("%s%c %s,", | |
| 654 mnem, | |
| 655 operand_size_code(), | |
| 656 NameOfCPURegister(rm)); | |
| 657 if (imm8 > 0) { | |
| 658 AppendToBuffer("%d", imm8); | |
| 659 } else { | |
| 660 AppendToBuffer("cl"); | |
| 661 } | |
| 662 } else { | |
| 663 UnimplementedInstruction(); | |
| 664 } | |
| 665 return num_bytes; | |
| 666 } | |
| 667 | |
| 668 | |
| 669 // Returns number of bytes used, including *data. | |
| 670 int DisassemblerX64::JumpShort(byte* data) { | |
| 671 assert(*data == 0xEB); | |
| 672 byte b = *(data + 1); | |
| 673 byte* dest = data + static_cast<int8_t>(b) + 2; | |
| 674 AppendToBuffer("jmp %s", NameOfAddress(dest)); | |
| 675 return 2; | |
| 676 } | |
| 677 | |
| 678 | |
| 679 // Returns number of bytes used, including *data. | |
| 680 int DisassemblerX64::JumpConditional(byte* data) { | |
| 681 assert(*data == 0x0F); | |
| 682 byte cond = *(data + 1) & 0x0F; | |
| 683 byte* dest = data + *reinterpret_cast<int32_t*>(data + 2) + 6; | |
| 684 const char* mnem = conditional_code_suffix[cond]; | |
| 685 AppendToBuffer("j%s %s", mnem, NameOfAddress(dest)); | |
| 686 return 6; // includes 0x0F | |
| 687 } | |
| 688 | |
| 689 | |
| 690 // Returns number of bytes used, including *data. | |
| 691 int DisassemblerX64::JumpConditionalShort(byte* data) { | |
| 692 byte cond = *data & 0x0F; | |
| 693 byte b = *(data + 1); | |
| 694 byte* dest = data + static_cast<int8_t>(b) + 2; | |
| 695 const char* mnem = conditional_code_suffix[cond]; | |
| 696 AppendToBuffer("j%s %s", mnem, NameOfAddress(dest)); | |
| 697 return 2; | |
| 698 } | |
| 699 | |
| 700 | |
| 701 // Returns number of bytes used, including *data. | |
| 702 int DisassemblerX64::SetCC(byte* data) { | |
| 703 assert(*data == 0x0F); | |
| 704 byte cond = *(data + 1) & 0x0F; | |
| 705 const char* mnem = conditional_code_suffix[cond]; | |
| 706 AppendToBuffer("set%s%c ", mnem, operand_size_code()); | |
| 707 PrintRightByteOperand(data + 2); | |
| 708 return 3; // includes 0x0F | |
| 709 } | |
| 710 | |
| 711 | |
| 712 // Returns number of bytes used, including *data. | |
| 713 int DisassemblerX64::FPUInstruction(byte* data) { | |
| 714 byte b1 = *data; | |
| 715 byte b2 = *(data + 1); | |
| 716 if (b1 == 0xD9) { | |
| 717 const char* mnem = NULL; | |
| 718 switch (b2) { | |
| 719 case 0xE8: | |
| 720 mnem = "fld1"; | |
| 721 break; | |
| 722 case 0xEE: | |
| 723 mnem = "fldz"; | |
| 724 break; | |
| 725 case 0xE1: | |
| 726 mnem = "fabs"; | |
| 727 break; | |
| 728 case 0xE0: | |
| 729 mnem = "fchs"; | |
| 730 break; | |
| 731 case 0xF8: | |
| 732 mnem = "fprem"; | |
| 733 break; | |
| 734 case 0xF5: | |
| 735 mnem = "fprem1"; | |
| 736 break; | |
| 737 case 0xF7: | |
| 738 mnem = "fincstp"; | |
| 739 break; | |
| 740 case 0xE4: | |
| 741 mnem = "ftst"; | |
| 742 break; | |
| 743 } | |
| 744 if (mnem != NULL) { | |
| 745 AppendToBuffer("%s", mnem); | |
| 746 return 2; | |
| 747 } else if ((b2 & 0xF8) == 0xC8) { | |
| 748 AppendToBuffer("fxch st%d", b2 & 0x7); | |
| 749 return 2; | |
| 750 } else { | |
| 751 int mod, regop, rm; | |
| 752 get_modrm(*(data + 1), &mod, ®op, &rm); | |
| 753 const char* mnem = "?"; | |
| 754 switch (regop) { | |
| 755 case 0: | |
| 756 mnem = "fld_s"; | |
| 757 break; | |
| 758 case 3: | |
| 759 mnem = "fstp_s"; | |
| 760 break; | |
| 761 default: | |
| 762 UnimplementedInstruction(); | |
| 763 } | |
| 764 AppendToBuffer("%s ", mnem); | |
| 765 int count = PrintRightOperand(data + 1); | |
| 766 return count + 1; | |
| 767 } | |
| 768 } else if (b1 == 0xDD) { | |
| 769 if ((b2 & 0xF8) == 0xC0) { | |
| 770 AppendToBuffer("ffree st%d", b2 & 0x7); | |
| 771 return 2; | |
| 772 } else { | |
| 773 int mod, regop, rm; | |
| 774 get_modrm(*(data + 1), &mod, ®op, &rm); | |
| 775 const char* mnem = "?"; | |
| 776 switch (regop) { | |
| 777 case 0: | |
| 778 mnem = "fld_d"; | |
| 779 break; | |
| 780 case 3: | |
| 781 mnem = "fstp_d"; | |
| 782 break; | |
| 783 default: | |
| 784 UnimplementedInstruction(); | |
| 785 } | |
| 786 AppendToBuffer("%s ", mnem); | |
| 787 int count = PrintRightOperand(data + 1); | |
| 788 return count + 1; | |
| 789 } | |
| 790 } else if (b1 == 0xDB) { | |
| 791 int mod, regop, rm; | |
| 792 get_modrm(*(data + 1), &mod, ®op, &rm); | |
| 793 const char* mnem = "?"; | |
| 794 switch (regop) { | |
| 795 case 0: | |
| 796 mnem = "fild_s"; | |
| 797 break; | |
| 798 case 2: | |
| 799 mnem = "fist_s"; | |
| 800 break; | |
| 801 case 3: | |
| 802 mnem = "fistp_s"; | |
| 803 break; | |
| 804 default: | |
| 805 UnimplementedInstruction(); | |
| 806 } | |
| 807 AppendToBuffer("%s ", mnem); | |
| 808 int count = PrintRightOperand(data + 1); | |
| 809 return count + 1; | |
| 810 } else if (b1 == 0xDF) { | |
| 811 if (b2 == 0xE0) { | |
| 812 AppendToBuffer("fnstsw_ax"); | |
| 813 return 2; | |
| 814 } | |
| 815 int mod, regop, rm; | |
| 816 get_modrm(*(data + 1), &mod, ®op, &rm); | |
| 817 const char* mnem = "?"; | |
| 818 switch (regop) { | |
| 819 case 5: | |
| 820 mnem = "fild_d"; | |
| 821 break; | |
| 822 case 7: | |
| 823 mnem = "fistp_d"; | |
| 824 break; | |
| 825 default: | |
| 826 UnimplementedInstruction(); | |
| 827 } | |
| 828 AppendToBuffer("%s ", mnem); | |
| 829 int count = PrintRightOperand(data + 1); | |
| 830 return count + 1; | |
| 831 } else if (b1 == 0xDC || b1 == 0xDE) { | |
| 832 bool is_pop = (b1 == 0xDE); | |
| 833 if (is_pop && b2 == 0xD9) { | |
| 834 AppendToBuffer("fcompp"); | |
| 835 return 2; | |
| 836 } | |
| 837 const char* mnem = "FP0xDC"; | |
| 838 switch (b2 & 0xF8) { | |
| 839 case 0xC0: | |
| 840 mnem = "fadd"; | |
| 841 break; | |
| 842 case 0xE8: | |
| 843 mnem = "fsub"; | |
| 844 break; | |
| 845 case 0xC8: | |
| 846 mnem = "fmul"; | |
| 847 break; | |
| 848 case 0xF8: | |
| 849 mnem = "fdiv"; | |
| 850 break; | |
| 851 default: | |
| 852 UnimplementedInstruction(); | |
| 853 } | |
| 854 AppendToBuffer("%s%s st%d", mnem, is_pop ? "p" : "", b2 & 0x7); | |
| 855 return 2; | |
| 856 } else if (b1 == 0xDA && b2 == 0xE9) { | |
| 857 const char* mnem = "fucompp"; | |
| 858 AppendToBuffer("%s", mnem); | |
| 859 return 2; | |
| 860 } | |
| 861 AppendToBuffer("Unknown FP instruction"); | |
| 862 return 2; | |
| 863 } | |
| 864 | |
| 865 // Mnemonics for instructions 0xF0 byte. | |
| 866 // Returns NULL if the instruction is not handled here. | |
| 867 static const char* F0Mnem(byte f0byte) { | |
| 868 switch (f0byte) { | |
| 869 case 0x1F: | |
| 870 return "nop"; | |
| 871 case 0x31: | |
| 872 return "rdtsc"; | |
| 873 case 0xA2: | |
| 874 return "cpuid"; | |
| 875 case 0xBE: | |
| 876 return "movsxb"; | |
| 877 case 0xBF: | |
| 878 return "movsxw"; | |
| 879 case 0xB6: | |
| 880 return "movzxb"; | |
| 881 case 0xB7: | |
| 882 return "movzxw"; | |
| 883 case 0xAF: | |
| 884 return "imul"; | |
| 885 case 0xA5: | |
| 886 return "shld"; | |
| 887 case 0xAD: | |
| 888 return "shrd"; | |
| 889 case 0xAB: | |
| 890 return "bts"; | |
| 891 default: | |
| 892 return NULL; | |
| 893 } | |
| 894 } | |
| 895 | |
| 896 // Disassembled instruction '*instr' and writes it into 'out_buffer'. | |
| 897 int DisassemblerX64::InstructionDecode(v8::internal::Vector<char> out_buffer, | |
| 898 byte* instr) { | |
| 899 tmp_buffer_pos_ = 0; // starting to write as position 0 | |
| 900 byte* data = instr; | |
| 901 bool processed = true; // Will be set to false if the current instruction | |
| 902 // is not in 'instructions' table. | |
| 903 byte current; | |
| 904 | |
| 905 // Scan for prefixes. | |
| 906 while (true) { | |
| 907 current = *data; | |
| 908 if (current == 0x66) { | |
| 909 setOperandSizePrefix(current); | |
| 910 data++; | |
| 911 } else if ((current & 0xF0) == 0x40) { | |
| 912 setRex(current); | |
| 913 if (rex_w()) AppendToBuffer("REX.W "); | |
| 914 data++; | |
| 915 } else { | |
| 916 break; | |
| 917 } | |
| 918 } | |
| 919 | |
| 920 const InstructionDesc& idesc = instruction_table.Get(current); | |
| 921 switch (idesc.type) { | |
| 922 case ZERO_OPERANDS_INSTR: | |
| 923 AppendToBuffer(idesc.mnem); | |
| 924 data++; | |
| 925 break; | |
| 926 | |
| 927 case TWO_OPERANDS_INSTR: | |
| 928 data++; | |
| 929 data += PrintOperands(idesc.mnem, idesc.op_order_, data); | |
| 930 break; | |
| 931 | |
| 932 case JUMP_CONDITIONAL_SHORT_INSTR: | |
| 933 data += JumpConditionalShort(data); | |
| 934 break; | |
| 935 | |
| 936 case REGISTER_INSTR: | |
| 937 AppendToBuffer("%s%c %s", | |
| 938 idesc.mnem, | |
| 939 operand_size_code(), | |
| 940 NameOfCPURegister(base_reg(current & 0x07))); | |
| 941 data++; | |
| 942 break; | |
| 943 case PUSHPOP_INSTR: | |
| 944 AppendToBuffer("%s %s", | |
| 945 idesc.mnem, | |
| 946 NameOfCPURegister(base_reg(current & 0x07))); | |
| 947 data++; | |
| 948 break; | |
| 949 case MOVE_REG_INSTR: { | |
| 950 byte* addr = NULL; | |
| 951 switch (operand_size()) { | |
| 952 case 16: | |
| 953 addr = reinterpret_cast<byte*>(*reinterpret_cast<int16_t*>(data + 1)); | |
| 954 data += 3; | |
| 955 break; | |
| 956 case 32: | |
| 957 addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1)); | |
| 958 data += 5; | |
| 959 break; | |
| 960 case 64: | |
| 961 addr = reinterpret_cast<byte*>(*reinterpret_cast<int64_t*>(data + 1)); | |
| 962 data += 9; | |
| 963 break; | |
| 964 default: | |
| 965 UNREACHABLE(); | |
| 966 } | |
| 967 AppendToBuffer("mov%c %s,%s", | |
| 968 operand_size_code(), | |
| 969 NameOfCPURegister(base_reg(current & 0x07)), | |
| 970 NameOfAddress(addr)); | |
| 971 break; | |
| 972 } | |
| 973 | |
| 974 case CALL_JUMP_INSTR: { | |
| 975 byte* addr = data + *reinterpret_cast<int32_t*>(data + 1) + 5; | |
| 976 AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr)); | |
| 977 data += 5; | |
| 978 break; | |
| 979 } | |
| 980 | |
| 981 case SHORT_IMMEDIATE_INSTR: { | |
| 982 byte* addr = | |
| 983 reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1)); | |
| 984 AppendToBuffer("%s rax, %s", idesc.mnem, NameOfAddress(addr)); | |
| 985 data += 5; | |
| 986 break; | |
| 987 } | |
| 988 | |
| 989 case NO_INSTR: | |
| 990 processed = false; | |
| 991 break; | |
| 992 | |
| 993 default: | |
| 994 UNIMPLEMENTED(); // This type is not implemented. | |
| 995 } | |
| 996 //---------------------------- | |
|
William Hesse
2009/07/07 12:30:59
This isn't really standard. Couldn't you put an e
| |
| 997 if (!processed) { | |
| 998 switch (*data) { | |
| 999 case 0xC2: | |
| 1000 AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data + 1)); | |
| 1001 data += 3; | |
| 1002 break; | |
| 1003 | |
| 1004 case 0x69: // fall through | |
| 1005 case 0x6B: { | |
| 1006 int mod, regop, rm; | |
| 1007 get_modrm(*(data + 1), &mod, ®op, &rm); | |
| 1008 int32_t imm = *data == 0x6B ? *(data + 2) | |
| 1009 : *reinterpret_cast<int32_t*>(data + 2); | |
| 1010 AppendToBuffer("imul %s,%s,0x%x", NameOfCPURegister(regop), | |
| 1011 NameOfCPURegister(rm), imm); | |
| 1012 data += 2 + (*data == 0x6B ? 1 : 4); | |
| 1013 } | |
| 1014 break; | |
| 1015 | |
| 1016 case 0xF6: { | |
| 1017 int mod, regop, rm; | |
| 1018 get_modrm(*(data + 1), &mod, ®op, &rm); | |
| 1019 if (mod == 3 && regop == 0) { | |
| 1020 AppendToBuffer("testb %s,%d", NameOfCPURegister(rm), *(data + 2)); | |
| 1021 } else { | |
| 1022 UnimplementedInstruction(); | |
| 1023 } | |
| 1024 data += 3; | |
| 1025 } | |
| 1026 break; | |
| 1027 | |
| 1028 case 0x81: // fall through | |
| 1029 case 0x83: // 0x81 with sign extension bit set | |
| 1030 data += PrintImmediateOp(data); | |
| 1031 break; | |
| 1032 | |
| 1033 case 0x0F: { | |
| 1034 byte f0byte = *(data + 1); | |
| 1035 const char* f0mnem = F0Mnem(f0byte); | |
| 1036 if (f0byte == 0x1F) { | |
| 1037 data += 1; | |
| 1038 byte modrm = *data; | |
| 1039 data += 1; | |
| 1040 if (((modrm >> 3) & 7) == 4) { | |
| 1041 // SIB byte present. | |
| 1042 data += 1; | |
| 1043 } | |
| 1044 int mod = modrm >> 6; | |
| 1045 if (mod == 1) { | |
| 1046 // Byte displacement. | |
| 1047 data += 1; | |
| 1048 } else if (mod == 2) { | |
| 1049 // 32-bit displacement. | |
| 1050 data += 4; | |
| 1051 } | |
| 1052 AppendToBuffer("nop"); | |
| 1053 } else if (f0byte == 0xA2 || f0byte == 0x31) { | |
| 1054 AppendToBuffer("%s", f0mnem); | |
| 1055 data += 2; | |
| 1056 } else if ((f0byte & 0xF0) == 0x80) { | |
| 1057 data += JumpConditional(data); | |
| 1058 } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 || f0byte | |
| 1059 == 0xB7 || f0byte == 0xAF) { | |
| 1060 data += 2; | |
| 1061 data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data); | |
| 1062 } else if ((f0byte & 0xF0) == 0x90) { | |
| 1063 data += SetCC(data); | |
| 1064 } else { | |
| 1065 data += 2; | |
| 1066 if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) { | |
| 1067 // shrd, shld, bts | |
| 1068 AppendToBuffer("%s ", f0mnem); | |
| 1069 int mod, regop, rm; | |
| 1070 get_modrm(*data, &mod, ®op, &rm); | |
| 1071 data += PrintRightOperand(data); | |
| 1072 if (f0byte == 0xAB) { | |
| 1073 AppendToBuffer(",%s", NameOfCPURegister(regop)); | |
| 1074 } else { | |
| 1075 AppendToBuffer(",%s,cl", NameOfCPURegister(regop)); | |
| 1076 } | |
| 1077 } else { | |
| 1078 UnimplementedInstruction(); | |
| 1079 } | |
| 1080 } | |
| 1081 } | |
| 1082 break; | |
| 1083 | |
| 1084 case 0x8F: { | |
| 1085 data++; | |
| 1086 int mod, regop, rm; | |
| 1087 get_modrm(*data, &mod, ®op, &rm); | |
| 1088 if (regop == 0) { | |
| 1089 AppendToBuffer("pop "); | |
| 1090 data += PrintRightOperand(data); | |
| 1091 } | |
| 1092 } | |
| 1093 break; | |
| 1094 | |
| 1095 case 0xFF: { | |
| 1096 data++; | |
| 1097 int mod, regop, rm; | |
| 1098 get_modrm(*data, &mod, ®op, &rm); | |
| 1099 const char* mnem = NULL; | |
| 1100 switch (regop) { | |
| 1101 case 0: | |
| 1102 mnem = "inc"; | |
| 1103 break; | |
| 1104 case 1: | |
| 1105 mnem = "dec"; | |
| 1106 break; | |
| 1107 case 2: | |
| 1108 mnem = "call"; | |
| 1109 break; | |
| 1110 case 4: | |
| 1111 mnem = "jmp"; | |
| 1112 break; | |
| 1113 case 6: | |
| 1114 mnem = "push"; | |
| 1115 break; | |
| 1116 default: | |
| 1117 mnem = "???"; | |
| 1118 } | |
| 1119 AppendToBuffer(((regop <= 1) ? "%s%c " : "%s "), | |
| 1120 mnem, | |
| 1121 operand_size_code()); | |
| 1122 data += PrintRightOperand(data); | |
| 1123 } | |
| 1124 break; | |
| 1125 | |
| 1126 case 0xC7: // imm32, fall through | |
| 1127 case 0xC6: // imm8 | |
| 1128 { | |
| 1129 bool is_byte = *data == 0xC6; | |
| 1130 data++; | |
| 1131 | |
| 1132 AppendToBuffer("mov%c ", is_byte ? 'b' : operand_size_code()); | |
| 1133 data += PrintRightOperand(data); | |
| 1134 int32_t imm = is_byte ? *data : *reinterpret_cast<int32_t*>(data); | |
| 1135 AppendToBuffer(",0x%x", imm); | |
| 1136 data += is_byte ? 1 : 4; | |
| 1137 } | |
| 1138 break; | |
| 1139 | |
| 1140 case 0x80: { | |
| 1141 data++; | |
| 1142 AppendToBuffer("cmpb "); | |
| 1143 data += PrintRightOperand(data); | |
| 1144 int32_t imm = *data; | |
| 1145 AppendToBuffer(",0x%x", imm); | |
| 1146 data++; | |
| 1147 } | |
| 1148 break; | |
| 1149 | |
| 1150 case 0x88: // 8bit, fall through | |
| 1151 case 0x89: // 32bit | |
| 1152 { | |
| 1153 bool is_byte = *data == 0x88; | |
| 1154 int mod, regop, rm; | |
| 1155 data++; | |
| 1156 get_modrm(*data, &mod, ®op, &rm); | |
| 1157 AppendToBuffer("mov%c ", is_byte ? 'b' : operand_size_code()); | |
| 1158 data += PrintRightOperand(data); | |
| 1159 AppendToBuffer(",%s", NameOfCPURegister(regop)); | |
| 1160 } | |
| 1161 break; | |
| 1162 | |
| 1163 case 0x90: | |
| 1164 case 0x91: | |
| 1165 case 0x92: | |
| 1166 case 0x93: | |
| 1167 case 0x94: | |
| 1168 case 0x95: | |
| 1169 case 0x96: | |
| 1170 case 0x97: { | |
| 1171 int reg = current & 0x7 | (rex_b() ? 8 : 0); | |
| 1172 if (reg == 0) { | |
| 1173 AppendToBuffer("nop"); // Common name for xchg rax,rax. | |
| 1174 } else { | |
| 1175 AppendToBuffer("xchg%c rax, %s", | |
| 1176 operand_size_code(), | |
| 1177 NameOfByteCPURegister(reg)); | |
| 1178 } | |
| 1179 } | |
| 1180 | |
| 1181 | |
| 1182 case 0xFE: { | |
| 1183 data++; | |
| 1184 int mod, regop, rm; | |
| 1185 get_modrm(*data, &mod, ®op, &rm); | |
| 1186 if (mod == 3 && regop == 1) { | |
| 1187 AppendToBuffer("decb %s", NameOfCPURegister(rm)); | |
| 1188 } else { | |
| 1189 UnimplementedInstruction(); | |
| 1190 } | |
| 1191 data++; | |
| 1192 } | |
| 1193 break; | |
| 1194 | |
| 1195 case 0x68: | |
| 1196 AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data + 1)); | |
| 1197 data += 5; | |
| 1198 break; | |
| 1199 | |
| 1200 case 0x6A: | |
| 1201 AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1)); | |
| 1202 data += 2; | |
| 1203 break; | |
| 1204 | |
| 1205 case 0xA8: | |
| 1206 AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data + 1)); | |
| 1207 data += 2; | |
| 1208 break; | |
| 1209 | |
| 1210 case 0xA9: | |
| 1211 AppendToBuffer("test%c rax,0x%x", // CHECKME! | |
| 1212 operand_size_code(), | |
| 1213 *reinterpret_cast<int32_t*>(data + 1)); | |
| 1214 data += 5; | |
| 1215 break; | |
| 1216 | |
| 1217 case 0xD1: // fall through | |
| 1218 case 0xD3: // fall through | |
| 1219 case 0xC1: | |
| 1220 data += D1D3C1Instruction(data); | |
| 1221 break; | |
| 1222 | |
| 1223 case 0xD9: // fall through | |
| 1224 case 0xDA: // fall through | |
| 1225 case 0xDB: // fall through | |
| 1226 case 0xDC: // fall through | |
| 1227 case 0xDD: // fall through | |
| 1228 case 0xDE: // fall through | |
| 1229 case 0xDF: | |
| 1230 data += FPUInstruction(data); | |
| 1231 break; | |
| 1232 | |
| 1233 case 0xEB: | |
| 1234 data += JumpShort(data); | |
| 1235 break; | |
| 1236 | |
| 1237 case 0xF2: | |
| 1238 if (*(data + 1) == 0x0F) { | |
| 1239 byte b2 = *(data + 2); | |
| 1240 if (b2 == 0x11) { | |
| 1241 AppendToBuffer("movsd "); | |
| 1242 data += 3; | |
| 1243 int mod, regop, rm; | |
| 1244 get_modrm(*data, &mod, ®op, &rm); | |
| 1245 data += PrintRightOperand(data); | |
| 1246 AppendToBuffer(",%s", NameOfXMMRegister(regop)); | |
| 1247 } else if (b2 == 0x10) { | |
| 1248 data += 3; | |
| 1249 int mod, regop, rm; | |
| 1250 get_modrm(*data, &mod, ®op, &rm); | |
| 1251 AppendToBuffer("movsd %s,", NameOfXMMRegister(regop)); | |
| 1252 data += PrintRightOperand(data); | |
| 1253 } else { | |
| 1254 const char* mnem = "?"; | |
| 1255 switch (b2) { | |
| 1256 case 0x2A: | |
| 1257 mnem = "cvtsi2sd"; | |
| 1258 break; | |
| 1259 case 0x58: | |
| 1260 mnem = "addsd"; | |
| 1261 break; | |
| 1262 case 0x59: | |
| 1263 mnem = "mulsd"; | |
| 1264 break; | |
| 1265 case 0x5C: | |
| 1266 mnem = "subsd"; | |
| 1267 break; | |
| 1268 case 0x5E: | |
| 1269 mnem = "divsd"; | |
| 1270 break; | |
| 1271 } | |
| 1272 data += 3; | |
| 1273 int mod, regop, rm; | |
| 1274 get_modrm(*data, &mod, ®op, &rm); | |
| 1275 if (b2 == 0x2A) { | |
| 1276 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop)); | |
| 1277 data += PrintRightOperand(data); | |
| 1278 } else { | |
| 1279 AppendToBuffer("%s %s,%s", mnem, NameOfXMMRegister(regop), | |
| 1280 NameOfXMMRegister(rm)); | |
| 1281 data++; | |
| 1282 } | |
| 1283 } | |
| 1284 } else { | |
| 1285 UnimplementedInstruction(); | |
| 1286 } | |
| 1287 break; | |
| 1288 | |
| 1289 case 0xF3: | |
| 1290 if (*(data + 1) == 0x0F && *(data + 2) == 0x2C) { | |
| 1291 data += 3; | |
| 1292 data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data); | |
| 1293 } else { | |
| 1294 UnimplementedInstruction(); | |
| 1295 } | |
| 1296 break; | |
| 1297 | |
| 1298 case 0xF7: | |
| 1299 data += F7Instruction(data); | |
| 1300 break; | |
| 1301 | |
| 1302 default: | |
| 1303 UnimplementedInstruction(); | |
| 1304 } | |
| 1305 } | |
| 1306 | |
| 1307 if (tmp_buffer_pos_ < sizeof tmp_buffer_) { | |
| 1308 tmp_buffer_[tmp_buffer_pos_] = '\0'; | |
| 1309 } | |
| 1310 | |
| 1311 int instr_len = data - instr; | |
| 1312 ASSERT(instr_len > 0); // Ensure progress. | |
| 1313 | |
| 1314 int outp = 0; | |
| 1315 // Instruction bytes. | |
| 1316 for (byte* bp = instr; bp < data; bp++) { | |
| 1317 outp += v8::internal::OS::SNPrintF(out_buffer + outp, "%02x", *bp); | |
| 1318 } | |
| 1319 for (int i = 6 - instr_len; i >= 0; i--) { | |
| 1320 outp += v8::internal::OS::SNPrintF(out_buffer + outp, " "); | |
| 1321 } | |
| 1322 | |
| 1323 outp += v8::internal::OS::SNPrintF(out_buffer + outp, " %s", | |
| 1324 tmp_buffer_.start()); | |
| 1325 return instr_len; | |
| 1326 } | |
| 1327 | |
| 1328 //------------------------------------------------------------------------------ | |
| 1329 | |
| 1330 | |
| 1331 static const char* cpu_regs[16] = { | |
| 1332 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", | |
| 1333 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" | |
| 1334 }; | |
| 1335 | |
| 1336 | |
| 1337 static const char* byte_cpu_regs[16] = { | |
| 1338 "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", | |
| 1339 "r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l" | |
| 1340 }; | |
| 1341 | |
| 1342 | |
| 1343 static const char* xmm_regs[16] = { | |
| 1344 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", | |
| 1345 "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" | |
| 1346 }; | |
| 1347 | |
| 1348 | |
| 1349 const char* NameConverter::NameOfAddress(byte* addr) const { | |
| 1350 static v8::internal::EmbeddedVector<char, 32> tmp_buffer; | |
| 1351 v8::internal::OS::SNPrintF(tmp_buffer, "%p", addr); | |
| 1352 return tmp_buffer.start(); | |
| 1353 } | |
| 1354 | |
| 1355 | |
| 1356 const char* NameConverter::NameOfConstant(byte* addr) const { | |
| 1357 return NameOfAddress(addr); | |
| 47 } | 1358 } |
| 48 | 1359 |
| 49 | 1360 |
| 50 const char* NameConverter::NameOfCPURegister(int reg) const { | 1361 const char* NameConverter::NameOfCPURegister(int reg) const { |
| 51 UNIMPLEMENTED(); | 1362 if (0 <= reg && reg < 16) |
| 52 return NULL; | 1363 return cpu_regs[reg]; |
| 53 } | 1364 return "noreg"; |
| 54 | 1365 } |
| 55 | 1366 |
| 56 int Disassembler::ConstantPoolSizeAt(unsigned char* addr) { | 1367 |
| 57 UNIMPLEMENTED(); | 1368 const char* NameConverter::NameOfByteCPURegister(int reg) const { |
| 58 return 0; | 1369 if (0 <= reg && reg < 16) |
| 59 } | 1370 return byte_cpu_regs[reg]; |
| 1371 return "noreg"; | |
| 1372 } | |
| 1373 | |
| 1374 | |
| 1375 const char* NameConverter::NameOfXMMRegister(int reg) const { | |
| 1376 if (0 <= reg && reg < 16) | |
| 1377 return xmm_regs[reg]; | |
| 1378 return "noxmmreg"; | |
| 1379 } | |
| 1380 | |
| 1381 | |
| 1382 const char* NameConverter::NameInCode(byte* addr) const { | |
| 1383 // X64 does not embed debug strings at the moment. | |
| 1384 UNREACHABLE(); | |
| 1385 return ""; | |
| 1386 } | |
| 1387 | |
| 1388 //------------------------------------------------------------------------------ | |
| 1389 | |
| 1390 Disassembler::Disassembler(const NameConverter& converter) | |
| 1391 : converter_(converter) { } | |
| 1392 | |
| 1393 Disassembler::~Disassembler() { } | |
| 60 | 1394 |
| 61 | 1395 |
| 62 int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer, | 1396 int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer, |
| 63 unsigned char* instruction) { | 1397 byte* instruction) { |
| 64 UNIMPLEMENTED(); | 1398 DisassemblerX64 d(converter_, CONTINUE_ON_UNIMPLEMENTED_OPCODE); |
| 65 return 0; | 1399 return d.InstructionDecode(buffer, instruction); |
| 66 } | 1400 } |
| 67 | 1401 |
| 68 const char* NameConverter::NameOfByteCPURegister(int a) const { | 1402 |
| 69 UNIMPLEMENTED(); | 1403 // The X64 assembler does not use constant pools. |
| 70 return NULL; | 1404 int Disassembler::ConstantPoolSizeAt(byte* instruction) { |
| 71 } | 1405 return -1; |
| 72 | 1406 } |
| 73 const char* NameConverter::NameOfXMMRegister(int a) const { | 1407 |
| 74 UNIMPLEMENTED(); | 1408 |
| 75 return NULL; | 1409 void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) { |
| 76 } | 1410 NameConverter converter; |
| 77 | 1411 Disassembler d(converter); |
| 78 const char* NameConverter::NameOfConstant(unsigned char* a) const { | 1412 for (byte* pc = begin; pc < end;) { |
| 79 UNIMPLEMENTED(); | 1413 v8::internal::EmbeddedVector<char, 128> buffer; |
| 80 return NULL; | 1414 buffer[0] = '\0'; |
| 81 } | 1415 byte* prev_pc = pc; |
| 82 | 1416 pc += d.InstructionDecode(buffer, pc); |
| 83 const char* NameConverter::NameInCode(unsigned char* a) const { | 1417 fprintf(f, "%p", prev_pc); |
| 84 UNIMPLEMENTED(); | 1418 fprintf(f, " "); |
| 85 return NULL; | 1419 |
| 1420 for (byte* bp = prev_pc; bp < pc; bp++) { | |
| 1421 fprintf(f, "%02x", *bp); | |
| 1422 } | |
| 1423 for (int i = 6 - (pc - prev_pc); i >= 0; i--) { | |
| 1424 fprintf(f, " "); | |
| 1425 } | |
| 1426 fprintf(f, " %s\n", buffer.start()); | |
| 1427 } | |
| 86 } | 1428 } |
| 87 | 1429 |
| 88 } // namespace disasm | 1430 } // namespace disasm |
| OLD | NEW |