The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / src / disasm-ia32.cc @ 40c0f755

History | View | Annotate | Download (34.1 KB)

1 40c0f755 Ryan
// 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, &regop, &rm);
342
  switch (mod) {
343
    case 0:
344
      if (rm == ebp) {
345
        int32_t disp = *reinterpret_cast<int32_t*>(modrmp+1);
346
        AppendToBuffer("[0x%x]", disp);
347
        return 5;
348
      } else if (rm == esp) {
349
        byte sib = *(modrmp + 1);
350
        int scale, index, base;
351
        get_sib(sib, &scale, &index, &base);
352
        if (index == esp && base == esp && scale == 0 /*times_1*/) {
353
          AppendToBuffer("[%s]", (this->*register_name)(rm));
354
          return 2;
355
        } else if (base == ebp) {
356
          int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
357
          AppendToBuffer("[%s*%d+0x%x]",
358
                         (this->*register_name)(index),
359
                         1 << scale,
360
                         disp);
361
          return 6;
362
        } else if (index != esp && base != ebp) {
363
          // [base+index*scale]
364
          AppendToBuffer("[%s+%s*%d]",
365
                         (this->*register_name)(base),
366
                         (this->*register_name)(index),
367
                         1 << scale);
368
          return 2;
369
        } else {
370
          UnimplementedInstruction();
371
          return 1;
372
        }
373
      } else {
374
        AppendToBuffer("[%s]", (this->*register_name)(rm));
375
        return 1;
376
      }
377
      break;
378
    case 1:  // fall through
379
    case 2:
380
      if (rm == esp) {
381
        byte sib = *(modrmp + 1);
382
        int scale, index, base;
383
        get_sib(sib, &scale, &index, &base);
384
        int disp =
385
            mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 2) : *(modrmp + 2);
386
        if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) {
387
          AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
388
        } else {
389
          AppendToBuffer("[%s+%s*%d+0x%x]",
390
                         (this->*register_name)(base),
391
                         (this->*register_name)(index),
392
                         1 << scale,
393
                         disp);
394
        }
395
        return mod == 2 ? 6 : 3;
396
      } else {
397
        // No sib.
398
        int disp =
399
            mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 1) : *(modrmp + 1);
400
        AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
401
        return mod == 2 ? 5 : 2;
402
      }
403
      break;
404
    case 3:
405
      AppendToBuffer("%s", (this->*register_name)(rm));
406
      return 1;
407
    default:
408
      UnimplementedInstruction();
409
      return 1;
410
  }
411
  UNREACHABLE();
412
}
413
414
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, &regop, &rm);
434
  int advance = 0;
435
  switch (op_order) {
436
    case REG_OPER_OP_ORDER: {
437
      AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
438
      advance = PrintRightOperand(data);
439
      break;
440
    }
441
    case OPER_REG_OP_ORDER: {
442
      AppendToBuffer("%s ", mnem);
443
      advance = PrintRightOperand(data);
444
      AppendToBuffer(",%s", NameOfCPURegister(regop));
445
      break;
446
    }
447
    default:
448
      UNREACHABLE();
449
      break;
450
  }
451
  return advance;
452
}
453
454
455
// Returns number of bytes used by machine instruction, including *data byte.
456
// Writes immediate instructions to 'tmp_buffer_'.
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, &regop, &rm);
462
  const char* mnem = "Imm???";
463
  switch (regop) {
464
    case 0: mnem = "add"; break;
465
    case 1: mnem = "or"; break;
466
    case 2: mnem = "adc"; break;
467
    case 4: mnem = "and"; break;
468
    case 5: mnem = "sub"; break;
469
    case 6: mnem = "xor"; break;
470
    case 7: mnem = "cmp"; break;
471
    default: UnimplementedInstruction();
472
  }
473
  AppendToBuffer("%s ", mnem);
474
  int count = PrintRightOperand(data+1);
