blob: adedf348994e34ce240a054f92c3c4e70f52b170 [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
Steve Block3ce2e202009-11-05 08:53:23 +0000127static const char* conditional_move_mnem[] = {
128 /*0*/ "cmovo", "cmovno", "cmovc", "cmovnc",
129 /*4*/ "cmovz", "cmovnz", "cmovna", "cmova",
130 /*8*/ "cmovs", "cmovns", "cmovpe", "cmovpo",
131 /*12*/ "cmovl", "cmovnl", "cmovng", "cmovg"
132};
133
134
Steve Blocka7e24c12009-10-30 11:49:00 +0000135enum InstructionType {
136 NO_INSTR,
137 ZERO_OPERANDS_INSTR,
138 TWO_OPERANDS_INSTR,
139 JUMP_CONDITIONAL_SHORT_INSTR,
140 REGISTER_INSTR,
141 MOVE_REG_INSTR,
142 CALL_JUMP_INSTR,
143 SHORT_IMMEDIATE_INSTR
144};
145
146
147struct InstructionDesc {
148 const char* mnem;
149 InstructionType type;
150 OperandOrder op_order_;
151};
152
153
154class InstructionTable {
155 public:
156 InstructionTable();
157 const InstructionDesc& Get(byte x) const { return instructions_[x]; }
158
159 private:
160 InstructionDesc instructions_[256];
161 void Clear();
162 void Init();
163 void CopyTable(ByteMnemonic bm[], InstructionType type);
164 void SetTableRange(InstructionType type,
165 byte start,
166 byte end,
167 const char* mnem);
168 void AddJumpConditionalShort();
169};
170
171
172InstructionTable::InstructionTable() {
173 Clear();
174 Init();
175}
176
177
178void InstructionTable::Clear() {
179 for (int i = 0; i < 256; i++) {
180 instructions_[i].mnem = "";
181 instructions_[i].type = NO_INSTR;
182 instructions_[i].op_order_ = UNSET_OP_ORDER;
183 }
184}
185
186
187void InstructionTable::Init() {
188 CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
189 CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR);
190 CopyTable(call_jump_instr, CALL_JUMP_INSTR);
191 CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR);
192 AddJumpConditionalShort();
193 SetTableRange(REGISTER_INSTR, 0x40, 0x47, "inc");
194 SetTableRange(REGISTER_INSTR, 0x48, 0x4F, "dec");
195 SetTableRange(REGISTER_INSTR, 0x50, 0x57, "push");
196 SetTableRange(REGISTER_INSTR, 0x58, 0x5F, "pop");
197 SetTableRange(REGISTER_INSTR, 0x91, 0x97, "xchg eax,"); // 0x90 is nop.
198 SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, "mov");
199}
200
201
202void InstructionTable::CopyTable(ByteMnemonic bm[], InstructionType type) {
203 for (int i = 0; bm[i].b >= 0; i++) {
204 InstructionDesc* id = &instructions_[bm[i].b];
205 id->mnem = bm[i].mnem;
206 id->op_order_ = bm[i].op_order_;
207 assert(id->type == NO_INSTR); // Information already entered
208 id->type = type;
209 }
210}
211
212
213void InstructionTable::SetTableRange(InstructionType type,
214 byte start,
215 byte end,
216 const char* mnem) {
217 for (byte b = start; b <= end; b++) {
218 InstructionDesc* id = &instructions_[b];
219 assert(id->type == NO_INSTR); // Information already entered
220 id->mnem = mnem;
221 id->type = type;
222 }
223}
224
225
226void InstructionTable::AddJumpConditionalShort() {
227 for (byte b = 0x70; b <= 0x7F; b++) {
228 InstructionDesc* id = &instructions_[b];
229 assert(id->type == NO_INSTR); // Information already entered
230 id->mnem = jump_conditional_mnem[b & 0x0F];
231 id->type = JUMP_CONDITIONAL_SHORT_INSTR;
232 }
233}
234
235
236static InstructionTable instruction_table;
237
238
239// The IA32 disassembler implementation.
240class DisassemblerIA32 {
241 public:
242 DisassemblerIA32(const NameConverter& converter,
243 bool abort_on_unimplemented = true)
244 : converter_(converter),
245 tmp_buffer_pos_(0),
246 abort_on_unimplemented_(abort_on_unimplemented) {
247 tmp_buffer_[0] = '\0';
248 }
249
250 virtual ~DisassemblerIA32() {}
251
252 // Writes one disassembled instruction into 'buffer' (0-terminated).
253 // Returns the length of the disassembled machine instruction in bytes.
254 int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
255
256 private:
257 const NameConverter& converter_;
258 v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
259 unsigned int tmp_buffer_pos_;
260 bool abort_on_unimplemented_;
261
262
263 enum {
264 eax = 0,
265 ecx = 1,
266 edx = 2,
267 ebx = 3,
268 esp = 4,
269 ebp = 5,
270 esi = 6,
271 edi = 7
272 };
273
274
275 const char* NameOfCPURegister(int reg) const {
276 return converter_.NameOfCPURegister(reg);
277 }
278
279
280 const char* NameOfByteCPURegister(int reg) const {
281 return converter_.NameOfByteCPURegister(reg);
282 }
283
284
285 const char* NameOfXMMRegister(int reg) const {
286 return converter_.NameOfXMMRegister(reg);
287 }
288
289
290 const char* NameOfAddress(byte* addr) const {
291 return converter_.NameOfAddress(addr);
292 }
293
294
295 // Disassembler helper functions.
296 static void get_modrm(byte data, int* mod, int* regop, int* rm) {
297 *mod = (data >> 6) & 3;
298 *regop = (data & 0x38) >> 3;
299 *rm = data & 7;
300 }
301
302
303 static void get_sib(byte data, int* scale, int* index, int* base) {
304 *scale = (data >> 6) & 3;
305 *index = (data >> 3) & 7;
306 *base = data & 7;
307 }
308
309 typedef const char* (DisassemblerIA32::*RegisterNameMapping)(int reg) const;
310
311 int PrintRightOperandHelper(byte* modrmp, RegisterNameMapping register_name);
312 int PrintRightOperand(byte* modrmp);
313 int PrintRightByteOperand(byte* modrmp);
314 int PrintOperands(const char* mnem, OperandOrder op_order, byte* data);
315 int PrintImmediateOp(byte* data);
316 int F7Instruction(byte* data);
317 int D1D3C1Instruction(byte* data);
318 int JumpShort(byte* data);
319 int JumpConditional(byte* data, const char* comment);
320 int JumpConditionalShort(byte* data, const char* comment);
321 int SetCC(byte* data);
Steve Block3ce2e202009-11-05 08:53:23 +0000322 int CMov(byte* data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000323 int FPUInstruction(byte* data);
324 void AppendToBuffer(const char* format, ...);
325
326
327 void UnimplementedInstruction() {
328 if (abort_on_unimplemented_) {
329 UNIMPLEMENTED();
330 } else {
331 AppendToBuffer("'Unimplemented Instruction'");
332 }
333 }
334};
335
336
337void DisassemblerIA32::AppendToBuffer(const char* format, ...) {
338 v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
339 va_list args;
340 va_start(args, format);
341 int result = v8::internal::OS::VSNPrintF(buf, format, args);
342 va_end(args);
343 tmp_buffer_pos_ += result;
344}
345
346int DisassemblerIA32::PrintRightOperandHelper(
347 byte* modrmp,
348 RegisterNameMapping register_name) {
349 int mod, regop, rm;
350 get_modrm(*modrmp, &mod, &regop, &rm);
351 switch (mod) {
352 case 0:
353 if (rm == ebp) {
354 int32_t disp = *reinterpret_cast<int32_t*>(modrmp+1);
355 AppendToBuffer("[0x%x]", disp);
356 return 5;
357 } else if (rm == esp) {
358 byte sib = *(modrmp + 1);
359 int scale, index, base;
360 get_sib(sib, &scale, &index, &base);
361 if (index == esp && base == esp && scale == 0 /*times_1*/) {
362 AppendToBuffer("[%s]", (this->*register_name)(rm));
363 return 2;
364 } else if (base == ebp) {
365 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
366 AppendToBuffer("[%s*%d+0x%x]",
367 (this->*register_name)(index),
368 1 << scale,
369 disp);
370 return 6;
371 } else if (index != esp && base != ebp) {
372 // [base+index*scale]
373 AppendToBuffer("[%s+%s*%d]",
374 (this->*register_name)(base),
375 (this->*register_name)(index),
376 1 << scale);
377 return 2;
378 } else {
379 UnimplementedInstruction();
380 return 1;
381 }
382 } else {
383 AppendToBuffer("[%s]", (this->*register_name)(rm));
384 return 1;
385 }
386 break;
387 case 1: // fall through
388 case 2:
389 if (rm == esp) {
390 byte sib = *(modrmp + 1);
391 int scale, index, base;
392 get_sib(sib, &scale, &index, &base);
393 int disp =
394 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 2) : *(modrmp + 2);
395 if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) {
396 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
397 } else {
398 AppendToBuffer("[%s+%s*%d+0x%x]",
399 (this->*register_name)(base),
400 (this->*register_name)(index),
401 1 << scale,
402 disp);
403 }
404 return mod == 2 ? 6 : 3;
405 } else {
406 // No sib.
407 int disp =
408 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 1) : *(modrmp + 1);
409 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
410 return mod == 2 ? 5 : 2;
411 }
412 break;
413 case 3:
414 AppendToBuffer("%s", (this->*register_name)(rm));
415 return 1;
416 default:
417 UnimplementedInstruction();
418 return 1;
419 }
420 UNREACHABLE();
421}
422
423
424int DisassemblerIA32::PrintRightOperand(byte* modrmp) {
425 return PrintRightOperandHelper(modrmp, &DisassemblerIA32::NameOfCPURegister);
426}
427
428
429int DisassemblerIA32::PrintRightByteOperand(byte* modrmp) {
430 return PrintRightOperandHelper(modrmp,
431 &DisassemblerIA32::NameOfByteCPURegister);
432}
433
434
435// Returns number of bytes used including the current *data.
436// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
437int DisassemblerIA32::PrintOperands(const char* mnem,
438 OperandOrder op_order,
439 byte* data) {
440 byte modrm = *data;
441 int mod, regop, rm;
442 get_modrm(modrm, &mod, &regop, &rm);
443 int advance = 0;
444 switch (op_order) {
445 case REG_OPER_OP_ORDER: {
446 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
447 advance = PrintRightOperand(data);
448 break;
449 }
450 case OPER_REG_OP_ORDER: {
451 AppendToBuffer("%s ", mnem);
452 advance = PrintRightOperand(data);
453 AppendToBuffer(",%s", NameOfCPURegister(regop));
454 break;
455 }
456 default:
457 UNREACHABLE();
458 break;
459 }
460 return advance;
461}
462
463
464// Returns number of bytes used by machine instruction, including *data byte.
465// Writes immediate instructions to 'tmp_buffer_'.
466int DisassemblerIA32::PrintImmediateOp(byte* data) {
467 bool sign_extension_bit = (*data & 0x02) != 0;
468 byte modrm = *(data+1);
469 int mod, regop, rm;
470 get_modrm(modrm, &mod, &regop, &rm);
471 const char* mnem = "Imm???";
472 switch (regop) {
473 case 0: mnem = "add"; break;
474 case 1: mnem = "or"; break;
475 case 2: mnem = "adc"; break;
476 case 4: mnem = "and"; break;
477 case 5: mnem = "sub"; break;
478 case 6: mnem = "xor"; break;
479 case 7: mnem = "cmp"; break;
480 default: UnimplementedInstruction();
481 }
482 AppendToBuffer("%s ", mnem);
483 int count = PrintRightOperand(data+1);
484 if (sign_extension_bit) {
485 AppendToBuffer(",0x%x", *(data + 1 + count));
486 return 1 + count + 1 /*int8*/;
487 } else {
488 AppendToBuffer(",0x%x", *reinterpret_cast<int32_t*>(data + 1 + count));
489 return 1 + count + 4 /*int32_t*/;
490 }
491}
492
493
494// Returns number of bytes used, including *data.
495int DisassemblerIA32::F7Instruction(byte* data) {
496 assert(*data == 0xF7);
497 byte modrm = *(data+1);
498 int mod, regop, rm;
499 get_modrm(modrm, &mod, &regop, &rm);
500 if (mod == 3 && regop != 0) {
501 const char* mnem = NULL;
502 switch (regop) {
503 case 2: mnem = "not"; break;
504 case 3: mnem = "neg"; break;
505 case 4: mnem = "mul"; break;
506 case 7: mnem = "idiv"; break;
507 default: UnimplementedInstruction();
508 }
509 AppendToBuffer("%s %s", mnem, NameOfCPURegister(rm));
510 return 2;
511 } else if (mod == 3 && regop == eax) {
512 int32_t imm = *reinterpret_cast<int32_t*>(data+2);
513 AppendToBuffer("test %s,0x%x", NameOfCPURegister(rm), imm);
514 return 6;
515 } else if (regop == eax) {
516 AppendToBuffer("test ");
517 int count = PrintRightOperand(data+1);
518 int32_t imm = *reinterpret_cast<int32_t*>(data+1+count);
519 AppendToBuffer(",0x%x", imm);
520 return 1+count+4 /*int32_t*/;
521 } else {
522 UnimplementedInstruction();
523 return 2;
524 }
525}
526
527int DisassemblerIA32::D1D3C1Instruction(byte* data) {
528 byte op = *data;
529 assert(op == 0xD1 || op == 0xD3 || op == 0xC1);
530 byte modrm = *(data+1);
531 int mod, regop, rm;
532 get_modrm(modrm, &mod, &regop, &rm);
533 int imm8 = -1;
534 int num_bytes = 2;
535 if (mod == 3) {
536 const char* mnem = NULL;
537 if (op == 0xD1) {
538 imm8 = 1;
539 switch (regop) {
540 case edx: mnem = "rcl"; break;
541 case edi: mnem = "sar"; break;
542 case esp: mnem = "shl"; break;
543 default: UnimplementedInstruction();
544 }
545 } else if (op == 0xC1) {
546 imm8 = *(data+2);
547 num_bytes = 3;
548 switch (regop) {
549 case edx: mnem = "rcl"; break;
550 case esp: mnem = "shl"; break;
551 case ebp: mnem = "shr"; break;
552 case edi: mnem = "sar"; break;
553 default: UnimplementedInstruction();
554 }
555 } else if (op == 0xD3) {
556 switch (regop) {
557 case esp: mnem = "shl"; break;
558 case ebp: mnem = "shr"; break;
559 case edi: mnem = "sar"; break;
560 default: UnimplementedInstruction();
561 }
562 }
563 assert(mnem != NULL);
564 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(rm));
565 if (imm8 > 0) {
566 AppendToBuffer("%d", imm8);
567 } else {
568 AppendToBuffer("cl");
569 }
570 } else {
571 UnimplementedInstruction();
572 }
573 return num_bytes;
574}
575
576
577// Returns number of bytes used, including *data.
578int DisassemblerIA32::JumpShort(byte* data) {
579 assert(*data == 0xEB);
580 byte b = *(data+1);
581 byte* dest = data + static_cast<int8_t>(b) + 2;
582 AppendToBuffer("jmp %s", NameOfAddress(dest));
583 return 2;
584}
585
586
587// Returns number of bytes used, including *data.
588int DisassemblerIA32::JumpConditional(byte* data, const char* comment) {
589 assert(*data == 0x0F);
590 byte cond = *(data+1) & 0x0F;
591 byte* dest = data + *reinterpret_cast<int32_t*>(data+2) + 6;
592 const char* mnem = jump_conditional_mnem[cond];
593 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
594 if (comment != NULL) {
595 AppendToBuffer(", %s", comment);
596 }
597 return 6; // includes 0x0F
598}
599
600
601// Returns number of bytes used, including *data.
602int DisassemblerIA32::JumpConditionalShort(byte* data, const char* comment) {
603 byte cond = *data & 0x0F;
604 byte b = *(data+1);
605 byte* dest = data + static_cast<int8_t>(b) + 2;
606 const char* mnem = jump_conditional_mnem[cond];
607 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
608 if (comment != NULL) {
609 AppendToBuffer(", %s", comment);
610 }
611 return 2;
612}
613
614
615// Returns number of bytes used, including *data.
616int DisassemblerIA32::SetCC(byte* data) {
617 assert(*data == 0x0F);
618 byte cond = *(data+1) & 0x0F;
619 const char* mnem = set_conditional_mnem[cond];
620 AppendToBuffer("%s ", mnem);
621 PrintRightByteOperand(data+2);
622 return 3; // includes 0x0F
623}
624
625
626// Returns number of bytes used, including *data.
Steve Block3ce2e202009-11-05 08:53:23 +0000627int DisassemblerIA32::CMov(byte* data) {
628 assert(*data == 0x0F);
629 byte cond = *(data + 1) & 0x0F;
630 const char* mnem = conditional_move_mnem[cond];
631 int op_size = PrintOperands(mnem, REG_OPER_OP_ORDER, data + 2);
632 return 2 + op_size; // includes 0x0F
633}
634
635
636// Returns number of bytes used, including *data.
Steve Blocka7e24c12009-10-30 11:49:00 +0000637int DisassemblerIA32::FPUInstruction(byte* data) {
638 byte b1 = *data;
639 byte b2 = *(data + 1);
640 if (b1 == 0xD9) {
641 const char* mnem = NULL;
642 switch (b2) {
643 case 0xE8: mnem = "fld1"; break;
644 case 0xEE: mnem = "fldz"; break;
645 case 0xE1: mnem = "fabs"; break;
646 case 0xE0: mnem = "fchs"; break;
647 case 0xF8: mnem = "fprem"; break;
648 case 0xF5: mnem = "fprem1"; break;
649 case 0xF7: mnem = "fincstp"; break;
650 case 0xE4: mnem = "ftst"; break;
651 }
652 if (mnem != NULL) {
653 AppendToBuffer("%s", mnem);
654 return 2;
655 } else if ((b2 & 0xF8) == 0xC8) {
656 AppendToBuffer("fxch st%d", b2 & 0x7);
657 return 2;
658 } else {
659 int mod, regop, rm;
660 get_modrm(*(data+1), &mod, &regop, &rm);
661 const char* mnem = "?";
662 switch (regop) {
663 case eax: mnem = "fld_s"; break;
664 case ebx: mnem = "fstp_s"; break;
665 default: UnimplementedInstruction();
666 }
667 AppendToBuffer("%s ", mnem);
668 int count = PrintRightOperand(data + 1);
669 return count + 1;
670 }
671 } else if (b1 == 0xDD) {
672 if ((b2 & 0xF8) == 0xC0) {
673 AppendToBuffer("ffree st%d", b2 & 0x7);
674 return 2;
675 } else {
676 int mod, regop, rm;
677 get_modrm(*(data+1), &mod, &regop, &rm);
678 const char* mnem = "?";
679 switch (regop) {
680 case eax: mnem = "fld_d"; break;
681 case ebx: mnem = "fstp_d"; break;
682 default: UnimplementedInstruction();
683 }
684 AppendToBuffer("%s ", mnem);
685 int count = PrintRightOperand(data + 1);
686 return count + 1;
687 }
688 } else if (b1 == 0xDB) {
689 int mod, regop, rm;
690 get_modrm(*(data+1), &mod, &regop, &rm);
691 const char* mnem = "?";
692 switch (regop) {
693 case eax: mnem = "fild_s"; break;
694 case edx: mnem = "fist_s"; break;
695 case ebx: mnem = "fistp_s"; break;
696 default: UnimplementedInstruction();
697 }
698 AppendToBuffer("%s ", mnem);
699 int count = PrintRightOperand(data + 1);
700 return count + 1;
701 } else if (b1 == 0xDF) {
702 if (b2 == 0xE0) {
703 AppendToBuffer("fnstsw_ax");
704 return 2;
705 }
706 int mod, regop, rm;
707 get_modrm(*(data+1), &mod, &regop, &rm);
708 const char* mnem = "?";
709 switch (regop) {
710 case ebp: mnem = "fild_d"; break;
711 case edi: mnem = "fistp_d"; break;
712 default: UnimplementedInstruction();
713 }
714 AppendToBuffer("%s ", mnem);
715 int count = PrintRightOperand(data + 1);
716 return count + 1;
717 } else if (b1 == 0xDC || b1 == 0xDE) {
718 bool is_pop = (b1 == 0xDE);
719 if (is_pop && b2 == 0xD9) {
720 AppendToBuffer("fcompp");
721 return 2;
722 }
723 const char* mnem = "FP0xDC";
724 switch (b2 & 0xF8) {
725 case 0xC0: mnem = "fadd"; break;
726 case 0xE8: mnem = "fsub"; break;
727 case 0xC8: mnem = "fmul"; break;
728 case 0xF8: mnem = "fdiv"; break;
729 default: UnimplementedInstruction();
730 }
731 AppendToBuffer("%s%s st%d", mnem, is_pop ? "p" : "", b2 & 0x7);
732 return 2;
733 } else if (b1 == 0xDA && b2 == 0xE9) {
734 const char* mnem = "fucompp";
735 AppendToBuffer("%s", mnem);
736 return 2;
737 }
738 AppendToBuffer("Unknown FP instruction");
739 return 2;
740}
741
742
743// Mnemonics for instructions 0xF0 byte.
744// Returns NULL if the instruction is not handled here.
745static const char* F0Mnem(byte f0byte) {
746 switch (f0byte) {
747 case 0xA2: return "cpuid";
748 case 0x31: return "rdtsc";
749 case 0xBE: return "movsx_b";
750 case 0xBF: return "movsx_w";
751 case 0xB6: return "movzx_b";
752 case 0xB7: return "movzx_w";
753 case 0xAF: return "imul";
754 case 0xA5: return "shld";
755 case 0xAD: return "shrd";
756 case 0xAB: return "bts";
757 default: return NULL;
758 }
759}
760
761
762// Disassembled instruction '*instr' and writes it into 'out_buffer'.
763int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
764 byte* instr) {
765 tmp_buffer_pos_ = 0; // starting to write as position 0
766 byte* data = instr;
767 // Check for hints.
768 const char* branch_hint = NULL;
769 // We use these two prefixes only with branch prediction
770 if (*data == 0x3E /*ds*/) {
771 branch_hint = "predicted taken";
772 data++;
773 } else if (*data == 0x2E /*cs*/) {
774 branch_hint = "predicted not taken";
775 data++;
776 }
777 bool processed = true; // Will be set to false if the current instruction
778 // is not in 'instructions' table.
779 const InstructionDesc& idesc = instruction_table.Get(*data);
780 switch (idesc.type) {
781 case ZERO_OPERANDS_INSTR:
782 AppendToBuffer(idesc.mnem);
783 data++;
784 break;
785
786 case TWO_OPERANDS_INSTR:
787 data++;
788 data += PrintOperands(idesc.mnem, idesc.op_order_, data);
789 break;
790
791 case JUMP_CONDITIONAL_SHORT_INSTR:
792 data += JumpConditionalShort(data, branch_hint);
793 break;
794
795 case REGISTER_INSTR:
796 AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07));
797 data++;
798 break;
799
800 case MOVE_REG_INSTR: {
801 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
802 AppendToBuffer("mov %s,%s",
803 NameOfCPURegister(*data & 0x07),
804 NameOfAddress(addr));
805 data += 5;
806 break;
807 }
808
809 case CALL_JUMP_INSTR: {
810 byte* addr = data + *reinterpret_cast<int32_t*>(data+1) + 5;
811 AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
812 data += 5;
813 break;
814 }
815
816 case SHORT_IMMEDIATE_INSTR: {
817 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
818 AppendToBuffer("%s eax, %s", idesc.mnem, NameOfAddress(addr));
819 data += 5;
820 break;
821 }
822
823 case NO_INSTR:
824 processed = false;
825 break;
826
827 default:
828 UNIMPLEMENTED(); // This type is not implemented.
829 }
830 //----------------------------
831 if (!processed) {
832 switch (*data) {
833 case 0xC2:
834 AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data+1));
835 data += 3;
836 break;
837
838 case 0x69: // fall through
839 case 0x6B:
840 { int mod, regop, rm;
841 get_modrm(*(data+1), &mod, &regop, &rm);
842 int32_t imm =
843 *data == 0x6B ? *(data+2) : *reinterpret_cast<int32_t*>(data+2);
844 AppendToBuffer("imul %s,%s,0x%x",
845 NameOfCPURegister(regop),
846 NameOfCPURegister(rm),
847 imm);
848 data += 2 + (*data == 0x6B ? 1 : 4);
849 }
850 break;
851
852 case 0xF6:
853 { int mod, regop, rm;
854 get_modrm(*(data+1), &mod, &regop, &rm);
855 if (mod == 3 && regop == eax) {
856 AppendToBuffer("test_b %s,%d", NameOfCPURegister(rm), *(data+2));
857 } else {
858 UnimplementedInstruction();
859 }
860 data += 3;
861 }
862 break;
863
864 case 0x81: // fall through
865 case 0x83: // 0x81 with sign extension bit set
866 data += PrintImmediateOp(data);
867 break;
868
869 case 0x0F:
870 { byte f0byte = *(data+1);
871 const char* f0mnem = F0Mnem(f0byte);
872 if (f0byte == 0xA2 || f0byte == 0x31) {
873 AppendToBuffer("%s", f0mnem);
874 data += 2;
875 } else if ((f0byte & 0xF0) == 0x80) {
876 data += JumpConditional(data, branch_hint);
877 } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
878 f0byte == 0xB7 || f0byte == 0xAF) {
879 data += 2;
880 data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data);
881 } else if ((f0byte & 0xF0) == 0x90) {
882 data += SetCC(data);
Steve Block3ce2e202009-11-05 08:53:23 +0000883 } else if ((f0byte & 0xF0) == 0x40) {
884 data += CMov(data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000885 } else {
886 data += 2;
887 if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) {
888 // shrd, shld, bts
889 AppendToBuffer("%s ", f0mnem);
890 int mod, regop, rm;
891 get_modrm(*data, &mod, &regop, &rm);
892 data += PrintRightOperand(data);
893 if (f0byte == 0xAB) {
894 AppendToBuffer(",%s", NameOfCPURegister(regop));
895 } else {
896 AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
897 }
898 } else {
899 UnimplementedInstruction();
900 }
901 }
902 }
903 break;
904
905 case 0x8F:
906 { data++;
907 int mod, regop, rm;
908 get_modrm(*data, &mod, &regop, &rm);
909 if (regop == eax) {
910 AppendToBuffer("pop ");
911 data += PrintRightOperand(data);
912 }
913 }
914 break;
915
916 case 0xFF:
917 { data++;
918 int mod, regop, rm;
919 get_modrm(*data, &mod, &regop, &rm);
920 const char* mnem = NULL;
921 switch (regop) {
922 case esi: mnem = "push"; break;
923 case eax: mnem = "inc"; break;
924 case ecx: mnem = "dec"; break;
925 case edx: mnem = "call"; break;
926 case esp: mnem = "jmp"; break;
927 default: mnem = "???";
928 }
929 AppendToBuffer("%s ", mnem);
930 data += PrintRightOperand(data);
931 }
932 break;
933
934 case 0xC7: // imm32, fall through
935 case 0xC6: // imm8
936 { bool is_byte = *data == 0xC6;
937 data++;
938 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
939 data += PrintRightOperand(data);
940 int32_t imm = is_byte ? *data : *reinterpret_cast<int32_t*>(data);
941 AppendToBuffer(",0x%x", imm);
942 data += is_byte ? 1 : 4;
943 }
944 break;
945
946 case 0x80:
947 { data++;
948 AppendToBuffer("%s ", "cmpb");
949 data += PrintRightOperand(data);
950 int32_t imm = *data;
951 AppendToBuffer(",0x%x", imm);
952 data++;
953 }
954 break;
955
956 case 0x88: // 8bit, fall through
957 case 0x89: // 32bit
958 { bool is_byte = *data == 0x88;
959 int mod, regop, rm;
960 data++;
961 get_modrm(*data, &mod, &regop, &rm);
962 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
963 data += PrintRightOperand(data);
964 AppendToBuffer(",%s", NameOfCPURegister(regop));
965 }
966 break;
967
968 case 0x66: // prefix
969 data++;
970 if (*data == 0x8B) {
971 data++;
972 data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data);
973 } else if (*data == 0x89) {
974 data++;
975 int mod, regop, rm;
976 get_modrm(*data, &mod, &regop, &rm);
977 AppendToBuffer("mov_w ");
978 data += PrintRightOperand(data);
979 AppendToBuffer(",%s", NameOfCPURegister(regop));
Steve Block3ce2e202009-11-05 08:53:23 +0000980 } else if (*data == 0x0F) {
981 data++;
982 if (*data == 0x2F) {
983 data++;
984 int mod, regop, rm;
985 get_modrm(*data, &mod, &regop, &rm);
986 AppendToBuffer("comisd %s,%s",
987 NameOfXMMRegister(regop),
988 NameOfXMMRegister(rm));
989 data++;
990 } else {
991 UnimplementedInstruction();
992 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000993 } else {
994 UnimplementedInstruction();
995 }
996 break;
997
998 case 0xFE:
999 { data++;
1000 int mod, regop, rm;
1001 get_modrm(*data, &mod, &regop, &rm);
1002 if (mod == 3 && regop == ecx) {
1003 AppendToBuffer("dec_b %s", NameOfCPURegister(rm));
1004 } else {
1005 UnimplementedInstruction();
1006 }
1007 data++;
1008 }
1009 break;
1010
1011 case 0x68:
1012 AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data+1));
1013 data += 5;
1014 break;
1015
1016 case 0x6A:
1017 AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
1018 data += 2;
1019 break;
1020
1021 case 0xA8:
1022 AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data+1));
1023 data += 2;
1024 break;
1025
1026 case 0xA9:
1027 AppendToBuffer("test eax,0x%x", *reinterpret_cast<int32_t*>(data+1));
1028 data += 5;
1029 break;
1030
1031 case 0xD1: // fall through
1032 case 0xD3: // fall through
1033 case 0xC1:
1034 data += D1D3C1Instruction(data);
1035 break;
1036
1037 case 0xD9: // fall through
1038 case 0xDA: // fall through
1039 case 0xDB: // fall through
1040 case 0xDC: // fall through
1041 case 0xDD: // fall through
1042 case 0xDE: // fall through
1043 case 0xDF:
1044 data += FPUInstruction(data);
1045 break;
1046
1047 case 0xEB:
1048 data += JumpShort(data);
1049 break;
1050
1051 case 0xF2:
1052 if (*(data+1) == 0x0F) {
1053 byte b2 = *(data+2);
1054 if (b2 == 0x11) {
1055 AppendToBuffer("movsd ");
1056 data += 3;
1057 int mod, regop, rm;
1058 get_modrm(*data, &mod, &regop, &rm);
1059 data += PrintRightOperand(data);
1060 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1061 } else if (b2 == 0x10) {
1062 data += 3;
1063 int mod, regop, rm;
1064 get_modrm(*data, &mod, &regop, &rm);
1065 AppendToBuffer("movsd %s,", NameOfXMMRegister(regop));
1066 data += PrintRightOperand(data);
1067 } else {
1068 const char* mnem = "?";
1069 switch (b2) {
1070 case 0x2A: mnem = "cvtsi2sd"; break;
1071 case 0x58: mnem = "addsd"; break;
1072 case 0x59: mnem = "mulsd"; break;
1073 case 0x5C: mnem = "subsd"; break;
1074 case 0x5E: mnem = "divsd"; break;
1075 }
1076 data += 3;
1077 int mod, regop, rm;
1078 get_modrm(*data, &mod, &regop, &rm);
1079 if (b2 == 0x2A) {
1080 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1081 data += PrintRightOperand(data);
1082 } else {
1083 AppendToBuffer("%s %s,%s",
1084 mnem,
1085 NameOfXMMRegister(regop),
1086 NameOfXMMRegister(rm));
1087 data++;
1088 }
1089 }
1090 } else {
1091 UnimplementedInstruction();
1092 }
1093 break;
1094
1095 case 0xF3:
1096 if (*(data+1) == 0x0F && *(data+2) == 0x2C) {
1097 data += 3;
1098 data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data);
1099 } else {
1100 UnimplementedInstruction();
1101 }
1102 break;
1103
1104 case 0xF7:
1105 data += F7Instruction(data);
1106 break;
1107
1108 default:
1109 UnimplementedInstruction();
1110 }
1111 }
1112
1113 if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1114 tmp_buffer_[tmp_buffer_pos_] = '\0';
1115 }
1116
1117 int instr_len = data - instr;
1118 ASSERT(instr_len > 0); // Ensure progress.
1119
1120 int outp = 0;
1121 // Instruction bytes.
1122 for (byte* bp = instr; bp < data; bp++) {
1123 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1124 "%02x",
1125 *bp);
1126 }
1127 for (int i = 6 - instr_len; i >= 0; i--) {
1128 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1129 " ");
1130 }
1131
1132 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1133 " %s",
1134 tmp_buffer_.start());
1135 return instr_len;
1136}
1137
1138
1139//------------------------------------------------------------------------------
1140
1141
1142static const char* cpu_regs[8] = {
1143 "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
1144};
1145
1146
1147static const char* byte_cpu_regs[8] = {
1148 "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"
1149};
1150
1151
1152static const char* xmm_regs[8] = {
1153 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
1154};
1155
1156
1157const char* NameConverter::NameOfAddress(byte* addr) const {
1158 static v8::internal::EmbeddedVector<char, 32> tmp_buffer;
1159 v8::internal::OS::SNPrintF(tmp_buffer, "%p", addr);
1160 return tmp_buffer.start();
1161}
1162
1163
1164const char* NameConverter::NameOfConstant(byte* addr) const {
1165 return NameOfAddress(addr);
1166}
1167
1168
1169const char* NameConverter::NameOfCPURegister(int reg) const {
1170 if (0 <= reg && reg < 8) return cpu_regs[reg];
1171 return "noreg";
1172}
1173
1174
1175const char* NameConverter::NameOfByteCPURegister(int reg) const {
1176 if (0 <= reg && reg < 8) return byte_cpu_regs[reg];
1177 return "noreg";
1178}
1179
1180
1181const char* NameConverter::NameOfXMMRegister(int reg) const {
1182 if (0 <= reg && reg < 8) return xmm_regs[reg];
1183 return "noxmmreg";
1184}
1185
1186
1187const char* NameConverter::NameInCode(byte* addr) const {
1188 // IA32 does not embed debug strings at the moment.
1189 UNREACHABLE();
1190 return "";
1191}
1192
1193
1194//------------------------------------------------------------------------------
1195
1196Disassembler::Disassembler(const NameConverter& converter)
1197 : converter_(converter) {}
1198
1199
1200Disassembler::~Disassembler() {}
1201
1202
1203int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
1204 byte* instruction) {
1205 DisassemblerIA32 d(converter_, false /*do not crash if unimplemented*/);
1206 return d.InstructionDecode(buffer, instruction);
1207}
1208
1209
1210// The IA-32 assembler does not currently use constant pools.
1211int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; }
1212
1213
1214/*static*/ void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
1215 NameConverter converter;
1216 Disassembler d(converter);
1217 for (byte* pc = begin; pc < end;) {
1218 v8::internal::EmbeddedVector<char, 128> buffer;
1219 buffer[0] = '\0';
1220 byte* prev_pc = pc;
1221 pc += d.InstructionDecode(buffer, pc);
1222 fprintf(f, "%p", prev_pc);
1223 fprintf(f, " ");
1224
1225 for (byte* bp = prev_pc; bp < pc; bp++) {
1226 fprintf(f, "%02x", *bp);
1227 }
1228 for (int i = 6 - (pc - prev_pc); i >= 0; i--) {
1229 fprintf(f, " ");
1230 }
1231 fprintf(f, " %s\n", buffer.start());
1232 }
1233}
1234
1235
1236} // namespace disasm