blob: df5a28a54b5e3db866b126862a829bdfa1e7cb4c [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_;
Steve Blockd0582a62009-12-15 09:54:21 +0000207 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000208 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];
Steve Blockd0582a62009-12-15 09:54:21 +0000219 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000220 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];
Steve Blockd0582a62009-12-15 09:54:21 +0000229 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000230 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
Steve Blockd0582a62009-12-15 09:54:21 +0000275 enum ShiftOpcodeExtension {
276 kROL = 0,
277 kROR = 1,
278 kRCL = 2,
279 kRCR = 3,
280 kSHL = 4,
281 KSHR = 5,
282 kSAR = 7
283 };
284
285
Steve Blocka7e24c12009-10-30 11:49:00 +0000286 const char* NameOfCPURegister(int reg) const {
287 return converter_.NameOfCPURegister(reg);
288 }
289
290
291 const char* NameOfByteCPURegister(int reg) const {
292 return converter_.NameOfByteCPURegister(reg);
293 }
294
295
296 const char* NameOfXMMRegister(int reg) const {
297 return converter_.NameOfXMMRegister(reg);
298 }
299
300
301 const char* NameOfAddress(byte* addr) const {
302 return converter_.NameOfAddress(addr);
303 }
304
305
306 // Disassembler helper functions.
307 static void get_modrm(byte data, int* mod, int* regop, int* rm) {
308 *mod = (data >> 6) & 3;
309 *regop = (data & 0x38) >> 3;
310 *rm = data & 7;
311 }
312
313
314 static void get_sib(byte data, int* scale, int* index, int* base) {
315 *scale = (data >> 6) & 3;
316 *index = (data >> 3) & 7;
317 *base = data & 7;
318 }
319
320 typedef const char* (DisassemblerIA32::*RegisterNameMapping)(int reg) const;
321
322 int PrintRightOperandHelper(byte* modrmp, RegisterNameMapping register_name);
323 int PrintRightOperand(byte* modrmp);
324 int PrintRightByteOperand(byte* modrmp);
325 int PrintOperands(const char* mnem, OperandOrder op_order, byte* data);
326 int PrintImmediateOp(byte* data);
327 int F7Instruction(byte* data);
328 int D1D3C1Instruction(byte* data);
329 int JumpShort(byte* data);
330 int JumpConditional(byte* data, const char* comment);
331 int JumpConditionalShort(byte* data, const char* comment);
332 int SetCC(byte* data);
Steve Block3ce2e202009-11-05 08:53:23 +0000333 int CMov(byte* data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000334 int FPUInstruction(byte* data);
Steve Blockd0582a62009-12-15 09:54:21 +0000335 int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start);
336 int RegisterFPUInstruction(int escape_opcode, byte modrm_byte);
Steve Blocka7e24c12009-10-30 11:49:00 +0000337 void AppendToBuffer(const char* format, ...);
338
339
340 void UnimplementedInstruction() {
341 if (abort_on_unimplemented_) {
342 UNIMPLEMENTED();
343 } else {
344 AppendToBuffer("'Unimplemented Instruction'");
345 }
346 }
347};
348
349
350void DisassemblerIA32::AppendToBuffer(const char* format, ...) {
351 v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
352 va_list args;
353 va_start(args, format);
354 int result = v8::internal::OS::VSNPrintF(buf, format, args);
355 va_end(args);
356 tmp_buffer_pos_ += result;
357}
358
359int DisassemblerIA32::PrintRightOperandHelper(
360 byte* modrmp,
361 RegisterNameMapping register_name) {
362 int mod, regop, rm;
363 get_modrm(*modrmp, &mod, &regop, &rm);
364 switch (mod) {
365 case 0:
366 if (rm == ebp) {
367 int32_t disp = *reinterpret_cast<int32_t*>(modrmp+1);
368 AppendToBuffer("[0x%x]", disp);
369 return 5;
370 } else if (rm == esp) {
371 byte sib = *(modrmp + 1);
372 int scale, index, base;
373 get_sib(sib, &scale, &index, &base);
374 if (index == esp && base == esp && scale == 0 /*times_1*/) {
375 AppendToBuffer("[%s]", (this->*register_name)(rm));
376 return 2;
377 } else if (base == ebp) {
378 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
379 AppendToBuffer("[%s*%d+0x%x]",
380 (this->*register_name)(index),
381 1 << scale,
382 disp);
383 return 6;
384 } else if (index != esp && base != ebp) {
385 // [base+index*scale]
386 AppendToBuffer("[%s+%s*%d]",
387 (this->*register_name)(base),
388 (this->*register_name)(index),
389 1 << scale);
390 return 2;
391 } else {
392 UnimplementedInstruction();
393 return 1;
394 }
395 } else {
396 AppendToBuffer("[%s]", (this->*register_name)(rm));
397 return 1;
398 }
399 break;
400 case 1: // fall through
401 case 2:
402 if (rm == esp) {
403 byte sib = *(modrmp + 1);
404 int scale, index, base;
405 get_sib(sib, &scale, &index, &base);
406 int disp =
407 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 2) : *(modrmp + 2);
408 if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) {
409 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
410 } else {
411 AppendToBuffer("[%s+%s*%d+0x%x]",
412 (this->*register_name)(base),
413 (this->*register_name)(index),
414 1 << scale,
415 disp);
416 }
417 return mod == 2 ? 6 : 3;
418 } else {
419 // No sib.
420 int disp =
421 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 1) : *(modrmp + 1);
422 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
423 return mod == 2 ? 5 : 2;
424 }
425 break;
426 case 3:
427 AppendToBuffer("%s", (this->*register_name)(rm));
428 return 1;
429 default:
430 UnimplementedInstruction();
431 return 1;
432 }
433 UNREACHABLE();
434}
435
436
437int DisassemblerIA32::PrintRightOperand(byte* modrmp) {
438 return PrintRightOperandHelper(modrmp, &DisassemblerIA32::NameOfCPURegister);
439}
440
441
442int DisassemblerIA32::PrintRightByteOperand(byte* modrmp) {
443 return PrintRightOperandHelper(modrmp,
444 &DisassemblerIA32::NameOfByteCPURegister);
445}
446
447
448// Returns number of bytes used including the current *data.
449// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
450int DisassemblerIA32::PrintOperands(const char* mnem,
451 OperandOrder op_order,
452 byte* data) {
453 byte modrm = *data;
454 int mod, regop, rm;
455 get_modrm(modrm, &mod, &regop, &rm);
456 int advance = 0;
457 switch (op_order) {
458 case REG_OPER_OP_ORDER: {
459 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
460 advance = PrintRightOperand(data);
461 break;
462 }
463 case OPER_REG_OP_ORDER: {
464 AppendToBuffer("%s ", mnem);
465 advance = PrintRightOperand(data);
466 AppendToBuffer(",%s", NameOfCPURegister(regop));
467 break;
468 }
469 default:
470 UNREACHABLE();
471 break;
472 }
473 return advance;
474}
475
476
477// Returns number of bytes used by machine instruction, including *data byte.
478// Writes immediate instructions to 'tmp_buffer_'.
479int DisassemblerIA32::PrintImmediateOp(byte* data) {
480 bool sign_extension_bit = (*data & 0x02) != 0;
481 byte modrm = *(data+1);
482 int mod, regop, rm;
483 get_modrm(modrm, &mod, &regop, &rm);
484 const char* mnem = "Imm???";
485 switch (regop) {
486 case 0: mnem = "add"; break;
487 case 1: mnem = "or"; break;
488 case 2: mnem = "adc"; break;
489 case 4: mnem = "and"; break;
490 case 5: mnem = "sub"; break;
491 case 6: mnem = "xor"; break;
492 case 7: mnem = "cmp"; break;
493 default: UnimplementedInstruction();
494 }
495 AppendToBuffer("%s ", mnem);
496 int count = PrintRightOperand(data+1);
497 if (sign_extension_bit) {
498 AppendToBuffer(",0x%x", *(data + 1 + count));
499 return 1 + count + 1 /*int8*/;
500 } else {
501 AppendToBuffer(",0x%x", *reinterpret_cast<int32_t*>(data + 1 + count));
502 return 1 + count + 4 /*int32_t*/;
503 }
504}
505
506
507// Returns number of bytes used, including *data.
508int DisassemblerIA32::F7Instruction(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000509 ASSERT_EQ(0xF7, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000510 byte modrm = *(data+1);
511 int mod, regop, rm;
512 get_modrm(modrm, &mod, &regop, &rm);
513 if (mod == 3 && regop != 0) {
514 const char* mnem = NULL;
515 switch (regop) {
516 case 2: mnem = "not"; break;
517 case 3: mnem = "neg"; break;
518 case 4: mnem = "mul"; break;
519 case 7: mnem = "idiv"; break;
520 default: UnimplementedInstruction();
521 }
522 AppendToBuffer("%s %s", mnem, NameOfCPURegister(rm));
523 return 2;
524 } else if (mod == 3 && regop == eax) {
525 int32_t imm = *reinterpret_cast<int32_t*>(data+2);
526 AppendToBuffer("test %s,0x%x", NameOfCPURegister(rm), imm);
527 return 6;
528 } else if (regop == eax) {
529 AppendToBuffer("test ");
530 int count = PrintRightOperand(data+1);
531 int32_t imm = *reinterpret_cast<int32_t*>(data+1+count);
532 AppendToBuffer(",0x%x", imm);
533 return 1+count+4 /*int32_t*/;
534 } else {
535 UnimplementedInstruction();
536 return 2;
537 }
538}
539
540int DisassemblerIA32::D1D3C1Instruction(byte* data) {
541 byte op = *data;
Steve Blockd0582a62009-12-15 09:54:21 +0000542 ASSERT(op == 0xD1 || op == 0xD3 || op == 0xC1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000543 byte modrm = *(data+1);
544 int mod, regop, rm;
545 get_modrm(modrm, &mod, &regop, &rm);
546 int imm8 = -1;
547 int num_bytes = 2;
548 if (mod == 3) {
549 const char* mnem = NULL;
Steve Blockd0582a62009-12-15 09:54:21 +0000550 switch (regop) {
551 case kROL: mnem = "rol"; break;
552 case kROR: mnem = "ror"; break;
553 case kRCL: mnem = "rcl"; break;
554 case kSHL: mnem = "shl"; break;
555 case KSHR: mnem = "shr"; break;
556 case kSAR: mnem = "sar"; break;
557 default: UnimplementedInstruction();
558 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000559 if (op == 0xD1) {
560 imm8 = 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000561 } else if (op == 0xC1) {
562 imm8 = *(data+2);
563 num_bytes = 3;
Steve Blocka7e24c12009-10-30 11:49:00 +0000564 } else if (op == 0xD3) {
Steve Blockd0582a62009-12-15 09:54:21 +0000565 // Shift/rotate by cl.
Steve Blocka7e24c12009-10-30 11:49:00 +0000566 }
Steve Blockd0582a62009-12-15 09:54:21 +0000567 ASSERT_NE(NULL, mnem);
Steve Blocka7e24c12009-10-30 11:49:00 +0000568 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(rm));
569 if (imm8 > 0) {
570 AppendToBuffer("%d", imm8);
571 } else {
572 AppendToBuffer("cl");
573 }
574 } else {
575 UnimplementedInstruction();
576 }
577 return num_bytes;
578}
579
580
581// Returns number of bytes used, including *data.
582int DisassemblerIA32::JumpShort(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000583 ASSERT_EQ(0xEB, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000584 byte b = *(data+1);
585 byte* dest = data + static_cast<int8_t>(b) + 2;
586 AppendToBuffer("jmp %s", NameOfAddress(dest));
587 return 2;
588}
589
590
591// Returns number of bytes used, including *data.
592int DisassemblerIA32::JumpConditional(byte* data, const char* comment) {
Steve Blockd0582a62009-12-15 09:54:21 +0000593 ASSERT_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000594 byte cond = *(data+1) & 0x0F;
595 byte* dest = data + *reinterpret_cast<int32_t*>(data+2) + 6;
596 const char* mnem = jump_conditional_mnem[cond];
597 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
598 if (comment != NULL) {
599 AppendToBuffer(", %s", comment);
600 }
601 return 6; // includes 0x0F
602}
603
604
605// Returns number of bytes used, including *data.
606int DisassemblerIA32::JumpConditionalShort(byte* data, const char* comment) {
607 byte cond = *data & 0x0F;
608 byte b = *(data+1);
609 byte* dest = data + static_cast<int8_t>(b) + 2;
610 const char* mnem = jump_conditional_mnem[cond];
611 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
612 if (comment != NULL) {
613 AppendToBuffer(", %s", comment);
614 }
615 return 2;
616}
617
618
619// Returns number of bytes used, including *data.
620int DisassemblerIA32::SetCC(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000621 ASSERT_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000622 byte cond = *(data+1) & 0x0F;
623 const char* mnem = set_conditional_mnem[cond];
624 AppendToBuffer("%s ", mnem);
625 PrintRightByteOperand(data+2);
Steve Blockd0582a62009-12-15 09:54:21 +0000626 return 3; // Includes 0x0F.
Steve Blocka7e24c12009-10-30 11:49:00 +0000627}
628
629
630// Returns number of bytes used, including *data.
Steve Block3ce2e202009-11-05 08:53:23 +0000631int DisassemblerIA32::CMov(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000632 ASSERT_EQ(0x0F, *data);
Steve Block3ce2e202009-11-05 08:53:23 +0000633 byte cond = *(data + 1) & 0x0F;
634 const char* mnem = conditional_move_mnem[cond];
635 int op_size = PrintOperands(mnem, REG_OPER_OP_ORDER, data + 2);
636 return 2 + op_size; // includes 0x0F
637}
638
639
640// Returns number of bytes used, including *data.
Steve Blocka7e24c12009-10-30 11:49:00 +0000641int DisassemblerIA32::FPUInstruction(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000642 byte escape_opcode = *data;
643 ASSERT_EQ(0xD8, escape_opcode & 0xF8);
644 byte modrm_byte = *(data+1);
645
646 if (modrm_byte >= 0xC0) {
647 return RegisterFPUInstruction(escape_opcode, modrm_byte);
648 } else {
649 return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000650 }
Steve Blockd0582a62009-12-15 09:54:21 +0000651}
652
653int DisassemblerIA32::MemoryFPUInstruction(int escape_opcode,
654 int modrm_byte,
655 byte* modrm_start) {
656 const char* mnem = "?";
657 int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte.
658 switch (escape_opcode) {
659 case 0xD9: switch (regop) {
660 case 0: mnem = "fld_s"; break;
661 case 3: mnem = "fstp_s"; break;
662 case 7: mnem = "fstcw"; break;
663 default: UnimplementedInstruction();
664 }
665 break;
666
667 case 0xDB: switch (regop) {
668 case 0: mnem = "fild_s"; break;
669 case 1: mnem = "fisttp_s"; break;
670 case 2: mnem = "fist_s"; break;
671 case 3: mnem = "fistp_s"; break;
672 default: UnimplementedInstruction();
673 }
674 break;
675
676 case 0xDD: switch (regop) {
677 case 0: mnem = "fld_d"; break;
678 case 3: mnem = "fstp_d"; break;
679 default: UnimplementedInstruction();
680 }
681 break;
682
683 case 0xDF: switch (regop) {
684 case 5: mnem = "fild_d"; break;
685 case 7: mnem = "fistp_d"; break;
686 default: UnimplementedInstruction();
687 }
688 break;
689
690 default: UnimplementedInstruction();
691 }
692 AppendToBuffer("%s ", mnem);
693 int count = PrintRightOperand(modrm_start);
694 return count + 1;
695}
696
697int DisassemblerIA32::RegisterFPUInstruction(int escape_opcode,
698 byte modrm_byte) {
699 bool has_register = false; // Is the FPU register encoded in modrm_byte?
700 const char* mnem = "?";
701
702 switch (escape_opcode) {
703 case 0xD8:
704 UnimplementedInstruction();
705 break;
706
707 case 0xD9:
708 switch (modrm_byte & 0xF8) {
709 case 0xC8:
710 mnem = "fxch";
711 has_register = true;
712 break;
713 default:
714 switch (modrm_byte) {
715 case 0xE0: mnem = "fchs"; break;
716 case 0xE1: mnem = "fabs"; break;
717 case 0xE4: mnem = "ftst"; break;
718 case 0xE8: mnem = "fld1"; break;
719 case 0xEE: mnem = "fldz"; break;
720 case 0xF5: mnem = "fprem1"; break;
721 case 0xF7: mnem = "fincstp"; break;
722 case 0xF8: mnem = "fprem"; break;
723 case 0xFE: mnem = "fsin"; break;
724 case 0xFF: mnem = "fcos"; break;
725 default: UnimplementedInstruction();
726 }
727 }
728 break;
729
730 case 0xDA:
731 if (modrm_byte == 0xE9) {
732 mnem = "fucompp";
733 } else {
734 UnimplementedInstruction();
735 }
736 break;
737
738 case 0xDB:
739 if ((modrm_byte & 0xF8) == 0xE8) {
740 mnem = "fucomi";
741 has_register = true;
742 } else if (modrm_byte == 0xE2) {
743 mnem = "fclex";
744 } else {
745 UnimplementedInstruction();
746 }
747 break;
748
749 case 0xDC:
750 has_register = true;
751 switch (modrm_byte & 0xF8) {
752 case 0xC0: mnem = "fadd"; break;
753 case 0xE8: mnem = "fsub"; break;
754 case 0xC8: mnem = "fmul"; break;
755 case 0xF8: mnem = "fdiv"; break;
756 default: UnimplementedInstruction();
757 }
758 break;
759
760 case 0xDD:
761 has_register = true;
762 switch (modrm_byte & 0xF8) {
763 case 0xC0: mnem = "ffree"; break;
764 case 0xD8: mnem = "fstp"; break;
765 default: UnimplementedInstruction();
766 }
767 break;
768
769 case 0xDE:
770 if (modrm_byte == 0xD9) {
771 mnem = "fcompp";
772 } else {
773 has_register = true;
774 switch (modrm_byte & 0xF8) {
775 case 0xC0: mnem = "faddp"; break;
776 case 0xE8: mnem = "fsubp"; break;
777 case 0xC8: mnem = "fmulp"; break;
778 case 0xF8: mnem = "fdivp"; break;
779 default: UnimplementedInstruction();
780 }
781 }
782 break;
783
784 case 0xDF:
785 if (modrm_byte == 0xE0) {
786 mnem = "fnstsw_ax";
787 } else if ((modrm_byte & 0xF8) == 0xE8) {
788 mnem = "fucomip";
789 has_register = true;
790 }
791 break;
792
793 default: UnimplementedInstruction();
794 }
795
796 if (has_register) {
797 AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
798 } else {
799 AppendToBuffer("%s", mnem);
800 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000801 return 2;
802}
803
804
805// Mnemonics for instructions 0xF0 byte.
806// Returns NULL if the instruction is not handled here.
807static const char* F0Mnem(byte f0byte) {
808 switch (f0byte) {
809 case 0xA2: return "cpuid";
810 case 0x31: return "rdtsc";
811 case 0xBE: return "movsx_b";
812 case 0xBF: return "movsx_w";
813 case 0xB6: return "movzx_b";
814 case 0xB7: return "movzx_w";
815 case 0xAF: return "imul";
816 case 0xA5: return "shld";
817 case 0xAD: return "shrd";
818 case 0xAB: return "bts";
819 default: return NULL;
820 }
821}
822
823
824// Disassembled instruction '*instr' and writes it into 'out_buffer'.
825int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
826 byte* instr) {
827 tmp_buffer_pos_ = 0; // starting to write as position 0
828 byte* data = instr;
829 // Check for hints.
830 const char* branch_hint = NULL;
831 // We use these two prefixes only with branch prediction
832 if (*data == 0x3E /*ds*/) {
833 branch_hint = "predicted taken";
834 data++;
835 } else if (*data == 0x2E /*cs*/) {
836 branch_hint = "predicted not taken";
837 data++;
838 }
839 bool processed = true; // Will be set to false if the current instruction
840 // is not in 'instructions' table.
841 const InstructionDesc& idesc = instruction_table.Get(*data);
842 switch (idesc.type) {
843 case ZERO_OPERANDS_INSTR:
844 AppendToBuffer(idesc.mnem);
845 data++;
846 break;
847
848 case TWO_OPERANDS_INSTR:
849 data++;
850 data += PrintOperands(idesc.mnem, idesc.op_order_, data);
851 break;
852
853 case JUMP_CONDITIONAL_SHORT_INSTR:
854 data += JumpConditionalShort(data, branch_hint);
855 break;
856
857 case REGISTER_INSTR:
858 AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07));
859 data++;
860 break;
861
862 case MOVE_REG_INSTR: {
863 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
864 AppendToBuffer("mov %s,%s",
865 NameOfCPURegister(*data & 0x07),
866 NameOfAddress(addr));
867 data += 5;
868 break;
869 }
870
871 case CALL_JUMP_INSTR: {
872 byte* addr = data + *reinterpret_cast<int32_t*>(data+1) + 5;
873 AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
874 data += 5;
875 break;
876 }
877
878 case SHORT_IMMEDIATE_INSTR: {
879 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
880 AppendToBuffer("%s eax, %s", idesc.mnem, NameOfAddress(addr));
881 data += 5;
882 break;
883 }
884
885 case NO_INSTR:
886 processed = false;
887 break;
888
889 default:
890 UNIMPLEMENTED(); // This type is not implemented.
891 }
892 //----------------------------
893 if (!processed) {
894 switch (*data) {
895 case 0xC2:
896 AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data+1));
897 data += 3;
898 break;
899
900 case 0x69: // fall through
901 case 0x6B:
902 { int mod, regop, rm;
903 get_modrm(*(data+1), &mod, &regop, &rm);
904 int32_t imm =
905 *data == 0x6B ? *(data+2) : *reinterpret_cast<int32_t*>(data+2);
906 AppendToBuffer("imul %s,%s,0x%x",
907 NameOfCPURegister(regop),
908 NameOfCPURegister(rm),
909 imm);
910 data += 2 + (*data == 0x6B ? 1 : 4);
911 }
912 break;
913
914 case 0xF6:
915 { int mod, regop, rm;
916 get_modrm(*(data+1), &mod, &regop, &rm);
917 if (mod == 3 && regop == eax) {
918 AppendToBuffer("test_b %s,%d", NameOfCPURegister(rm), *(data+2));
919 } else {
920 UnimplementedInstruction();
921 }
922 data += 3;
923 }
924 break;
925
926 case 0x81: // fall through
927 case 0x83: // 0x81 with sign extension bit set
928 data += PrintImmediateOp(data);
929 break;
930
931 case 0x0F:
932 { byte f0byte = *(data+1);
933 const char* f0mnem = F0Mnem(f0byte);
934 if (f0byte == 0xA2 || f0byte == 0x31) {
935 AppendToBuffer("%s", f0mnem);
936 data += 2;
937 } else if ((f0byte & 0xF0) == 0x80) {
938 data += JumpConditional(data, branch_hint);
939 } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
940 f0byte == 0xB7 || f0byte == 0xAF) {
941 data += 2;
942 data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data);
943 } else if ((f0byte & 0xF0) == 0x90) {
944 data += SetCC(data);
Steve Block3ce2e202009-11-05 08:53:23 +0000945 } else if ((f0byte & 0xF0) == 0x40) {
946 data += CMov(data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000947 } else {
948 data += 2;
949 if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) {
950 // shrd, shld, bts
951 AppendToBuffer("%s ", f0mnem);
952 int mod, regop, rm;
953 get_modrm(*data, &mod, &regop, &rm);
954 data += PrintRightOperand(data);
955 if (f0byte == 0xAB) {
956 AppendToBuffer(",%s", NameOfCPURegister(regop));
957 } else {
958 AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
959 }
960 } else {
961 UnimplementedInstruction();
962 }
963 }
964 }
965 break;
966
967 case 0x8F:
968 { data++;
969 int mod, regop, rm;
970 get_modrm(*data, &mod, &regop, &rm);
971 if (regop == eax) {
972 AppendToBuffer("pop ");
973 data += PrintRightOperand(data);
974 }
975 }
976 break;
977
978 case 0xFF:
979 { data++;
980 int mod, regop, rm;
981 get_modrm(*data, &mod, &regop, &rm);
982 const char* mnem = NULL;
983 switch (regop) {
984 case esi: mnem = "push"; break;
985 case eax: mnem = "inc"; break;
986 case ecx: mnem = "dec"; break;
987 case edx: mnem = "call"; break;
988 case esp: mnem = "jmp"; break;
989 default: mnem = "???";
990 }
991 AppendToBuffer("%s ", mnem);
992 data += PrintRightOperand(data);
993 }
994 break;
995
996 case 0xC7: // imm32, fall through
997 case 0xC6: // imm8
998 { bool is_byte = *data == 0xC6;
999 data++;
1000 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
1001 data += PrintRightOperand(data);
1002 int32_t imm = is_byte ? *data : *reinterpret_cast<int32_t*>(data);
1003 AppendToBuffer(",0x%x", imm);
1004 data += is_byte ? 1 : 4;
1005 }
1006 break;
1007
1008 case 0x80:
1009 { data++;
1010 AppendToBuffer("%s ", "cmpb");
1011 data += PrintRightOperand(data);
1012 int32_t imm = *data;
1013 AppendToBuffer(",0x%x", imm);
1014 data++;
1015 }
1016 break;
1017
1018 case 0x88: // 8bit, fall through
1019 case 0x89: // 32bit
1020 { bool is_byte = *data == 0x88;
1021 int mod, regop, rm;
1022 data++;
1023 get_modrm(*data, &mod, &regop, &rm);
1024 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
1025 data += PrintRightOperand(data);
1026 AppendToBuffer(",%s", NameOfCPURegister(regop));
1027 }
1028 break;
1029
1030 case 0x66: // prefix
1031 data++;
1032 if (*data == 0x8B) {
1033 data++;
1034 data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data);
1035 } else if (*data == 0x89) {
1036 data++;
1037 int mod, regop, rm;
1038 get_modrm(*data, &mod, &regop, &rm);
1039 AppendToBuffer("mov_w ");
1040 data += PrintRightOperand(data);
1041 AppendToBuffer(",%s", NameOfCPURegister(regop));
Steve Block3ce2e202009-11-05 08:53:23 +00001042 } else if (*data == 0x0F) {
1043 data++;
1044 if (*data == 0x2F) {
1045 data++;
1046 int mod, regop, rm;
1047 get_modrm(*data, &mod, &regop, &rm);
1048 AppendToBuffer("comisd %s,%s",
1049 NameOfXMMRegister(regop),
1050 NameOfXMMRegister(rm));
1051 data++;
1052 } else {
1053 UnimplementedInstruction();
1054 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001055 } else {
1056 UnimplementedInstruction();
1057 }
1058 break;
1059
1060 case 0xFE:
1061 { data++;
1062 int mod, regop, rm;
1063 get_modrm(*data, &mod, &regop, &rm);
1064 if (mod == 3 && regop == ecx) {
1065 AppendToBuffer("dec_b %s", NameOfCPURegister(rm));
1066 } else {
1067 UnimplementedInstruction();
1068 }
1069 data++;
1070 }
1071 break;
1072
1073 case 0x68:
1074 AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data+1));
1075 data += 5;
1076 break;
1077
1078 case 0x6A:
1079 AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
1080 data += 2;
1081 break;
1082
1083 case 0xA8:
1084 AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data+1));
1085 data += 2;
1086 break;
1087
1088 case 0xA9:
1089 AppendToBuffer("test eax,0x%x", *reinterpret_cast<int32_t*>(data+1));
1090 data += 5;
1091 break;
1092
1093 case 0xD1: // fall through
1094 case 0xD3: // fall through
1095 case 0xC1:
1096 data += D1D3C1Instruction(data);
1097 break;
1098
1099 case 0xD9: // fall through
1100 case 0xDA: // fall through
1101 case 0xDB: // fall through
1102 case 0xDC: // fall through
1103 case 0xDD: // fall through
1104 case 0xDE: // fall through
1105 case 0xDF:
1106 data += FPUInstruction(data);
1107 break;
1108
1109 case 0xEB:
1110 data += JumpShort(data);
1111 break;
1112
1113 case 0xF2:
1114 if (*(data+1) == 0x0F) {
1115 byte b2 = *(data+2);
1116 if (b2 == 0x11) {
1117 AppendToBuffer("movsd ");
1118 data += 3;
1119 int mod, regop, rm;
1120 get_modrm(*data, &mod, &regop, &rm);
1121 data += PrintRightOperand(data);
1122 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1123 } else if (b2 == 0x10) {
1124 data += 3;
1125 int mod, regop, rm;
1126 get_modrm(*data, &mod, &regop, &rm);
1127 AppendToBuffer("movsd %s,", NameOfXMMRegister(regop));
1128 data += PrintRightOperand(data);
1129 } else {
1130 const char* mnem = "?";
1131 switch (b2) {
1132 case 0x2A: mnem = "cvtsi2sd"; break;
1133 case 0x58: mnem = "addsd"; break;
1134 case 0x59: mnem = "mulsd"; break;
1135 case 0x5C: mnem = "subsd"; break;
1136 case 0x5E: mnem = "divsd"; break;
1137 }
1138 data += 3;
1139 int mod, regop, rm;
1140 get_modrm(*data, &mod, &regop, &rm);
1141 if (b2 == 0x2A) {
1142 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1143 data += PrintRightOperand(data);
1144 } else {
1145 AppendToBuffer("%s %s,%s",
1146 mnem,
1147 NameOfXMMRegister(regop),
1148 NameOfXMMRegister(rm));
1149 data++;
1150 }
1151 }
1152 } else {
1153 UnimplementedInstruction();
1154 }
1155 break;
1156
1157 case 0xF3:
1158 if (*(data+1) == 0x0F && *(data+2) == 0x2C) {
1159 data += 3;
1160 data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data);
1161 } else {
1162 UnimplementedInstruction();
1163 }
1164 break;
1165
1166 case 0xF7:
1167 data += F7Instruction(data);
1168 break;
1169
1170 default:
1171 UnimplementedInstruction();
1172 }
1173 }
1174
1175 if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1176 tmp_buffer_[tmp_buffer_pos_] = '\0';
1177 }
1178
1179 int instr_len = data - instr;
1180 ASSERT(instr_len > 0); // Ensure progress.
1181
1182 int outp = 0;
1183 // Instruction bytes.
1184 for (byte* bp = instr; bp < data; bp++) {
1185 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1186 "%02x",
1187 *bp);
1188 }
1189 for (int i = 6 - instr_len; i >= 0; i--) {
1190 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1191 " ");
1192 }
1193
1194 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1195 " %s",
1196 tmp_buffer_.start());
1197 return instr_len;
1198}
1199
1200
1201//------------------------------------------------------------------------------
1202
1203
1204static const char* cpu_regs[8] = {
1205 "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
1206};
1207
1208
1209static const char* byte_cpu_regs[8] = {
1210 "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"
1211};
1212
1213
1214static const char* xmm_regs[8] = {
1215 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
1216};
1217
1218
1219const char* NameConverter::NameOfAddress(byte* addr) const {
1220 static v8::internal::EmbeddedVector<char, 32> tmp_buffer;
1221 v8::internal::OS::SNPrintF(tmp_buffer, "%p", addr);
1222 return tmp_buffer.start();
1223}
1224
1225
1226const char* NameConverter::NameOfConstant(byte* addr) const {
1227 return NameOfAddress(addr);
1228}
1229
1230
1231const char* NameConverter::NameOfCPURegister(int reg) const {
1232 if (0 <= reg && reg < 8) return cpu_regs[reg];
1233 return "noreg";
1234}
1235
1236
1237const char* NameConverter::NameOfByteCPURegister(int reg) const {
1238 if (0 <= reg && reg < 8) return byte_cpu_regs[reg];
1239 return "noreg";
1240}
1241
1242
1243const char* NameConverter::NameOfXMMRegister(int reg) const {
1244 if (0 <= reg && reg < 8) return xmm_regs[reg];
1245 return "noxmmreg";
1246}
1247
1248
1249const char* NameConverter::NameInCode(byte* addr) const {
1250 // IA32 does not embed debug strings at the moment.
1251 UNREACHABLE();
1252 return "";
1253}
1254
1255
1256//------------------------------------------------------------------------------
1257
1258Disassembler::Disassembler(const NameConverter& converter)
1259 : converter_(converter) {}
1260
1261
1262Disassembler::~Disassembler() {}
1263
1264
1265int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
1266 byte* instruction) {
1267 DisassemblerIA32 d(converter_, false /*do not crash if unimplemented*/);
1268 return d.InstructionDecode(buffer, instruction);
1269}
1270
1271
1272// The IA-32 assembler does not currently use constant pools.
1273int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; }
1274
1275
1276/*static*/ void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
1277 NameConverter converter;
1278 Disassembler d(converter);
1279 for (byte* pc = begin; pc < end;) {
1280 v8::internal::EmbeddedVector<char, 128> buffer;
1281 buffer[0] = '\0';
1282 byte* prev_pc = pc;
1283 pc += d.InstructionDecode(buffer, pc);
1284 fprintf(f, "%p", prev_pc);
1285 fprintf(f, " ");
1286
1287 for (byte* bp = prev_pc; bp < pc; bp++) {
1288 fprintf(f, "%02x", *bp);
1289 }
1290 for (int i = 6 - (pc - prev_pc); i >= 0; i--) {
1291 fprintf(f, " ");
1292 }
1293 fprintf(f, " %s\n", buffer.start());
1294 }
1295}
1296
1297
1298} // namespace disasm