475
  if (sign_extension_bit) {
476
    AppendToBuffer(",0x%x", *(data + 1 + count));
477
    return 1 + count + 1 /*int8*/;
478
  } else {
479
    AppendToBuffer(",0x%x", *reinterpret_cast<int32_t*>(data + 1 + count));
480
    return 1 + count + 4 /*int32_t*/;
481
  }
482
}
483
484
485
// Returns number of bytes used, including *data.
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, &regop, &rm);
491
  if (mod == 3 && regop != 0) {
492
    const char* mnem = NULL;
493
    switch (regop) {
494
      case 2: mnem = "not"; break;
495
      case 3: mnem = "neg"; break;
496
      case 4: mnem = "mul"; break;
497
      case 7: mnem = "idiv"; break;
498
      default: UnimplementedInstruction();
499
    }
500
    AppendToBuffer("%s %s", mnem, NameOfCPURegister(rm));
501
    return 2;
502
  } else if (mod == 3 && regop == eax) {
503
    int32_t imm = *reinterpret_cast<int32_t*>(data+2);
504
    AppendToBuffer("test %s,0x%x", NameOfCPURegister(rm), imm);
505
    return 6;
506
  } else if (regop == eax) {
507
    AppendToBuffer("test ");
508
    int count = PrintRightOperand(data+1);
509
    int32_t imm = *reinterpret_cast<int32_t*>(data+1+count);
510
    AppendToBuffer(",0x%x", imm);
511
    return 1+count+4 /*int32_t*/;
512
  } else {
513
    UnimplementedInstruction();
514
    return 2;
515
  }
516
}
517
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, &regop, &rm);
524
  int imm8 = -1;
525
  int num_bytes = 2;
526
  if (mod == 3) {
527
    const char* mnem = NULL;
528
    if (op == 0xD1) {
529
      imm8 = 1;
530
      switch (regop) {
531
        case edx: mnem = "rcl"; break;
532
        case edi: mnem = "sar"; break;
533
        case esp: mnem = "shl"; break;
534
        default: UnimplementedInstruction();
535
      }
536
    } else if (op == 0xC1) {
537
      imm8 = *(data+2);
538
      num_bytes = 3;
539
      switch (regop) {
540
        case edx: mnem = "rcl"; break;
541
        case esp: mnem = "shl"; break;
542
        case ebp: mnem = "shr"; break;
543
        case edi: mnem = "sar"; break;
544
        default: UnimplementedInstruction();
545
      }
546
    } else if (op == 0xD3) {
547
      switch (regop) {
548
        case esp: mnem = "shl"; break;
549
        case ebp: mnem = "shr"; break;
550
        case edi: mnem = "sar"; break;
551
        default: UnimplementedInstruction();
552
      }
553
    }
554
    assert(mnem != NULL);
555
    AppendToBuffer("%s %s,", mnem, NameOfCPURegister(rm));
556
    if (imm8 > 0) {
557
      AppendToBuffer("%d", imm8);
558
    } else {
559
      AppendToBuffer("cl");
560
    }
561
  } else {
562
    UnimplementedInstruction();
563
  }
564
  return num_bytes;
565
}
566
567
568
// Returns number of bytes used, including *data.
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, &regop, &rm);
642
      const char* mnem = "?";
643
      switch (regop) {
644
        case eax: mnem = "fld_s"; break;
645
        case ebx: mnem = "fstp_s"; break;
646
        default: UnimplementedInstruction();
647
      }
648
      AppendToBuffer("%s ", mnem);
649
      int count = PrintRightOperand(data + 1);
650
      return count + 1;
651
    }
