blob: 458844e050254214b3f44d17c38efc88525bd70d [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2007-2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.
27
28#include <assert.h>
29#include <stdio.h>
30#include <stdarg.h>
31
32#include "v8.h"
33#include "disasm.h"
34
35namespace disasm {
36
37enum OperandOrder {
38 UNSET_OP_ORDER = 0,
39 REG_OPER_OP_ORDER,
40 OPER_REG_OP_ORDER
41};
42
43
44//------------------------------------------------------------------
45// Tables
46//------------------------------------------------------------------
47struct ByteMnemonic {
48 int b; // -1 terminates, otherwise must be in range (0..255)
49 const char* mnem;
50 OperandOrder op_order_;
51};
52
53
54static ByteMnemonic two_operands_instr[] = {
55 {0x03, "add", REG_OPER_OP_ORDER},
56 {0x21, "and", OPER_REG_OP_ORDER},
57 {0x23, "and", REG_OPER_OP_ORDER},
58 {0x3B, "cmp", REG_OPER_OP_ORDER},
59 {0x8D, "lea", REG_OPER_OP_ORDER},
60 {0x09, "or", OPER_REG_OP_ORDER},
61 {0x0B, "or", REG_OPER_OP_ORDER},
62 {0x1B, "sbb", REG_OPER_OP_ORDER},
63 {0x29, "sub", OPER_REG_OP_ORDER},
64 {0x2B, "sub", REG_OPER_OP_ORDER},
65 {0x85, "test", REG_OPER_OP_ORDER},
66 {0x31, "xor", OPER_REG_OP_ORDER},
67 {0x33, "xor", REG_OPER_OP_ORDER},
68 {0x87, "xchg", REG_OPER_OP_ORDER},
69 {0x8A, "mov_b", REG_OPER_OP_ORDER},
70 {0x8B, "mov", REG_OPER_OP_ORDER},
71 {-1, "", UNSET_OP_ORDER}
72};
73
74
75static ByteMnemonic zero_operands_instr[] = {
76 {0xC3, "ret", UNSET_OP_ORDER},
77 {0xC9, "leave", UNSET_OP_ORDER},
78 {0x90, "nop", UNSET_OP_ORDER},
79 {0xF4, "hlt", UNSET_OP_ORDER},
80 {0xCC, "int3", UNSET_OP_ORDER},
81 {0x60, "pushad", UNSET_OP_ORDER},
82 {0x61, "popad", UNSET_OP_ORDER},
83 {0x9C, "pushfd", UNSET_OP_ORDER},
84 {0x9D, "popfd", UNSET_OP_ORDER},
85 {0x9E, "sahf", UNSET_OP_ORDER},
86 {0x99, "cdq", UNSET_OP_ORDER},
87 {0x9B, "fwait", UNSET_OP_ORDER},
88 {-1, "", UNSET_OP_ORDER}
89};
90
91
92static ByteMnemonic call_jump_instr[] = {
93 {0xE8, "call", UNSET_OP_ORDER},
94 {0xE9, "jmp", UNSET_OP_ORDER},
95 {-1, "", UNSET_OP_ORDER}
96};
97
98
99static ByteMnemonic short_immediate_instr[] = {
100 {0x05, "add", UNSET_OP_ORDER},
101 {0x0D, "or", UNSET_OP_ORDER},
102 {0x15, "adc", UNSET_OP_ORDER},
103 {0x25, "and", UNSET_OP_ORDER},
104 {0x2D, "sub", UNSET_OP_ORDER},
105 {0x35, "xor", UNSET_OP_ORDER},
106 {0x3D, "cmp", UNSET_OP_ORDER},
107 {-1, "", UNSET_OP_ORDER}
108};
109
110
111static const char* jump_conditional_mnem[] = {
112 /*0*/ "jo", "jno", "jc", "jnc",
113 /*4*/ "jz", "jnz", "jna", "ja",
114 /*8*/ "js", "jns", "jpe", "jpo",
115 /*12*/ "jl", "jnl", "jng", "jg"
116};
117
118
119static const char* set_conditional_mnem[] = {
120 /*0*/ "seto", "setno", "setc", "setnc",
121 /*4*/ "setz", "setnz", "setna", "seta",
122 /*8*/ "sets", "setns", "setpe", "setpo",
123 /*12*/ "setl", "setnl", "setng", "setg"
124};
125
126
127enum InstructionType {
128 NO_INSTR,
129 ZERO_OPERANDS_INSTR,
130 TWO_OPERANDS_INSTR,
131 JUMP_CONDITIONAL_SHORT_INSTR,
132 REGISTER_INSTR,
133 MOVE_REG_INSTR,
134 CALL_JUMP_INSTR,
135 SHORT_IMMEDIATE_INSTR
136};
137
138
139struct InstructionDesc {
140 const char* mnem;
141 InstructionType type;
142 OperandOrder op_order_;
143};
144
145
146class InstructionTable {
147 public:
148 InstructionTable();
149 const InstructionDesc& Get(byte x) const { return instructions_[x]; }
150
151 private:
152 InstructionDesc instructions_[256];
153 void Clear();
154 void Init();
155 void CopyTable(ByteMnemonic bm[], InstructionType type);
156 void SetTableRange(InstructionType type,
157 byte start,
158 byte end,
159 const char* mnem);
160 void AddJumpConditionalShort();
161};
162
163
164InstructionTable::InstructionTable() {
165 Clear();
166 Init();
167}
168
169
170void InstructionTable::Clear() {
171 for (int i = 0; i < 256; i++) {
172 instructions_[i].mnem = "";
173 instructions_[i].type = NO_INSTR;
174 instructions_[i].op_order_ = UNSET_OP_ORDER;
175 }
176}
177
178
179void InstructionTable::Init() {
180 CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
181 CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR);
182 CopyTable(call_jump_instr, CALL_JUMP_INSTR);
183 CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR);
184 AddJumpConditionalShort();
185 SetTableRange(REGISTER_INSTR, 0x40, 0x47, "inc");
186 SetTableRange(REGISTER_INSTR, 0x48, 0x4F, "dec");
187 SetTableRange(REGISTER_INSTR, 0x50, 0x57, "push");
188 SetTableRange(REGISTER_INSTR, 0x58, 0x5F, "pop");
189 SetTableRange(REGISTER_INSTR, 0x91, 0x97, "xchg eax,"); // 0x90 is nop.
190 SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, "mov");
191}
192
193
194void InstructionTable::CopyTable(ByteMnemonic bm[], InstructionType type) {
195 for (int i = 0; bm[i].b >= 0; i++) {
196 InstructionDesc* id = &instructions_[bm[i].b];
197 id->mnem = bm[i].mnem;
198 id->op_order_ = bm[i].op_order_;
199 assert(id->type == NO_INSTR); // Information already entered
200 id->type = type;
201 }
202}
203
204
205void InstructionTable::SetTableRange(InstructionType type,
206 byte start,
207 byte end,
208 const char* mnem) {
209 for (byte b = start; b <= end; b++) {
210 InstructionDesc* id = &instructions_[b];
211 assert(id->type == NO_INSTR); // Information already entered
212 id->mnem = mnem;
213 id->type = type;
214 }
215}
216
217
218void InstructionTable::AddJumpConditionalShort() {
219 for (byte b = 0x70; b <= 0x7F; b++) {
220 InstructionDesc* id = &instructions_[b];
221 assert(id->type == NO_INSTR); // Information already entered
222 id->mnem = jump_conditional_mnem[b & 0x0F];
223 id->type = JUMP_CONDITIONAL_SHORT_INSTR;
224 }
225}
226
227
228static InstructionTable instruction_table;
229
230
231// The IA32 disassembler implementation.
232class DisassemblerIA32 {
233 public:
234 DisassemblerIA32(const NameConverter& converter,
235 bool abort_on_unimplemented = true)
236 : converter_(converter),
237 tmp_buffer_pos_(0),
238 abort_on_unimplemented_(abort_on_unimplemented) {
239 tmp_buffer_[0] = '\0';
240 }
241
242 virtual ~DisassemblerIA32() {}
243
244 // Writes one disassembled instruction into 'buffer' (0-terminated).
245 // Returns the length of the disassembled machine instruction in bytes.
246 int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
247
248 private:
249 const NameConverter& converter_;
250 v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
251 unsigned int tmp_buffer_pos_;
252 bool abort_on_unimplemented_;
253
254
255 enum {
256 eax = 0,
257 ecx = 1,
258 edx = 2,
259 ebx = 3,
260 esp = 4,
261 ebp = 5,
262 esi = 6,
263 edi = 7
264 };
265
266
267 const char* NameOfCPURegister(int reg) const {
268 return converter_.NameOfCPURegister(reg);
269 }
270
271
272 const char* NameOfByteCPURegister(int reg) const {
273 return converter_.NameOfByteCPURegister(reg);
274 }
275
276
277 const char* NameOfXMMRegister(int reg) const {
278 return converter_.NameOfXMMRegister(reg);
279 }
280
281
282 const char* NameOfAddress(byte* addr) const {
283 return converter_.NameOfAddress(addr);
284 }
285
286
287 // Disassembler helper functions.
288 static void get_modrm(byte data, int* mod, int* regop, int* rm) {
289 *mod = (data >> 6) & 3;
290 *regop = (data & 0x38) >> 3;
291 *rm = data & 7;
292 }
293
294
295 static void get_sib(byte data, int* scale, int* index, int* base) {
296 *scale = (data >> 6) & 3;
297 *index = (data >> 3) & 7;
298 *base = data & 7;
299 }
300
301 typedef const char* (DisassemblerIA32::*RegisterNameMapping)(int reg) const;
302
303 int PrintRightOperandHelper(byte* modrmp, RegisterNameMapping register_name);
304 int PrintRightOperand(byte* modrmp);
305 int PrintRightByteOperand(byte* modrmp);
306 int PrintOperands(const char* mnem, OperandOrder op_order, byte* data);
307 int PrintImmediateOp(byte* data);
308 int F7Instruction(byte* data);
309 int D1D3C1Instruction(byte* data);
310 int JumpShort(byte* data);
311 int JumpConditional(byte* data, const char* comment);
312 int JumpConditionalShort(byte* data, const char* comment);
313 int SetCC(byte* data);
314 int FPUInstruction(byte* data);
315 void AppendToBuffer(const char* format, ...);
316
317
318 void UnimplementedInstruction() {
319 if (abort_on_unimplemented_) {
320 UNIMPLEMENTED();
321 } else {
322 AppendToBuffer("'Unimplemented Instruction'");
323 }
324 }
325};
326
327
328void DisassemblerIA32::AppendToBuffer(const char* format, ...) {
329 v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
330 va_list args;
331 va_start(args, format);
332 int result = v8::internal::OS::VSNPrintF(buf, format, args);
333 va_end(args);
334 tmp_buffer_pos_ += result;
335}
336
337int DisassemblerIA32::PrintRightOperandHelper(
338 byte* modrmp,
339 RegisterNameMapping register_name) {
340 int mod, regop, rm;
341 get_modrm(*modrmp, &mod, &regop, &rm);
342 switch (mod) {
343 case 0:
344 if (rm == ebp) {
345 int32_t disp = *reinterpret_cast<int32_t*>(modrmp+1);
346 AppendToBuffer("[0x%x]", disp);
347 return 5;
348 } else if (rm == esp) {
349 byte sib = *(modrmp + 1);
350 int scale, index, base;
351 get_sib(sib, &scale, &index, &base);
352 if (index == esp && base == esp && scale == 0 /*times_1*/) {
353 AppendToBuffer("[%s]", (this->*register_name)(rm));
354 return 2;
355 } else if (base == ebp) {
356 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
357 AppendToBuffer("[%s*%d+0x%x]",
358 (this->*register_name)(index),
359 1 << scale,
360 disp);
361 return 6;
362 } else if (index != esp && base != ebp) {
363 // [base+index*scale]
364 AppendToBuffer("[%s+%s*%d]",
365 (this->*register_name)(base),
366 (this->*register_name)(index),
367 1 << scale);
368 return 2;
369 } else {
370 UnimplementedInstruction();
371 return 1;
372 }
373 } else {
374 AppendToBuffer("[%s]", (this->*register_name)(rm));
375 return 1;
376 }
377 break;
378 case 1: // fall through
379 case 2:
380 if (rm == esp) {
381 byte sib = *(modrmp + 1);
382 int scale, index, base;
383 get_sib(sib, &scale, &index, &base);
384 int disp =
385 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 2) : *(modrmp + 2);
386 if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) {
387 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
388 } else {
389 AppendToBuffer("[%s+%s*%d+0x%x]",
390 (this->*register_name)(base),
391 (this->*register_name)(index),
392 1 << scale,
393 disp);
394 }
395 return mod == 2 ? 6 : 3;
396 } else {
397 // No sib.
398 int disp =
399 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 1) : *(modrmp + 1);
400 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
401 return mod == 2 ? 5 : 2;
402 }
403 break;
404 case 3:
405 AppendToBuffer("%s", (this->*register_name)(rm));
406 return 1;
407 default:
408 UnimplementedInstruction();
409 return 1;
410 }
411 UNREACHABLE();
412}
413
414
415int DisassemblerIA32::PrintRightOperand(byte* modrmp) {
416 return PrintRightOperandHelper(modrmp, &DisassemblerIA32::NameOfCPURegister);
417}
418
419
420int DisassemblerIA32::PrintRightByteOperand(byte* modrmp) {
421 return PrintRightOperandHelper(modrmp,
422 &DisassemblerIA32::NameOfByteCPURegister);
423}
424
425
426// Returns number of bytes used including the current *data.
427// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
428int DisassemblerIA32::PrintOperands(const char* mnem,
429 OperandOrder op_order,
430 byte* data) {
431 byte modrm = *data;
432 int mod, regop, rm;
433 get_modrm(modrm, &mod, &regop, &rm);
434 int advance = 0;
435 switch (op_order) {
436 case REG_OPER_OP_ORDER: {
437 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
438 advance = PrintRightOperand(data);
439 break;
440 }
441 case OPER_REG_OP_ORDER: {
442 AppendToBuffer("%s ", mnem);
443 advance = PrintRightOperand(data);
444 AppendToBuffer(",%s", NameOfCPURegister(regop));
445 break;
446 }
447 default:
448 UNREACHABLE();
449 break;
450 }
451 return advance;
452}
453
454
455// Returns number of bytes used by machine instruction, including *data byte.
456// Writes immediate instructions to 'tmp_buffer_'.
457int DisassemblerIA32::PrintImmediateOp(byte* data) {
458 bool sign_extension_bit = (*data & 0x02) != 0;
459 byte modrm = *(data+1);
460 int mod, regop, rm;
461 get_modrm(modrm, &mod, &regop, &rm);
462 const char* mnem = "Imm???";
463 switch (regop) {
464 case 0: mnem = "add"; break;
465 case 1: mnem = "or"; break;
466 case 2: mnem = "adc"; break;
467 case 4: mnem = "and"; break;
468 case 5: mnem = "sub"; break;
469 case 6: mnem = "xor"; break;
470 case 7: mnem = "cmp"; break;
471 default: UnimplementedInstruction();
472 }
473 AppendToBuffer("%s ", mnem);
474 int count = PrintRightOperand(data+1);
475 if (sign_extension_bit) {
476 AppendToBuffer(",0x%x", *(data + 1 + count));
477 return 1 + count + 1 /*int8*/;
478 } else {
479 AppendToBuffer(",0x%x", *reinterpret_cast<int32_t*>(data + 1 + count));
480 return 1 + count + 4 /*int32_t*/;
481 }
482}
483
484
485// Returns number of bytes used, including *data.
486int DisassemblerIA32::F7Instruction(byte* data) {
487 assert(*data == 0xF7);
488 byte modrm = *(data+1);
489 int mod, regop, rm;
490 get_modrm(modrm, &mod, &regop, &rm);
491 if (mod == 3 && regop != 0) {
492 const char* mnem = NULL;
493 switch (regop) {
494 case 2: mnem = "not"; break;
495 case 3: mnem = "neg"; break;
496 case 4: mnem = "mul"; break;
497 case 7: mnem = "idiv"; break;
498 default: UnimplementedInstruction();
499 }
500 AppendToBuffer("%s %s", mnem, NameOfCPURegister(rm));
501 return 2;
502 } else if (mod == 3 && regop == eax) {
503 int32_t imm = *reinterpret_cast<int32_t*>(data+2);
504 AppendToBuffer("test %s,0x%x", NameOfCPURegister(rm), imm);
505 return 6;
506 } else if (regop == eax) {
507 AppendToBuffer("test ");
508 int count = PrintRightOperand(data+1);
509 int32_t imm = *reinterpret_cast<int32_t*>(data+1+count);
510 AppendToBuffer(",0x%x", imm);
511 return 1+count+4 /*int32_t*/;
512 } else {
513 UnimplementedInstruction();
514 return 2;
515 }
516}
517
518int DisassemblerIA32::D1D3C1Instruction(byte* data) {
519 byte op = *data;
520 assert(op == 0xD1 || op == 0xD3 || op == 0xC1);
521 byte modrm = *(data+1);
522 int mod, regop, rm;
523 get_modrm(modrm, &mod, &regop, &rm);
524 int imm8 = -1;
525 int num_bytes = 2;
526 if (mod == 3) {
527 const char* mnem = NULL;
528 if (op == 0xD1) {
529 imm8 = 1;
530 switch (regop) {
531 case edx: mnem = "rcl"; break;
532 case edi: mnem = "sar"; break;
533 case esp: mnem = "shl"; break;
534 default: UnimplementedInstruction();
535 }
536 } else if (op == 0xC1) {
537 imm8 = *(data+2);
538 num_bytes = 3;
539 switch (regop) {
540 case edx: mnem = "rcl"; break;
541 case esp: mnem = "shl"; break;
542 case ebp: mnem = "shr"; break;
543 case edi: mnem = "sar"; break;
544 default: UnimplementedInstruction();
545 }
546 } else if (op == 0xD3) {
547 switch (regop) {
548 case esp: mnem = "shl"; break;
549 case ebp: mnem = "shr"; break;
550 case edi: mnem = "sar"; break;
551 default: UnimplementedInstruction();
552 }
553 }
554 assert(mnem != NULL);
555 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(rm));
556 if (imm8 > 0) {
557 AppendToBuffer("%d", imm8);
558 } else {
559 AppendToBuffer("cl");
560 }
561 } else {
562 UnimplementedInstruction();
563 }
564 return num_bytes;
565}
566
567
568// Returns number of bytes used, including *data.
569int DisassemblerIA32::JumpShort(byte* data) {
570 assert(*data == 0xEB);
571 byte b = *(data+1);
572 byte* dest = data + static_cast<int8_t>(b) + 2;
573 AppendToBuffer("jmp %s", NameOfAddress(dest));
574 return 2;
575}
576
577
578// Returns number of bytes used, including *data.
579int DisassemblerIA32::JumpConditional(byte* data, const char* comment) {
580 assert(*data == 0x0F);
581 byte cond = *(data+1) & 0x0F;
582 byte* dest = data + *reinterpret_cast<int32_t*>(data+2) + 6;
583 const char* mnem = jump_conditional_mnem[cond];
584 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
585 if (comment != NULL) {
586 AppendToBuffer(", %s", comment);
587 }
588 return 6; // includes 0x0F
589}
590
591
592// Returns number of bytes used, including *data.
593int DisassemblerIA32::JumpConditionalShort(byte* data, const char* comment) {
594 byte cond = *data & 0x0F;
595 byte b = *(data+1);
596 byte* dest = data + static_cast<int8_t>(b) + 2;
597 const char* mnem = jump_conditional_mnem[cond];
598 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
599 if (comment != NULL) {
600 AppendToBuffer(", %s", comment);
601 }
602 return 2;
603}
604
605
606// Returns number of bytes used, including *data.
607int DisassemblerIA32::SetCC(byte* data) {
608 assert(*data == 0x0F);
609 byte cond = *(data+1) & 0x0F;
610 const char* mnem = set_conditional_mnem[cond];
611 AppendToBuffer("%s ", mnem);
612 PrintRightByteOperand(data+2);
613 return 3; // includes 0x0F
614}
615
616
617// Returns number of bytes used, including *data.
618int DisassemblerIA32::FPUInstruction(byte* data) {
619 byte b1 = *data;
620 byte b2 = *(data + 1);
621 if (b1 == 0xD9) {
622 const char* mnem = NULL;
623 switch (b2) {
624 case 0xE8: mnem = "fld1"; break;
625 case 0xEE: mnem = "fldz"; break;
626 case 0xE1: mnem = "fabs"; break;
627 case 0xE0: mnem = "fchs"; break;
628 case 0xF8: mnem = "fprem"; break;
629 case 0xF5: mnem = "fprem1"; break;
630 case 0xF7: mnem = "fincstp"; break;
631 case 0xE4: mnem = "ftst"; break;
632 }
633 if (mnem != NULL) {
634 AppendToBuffer("%s", mnem);
635 return 2;
636 } else if ((b2 & 0xF8) == 0xC8) {
637 AppendToBuffer("fxch st%d", b2 & 0x7);
638 return 2;
639 } else {
640 int mod, regop, rm;
641 get_modrm(*(data+1), &mod, &regop, &rm);
642 const char* mnem = "?";
643 switch (regop) {
644 case eax: mnem = "fld_s"; break;
645 case ebx: mnem = "fstp_s"; break;
646 default: UnimplementedInstruction();
647 }
648 AppendToBuffer("%s ", mnem);
649 int count = PrintRightOperand(data + 1);
650 return count + 1;
651 }
652 } else if (b1 == 0xDD) {
653 if ((b2 & 0xF8) == 0xC0) {
654 AppendToBuffer("ffree st%d", b2 & 0x7);
655 return 2;
656 } else {
657 int mod, regop, rm;
658 get_modrm(*(data+1), &mod, &regop, &rm);
659 const char* mnem = "?";
660 switch (regop) {
661 case eax: mnem = "fld_d"; break;
662 case ebx: mnem = "fstp_d"; break;
663 default: UnimplementedInstruction();
664 }
665 AppendToBuffer("%s ", mnem);
666 int count = PrintRightOperand(data + 1);
667 return count + 1;
668 }
669 } else if (b1 == 0xDB) {
670 int mod, regop, rm;
671 get_modrm(*(data+1), &mod, &regop, &rm);
672 const char* mnem = "?";
673 switch (regop) {
674 case eax: mnem = "fild_s"; break;
675 case edx: mnem = "fist_s"; break;
676 case ebx: mnem = "fistp_s"; break;
677 default: UnimplementedInstruction();
678 }
679 AppendToBuffer("%s ", mnem);
680 int count = PrintRightOperand(data + 1);
681 return count + 1;
682 } else if (b1 == 0xDF) {
683 if (b2 == 0xE0) {
684 AppendToBuffer("fnstsw_ax");
685 return 2;
686 }
687 int mod, regop, rm;
688 get_modrm(*(data+1), &mod, &regop, &rm);
689 const char* mnem = "?";
690 switch (regop) {
691 case ebp: mnem = "fild_d"; break;
692 case edi: mnem = "fistp_d"; break;
693 default: UnimplementedInstruction();
694 }
695 AppendToBuffer("%s ", mnem);
696 int count = PrintRightOperand(data + 1);
697 return count + 1;
698 } else if (b1 == 0xDC || b1 == 0xDE) {
699 bool is_pop = (b1 == 0xDE);
700 if (is_pop && b2 == 0xD9) {
701 AppendToBuffer("fcompp");
702 return 2;
703 }
704 const char* mnem = "FP0xDC";
705 switch (b2 & 0xF8) {
706 case 0xC0: mnem = "fadd"; break;
707 case 0xE8: mnem = "fsub"; break;
708 case 0xC8: mnem = "fmul"; break;
709 case 0xF8: mnem = "fdiv"; break;
710 default: UnimplementedInstruction();
711 }
712 AppendToBuffer("%s%s st%d", mnem, is_pop ? "p" : "", b2 & 0x7);
713 return 2;
714 } else if (b1 == 0xDA && b2 == 0xE9) {
715 const char* mnem = "fucompp";
716 AppendToBuffer("%s", mnem);
717 return 2;
718 }
719 AppendToBuffer("Unknown FP instruction");
720 return 2;
721}
722
723
724// Mnemonics for instructions 0xF0 byte.
725// Returns NULL if the instruction is not handled here.
726static const char* F0Mnem(byte f0byte) {
727 switch (f0byte) {
728 case 0xA2: return "cpuid";
729 case 0x31: return "rdtsc";
730 case 0xBE: return "movsx_b";
731 case 0xBF: return "movsx_w";
732 case 0xB6: return "movzx_b";
733 case 0xB7: return "movzx_w";
734 case 0xAF: return "imul";
735 case 0xA5: return "shld";
736 case 0xAD: return "shrd";
737 case 0xAB: return "bts";
738 default: return NULL;
739 }
740}
741
742
743// Disassembled instruction '*instr' and writes it into 'out_buffer'.
744int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
745 byte* instr) {
746 tmp_buffer_pos_ = 0; // starting to write as position 0
747 byte* data = instr;
748 // Check for hints.
749 const char* branch_hint = NULL;
750 // We use these two prefixes only with branch prediction
751 if (*data == 0x3E /*ds*/) {
752 branch_hint = "predicted taken";
753 data++;
754 } else if (*data == 0x2E /*cs*/) {
755 branch_hint = "predicted not taken";
756 data++;
757 }
758 bool processed = true; // Will be set to false if the current instruction
759 // is not in 'instructions' table.
760 const InstructionDesc& idesc = instruction_table.Get(*data);
761 switch (idesc.type) {
762 case ZERO_OPERANDS_INSTR:
763 AppendToBuffer(idesc.mnem);
764 data++;
765 break;
766
767 case TWO_OPERANDS_INSTR:
768 data++;
769 data += PrintOperands(idesc.mnem, idesc.op_order_, data);
770 break;
771
772 case JUMP_CONDITIONAL_SHORT_INSTR:
773 data += JumpConditionalShort(data, branch_hint);
774 break;
775
776 case REGISTER_INSTR:
777 AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07));
778 data++;
779 break;
780
781 case MOVE_REG_INSTR: {
782 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
783 AppendToBuffer("mov %s,%s",
784 NameOfCPURegister(*data & 0x07),
785 NameOfAddress(addr));
786 data += 5;
787 break;
788 }
789
790 case CALL_JUMP_INSTR: {
791 byte* addr = data + *reinterpret_cast<int32_t*>(data+1) + 5;
792 AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
793 data += 5;
794 break;
795 }
796
797 case SHORT_IMMEDIATE_INSTR: {
798 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
799 AppendToBuffer("%s eax, %s", idesc.mnem, NameOfAddress(addr));
800 data += 5;
801 break;
802 }
803
804 case NO_INSTR:
805 processed = false;
806 break;
807
808 default:
809 UNIMPLEMENTED(); // This type is not implemented.
810 }
811 //----------------------------
812 if (!processed) {
813 switch (*data) {
814 case 0xC2:
815 AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data+1));
816 data += 3;
817 break;
818
819 case 0x69: // fall through
820 case 0x6B:
821 { int mod, regop, rm;
822 get_modrm(*(data+1), &mod, &regop, &rm);
823 int32_t imm =
824 *data == 0x6B ? *(data+2) : *reinterpret_cast<int32_t*>(data+2);
825 AppendToBuffer("imul %s,%s,0x%x",
826 NameOfCPURegister(regop),
827 NameOfCPURegister(rm),
828 imm);
829 data += 2 + (*data == 0x6B ? 1 : 4);
830 }
831 break;
832
833 case 0xF6:
834 { int mod, regop, rm;
835 get_modrm(*(data+1), &mod, &regop, &rm);
836 if (mod == 3 && regop == eax) {
837 AppendToBuffer("test_b %s,%d", NameOfCPURegister(rm), *(data+2));
838 } else {
839 UnimplementedInstruction();
840 }
841 data += 3;
842 }
843 break;
844
845 case 0x81: // fall through
846 case 0x83: // 0x81 with sign extension bit set
847 data += PrintImmediateOp(data);
848 break;
849
850 case 0x0F:
851 { byte f0byte = *(data+1);
852 const char* f0mnem = F0Mnem(f0byte);
853 if (f0byte == 0xA2 || f0byte == 0x31) {
854 AppendToBuffer("%s", f0mnem);
855 data += 2;
856 } else if ((f0byte & 0xF0) == 0x80) {
857 data += JumpConditional(data, branch_hint);
858 } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
859 f0byte == 0xB7 || f0byte == 0xAF) {
860 data += 2;
861 data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data);
862 } else if ((f0byte & 0xF0) == 0x90) {
863 data += SetCC(data);
864 } else {
865 data += 2;
866 if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) {
867 // shrd, shld, bts
868 AppendToBuffer("%s ", f0mnem);
869 int mod, regop, rm;
870 get_modrm(*data, &mod, &regop, &rm);
871 data += PrintRightOperand(data);
872 if (f0byte == 0xAB) {
873 AppendToBuffer(",%s", NameOfCPURegister(regop));
874 } else {
875 AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
876 }
877 } else {
878 UnimplementedInstruction();
879 }
880 }
881 }
882 break;
883
884 case 0x8F:
885 { data++;
886 int mod, regop, rm;
887 get_modrm(*data, &mod, &regop, &rm);
888 if (regop == eax) {
889 AppendToBuffer("pop ");
890 data += PrintRightOperand(data);
891 }
892 }
893 break;
894
895 case 0xFF:
896 { data++;
897 int mod, regop, rm;
898 get_modrm(*data, &mod, &regop, &rm);
899 const char* mnem = NULL;
900 switch (regop) {
901 case esi: mnem = "push"; break;
902 case eax: mnem = "inc"; break;
903 case ecx: mnem = "dec"; break;
904 case edx: mnem = "call"; break;
905 case esp: mnem = "jmp"; break;
906 default: mnem = "???";
907 }
908 AppendToBuffer("%s ", mnem);
909 data += PrintRightOperand(data);
910 }
911 break;
912
913 case 0xC7: // imm32, fall through
914 case 0xC6: // imm8
915 { bool is_byte = *data == 0xC6;
916 data++;
917 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
918 data += PrintRightOperand(data);
919 int32_t imm = is_byte ? *data : *reinterpret_cast<int32_t*>(data);
920 AppendToBuffer(",0x%x", imm);
921 data += is_byte ? 1 : 4;
922 }
923 break;
924
925 case 0x80:
926 { data++;
927 AppendToBuffer("%s ", "cmpb");
928 data += PrintRightOperand(data);
929 int32_t imm = *data;
930 AppendToBuffer(",0x%x", imm);
931 data++;
932 }
933 break;
934
935 case 0x88: // 8bit, fall through
936 case 0x89: // 32bit
937 { bool is_byte = *data == 0x88;
938 int mod, regop, rm;
939 data++;
940 get_modrm(*data, &mod, &regop, &rm);
941 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
942 data += PrintRightOperand(data);
943 AppendToBuffer(",%s", NameOfCPURegister(regop));
944 }
945 break;
946
947 case 0x66: // prefix
948 data++;
949 if (*data == 0x8B) {
950 data++;
951 data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data);
952 } else if (*data == 0x89) {
953 data++;
954 int mod, regop, rm;
955 get_modrm(*data, &mod, &regop, &rm);
956 AppendToBuffer("mov_w ");
957 data += PrintRightOperand(data);
958 AppendToBuffer(",%s", NameOfCPURegister(regop));
959 } else {
960 UnimplementedInstruction();
961 }
962 break;
963
964 case 0xFE:
965 { data++;
966 int mod, regop, rm;
967 get_modrm(*data, &mod, &regop, &rm);
968 if (mod == 3 && regop == ecx) {
969 AppendToBuffer("dec_b %s", NameOfCPURegister(rm));
970 } else {
971 UnimplementedInstruction();
972 }
973 data++;
974 }
975 break;
976
977 case 0x68:
978 AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data+1));
979 data += 5;
980 break;
981
982 case 0x6A:
983 AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
984 data += 2;
985 break;
986
987 case 0xA8:
988 AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data+1));
989 data += 2;
990 break;
991
992 case 0xA9:
993 AppendToBuffer("test eax,0x%x", *reinterpret_cast<int32_t*>(data+1));
994 data += 5;
995 break;
996
997 case 0xD1: // fall through
998 case 0xD3: // fall through
999 case 0xC1:
1000 data += D1D3C1Instruction(data);
1001 break;
1002
1003 case 0xD9: // fall through
1004 case 0xDA: // fall through
1005 case 0xDB: // fall through
1006 case 0xDC: // fall through
1007 case 0xDD: // fall through
1008 case 0xDE: // fall through
1009 case 0xDF:
1010 data += FPUInstruction(data);
1011 break;
1012
1013 case 0xEB:
1014 data += JumpShort(data);
1015 break;
1016
1017 case 0xF2:
1018 if (*(data+1) == 0x0F) {
1019 byte b2 = *(data+2);
1020 if (b2 == 0x11) {
1021 AppendToBuffer("movsd ");
1022 data += 3;
1023 int mod, regop, rm;
1024 get_modrm(*data, &mod, &regop, &rm);
1025 data += PrintRightOperand(data);
1026 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1027 } else if (b2 == 0x10) {
1028 data += 3;
1029 int mod, regop, rm;
1030 get_modrm(*data, &mod, &regop, &rm);
1031 AppendToBuffer("movsd %s,", NameOfXMMRegister(regop));
1032 data += PrintRightOperand(data);
1033 } else {
1034 const char* mnem = "?";
1035 switch (b2) {
1036 case 0x2A: mnem = "cvtsi2sd"; break;
1037 case 0x58: mnem = "addsd"; break;
1038 case 0x59: mnem = "mulsd"; break;
1039 case 0x5C: mnem = "subsd"; break;
1040 case 0x5E: mnem = "divsd"; break;
1041 }
1042 data += 3;
1043 int mod, regop, rm;
1044 get_modrm(*data, &mod, &regop, &rm);
1045 if (b2 == 0x2A) {
1046 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1047 data += PrintRightOperand(data);
1048 } else {
1049 AppendToBuffer("%s %s,%s",
1050 mnem,
1051 NameOfXMMRegister(regop),
1052 NameOfXMMRegister(rm));
1053 data++;
1054 }
1055 }
1056 } else {
1057 UnimplementedInstruction();
1058 }
1059 break;
1060
1061 case 0xF3:
1062 if (*(data+1) == 0x0F && *(data+2) == 0x2C) {
1063 data += 3;
1064 data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data);
1065 } else {
1066 UnimplementedInstruction();
1067 }
1068 break;
1069
1070 case 0xF7:
1071 data += F7Instruction(data);
1072 break;
1073
1074 default:
1075 UnimplementedInstruction();
1076 }
1077 }
1078
1079 if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1080 tmp_buffer_[tmp_buffer_pos_] = '\0';
1081 }
1082
1083 int instr_len = data - instr;
1084 ASSERT(instr_len > 0); // Ensure progress.
1085
1086 int outp = 0;
1087 // Instruction bytes.
1088 for (byte* bp = instr; bp < data; bp++) {
1089 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1090 "%02x",
1091 *bp);
1092 }
1093 for (int i = 6 - instr_len; i >= 0; i--) {
1094 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1095 " ");
1096 }
1097
1098 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1099 " %s",
1100 tmp_buffer_.start());
1101 return instr_len;
1102}
1103
1104
1105//------------------------------------------------------------------------------
1106
1107
1108static const char* cpu_regs[8] = {
1109 "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
1110};
1111
1112
1113static const char* byte_cpu_regs[8] = {
1114 "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"
1115};
1116
1117
1118static const char* xmm_regs[8] = {
1119 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
1120};
1121
1122
1123const char* NameConverter::NameOfAddress(byte* addr) const {
1124 static v8::internal::EmbeddedVector<char, 32> tmp_buffer;
1125 v8::internal::OS::SNPrintF(tmp_buffer, "%p", addr);
1126 return tmp_buffer.start();
1127}
1128
1129
1130const char* NameConverter::NameOfConstant(byte* addr) const {
1131 return NameOfAddress(addr);
1132}
1133
1134
1135const char* NameConverter::NameOfCPURegister(int reg) const {
1136 if (0 <= reg && reg < 8) return cpu_regs[reg];
1137 return "noreg";
1138}
1139
1140
1141const char* NameConverter::NameOfByteCPURegister(int reg) const {
1142 if (0 <= reg && reg < 8) return byte_cpu_regs[reg];
1143 return "noreg";
1144}
1145
1146
1147const char* NameConverter::NameOfXMMRegister(int reg) const {
1148 if (0 <= reg && reg < 8) return xmm_regs[reg];
1149 return "noxmmreg";
1150}
1151
1152
1153const char* NameConverter::NameInCode(byte* addr) const {
1154 // IA32 does not embed debug strings at the moment.
1155 UNREACHABLE();
1156 return "";
1157}
1158
1159
1160//------------------------------------------------------------------------------
1161
1162Disassembler::Disassembler(const NameConverter& converter)
1163 : converter_(converter) {}
1164
1165
1166Disassembler::~Disassembler() {}
1167
1168
1169int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
1170 byte* instruction) {
1171 DisassemblerIA32 d(converter_, false /*do not crash if unimplemented*/);
1172 return d.InstructionDecode(buffer, instruction);
1173}
1174
1175
1176// The IA-32 assembler does not currently use constant pools.
1177int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; }
1178
1179
1180/*static*/ void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
1181 NameConverter converter;
1182 Disassembler d(converter);
1183 for (byte* pc = begin; pc < end;) {
1184 v8::internal::EmbeddedVector<char, 128> buffer;
1185 buffer[0] = '\0';
1186 byte* prev_pc = pc;
1187 pc += d.InstructionDecode(buffer, pc);
1188 fprintf(f, "%p", prev_pc);
1189 fprintf(f, " ");
1190
1191 for (byte* bp = prev_pc; bp < pc; bp++) {
1192 fprintf(f, "%02x", *bp);
1193 }
1194 for (int i = 6 - (pc - prev_pc); i >= 0; i--) {
1195 fprintf(f, " ");
1196 }
1197 fprintf(f, " %s\n", buffer.start());
1198 }
1199}
1200
1201
1202} // namespace disasm