1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <assert.h>
6 #include <stdio.h>
7 #include <stdarg.h>
8
9 #include "src/v8.h"
10
11 #if V8_TARGET_ARCH_X64
12
13 #include "src/base/lazy-instance.h"
14 #include "src/disasm.h"
15
16 namespace disasm {
17
18 enum OperandType {
19 UNSET_OP_ORDER = 0,
20 // Operand size decides between 16, 32 and 64 bit operands.
21 REG_OPER_OP_ORDER = 1, // Register destination, operand source.
22 OPER_REG_OP_ORDER = 2, // Operand destination, register source.
23 // Fixed 8-bit operands.
24 BYTE_SIZE_OPERAND_FLAG = 4,
25 BYTE_REG_OPER_OP_ORDER = REG_OPER_OP_ORDER | BYTE_SIZE_OPERAND_FLAG,
26 BYTE_OPER_REG_OP_ORDER = OPER_REG_OP_ORDER | BYTE_SIZE_OPERAND_FLAG
27 };
28
29
30 //------------------------------------------------------------------
31 // Tables
32 //------------------------------------------------------------------
33 struct ByteMnemonic {
34 int b; // -1 terminates, otherwise must be in range (0..255)
35 OperandType op_order_;
36 const char* mnem;
37 };
38
39
40 static const ByteMnemonic two_operands_instr[] = {
41 { 0x00, BYTE_OPER_REG_OP_ORDER, "add" },
42 { 0x01, OPER_REG_OP_ORDER, "add" },
43 { 0x02, BYTE_REG_OPER_OP_ORDER, "add" },
44 { 0x03, REG_OPER_OP_ORDER, "add" },
45 { 0x08, BYTE_OPER_REG_OP_ORDER, "or" },
46 { 0x09, OPER_REG_OP_ORDER, "or" },
47 { 0x0A, BYTE_REG_OPER_OP_ORDER, "or" },
48 { 0x0B, REG_OPER_OP_ORDER, "or" },
49 { 0x10, BYTE_OPER_REG_OP_ORDER, "adc" },
50 { 0x11, OPER_REG_OP_ORDER, "adc" },
51 { 0x12, BYTE_REG_OPER_OP_ORDER, "adc" },
52 { 0x13, REG_OPER_OP_ORDER, "adc" },
53 { 0x18, BYTE_OPER_REG_OP_ORDER, "sbb" },
54 { 0x19, OPER_REG_OP_ORDER, "sbb" },
55 { 0x1A, BYTE_REG_OPER_OP_ORDER, "sbb" },
56 { 0x1B, REG_OPER_OP_ORDER, "sbb" },
57 { 0x20, BYTE_OPER_REG_OP_ORDER, "and" },
58 { 0x21, OPER_REG_OP_ORDER, "and" },
59 { 0x22, BYTE_REG_OPER_OP_ORDER, "and" },
60 { 0x23, REG_OPER_OP_ORDER, "and" },
61 { 0x28, BYTE_OPER_REG_OP_ORDER, "sub" },
62 { 0x29, OPER_REG_OP_ORDER, "sub" },
63 { 0x2A, BYTE_REG_OPER_OP_ORDER, "sub" },
64 { 0x2B, REG_OPER_OP_ORDER, "sub" },
65 { 0x30, BYTE_OPER_REG_OP_ORDER, "xor" },
66 { 0x31, OPER_REG_OP_ORDER, "xor" },
67 { 0x32, BYTE_REG_OPER_OP_ORDER, "xor" },
68 { 0x33, REG_OPER_OP_ORDER, "xor" },
69 { 0x38, BYTE_OPER_REG_OP_ORDER, "cmp" },
70 { 0x39, OPER_REG_OP_ORDER, "cmp" },
71 { 0x3A, BYTE_REG_OPER_OP_ORDER, "cmp" },
72 { 0x3B, REG_OPER_OP_ORDER, "cmp" },
73 { 0x63, REG_OPER_OP_ORDER, "movsxl" },
74 { 0x84, BYTE_REG_OPER_OP_ORDER, "test" },
75 { 0x85, REG_OPER_OP_ORDER, "test" },
76 { 0x86, BYTE_REG_OPER_OP_ORDER, "xchg" },
77 { 0x87, REG_OPER_OP_ORDER, "xchg" },
78 { 0x88, BYTE_OPER_REG_OP_ORDER, "mov" },
79 { 0x89, OPER_REG_OP_ORDER, "mov" },
80 { 0x8A, BYTE_REG_OPER_OP_ORDER, "mov" },
81 { 0x8B, REG_OPER_OP_ORDER, "mov" },
82 { 0x8D, REG_OPER_OP_ORDER, "lea" },
83 { -1, UNSET_OP_ORDER, "" }
84 };
85
86
87 static const ByteMnemonic zero_operands_instr[] = {
88 { 0xC3, UNSET_OP_ORDER, "ret" },
89 { 0xC9, UNSET_OP_ORDER, "leave" },
90 { 0xF4, UNSET_OP_ORDER, "hlt" },
91 { 0xFC, UNSET_OP_ORDER, "cld" },
92 { 0xCC, UNSET_OP_ORDER, "int3" },
93 { 0x60, UNSET_OP_ORDER, "pushad" },
94 { 0x61, UNSET_OP_ORDER, "popad" },
95 { 0x9C, UNSET_OP_ORDER, "pushfd" },
96 { 0x9D, UNSET_OP_ORDER, "popfd" },
97 { 0x9E, UNSET_OP_ORDER, "sahf" },
98 { 0x99, UNSET_OP_ORDER, "cdq" },
99 { 0x9B, UNSET_OP_ORDER, "fwait" },
100 { 0xA4, UNSET_OP_ORDER, "movs" },
101 { 0xA5, UNSET_OP_ORDER, "movs" },
102 { 0xA6, UNSET_OP_ORDER, "cmps" },
103 { 0xA7, UNSET_OP_ORDER, "cmps" },
104 { -1, UNSET_OP_ORDER, "" }
105 };
106
107
108 static const ByteMnemonic call_jump_instr[] = {
109 { 0xE8, UNSET_OP_ORDER, "call" },
110 { 0xE9, UNSET_OP_ORDER, "jmp" },
111 { -1, UNSET_OP_ORDER, "" }
112 };
113
114
115 static const ByteMnemonic short_immediate_instr[] = {
116 { 0x05, UNSET_OP_ORDER, "add" },
117 { 0x0D, UNSET_OP_ORDER, "or" },
118 { 0x15, UNSET_OP_ORDER, "adc" },
119 { 0x1D, UNSET_OP_ORDER, "sbb" },
120 { 0x25, UNSET_OP_ORDER, "and" },
121 { 0x2D, UNSET_OP_ORDER, "sub" },
122 { 0x35, UNSET_OP_ORDER, "xor" },
123 { 0x3D, UNSET_OP_ORDER, "cmp" },
124 { -1, UNSET_OP_ORDER, "" }
125 };
126
127
128 static const char* const conditional_code_suffix[] = {
129 "o", "no", "c", "nc", "z", "nz", "na", "a",
130 "s", "ns", "pe", "po", "l", "ge", "le", "g"
131 };
132
133
134 enum InstructionType {
135 NO_INSTR,
136 ZERO_OPERANDS_INSTR,
137 TWO_OPERANDS_INSTR,
138 JUMP_CONDITIONAL_SHORT_INSTR,
139 REGISTER_INSTR,
140 PUSHPOP_INSTR, // Has implicit 64-bit operand size.
141 MOVE_REG_INSTR,
142 CALL_JUMP_INSTR,
143 SHORT_IMMEDIATE_INSTR
144 };
145
146
147 enum Prefixes {
148 ESCAPE_PREFIX = 0x0F,
149 OPERAND_SIZE_OVERRIDE_PREFIX = 0x66,
150 ADDRESS_SIZE_OVERRIDE_PREFIX = 0x67,
151 REPNE_PREFIX = 0xF2,
152 REP_PREFIX = 0xF3,
153 REPEQ_PREFIX = REP_PREFIX
154 };
155
156
157 struct InstructionDesc {
158 const char* mnem;
159 InstructionType type;
160 OperandType op_order_;
161 bool byte_size_operation; // Fixed 8-bit operation.
162 };
163
164
165 class InstructionTable {
166 public:
167 InstructionTable();
Get(byte x) const168 const InstructionDesc& Get(byte x) const {
169 return instructions_[x];
170 }
171
172 private:
173 InstructionDesc instructions_[256];
174 void Clear();
175 void Init();
176 void CopyTable(const ByteMnemonic bm[], InstructionType type);
177 void SetTableRange(InstructionType type, byte start, byte end, bool byte_size,
178 const char* mnem);
179 void AddJumpConditionalShort();
180 };
181
182
InstructionTable()183 InstructionTable::InstructionTable() {
184 Clear();
185 Init();
186 }
187
188
Clear()189 void InstructionTable::Clear() {
190 for (int i = 0; i < 256; i++) {
191 instructions_[i].mnem = "(bad)";
192 instructions_[i].type = NO_INSTR;
193 instructions_[i].op_order_ = UNSET_OP_ORDER;
194 instructions_[i].byte_size_operation = false;
195 }
196 }
197
198
Init()199 void InstructionTable::Init() {
200 CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
201 CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR);
202 CopyTable(call_jump_instr, CALL_JUMP_INSTR);
203 CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR);
204 AddJumpConditionalShort();
205 SetTableRange(PUSHPOP_INSTR, 0x50, 0x57, false, "push");
206 SetTableRange(PUSHPOP_INSTR, 0x58, 0x5F, false, "pop");
207 SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, false, "mov");
208 }
209
210
CopyTable(const ByteMnemonic bm[],InstructionType type)211 void InstructionTable::CopyTable(const ByteMnemonic bm[],
212 InstructionType type) {
213 for (int i = 0; bm[i].b >= 0; i++) {
214 InstructionDesc* id = &instructions_[bm[i].b];
215 id->mnem = bm[i].mnem;
216 OperandType op_order = bm[i].op_order_;
217 id->op_order_ =
218 static_cast<OperandType>(op_order & ~BYTE_SIZE_OPERAND_FLAG);
219 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered
220 id->type = type;
221 id->byte_size_operation = ((op_order & BYTE_SIZE_OPERAND_FLAG) != 0);
222 }
223 }
224
225
SetTableRange(InstructionType type,byte start,byte end,bool byte_size,const char * mnem)226 void InstructionTable::SetTableRange(InstructionType type,
227 byte start,
228 byte end,
229 bool byte_size,
230 const char* mnem) {
231 for (byte b = start; b <= end; b++) {
232 InstructionDesc* id = &instructions_[b];
233 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered
234 id->mnem = mnem;
235 id->type = type;
236 id->byte_size_operation = byte_size;
237 }
238 }
239
240
AddJumpConditionalShort()241 void InstructionTable::AddJumpConditionalShort() {
242 for (byte b = 0x70; b <= 0x7F; b++) {
243 InstructionDesc* id = &instructions_[b];
244 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered
245 id->mnem = NULL; // Computed depending on condition code.
246 id->type = JUMP_CONDITIONAL_SHORT_INSTR;
247 }
248 }
249
250
251 static v8::base::LazyInstance<InstructionTable>::type instruction_table =
252 LAZY_INSTANCE_INITIALIZER;
253
254
255 static InstructionDesc cmov_instructions[16] = {
256 {"cmovo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
257 {"cmovno", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
258 {"cmovc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
259 {"cmovnc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
260 {"cmovz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
261 {"cmovnz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
262 {"cmovna", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
263 {"cmova", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
264 {"cmovs", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
265 {"cmovns", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
266 {"cmovpe", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
267 {"cmovpo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
268 {"cmovl", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
269 {"cmovge", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
270 {"cmovle", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
271 {"cmovg", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}
272 };
273
274
275 //------------------------------------------------------------------------------
276 // DisassemblerX64 implementation.
277
278 enum UnimplementedOpcodeAction {
279 CONTINUE_ON_UNIMPLEMENTED_OPCODE,
280 ABORT_ON_UNIMPLEMENTED_OPCODE
281 };
282
283
284 // A new DisassemblerX64 object is created to disassemble each instruction.
285 // The object can only disassemble a single instruction.
286 class DisassemblerX64 {
287 public:
DisassemblerX64(const NameConverter & converter,UnimplementedOpcodeAction unimplemented_action=ABORT_ON_UNIMPLEMENTED_OPCODE)288 DisassemblerX64(const NameConverter& converter,
289 UnimplementedOpcodeAction unimplemented_action =
290 ABORT_ON_UNIMPLEMENTED_OPCODE)
291 : converter_(converter),
292 tmp_buffer_pos_(0),
293 abort_on_unimplemented_(
294 unimplemented_action == ABORT_ON_UNIMPLEMENTED_OPCODE),
295 rex_(0),
296 operand_size_(0),
297 group_1_prefix_(0),
298 byte_size_operand_(false),
299 instruction_table_(instruction_table.Pointer()) {
300 tmp_buffer_[0] = '\0';
301 }
302
~DisassemblerX64()303 virtual ~DisassemblerX64() {
304 }
305
306 // Writes one disassembled instruction into 'buffer' (0-terminated).
307 // Returns the length of the disassembled machine instruction in bytes.
308 int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
309
310 private:
311 enum OperandSize {
312 OPERAND_BYTE_SIZE = 0,
313 OPERAND_WORD_SIZE = 1,
314 OPERAND_DOUBLEWORD_SIZE = 2,
315 OPERAND_QUADWORD_SIZE = 3
316 };
317
318 const NameConverter& converter_;
319 v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
320 unsigned int tmp_buffer_pos_;
321 bool abort_on_unimplemented_;
322 // Prefixes parsed
323 byte rex_;
324 byte operand_size_; // 0x66 or (if no group 3 prefix is present) 0x0.
325 byte group_1_prefix_; // 0xF2, 0xF3, or (if no group 1 prefix is present) 0.
326 // Byte size operand override.
327 bool byte_size_operand_;
328 const InstructionTable* const instruction_table_;
329
setRex(byte rex)330 void setRex(byte rex) {
331 ASSERT_EQ(0x40, rex & 0xF0);
332 rex_ = rex;
333 }
334
rex()335 bool rex() { return rex_ != 0; }
336
rex_b()337 bool rex_b() { return (rex_ & 0x01) != 0; }
338
339 // Actual number of base register given the low bits and the rex.b state.
base_reg(int low_bits)340 int base_reg(int low_bits) { return low_bits | ((rex_ & 0x01) << 3); }
341
rex_x()342 bool rex_x() { return (rex_ & 0x02) != 0; }
343
rex_r()344 bool rex_r() { return (rex_ & 0x04) != 0; }
345
rex_w()346 bool rex_w() { return (rex_ & 0x08) != 0; }
347
operand_size()348 OperandSize operand_size() {
349 if (byte_size_operand_) return OPERAND_BYTE_SIZE;
350 if (rex_w()) return OPERAND_QUADWORD_SIZE;
351 if (operand_size_ != 0) return OPERAND_WORD_SIZE;
352 return OPERAND_DOUBLEWORD_SIZE;
353 }
354
operand_size_code()355 char operand_size_code() {
356 return "bwlq"[operand_size()];
357 }
358
NameOfCPURegister(int reg) const359 const char* NameOfCPURegister(int reg) const {
360 return converter_.NameOfCPURegister(reg);
361 }
362
NameOfByteCPURegister(int reg) const363 const char* NameOfByteCPURegister(int reg) const {
364 return converter_.NameOfByteCPURegister(reg);
365 }
366
NameOfXMMRegister(int reg) const367 const char* NameOfXMMRegister(int reg) const {
368 return converter_.NameOfXMMRegister(reg);
369 }
370
NameOfAddress(byte * addr) const371 const char* NameOfAddress(byte* addr) const {
372 return converter_.NameOfAddress(addr);
373 }
374
375 // Disassembler helper functions.
get_modrm(byte data,int * mod,int * regop,int * rm)376 void get_modrm(byte data,
377 int* mod,
378 int* regop,
379 int* rm) {
380 *mod = (data >> 6) & 3;
381 *regop = ((data & 0x38) >> 3) | (rex_r() ? 8 : 0);
382 *rm = (data & 7) | (rex_b() ? 8 : 0);
383 }
384
get_sib(byte data,int * scale,int * index,int * base)385 void get_sib(byte data,
386 int* scale,
387 int* index,
388 int* base) {
389 *scale = (data >> 6) & 3;
390 *index = ((data >> 3) & 7) | (rex_x() ? 8 : 0);
391 *base = (data & 7) | (rex_b() ? 8 : 0);
392 }
393
394 typedef const char* (DisassemblerX64::*RegisterNameMapping)(int reg) const;
395
396 int PrintRightOperandHelper(byte* modrmp,
397 RegisterNameMapping register_name);
398 int PrintRightOperand(byte* modrmp);
399 int PrintRightByteOperand(byte* modrmp);
400 int PrintRightXMMOperand(byte* modrmp);
401 int PrintOperands(const char* mnem,
402 OperandType op_order,
403 byte* data);
404 int PrintImmediate(byte* data, OperandSize size);
405 int PrintImmediateOp(byte* data);
406 const char* TwoByteMnemonic(byte opcode);
407 int TwoByteOpcodeInstruction(byte* data);
408 int F6F7Instruction(byte* data);
409 int ShiftInstruction(byte* data);
410 int JumpShort(byte* data);
411 int JumpConditional(byte* data);
412 int JumpConditionalShort(byte* data);
413 int SetCC(byte* data);
414 int FPUInstruction(byte* data);
415 int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start);
416 int RegisterFPUInstruction(int escape_opcode, byte modrm_byte);
417 void AppendToBuffer(const char* format, ...);
418
UnimplementedInstruction()419 void UnimplementedInstruction() {
420 if (abort_on_unimplemented_) {
421 CHECK(false);
422 } else {
423 AppendToBuffer("'Unimplemented Instruction'");
424 }
425 }
426 };
427
428
AppendToBuffer(const char * format,...)429 void DisassemblerX64::AppendToBuffer(const char* format, ...) {
430 v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
431 va_list args;
432 va_start(args, format);
433 int result = v8::internal::VSNPrintF(buf, format, args);
434 va_end(args);
435 tmp_buffer_pos_ += result;
436 }
437
438
PrintRightOperandHelper(byte * modrmp,RegisterNameMapping direct_register_name)439 int DisassemblerX64::PrintRightOperandHelper(
440 byte* modrmp,
441 RegisterNameMapping direct_register_name) {
442 int mod, regop, rm;
443 get_modrm(*modrmp, &mod, ®op, &rm);
444 RegisterNameMapping register_name = (mod == 3) ? direct_register_name :
445 &DisassemblerX64::NameOfCPURegister;
446 switch (mod) {
447 case 0:
448 if ((rm & 7) == 5) {
449 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 1);
450 AppendToBuffer("[0x%x]", disp);
451 return 5;
452 } else if ((rm & 7) == 4) {
453 // Codes for SIB byte.
454 byte sib = *(modrmp + 1);
455 int scale, index, base;
456 get_sib(sib, &scale, &index, &base);
457 if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
458 // index == rsp means no index. Only use sib byte with no index for
459 // rsp and r12 base.
460 AppendToBuffer("[%s]", NameOfCPURegister(base));
461 return 2;
462 } else if (base == 5) {
463 // base == rbp means no base register (when mod == 0).
464 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
465 AppendToBuffer("[%s*%d%s0x%x]",
466 NameOfCPURegister(index),
467 1 << scale,
468 disp < 0 ? "-" : "+",
469 disp < 0 ? -disp : disp);
470 return 6;
471 } else if (index != 4 && base != 5) {
472 // [base+index*scale]
473 AppendToBuffer("[%s+%s*%d]",
474 NameOfCPURegister(base),
475 NameOfCPURegister(index),
476 1 << scale);
477 return 2;
478 } else {
479 UnimplementedInstruction();
480 return 1;
481 }
482 } else {
483 AppendToBuffer("[%s]", NameOfCPURegister(rm));
484 return 1;
485 }
486 break;
487 case 1: // fall through
488 case 2:
489 if ((rm & 7) == 4) {
490 byte sib = *(modrmp + 1);
491 int scale, index, base;
492 get_sib(sib, &scale, &index, &base);
493 int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 2)
494 : *reinterpret_cast<int8_t*>(modrmp + 2);
495 if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
496 AppendToBuffer("[%s%s0x%x]",
497 NameOfCPURegister(base),
498 disp < 0 ? "-" : "+",
499 disp < 0 ? -disp : disp);
500 } else {
501 AppendToBuffer("[%s+%s*%d%s0x%x]",
502 NameOfCPURegister(base),
503 NameOfCPURegister(index),
504 1 << scale,
505 disp < 0 ? "-" : "+",
506 disp < 0 ? -disp : disp);
507 }
508 return mod == 2 ? 6 : 3;
509 } else {
510 // No sib.
511 int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 1)
512 : *reinterpret_cast<int8_t*>(modrmp + 1);
513 AppendToBuffer("[%s%s0x%x]",
514 NameOfCPURegister(rm),
515 disp < 0 ? "-" : "+",
516 disp < 0 ? -disp : disp);
517 return (mod == 2) ? 5 : 2;
518 }
519 break;
520 case 3:
521 AppendToBuffer("%s", (this->*register_name)(rm));
522 return 1;
523 default:
524 UnimplementedInstruction();
525 return 1;
526 }
527 UNREACHABLE();
528 }
529
530
PrintImmediate(byte * data,OperandSize size)531 int DisassemblerX64::PrintImmediate(byte* data, OperandSize size) {
532 int64_t value;
533 int count;
534 switch (size) {
535 case OPERAND_BYTE_SIZE:
536 value = *data;
537 count = 1;
538 break;
539 case OPERAND_WORD_SIZE:
540 value = *reinterpret_cast<int16_t*>(data);
541 count = 2;
542 break;
543 case OPERAND_DOUBLEWORD_SIZE:
544 value = *reinterpret_cast<uint32_t*>(data);
545 count = 4;
546 break;
547 case OPERAND_QUADWORD_SIZE:
548 value = *reinterpret_cast<int32_t*>(data);
549 count = 4;
550 break;
551 default:
552 UNREACHABLE();
553 value = 0; // Initialize variables on all paths to satisfy the compiler.
554 count = 0;
555 }
556 AppendToBuffer("%" V8_PTR_PREFIX "x", value);
557 return count;
558 }
559
560
PrintRightOperand(byte * modrmp)561 int DisassemblerX64::PrintRightOperand(byte* modrmp) {
562 return PrintRightOperandHelper(modrmp,
563 &DisassemblerX64::NameOfCPURegister);
564 }
565
566
PrintRightByteOperand(byte * modrmp)567 int DisassemblerX64::PrintRightByteOperand(byte* modrmp) {
568 return PrintRightOperandHelper(modrmp,
569 &DisassemblerX64::NameOfByteCPURegister);
570 }
571
572
PrintRightXMMOperand(byte * modrmp)573 int DisassemblerX64::PrintRightXMMOperand(byte* modrmp) {
574 return PrintRightOperandHelper(modrmp,
575 &DisassemblerX64::NameOfXMMRegister);
576 }
577
578
579 // Returns number of bytes used including the current *data.
580 // Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
PrintOperands(const char * mnem,OperandType op_order,byte * data)581 int DisassemblerX64::PrintOperands(const char* mnem,
582 OperandType op_order,
583 byte* data) {
584 byte modrm = *data;
585 int mod, regop, rm;
586 get_modrm(modrm, &mod, ®op, &rm);
587 int advance = 0;
588 const char* register_name =
589 byte_size_operand_ ? NameOfByteCPURegister(regop)
590 : NameOfCPURegister(regop);
591 switch (op_order) {
592 case REG_OPER_OP_ORDER: {
593 AppendToBuffer("%s%c %s,",
594 mnem,
595 operand_size_code(),
596 register_name);
597 advance = byte_size_operand_ ? PrintRightByteOperand(data)
598 : PrintRightOperand(data);
599 break;
600 }
601 case OPER_REG_OP_ORDER: {
602 AppendToBuffer("%s%c ", mnem, operand_size_code());
603 advance = byte_size_operand_ ? PrintRightByteOperand(data)
604 : PrintRightOperand(data);
605 AppendToBuffer(",%s", register_name);
606 break;
607 }
608 default:
609 UNREACHABLE();
610 break;
611 }
612 return advance;
613 }
614
615
616 // Returns number of bytes used by machine instruction, including *data byte.
617 // Writes immediate instructions to 'tmp_buffer_'.
PrintImmediateOp(byte * data)618 int DisassemblerX64::PrintImmediateOp(byte* data) {
619 bool byte_size_immediate = (*data & 0x02) != 0;
620 byte modrm = *(data + 1);
621 int mod, regop, rm;
622 get_modrm(modrm, &mod, ®op, &rm);
623 const char* mnem = "Imm???";
624 switch (regop) {
625 case 0:
626 mnem = "add";
627 break;
628 case 1:
629 mnem = "or";
630 break;
631 case 2:
632 mnem = "adc";
633 break;
634 case 3:
635 mnem = "sbb";
636 break;
637 case 4:
638 mnem = "and";
639 break;
640 case 5:
641 mnem = "sub";
642 break;
643 case 6:
644 mnem = "xor";
645 break;
646 case 7:
647 mnem = "cmp";
648 break;
649 default:
650 UnimplementedInstruction();
651 }
652 AppendToBuffer("%s%c ", mnem, operand_size_code());
653 int count = PrintRightOperand(data + 1);
654 AppendToBuffer(",0x");
655 OperandSize immediate_size =
656 byte_size_immediate ? OPERAND_BYTE_SIZE : operand_size();
657 count += PrintImmediate(data + 1 + count, immediate_size);
658 return 1 + count;
659 }
660
661
662 // Returns number of bytes used, including *data.
F6F7Instruction(byte * data)663 int DisassemblerX64::F6F7Instruction(byte* data) {
664 ASSERT(*data == 0xF7 || *data == 0xF6);
665 byte modrm = *(data + 1);
666 int mod, regop, rm;
667 get_modrm(modrm, &mod, ®op, &rm);
668 if (mod == 3 && regop != 0) {
669 const char* mnem = NULL;
670 switch (regop) {
671 case 2:
672 mnem = "not";
673 break;
674 case 3:
675 mnem = "neg";
676 break;
677 case 4:
678 mnem = "mul";
679 break;
680 case 5:
681 mnem = "imul";
682 break;
683 case 7:
684 mnem = "idiv";
685 break;
686 default:
687 UnimplementedInstruction();
688 }
689 AppendToBuffer("%s%c %s",
690 mnem,
691 operand_size_code(),
692 NameOfCPURegister(rm));
693 return 2;
694 } else if (regop == 0) {
695 AppendToBuffer("test%c ", operand_size_code());
696 int count = PrintRightOperand(data + 1); // Use name of 64-bit register.
697 AppendToBuffer(",0x");
698 count += PrintImmediate(data + 1 + count, operand_size());
699 return 1 + count;
700 } else {
701 UnimplementedInstruction();
702 return 2;
703 }
704 }
705
706
ShiftInstruction(byte * data)707 int DisassemblerX64::ShiftInstruction(byte* data) {
708 byte op = *data & (~1);
709 if (op != 0xD0 && op != 0xD2 && op != 0xC0) {
710 UnimplementedInstruction();
711 return 1;
712 }
713 byte modrm = *(data + 1);
714 int mod, regop, rm;
715 get_modrm(modrm, &mod, ®op, &rm);
716 regop &= 0x7; // The REX.R bit does not affect the operation.
717 int imm8 = -1;
718 int num_bytes = 2;
719 if (mod != 3) {
720 UnimplementedInstruction();
721 return num_bytes;
722 }
723 const char* mnem = NULL;
724 switch (regop) {
725 case 0:
726 mnem = "rol";
727 break;
728 case 1:
729 mnem = "ror";
730 break;
731 case 2:
732 mnem = "rcl";
733 break;
734 case 3:
735 mnem = "rcr";
736 break;
737 case 4:
738 mnem = "shl";
739 break;
740 case 5:
741 mnem = "shr";
742 break;
743 case 7:
744 mnem = "sar";
745 break;
746 default:
747 UnimplementedInstruction();
748 return num_bytes;
749 }
750 ASSERT_NE(NULL, mnem);
751 if (op == 0xD0) {
752 imm8 = 1;
753 } else if (op == 0xC0) {
754 imm8 = *(data + 2);
755 num_bytes = 3;
756 }
757 AppendToBuffer("%s%c %s,",
758 mnem,
759 operand_size_code(),
760 byte_size_operand_ ? NameOfByteCPURegister(rm)
761 : NameOfCPURegister(rm));
762 if (op == 0xD2) {
763 AppendToBuffer("cl");
764 } else {
765 AppendToBuffer("%d", imm8);
766 }
767 return num_bytes;
768 }
769
770
771 // Returns number of bytes used, including *data.
JumpShort(byte * data)772 int DisassemblerX64::JumpShort(byte* data) {
773 ASSERT_EQ(0xEB, *data);
774 byte b = *(data + 1);
775 byte* dest = data + static_cast<int8_t>(b) + 2;
776 AppendToBuffer("jmp %s", NameOfAddress(dest));
777 return 2;
778 }
779
780
781 // Returns number of bytes used, including *data.
JumpConditional(byte * data)782 int DisassemblerX64::JumpConditional(byte* data) {
783 ASSERT_EQ(0x0F, *data);
784 byte cond = *(data + 1) & 0x0F;
785 byte* dest = data + *reinterpret_cast<int32_t*>(data + 2) + 6;
786 const char* mnem = conditional_code_suffix[cond];
787 AppendToBuffer("j%s %s", mnem, NameOfAddress(dest));
788 return 6; // includes 0x0F
789 }
790
791
792 // Returns number of bytes used, including *data.
JumpConditionalShort(byte * data)793 int DisassemblerX64::JumpConditionalShort(byte* data) {
794 byte cond = *data & 0x0F;
795 byte b = *(data + 1);
796 byte* dest = data + static_cast<int8_t>(b) + 2;
797 const char* mnem = conditional_code_suffix[cond];
798 AppendToBuffer("j%s %s", mnem, NameOfAddress(dest));
799 return 2;
800 }
801
802
803 // Returns number of bytes used, including *data.
SetCC(byte * data)804 int DisassemblerX64::SetCC(byte* data) {
805 ASSERT_EQ(0x0F, *data);
806 byte cond = *(data + 1) & 0x0F;
807 const char* mnem = conditional_code_suffix[cond];
808 AppendToBuffer("set%s%c ", mnem, operand_size_code());
809 PrintRightByteOperand(data + 2);
810 return 3; // includes 0x0F
811 }
812
813
814 // Returns number of bytes used, including *data.
FPUInstruction(byte * data)815 int DisassemblerX64::FPUInstruction(byte* data) {
816 byte escape_opcode = *data;
817 ASSERT_EQ(0xD8, escape_opcode & 0xF8);
818 byte modrm_byte = *(data+1);
819
820 if (modrm_byte >= 0xC0) {
821 return RegisterFPUInstruction(escape_opcode, modrm_byte);
822 } else {
823 return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1);
824 }
825 }
826
MemoryFPUInstruction(int escape_opcode,int modrm_byte,byte * modrm_start)827 int DisassemblerX64::MemoryFPUInstruction(int escape_opcode,
828 int modrm_byte,
829 byte* modrm_start) {
830 const char* mnem = "?";
831 int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte.
832 switch (escape_opcode) {
833 case 0xD9: switch (regop) {
834 case 0: mnem = "fld_s"; break;
835 case 3: mnem = "fstp_s"; break;
836 case 7: mnem = "fstcw"; break;
837 default: UnimplementedInstruction();
838 }
839 break;
840
841 case 0xDB: switch (regop) {
842 case 0: mnem = "fild_s"; break;
843 case 1: mnem = "fisttp_s"; break;
844 case 2: mnem = "fist_s"; break;
845 case 3: mnem = "fistp_s"; break;
846 default: UnimplementedInstruction();
847 }
848 break;
849
850 case 0xDD: switch (regop) {
851 case 0: mnem = "fld_d"; break;
852 case 3: mnem = "fstp_d"; break;
853 default: UnimplementedInstruction();
854 }
855 break;
856
857 case 0xDF: switch (regop) {
858 case 5: mnem = "fild_d"; break;
859 case 7: mnem = "fistp_d"; break;
860 default: UnimplementedInstruction();
861 }
862 break;
863
864 default: UnimplementedInstruction();
865 }
866 AppendToBuffer("%s ", mnem);
867 int count = PrintRightOperand(modrm_start);
868 return count + 1;
869 }
870
RegisterFPUInstruction(int escape_opcode,byte modrm_byte)871 int DisassemblerX64::RegisterFPUInstruction(int escape_opcode,
872 byte modrm_byte) {
873 bool has_register = false; // Is the FPU register encoded in modrm_byte?
874 const char* mnem = "?";
875
876 switch (escape_opcode) {
877 case 0xD8:
878 UnimplementedInstruction();
879 break;
880
881 case 0xD9:
882 switch (modrm_byte & 0xF8) {
883 case 0xC0:
884 mnem = "fld";
885 has_register = true;
886 break;
887 case 0xC8:
888 mnem = "fxch";
889 has_register = true;
890 break;
891 default:
892 switch (modrm_byte) {
893 case 0xE0: mnem = "fchs"; break;
894 case 0xE1: mnem = "fabs"; break;
895 case 0xE3: mnem = "fninit"; break;
896 case 0xE4: mnem = "ftst"; break;
897 case 0xE8: mnem = "fld1"; break;
898 case 0xEB: mnem = "fldpi"; break;
899 case 0xED: mnem = "fldln2"; break;
900 case 0xEE: mnem = "fldz"; break;
901 case 0xF0: mnem = "f2xm1"; break;
902 case 0xF1: mnem = "fyl2x"; break;
903 case 0xF2: mnem = "fptan"; break;
904 case 0xF5: mnem = "fprem1"; break;
905 case 0xF7: mnem = "fincstp"; break;
906 case 0xF8: mnem = "fprem"; break;
907 case 0xFC: mnem = "frndint"; break;
908 case 0xFD: mnem = "fscale"; break;
909 case 0xFE: mnem = "fsin"; break;
910 case 0xFF: mnem = "fcos"; break;
911 default: UnimplementedInstruction();
912 }
913 }
914 break;
915
916 case 0xDA:
917 if (modrm_byte == 0xE9) {
918 mnem = "fucompp";
919 } else {
920 UnimplementedInstruction();
921 }
922 break;
923
924 case 0xDB:
925 if ((modrm_byte & 0xF8) == 0xE8) {
926 mnem = "fucomi";
927 has_register = true;
928 } else if (modrm_byte == 0xE2) {
929 mnem = "fclex";
930 } else if (modrm_byte == 0xE3) {
931 mnem = "fninit";
932 } else {
933 UnimplementedInstruction();
934 }
935 break;
936
937 case 0xDC:
938 has_register = true;
939 switch (modrm_byte & 0xF8) {
940 case 0xC0: mnem = "fadd"; break;
941 case 0xE8: mnem = "fsub"; break;
942 case 0xC8: mnem = "fmul"; break;
943 case 0xF8: mnem = "fdiv"; break;
944 default: UnimplementedInstruction();
945 }
946 break;
947
948 case 0xDD:
949 has_register = true;
950 switch (modrm_byte & 0xF8) {
951 case 0xC0: mnem = "ffree"; break;
952 case 0xD8: mnem = "fstp"; break;
953 default: UnimplementedInstruction();
954 }
955 break;
956
957 case 0xDE:
958 if (modrm_byte == 0xD9) {
959 mnem = "fcompp";
960 } else {
961 has_register = true;
962 switch (modrm_byte & 0xF8) {
963 case 0xC0: mnem = "faddp"; break;
964 case 0xE8: mnem = "fsubp"; break;
965 case 0xC8: mnem = "fmulp"; break;
966 case 0xF8: mnem = "fdivp"; break;
967 default: UnimplementedInstruction();
968 }
969 }
970 break;
971
972 case 0xDF:
973 if (modrm_byte == 0xE0) {
974 mnem = "fnstsw_ax";
975 } else if ((modrm_byte & 0xF8) == 0xE8) {
976 mnem = "fucomip";
977 has_register = true;
978 }
979 break;
980
981 default: UnimplementedInstruction();
982 }
983
984 if (has_register) {
985 AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
986 } else {
987 AppendToBuffer("%s", mnem);
988 }
989 return 2;
990 }
991
992
993
994 // Handle all two-byte opcodes, which start with 0x0F.
995 // These instructions may be affected by an 0x66, 0xF2, or 0xF3 prefix.
996 // We do not use any three-byte opcodes, which start with 0x0F38 or 0x0F3A.
TwoByteOpcodeInstruction(byte * data)997 int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
998 byte opcode = *(data + 1);
999 byte* current = data + 2;
1000 // At return, "current" points to the start of the next instruction.
1001 const char* mnemonic = TwoByteMnemonic(opcode);
1002 if (operand_size_ == 0x66) {
1003 // 0x66 0x0F prefix.
1004 int mod, regop, rm;
1005 if (opcode == 0x3A) {
1006 byte third_byte = *current;
1007 current = data + 3;
1008 if (third_byte == 0x17) {
1009 get_modrm(*current, &mod, ®op, &rm);
1010 AppendToBuffer("extractps "); // reg/m32, xmm, imm8
1011 current += PrintRightOperand(current);
1012 AppendToBuffer(",%s,%d", NameOfXMMRegister(regop), (*current) & 3);
1013 current += 1;
1014 } else if (third_byte == 0x0b) {
1015 get_modrm(*current, &mod, ®op, &rm);
1016 // roundsd xmm, xmm/m64, imm8
1017 AppendToBuffer("roundsd %s,", NameOfXMMRegister(regop));
1018 current += PrintRightXMMOperand(current);
1019 AppendToBuffer(",%d", (*current) & 3);
1020 current += 1;
1021 } else {
1022 UnimplementedInstruction();
1023 }
1024 } else {
1025 get_modrm(*current, &mod, ®op, &rm);
1026 if (opcode == 0x1f) {
1027 current++;
1028 if (rm == 4) { // SIB byte present.
1029 current++;
1030 }
1031 if (mod == 1) { // Byte displacement.
1032 current += 1;
1033 } else if (mod == 2) { // 32-bit displacement.
1034 current += 4;
1035 } // else no immediate displacement.
1036 AppendToBuffer("nop");
1037 } else if (opcode == 0x28) {
1038 AppendToBuffer("movapd %s,", NameOfXMMRegister(regop));
1039 current += PrintRightXMMOperand(current);
1040 } else if (opcode == 0x29) {
1041 AppendToBuffer("movapd ");
1042 current += PrintRightXMMOperand(current);
1043 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1044 } else if (opcode == 0x6E) {
1045 AppendToBuffer("mov%c %s,",
1046 rex_w() ? 'q' : 'd',
1047 NameOfXMMRegister(regop));
1048 current += PrintRightOperand(current);
1049 } else if (opcode == 0x6F) {
1050 AppendToBuffer("movdqa %s,",
1051 NameOfXMMRegister(regop));
1052 current += PrintRightXMMOperand(current);
1053 } else if (opcode == 0x7E) {
1054 AppendToBuffer("mov%c ",
1055 rex_w() ? 'q' : 'd');
1056 current += PrintRightOperand(current);
1057 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1058 } else if (opcode == 0x7F) {
1059 AppendToBuffer("movdqa ");
1060 current += PrintRightXMMOperand(current);
1061 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1062 } else if (opcode == 0xD6) {
1063 AppendToBuffer("movq ");
1064 current += PrintRightXMMOperand(current);
1065 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1066 } else if (opcode == 0x50) {
1067 AppendToBuffer("movmskpd %s,", NameOfCPURegister(regop));
1068 current += PrintRightXMMOperand(current);
1069 } else if (opcode == 0x73) {
1070 current += 1;
1071 ASSERT(regop == 6);
1072 AppendToBuffer("psllq,%s,%d", NameOfXMMRegister(rm), *current & 0x7f);
1073 current += 1;
1074 } else {
1075 const char* mnemonic = "?";
1076 if (opcode == 0x54) {
1077 mnemonic = "andpd";
1078 } else if (opcode == 0x56) {
1079 mnemonic = "orpd";
1080 } else if (opcode == 0x57) {
1081 mnemonic = "xorpd";
1082 } else if (opcode == 0x2E) {
1083 mnemonic = "ucomisd";
1084 } else if (opcode == 0x2F) {
1085 mnemonic = "comisd";
1086 } else {
1087 UnimplementedInstruction();
1088 }
1089 AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
1090 current += PrintRightXMMOperand(current);
1091 }
1092 }
1093 } else if (group_1_prefix_ == 0xF2) {
1094 // Beginning of instructions with prefix 0xF2.
1095
1096 if (opcode == 0x11 || opcode == 0x10) {
1097 // MOVSD: Move scalar double-precision fp to/from/between XMM registers.
1098 AppendToBuffer("movsd ");
1099 int mod, regop, rm;
1100 get_modrm(*current, &mod, ®op, &rm);
1101 if (opcode == 0x11) {
1102 current += PrintRightXMMOperand(current);
1103 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1104 } else {
1105 AppendToBuffer("%s,", NameOfXMMRegister(regop));
1106 current += PrintRightXMMOperand(current);
1107 }
1108 } else if (opcode == 0x2A) {
1109 // CVTSI2SD: integer to XMM double conversion.
1110 int mod, regop, rm;
1111 get_modrm(*current, &mod, ®op, &rm);
1112 AppendToBuffer("%sd %s,", mnemonic, NameOfXMMRegister(regop));
1113 current += PrintRightOperand(current);
1114 } else if (opcode == 0x2C) {
1115 // CVTTSD2SI:
1116 // Convert with truncation scalar double-precision FP to integer.
1117 int mod, regop, rm;
1118 get_modrm(*current, &mod, ®op, &rm);
1119 AppendToBuffer("cvttsd2si%c %s,",
1120 operand_size_code(), NameOfCPURegister(regop));
1121 current += PrintRightXMMOperand(current);
1122 } else if (opcode == 0x2D) {
1123 // CVTSD2SI: Convert scalar double-precision FP to integer.
1124 int mod, regop, rm;
1125 get_modrm(*current, &mod, ®op, &rm);
1126 AppendToBuffer("cvtsd2si%c %s,",
1127 operand_size_code(), NameOfCPURegister(regop));
1128 current += PrintRightXMMOperand(current);
1129 } else if ((opcode & 0xF8) == 0x58 || opcode == 0x51) {
1130 // XMM arithmetic. Mnemonic was retrieved at the start of this function.
1131 int mod, regop, rm;
1132 get_modrm(*current, &mod, ®op, &rm);
1133 AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
1134 current += PrintRightXMMOperand(current);
1135 } else if (opcode == 0xC2) {
1136 // Intel manual 2A, Table 3-18.
1137 int mod, regop, rm;
1138 get_modrm(*current, &mod, ®op, &rm);
1139 const char* const pseudo_op[] = {
1140 "cmpeqsd",
1141 "cmpltsd",
1142 "cmplesd",
1143 "cmpunordsd",
1144 "cmpneqsd",
1145 "cmpnltsd",
1146 "cmpnlesd",
1147 "cmpordsd"
1148 };
1149 AppendToBuffer("%s %s,%s",
1150 pseudo_op[current[1]],
1151 NameOfXMMRegister(regop),
1152 NameOfXMMRegister(rm));
1153 current += 2;
1154 } else {
1155 UnimplementedInstruction();
1156 }
1157 } else if (group_1_prefix_ == 0xF3) {
1158 // Instructions with prefix 0xF3.
1159 if (opcode == 0x11 || opcode == 0x10) {
1160 // MOVSS: Move scalar double-precision fp to/from/between XMM registers.
1161 AppendToBuffer("movss ");
1162 int mod, regop, rm;
1163 get_modrm(*current, &mod, ®op, &rm);
1164 if (opcode == 0x11) {
1165 current += PrintRightOperand(current);
1166 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1167 } else {
1168 AppendToBuffer("%s,", NameOfXMMRegister(regop));
1169 current += PrintRightOperand(current);
1170 }
1171 } else if (opcode == 0x2A) {
1172 // CVTSI2SS: integer to XMM single conversion.
1173 int mod, regop, rm;
1174 get_modrm(*current, &mod, ®op, &rm);
1175 AppendToBuffer("%ss %s,", mnemonic, NameOfXMMRegister(regop));
1176 current += PrintRightOperand(current);
1177 } else if (opcode == 0x2C) {
1178 // CVTTSS2SI:
1179 // Convert with truncation scalar single-precision FP to dword integer.
1180 int mod, regop, rm;
1181 get_modrm(*current, &mod, ®op, &rm);
1182 AppendToBuffer("cvttss2si%c %s,",
1183 operand_size_code(), NameOfCPURegister(regop));
1184 current += PrintRightXMMOperand(current);
1185 } else if (opcode == 0x5A) {
1186 // CVTSS2SD:
1187 // Convert scalar single-precision FP to scalar double-precision FP.
1188 int mod, regop, rm;
1189 get_modrm(*current, &mod, ®op, &rm);
1190 AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop));
1191 current += PrintRightXMMOperand(current);
1192 } else if (opcode == 0x7E) {
1193 int mod, regop, rm;
1194 get_modrm(*current, &mod, ®op, &rm);
1195 AppendToBuffer("movq %s,", NameOfXMMRegister(regop));
1196 current += PrintRightXMMOperand(current);
1197 } else {
1198 UnimplementedInstruction();
1199 }
1200 } else if (opcode == 0x1F) {
1201 // NOP
1202 int mod, regop, rm;
1203 get_modrm(*current, &mod, ®op, &rm);
1204 current++;
1205 if (rm == 4) { // SIB byte present.
1206 current++;
1207 }
1208 if (mod == 1) { // Byte displacement.
1209 current += 1;
1210 } else if (mod == 2) { // 32-bit displacement.
1211 current += 4;
1212 } // else no immediate displacement.
1213 AppendToBuffer("nop");
1214
1215 } else if (opcode == 0x28) {
1216 // movaps xmm, xmm/m128
1217 int mod, regop, rm;
1218 get_modrm(*current, &mod, ®op, &rm);
1219 AppendToBuffer("movaps %s,", NameOfXMMRegister(regop));
1220 current += PrintRightXMMOperand(current);
1221
1222 } else if (opcode == 0x29) {
1223 // movaps xmm/m128, xmm
1224 int mod, regop, rm;
1225 get_modrm(*current, &mod, ®op, &rm);
1226 AppendToBuffer("movaps ");
1227 current += PrintRightXMMOperand(current);
1228 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1229
1230 } else if (opcode == 0xA2) {
1231 // CPUID
1232 AppendToBuffer("%s", mnemonic);
1233
1234 } else if ((opcode & 0xF0) == 0x40) {
1235 // CMOVcc: conditional move.
1236 int condition = opcode & 0x0F;
1237 const InstructionDesc& idesc = cmov_instructions[condition];
1238 byte_size_operand_ = idesc.byte_size_operation;
1239 current += PrintOperands(idesc.mnem, idesc.op_order_, current);
1240
1241 } else if (opcode >= 0x53 && opcode <= 0x5F) {
1242 const char* const pseudo_op[] = {
1243 "rcpps",
1244 "andps",
1245 "andnps",
1246 "orps",
1247 "xorps",
1248 "addps",
1249 "mulps",
1250 "cvtps2pd",
1251 "cvtdq2ps",
1252 "subps",
1253 "minps",
1254 "divps",
1255 "maxps",
1256 };
1257 int mod, regop, rm;
1258 get_modrm(*current, &mod, ®op, &rm);
1259 AppendToBuffer("%s %s,",
1260 pseudo_op[opcode - 0x53],
1261 NameOfXMMRegister(regop));
1262 current += PrintRightXMMOperand(current);
1263
1264 } else if (opcode == 0xC6) {
1265 // shufps xmm, xmm/m128, imm8
1266 int mod, regop, rm;
1267 get_modrm(*current, &mod, ®op, &rm);
1268 AppendToBuffer("shufps %s, ", NameOfXMMRegister(regop));
1269 current += PrintRightXMMOperand(current);
1270 AppendToBuffer(", %d", (*current) & 3);
1271 current += 1;
1272
1273 } else if (opcode == 0x50) {
1274 // movmskps reg, xmm
1275 int mod, regop, rm;
1276 get_modrm(*current, &mod, ®op, &rm);
1277 AppendToBuffer("movmskps %s,", NameOfCPURegister(regop));
1278 current += PrintRightXMMOperand(current);
1279
1280 } else if ((opcode & 0xF0) == 0x80) {
1281 // Jcc: Conditional jump (branch).
1282 current = data + JumpConditional(data);
1283
1284 } else if (opcode == 0xBE || opcode == 0xBF || opcode == 0xB6 ||
1285 opcode == 0xB7 || opcode == 0xAF) {
1286 // Size-extending moves, IMUL.
1287 current += PrintOperands(mnemonic, REG_OPER_OP_ORDER, current);
1288
1289 } else if ((opcode & 0xF0) == 0x90) {
1290 // SETcc: Set byte on condition. Needs pointer to beginning of instruction.
1291 current = data + SetCC(data);
1292
1293 } else if (opcode == 0xAB || opcode == 0xA5 || opcode == 0xAD) {
1294 // SHLD, SHRD (double-precision shift), BTS (bit set).
1295 AppendToBuffer("%s ", mnemonic);
1296 int mod, regop, rm;
1297 get_modrm(*current, &mod, ®op, &rm);
1298 current += PrintRightOperand(current);
1299 if (opcode == 0xAB) {
1300 AppendToBuffer(",%s", NameOfCPURegister(regop));
1301 } else {
1302 AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
1303 }
1304 } else if (opcode == 0xBD) {
1305 AppendToBuffer("%s%c ", mnemonic, operand_size_code());
1306 int mod, regop, rm;
1307 get_modrm(*current, &mod, ®op, &rm);
1308 AppendToBuffer("%s,", NameOfCPURegister(regop));
1309 current += PrintRightOperand(current);
1310 } else {
1311 UnimplementedInstruction();
1312 }
1313 return static_cast<int>(current - data);
1314 }
1315
1316
1317 // Mnemonics for two-byte opcode instructions starting with 0x0F.
1318 // The argument is the second byte of the two-byte opcode.
1319 // Returns NULL if the instruction is not handled here.
TwoByteMnemonic(byte opcode)1320 const char* DisassemblerX64::TwoByteMnemonic(byte opcode) {
1321 switch (opcode) {
1322 case 0x1F:
1323 return "nop";
1324 case 0x2A: // F2/F3 prefix.
1325 return "cvtsi2s";
1326 case 0x51: // F2 prefix.
1327 return "sqrtsd";
1328 case 0x58: // F2 prefix.
1329 return "addsd";
1330 case 0x59: // F2 prefix.
1331 return "mulsd";
1332 case 0x5A: // F2 prefix.
1333 return "cvtsd2ss";
1334 case 0x5C: // F2 prefix.
1335 return "subsd";
1336 case 0x5E: // F2 prefix.
1337 return "divsd";
1338 case 0xA2:
1339 return "cpuid";
1340 case 0xA5:
1341 return "shld";
1342 case 0xAB:
1343 return "bts";
1344 case 0xAD:
1345 return "shrd";
1346 case 0xAF:
1347 return "imul";
1348 case 0xB6:
1349 return "movzxb";
1350 case 0xB7:
1351 return "movzxw";
1352 case 0xBD:
1353 return "bsr";
1354 case 0xBE:
1355 return "movsxb";
1356 case 0xBF:
1357 return "movsxw";
1358 default:
1359 return NULL;
1360 }
1361 }
1362
1363
1364 // Disassembles the instruction at instr, and writes it into out_buffer.
InstructionDecode(v8::internal::Vector<char> out_buffer,byte * instr)1365 int DisassemblerX64::InstructionDecode(v8::internal::Vector<char> out_buffer,
1366 byte* instr) {
1367 tmp_buffer_pos_ = 0; // starting to write as position 0
1368 byte* data = instr;
1369 bool processed = true; // Will be set to false if the current instruction
1370 // is not in 'instructions' table.
1371 byte current;
1372
1373 // Scan for prefixes.
1374 while (true) {
1375 current = *data;
1376 if (current == OPERAND_SIZE_OVERRIDE_PREFIX) { // Group 3 prefix.
1377 operand_size_ = current;
1378 } else if ((current & 0xF0) == 0x40) { // REX prefix.
1379 setRex(current);
1380 if (rex_w()) AppendToBuffer("REX.W ");
1381 } else if ((current & 0xFE) == 0xF2) { // Group 1 prefix (0xF2 or 0xF3).
1382 group_1_prefix_ = current;
1383 } else { // Not a prefix - an opcode.
1384 break;
1385 }
1386 data++;
1387 }
1388
1389 const InstructionDesc& idesc = instruction_table_->Get(current);
1390 byte_size_operand_ = idesc.byte_size_operation;
1391 switch (idesc.type) {
1392 case ZERO_OPERANDS_INSTR:
1393 if (current >= 0xA4 && current <= 0xA7) {
1394 // String move or compare operations.
1395 if (group_1_prefix_ == REP_PREFIX) {
1396 // REP.
1397 AppendToBuffer("rep ");
1398 }
1399 if (rex_w()) AppendToBuffer("REX.W ");
1400 AppendToBuffer("%s%c", idesc.mnem, operand_size_code());
1401 } else {
1402 AppendToBuffer("%s", idesc.mnem, operand_size_code());
1403 }
1404 data++;
1405 break;
1406
1407 case TWO_OPERANDS_INSTR:
1408 data++;
1409 data += PrintOperands(idesc.mnem, idesc.op_order_, data);
1410 break;
1411
1412 case JUMP_CONDITIONAL_SHORT_INSTR:
1413 data += JumpConditionalShort(data);
1414 break;
1415
1416 case REGISTER_INSTR:
1417 AppendToBuffer("%s%c %s",
1418 idesc.mnem,
1419 operand_size_code(),
1420 NameOfCPURegister(base_reg(current & 0x07)));
1421 data++;
1422 break;
1423 case PUSHPOP_INSTR:
1424 AppendToBuffer("%s %s",
1425 idesc.mnem,
1426 NameOfCPURegister(base_reg(current & 0x07)));
1427 data++;
1428 break;
1429 case MOVE_REG_INSTR: {
1430 byte* addr = NULL;
1431 switch (operand_size()) {
1432 case OPERAND_WORD_SIZE:
1433 addr = reinterpret_cast<byte*>(*reinterpret_cast<int16_t*>(data + 1));
1434 data += 3;
1435 break;
1436 case OPERAND_DOUBLEWORD_SIZE:
1437 addr =
1438 reinterpret_cast<byte*>(*reinterpret_cast<uint32_t*>(data + 1));
1439 data += 5;
1440 break;
1441 case OPERAND_QUADWORD_SIZE:
1442 addr = reinterpret_cast<byte*>(*reinterpret_cast<int64_t*>(data + 1));
1443 data += 9;
1444 break;
1445 default:
1446 UNREACHABLE();
1447 }
1448 AppendToBuffer("mov%c %s,%s",
1449 operand_size_code(),
1450 NameOfCPURegister(base_reg(current & 0x07)),
1451 NameOfAddress(addr));
1452 break;
1453 }
1454
1455 case CALL_JUMP_INSTR: {
1456 byte* addr = data + *reinterpret_cast<int32_t*>(data + 1) + 5;
1457 AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
1458 data += 5;
1459 break;
1460 }
1461
1462 case SHORT_IMMEDIATE_INSTR: {
1463 byte* addr =
1464 reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1));
1465 AppendToBuffer("%s rax,%s", idesc.mnem, NameOfAddress(addr));
1466 data += 5;
1467 break;
1468 }
1469
1470 case NO_INSTR:
1471 processed = false;
1472 break;
1473
1474 default:
1475 UNIMPLEMENTED(); // This type is not implemented.
1476 }
1477
1478 // The first byte didn't match any of the simple opcodes, so we
1479 // need to do special processing on it.
1480 if (!processed) {
1481 switch (*data) {
1482 case 0xC2:
1483 AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data + 1));
1484 data += 3;
1485 break;
1486
1487 case 0x69: // fall through
1488 case 0x6B: {
1489 int mod, regop, rm;
1490 get_modrm(*(data + 1), &mod, ®op, &rm);
1491 int32_t imm = *data == 0x6B ? *(data + 2)
1492 : *reinterpret_cast<int32_t*>(data + 2);
1493 AppendToBuffer("imul%c %s,%s,0x%x",
1494 operand_size_code(),
1495 NameOfCPURegister(regop),
1496 NameOfCPURegister(rm), imm);
1497 data += 2 + (*data == 0x6B ? 1 : 4);
1498 break;
1499 }
1500
1501 case 0x81: // fall through
1502 case 0x83: // 0x81 with sign extension bit set
1503 data += PrintImmediateOp(data);
1504 break;
1505
1506 case 0x0F:
1507 data += TwoByteOpcodeInstruction(data);
1508 break;
1509
1510 case 0x8F: {
1511 data++;
1512 int mod, regop, rm;
1513 get_modrm(*data, &mod, ®op, &rm);
1514 if (regop == 0) {
1515 AppendToBuffer("pop ");
1516 data += PrintRightOperand(data);
1517 }
1518 }
1519 break;
1520
1521 case 0xFF: {
1522 data++;
1523 int mod, regop, rm;
1524 get_modrm(*data, &mod, ®op, &rm);
1525 const char* mnem = NULL;
1526 switch (regop) {
1527 case 0:
1528 mnem = "inc";
1529 break;
1530 case 1:
1531 mnem = "dec";
1532 break;
1533 case 2:
1534 mnem = "call";
1535 break;
1536 case 4:
1537 mnem = "jmp";
1538 break;
1539 case 6:
1540 mnem = "push";
1541 break;
1542 default:
1543 mnem = "???";
1544 }
1545 AppendToBuffer(((regop <= 1) ? "%s%c " : "%s "),
1546 mnem,
1547 operand_size_code());
1548 data += PrintRightOperand(data);
1549 }
1550 break;
1551
1552 case 0xC7: // imm32, fall through
1553 case 0xC6: // imm8
1554 {
1555 bool is_byte = *data == 0xC6;
1556 data++;
1557 if (is_byte) {
1558 AppendToBuffer("movb ");
1559 data += PrintRightByteOperand(data);
1560 int32_t imm = *data;
1561 AppendToBuffer(",0x%x", imm);
1562 data++;
1563 } else {
1564 AppendToBuffer("mov%c ", operand_size_code());
1565 data += PrintRightOperand(data);
1566 if (operand_size() == OPERAND_WORD_SIZE) {
1567 int16_t imm = *reinterpret_cast<int16_t*>(data);
1568 AppendToBuffer(",0x%x", imm);
1569 data += 2;
1570 } else {
1571 int32_t imm = *reinterpret_cast<int32_t*>(data);
1572 AppendToBuffer(",0x%x", imm);
1573 data += 4;
1574 }
1575 }
1576 }
1577 break;
1578
1579 case 0x80: {
1580 data++;
1581 AppendToBuffer("cmpb ");
1582 data += PrintRightByteOperand(data);
1583 int32_t imm = *data;
1584 AppendToBuffer(",0x%x", imm);
1585 data++;
1586 }
1587 break;
1588
1589 case 0x88: // 8bit, fall through
1590 case 0x89: // 32bit
1591 {
1592 bool is_byte = *data == 0x88;
1593 int mod, regop, rm;
1594 data++;
1595 get_modrm(*data, &mod, ®op, &rm);
1596 if (is_byte) {
1597 AppendToBuffer("movb ");
1598 data += PrintRightByteOperand(data);
1599 AppendToBuffer(",%s", NameOfByteCPURegister(regop));
1600 } else {
1601 AppendToBuffer("mov%c ", operand_size_code());
1602 data += PrintRightOperand(data);
1603 AppendToBuffer(",%s", NameOfCPURegister(regop));
1604 }
1605 }
1606 break;
1607
1608 case 0x90:
1609 case 0x91:
1610 case 0x92:
1611 case 0x93:
1612 case 0x94:
1613 case 0x95:
1614 case 0x96:
1615 case 0x97: {
1616 int reg = (*data & 0x7) | (rex_b() ? 8 : 0);
1617 if (reg == 0) {
1618 AppendToBuffer("nop"); // Common name for xchg rax,rax.
1619 } else {
1620 AppendToBuffer("xchg%c rax,%s",
1621 operand_size_code(),
1622 NameOfCPURegister(reg));
1623 }
1624 data++;
1625 }
1626 break;
1627 case 0xB0:
1628 case 0xB1:
1629 case 0xB2:
1630 case 0xB3:
1631 case 0xB4:
1632 case 0xB5:
1633 case 0xB6:
1634 case 0xB7:
1635 case 0xB8:
1636 case 0xB9:
1637 case 0xBA:
1638 case 0xBB:
1639 case 0xBC:
1640 case 0xBD:
1641 case 0xBE:
1642 case 0xBF: {
1643 // mov reg8,imm8 or mov reg32,imm32
1644 byte opcode = *data;
1645 data++;
1646 bool is_32bit = (opcode >= 0xB8);
1647 int reg = (opcode & 0x7) | (rex_b() ? 8 : 0);
1648 if (is_32bit) {
1649 AppendToBuffer("mov%c %s,",
1650 operand_size_code(),
1651 NameOfCPURegister(reg));
1652 data += PrintImmediate(data, OPERAND_DOUBLEWORD_SIZE);
1653 } else {
1654 AppendToBuffer("movb %s,",
1655 NameOfByteCPURegister(reg));
1656 data += PrintImmediate(data, OPERAND_BYTE_SIZE);
1657 }
1658 break;
1659 }
1660 case 0xFE: {
1661 data++;
1662 int mod, regop, rm;
1663 get_modrm(*data, &mod, ®op, &rm);
1664 if (regop == 1) {
1665 AppendToBuffer("decb ");
1666 data += PrintRightByteOperand(data);
1667 } else {
1668 UnimplementedInstruction();
1669 }
1670 break;
1671 }
1672 case 0x68:
1673 AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data + 1));
1674 data += 5;
1675 break;
1676
1677 case 0x6A:
1678 AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
1679 data += 2;
1680 break;
1681
1682 case 0xA1: // Fall through.
1683 case 0xA3:
1684 switch (operand_size()) {
1685 case OPERAND_DOUBLEWORD_SIZE: {
1686 const char* memory_location = NameOfAddress(
1687 reinterpret_cast<byte*>(
1688 *reinterpret_cast<int32_t*>(data + 1)));
1689 if (*data == 0xA1) { // Opcode 0xA1
1690 AppendToBuffer("movzxlq rax,(%s)", memory_location);
1691 } else { // Opcode 0xA3
1692 AppendToBuffer("movzxlq (%s),rax", memory_location);
1693 }
1694 data += 5;
1695 break;
1696 }
1697 case OPERAND_QUADWORD_SIZE: {
1698 // New x64 instruction mov rax,(imm_64).
1699 const char* memory_location = NameOfAddress(
1700 *reinterpret_cast<byte**>(data + 1));
1701 if (*data == 0xA1) { // Opcode 0xA1
1702 AppendToBuffer("movq rax,(%s)", memory_location);
1703 } else { // Opcode 0xA3
1704 AppendToBuffer("movq (%s),rax", memory_location);
1705 }
1706 data += 9;
1707 break;
1708 }
1709 default:
1710 UnimplementedInstruction();
1711 data += 2;
1712 }
1713 break;
1714
1715 case 0xA8:
1716 AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data + 1));
1717 data += 2;
1718 break;
1719
1720 case 0xA9: {
1721 int64_t value = 0;
1722 switch (operand_size()) {
1723 case OPERAND_WORD_SIZE:
1724 value = *reinterpret_cast<uint16_t*>(data + 1);
1725 data += 3;
1726 break;
1727 case OPERAND_DOUBLEWORD_SIZE:
1728 value = *reinterpret_cast<uint32_t*>(data + 1);
1729 data += 5;
1730 break;
1731 case OPERAND_QUADWORD_SIZE:
1732 value = *reinterpret_cast<int32_t*>(data + 1);
1733 data += 5;
1734 break;
1735 default:
1736 UNREACHABLE();
1737 }
1738 AppendToBuffer("test%c rax,0x%" V8_PTR_PREFIX "x",
1739 operand_size_code(),
1740 value);
1741 break;
1742 }
1743 case 0xD1: // fall through
1744 case 0xD3: // fall through
1745 case 0xC1:
1746 data += ShiftInstruction(data);
1747 break;
1748 case 0xD0: // fall through
1749 case 0xD2: // fall through
1750 case 0xC0:
1751 byte_size_operand_ = true;
1752 data += ShiftInstruction(data);
1753 break;
1754
1755 case 0xD9: // fall through
1756 case 0xDA: // fall through
1757 case 0xDB: // fall through
1758 case 0xDC: // fall through
1759 case 0xDD: // fall through
1760 case 0xDE: // fall through
1761 case 0xDF:
1762 data += FPUInstruction(data);
1763 break;
1764
1765 case 0xEB:
1766 data += JumpShort(data);
1767 break;
1768
1769 case 0xF6:
1770 byte_size_operand_ = true; // fall through
1771 case 0xF7:
1772 data += F6F7Instruction(data);
1773 break;
1774
1775 case 0x3C:
1776 AppendToBuffer("cmp al,0x%x", *reinterpret_cast<int8_t*>(data + 1));
1777 data +=2;
1778 break;
1779
1780 default:
1781 UnimplementedInstruction();
1782 data += 1;
1783 }
1784 } // !processed
1785
1786 if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1787 tmp_buffer_[tmp_buffer_pos_] = '\0';
1788 }
1789
1790 int instr_len = static_cast<int>(data - instr);
1791 ASSERT(instr_len > 0); // Ensure progress.
1792
1793 int outp = 0;
1794 // Instruction bytes.
1795 for (byte* bp = instr; bp < data; bp++) {
1796 outp += v8::internal::SNPrintF(out_buffer + outp, "%02x", *bp);
1797 }
1798 for (int i = 6 - instr_len; i >= 0; i--) {
1799 outp += v8::internal::SNPrintF(out_buffer + outp, " ");
1800 }
1801
1802 outp += v8::internal::SNPrintF(out_buffer + outp, " %s",
1803 tmp_buffer_.start());
1804 return instr_len;
1805 }
1806
1807
1808 //------------------------------------------------------------------------------
1809
1810
1811 static const char* cpu_regs[16] = {
1812 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
1813 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
1814 };
1815
1816
1817 static const char* byte_cpu_regs[16] = {
1818 "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
1819 "r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l"
1820 };
1821
1822
1823 static const char* xmm_regs[16] = {
1824 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
1825 "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"
1826 };
1827
1828
NameOfAddress(byte * addr) const1829 const char* NameConverter::NameOfAddress(byte* addr) const {
1830 v8::internal::SNPrintF(tmp_buffer_, "%p", addr);
1831 return tmp_buffer_.start();
1832 }
1833
1834
NameOfConstant(byte * addr) const1835 const char* NameConverter::NameOfConstant(byte* addr) const {
1836 return NameOfAddress(addr);
1837 }
1838
1839
NameOfCPURegister(int reg) const1840 const char* NameConverter::NameOfCPURegister(int reg) const {
1841 if (0 <= reg && reg < 16)
1842 return cpu_regs[reg];
1843 return "noreg";
1844 }
1845
1846
NameOfByteCPURegister(int reg) const1847 const char* NameConverter::NameOfByteCPURegister(int reg) const {
1848 if (0 <= reg && reg < 16)
1849 return byte_cpu_regs[reg];
1850 return "noreg";
1851 }
1852
1853
NameOfXMMRegister(int reg) const1854 const char* NameConverter::NameOfXMMRegister(int reg) const {
1855 if (0 <= reg && reg < 16)
1856 return xmm_regs[reg];
1857 return "noxmmreg";
1858 }
1859
1860
NameInCode(byte * addr) const1861 const char* NameConverter::NameInCode(byte* addr) const {
1862 // X64 does not embed debug strings at the moment.
1863 UNREACHABLE();
1864 return "";
1865 }
1866
1867
1868 //------------------------------------------------------------------------------
1869
Disassembler(const NameConverter & converter)1870 Disassembler::Disassembler(const NameConverter& converter)
1871 : converter_(converter) { }
1872
~Disassembler()1873 Disassembler::~Disassembler() { }
1874
1875
InstructionDecode(v8::internal::Vector<char> buffer,byte * instruction)1876 int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
1877 byte* instruction) {
1878 DisassemblerX64 d(converter_, CONTINUE_ON_UNIMPLEMENTED_OPCODE);
1879 return d.InstructionDecode(buffer, instruction);
1880 }
1881
1882
1883 // The X64 assembler does not use constant pools.
ConstantPoolSizeAt(byte * instruction)1884 int Disassembler::ConstantPoolSizeAt(byte* instruction) {
1885 return -1;
1886 }
1887
1888
Disassemble(FILE * f,byte * begin,byte * end)1889 void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
1890 NameConverter converter;
1891 Disassembler d(converter);
1892 for (byte* pc = begin; pc < end;) {
1893 v8::internal::EmbeddedVector<char, 128> buffer;
1894 buffer[0] = '\0';
1895 byte* prev_pc = pc;
1896 pc += d.InstructionDecode(buffer, pc);
1897 fprintf(f, "%p", prev_pc);
1898 fprintf(f, " ");
1899
1900 for (byte* bp = prev_pc; bp < pc; bp++) {
1901 fprintf(f, "%02x", *bp);
1902 }
1903 for (int i = 6 - static_cast<int>(pc - prev_pc); i >= 0; i--) {
1904 fprintf(f, " ");
1905 }
1906 fprintf(f, " %s\n", buffer.start());
1907 }
1908 }
1909
1910 } // namespace disasm
1911
1912 #endif // V8_TARGET_ARCH_X64
1913