652
  } else if (b1 == 0xDD) {
653
    if ((b2 & 0xF8) == 0xC0) {
654
      AppendToBuffer("ffree st%d", b2 & 0x7);
655
      return 2;
656
    } else {
657
      int mod, regop, rm;
658
      get_modrm(*(data+1), &mod, &regop, &rm);
659
      const char* mnem = "?";
660
      switch (regop) {
661
        case eax: mnem = "fld_d"; break;
662
        case ebx: mnem = "fstp_d"; break;
663
        default: UnimplementedInstruction();
664
      }
665
      AppendToBuffer("%s ", mnem);
666
      int count = PrintRightOperand(data + 1);
667
      return count + 1;
668
    }
669
  } else if (b1 == 0xDB) {
670
    int mod, regop, rm;
671
    get_modrm(*(data+1), &mod, &regop, &rm);
672
    const char* mnem = "?";
673
    switch (regop) {
674
      case eax: mnem = "fild_s"; break;
675
      case edx: mnem = "fist_s"; break;
676
      case ebx: mnem = "fistp_s"; break;
677
      default: UnimplementedInstruction();
678
    }
679
    AppendToBuffer("%s ", mnem);
680
    int count = PrintRightOperand(data + 1);
681
    return count + 1;
682
  } else if (b1 == 0xDF) {
683
    if (b2 == 0xE0) {
684
      AppendToBuffer("fnstsw_ax");
685
      return 2;
686
    }
687
    int mod, regop, rm;
688
    get_modrm(*(data+1), &mod, &regop, &rm);
689
    const char* mnem = "?";
690
    switch (regop) {
691
      case ebp: mnem = "fild_d"; break;
692
      case edi: mnem = "fistp_d"; break;
693
      default: UnimplementedInstruction();
694
    }
695
    AppendToBuffer("%s ", mnem);
696
    int count = PrintRightOperand(data + 1);
697
    return count + 1;
698
  } else if (b1 == 0xDC || b1 == 0xDE) {
699
    bool is_pop = (b1 == 0xDE);
700
    if (is_pop && b2 == 0xD9) {
701
      AppendToBuffer("fcompp");
702
      return 2;
703
    }
704
    const char* mnem = "FP0xDC";
705
    switch (b2 & 0xF8) {
706
      case 0xC0: mnem = "fadd"; break;
707
      case 0xE8: mnem = "fsub"; break;
708
      case 0xC8: mnem = "fmul"; break;
709
      case 0xF8: mnem = "fdiv"; break;
710
      default: UnimplementedInstruction();
711
    }
712
    AppendToBuffer("%s%s st%d", mnem, is_pop ? "p" : "", b2 & 0x7);
713
    return 2;
714
  } else if (b1 == 0xDA && b2 == 0xE9) {
715
    const char* mnem = "fucompp";
716
    AppendToBuffer("%s", mnem);
717
    return 2;
718
  }
719
  AppendToBuffer("Unknown FP instruction");
720
  return 2;
721
}
722
723
724
// Mnemonics for instructions 0xF0 byte.
725
// Returns NULL if the instruction is not handled here.
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, &regop, &rm);
823
          int32_t imm =
824
              *data == 0x6B ? *(data+2) : *reinterpret_cast<int32_t*>(data+2);
825
          AppendToBuffer("imul %s,%s,0x%x",
826
                         NameOfCPURegister(regop),
827
                         NameOfCPURegister(rm),
828
                         imm);
829
          data += 2 + (*data == 0x6B ? 1 : 4);
830
        }
831
        break;
832
833
      case 0xF6:
834
        { int mod, regop, rm;
835
          get_modrm(*(data+1), &mod, &regop, &rm);
836
          if (mod == 3 && regop == eax) {
837
            AppendToBuffer("test_b %s,%d", NameOfCPURegister(rm), *(data+2));
838
          } else {
839
            UnimplementedInstruction();
840
          }
841
          data += 3;
842
        }
843
        break;
844
845
      case 0x81:  // fall through
846
      case 0x83:  // 0x81 with sign extension bit set
847
        data += PrintImmediateOp(data);
848
        break;
849
850
      case 0x0F:
851
        { byte f0byte = *(data+1);
852
          const char* f0mnem = F0Mnem(f0byte);
853
          if (f0byte == 0xA2 || f0byte == 0x31) {
854
            AppendToBuffer("%s", f0mnem);
855
            data += 2;
856
          } else if ((f0byte & 0xF0) == 0x80) {
857
            data += JumpConditional(data, branch_hint);
858
          } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
859
                     f0byte == 0xB7 || f0byte == 0xAF) {
860
            data += 2;
861
            data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data);
