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