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