862
          } else if ((f0byte & 0xF0) == 0x90) {
863
            data += SetCC(data);
864
          } else {
865
            data += 2;
866
            if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) {
867
              // shrd, shld, bts
868
              AppendToBuffer("%s ", f0mnem);
869
              int mod, regop, rm;
870
              get_modrm(*data, &mod, &regop, &rm);
871
              data += PrintRightOperand(data);
872
              if (f0byte == 0xAB) {
873
                AppendToBuffer(",%s", NameOfCPURegister(regop));
874
              } else {
875
                AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
876
              }
877
            } else {
878
              UnimplementedInstruction();
879
            }
880
          }
881
        }
882
        break;
883
884
      case 0x8F:
885
        { data++;
886
          int mod, regop, rm;
887
          get_modrm(*data, &mod, &regop, &rm);
888
          if (regop == eax) {
889
            AppendToBuffer("pop ");
890
            data += PrintRightOperand(data);
891
          }
892
        }
893
        break;
894
895
      case 0xFF:
896
        { data++;
897
          int mod, regop, rm;
898
          get_modrm(*data, &mod, &regop, &rm);
899
          const char* mnem = NULL;
900
          switch (regop) {
901
            case esi: mnem = "push"; break;
902
            case eax: mnem = "inc"; break;
903
            case ecx: mnem = "dec"; break;
904
            case edx: mnem = "call"; break;
905
            case esp: mnem = "jmp"; break;
906
            default: mnem = "???";
907
          }
908
          AppendToBuffer("%s ", mnem);
909
          data += PrintRightOperand(data);
910
        }
911
        break;
912
913
      case 0xC7:  // imm32, fall through
914
      case 0xC6:  // imm8
915
        { bool is_byte = *data == 0xC6;
916
          data++;
917
          AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
918
          data += PrintRightOperand(data);
919
          int32_t imm = is_byte ? *data : *reinterpret_cast<int32_t*>(data);
920
          AppendToBuffer(",0x%x", imm);
921
          data += is_byte ? 1 : 4;
922
        }
923
        break;
924
925
      case 0x80:
926
        { data++;
927
          AppendToBuffer("%s ", "cmpb");
928
          data += PrintRightOperand(data);
929
          int32_t imm = *data;
930
          AppendToBuffer(",0x%x", imm);
931
          data++;
932
        }
933
        break;
934
935
      case 0x88:  // 8bit, fall through
936
      case 0x89:  // 32bit
937
        { bool is_byte = *data == 0x88;
938
          int mod, regop, rm;
939
          data++;
940
          get_modrm(*data, &mod, &regop, &rm);
941
          AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
942
          data += PrintRightOperand(data);
943
          AppendToBuffer(",%s", NameOfCPURegister(regop));
944
        }
945
        break;
946
947
      case 0x66:  // prefix
948
        data++;
949
        if (*data == 0x8B) {
950
          data++;
951
          data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data);
952
        } else if (*data == 0x89) {
953
          data++;
954
          int mod, regop, rm;
955
          get_modrm(*data, &mod, &regop, &rm);
956
          AppendToBuffer("mov_w ");
957
          data += PrintRightOperand(data);
958
          AppendToBuffer(",%s", NameOfCPURegister(regop));
959
        } else {
960
          UnimplementedInstruction();
961
        }
962
        break;
963
964
      case 0xFE:
965
        { data++;
966
          int mod, regop, rm;
967
          get_modrm(*data, &mod, &regop, &rm);
968
          if (mod == 3 && regop == ecx) {
969
            AppendToBuffer("dec_b %s", NameOfCPURegister(rm));
970
          } else {
971
            UnimplementedInstruction();
972
          }
973
          data++;
974
        }
975
        break;
976
977
      case 0x68:
978
        AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data+1));
979
        data += 5;
980
        break;
981
982
      case 0x6A:
983
        AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
984
        data += 2;
985
        break;
986
987
      case 0xA8:
988
        AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data+1));
989
        data += 2;
990
        break;
991
992
      case 0xA9:
993
        AppendToBuffer("test eax,0x%x", *reinterpret_cast<int32_t*>(data+1));
994
        data += 5;
995
        break;
996
997
      case 0xD1:  // fall through
998
      case 0xD3:  // fall through
999
      case 0xC1:
1000
        data += D1D3C1Instruction(data);
1001
        break;
1002
1003
      case 0xD9:  // fall through
1004
      case 0xDA:  // fall through
1005
      case 0xDB:  // fall through
1006
      case 0xDC:  // fall through
1007
      case 0xDD:  // fall through
1008
      case 0xDE:  // fall through
1009
      case 0xDF:
1010
        data += FPUInstruction(data);
1011
        break;
1012
1013
      case 0xEB:
1014
        data += JumpShort(data);
1015
        break;
1016
1017
      case 0xF2:
1018
        if (*(data+1) == 0x0F) {
1019
          byte b2 = *(data+2);
1020
          if (b2 == 0x11) {
1021
            AppendToBuffer("movsd ");
1022
            data += 3;
1023
            int mod, regop, rm;
1024
            get_modrm(*data, &mod, &regop, &rm);
1025
            data += PrintRightOperand(data);
1026
            AppendToBuffer(",%s", NameOfXMMRegister(regop));
1027
          } else if (b2 == 0x10) {
1028
            data += 3;
1029
            int mod, regop, rm;
1030
            get_modrm(*data, &mod, &regop, &rm);
1031
            AppendToBuffer("movsd %s,", NameOfXMMRegister(regop));
1032
            data += PrintRightOperand(data);
1033
          } else {
1034
            const char* mnem = "?";
1035
            switch (b2) {
1036
              case 0x2A: mnem = "cvtsi2sd"; break;
1037
              case 0x58: mnem = "addsd"; break;
1038
              case 0x59: mnem = "mulsd"; break;
1039
              case 0x5C: mnem = "subsd"; break;
1040
              case 0x5E: mnem = "divsd"; break;
1041
            }
1042
            data += 3;
1043
            int mod, regop, rm;
1044
            get_modrm(*data, &mod, &regop, &rm);
1045
            if (b2 == 0x2A) {
1046
              AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1047
              data += PrintRightOperand(data);
1048
            } else {
1049
              AppendToBuffer("%s %s,%s",
1050
                             mnem,
1051
                             NameOfXMMRegister(regop),
1052
                             NameOfXMMRegister(rm));
1053
              data++;
1054
            }
1055
          }
1056
        } else {
1057
          UnimplementedInstruction();
1058
        }
1059
        break;
1060
1061
      case 0xF3:
1062
        if (*(data+1) == 0x0F && *(data+2) == 0x2C) {
1063
          data += 3;
1064
          data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data);
1065
        } else {
1066
          UnimplementedInstruction();
1067
        }
1068
        break;
1069
1070
      case 0xF7:
1071
        data += F7Instruction(data);
1072
        break;
1073
1074
      default:
1075
        UnimplementedInstruction();
1076
    }
1077
  }
1078
1079
  if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1080
    tmp_buffer_[tmp_buffer_pos_] = '\0';
1081
  }
1082
1083
  int instr_len = data - instr;
1084
  ASSERT(instr_len > 0);  // Ensure progress.
1085
1086
  int outp = 0;
1087
  // Instruction bytes.
1088
  for (byte* bp = instr; bp < data; bp++) {
1089
    outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1090
                                       "%02x",
1091
                                       *bp);
1092
  }
1093
  for (int i = 6 - instr_len; i >= 0; i--) {
1094
    outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1095
                                       "  ");
1096
  }
1097
1098
  outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1099
                                     " %s",
1100
                                     tmp_buffer_.start());
1101
  return instr_len;
1102
}
1103
1104
1105
//------------------------------------------------------------------------------
1106
1107
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