| OLD | NEW |
| 1 // Copyright 2009 the V8 project authors. All rights reserved. | 1 // Copyright 2009 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 211 } | 211 } |
| 212 | 212 |
| 213 | 213 |
| 214 void InstructionTable::CopyTable(ByteMnemonic bm[], InstructionType type) { | 214 void InstructionTable::CopyTable(ByteMnemonic bm[], InstructionType type) { |
| 215 for (int i = 0; bm[i].b >= 0; i++) { | 215 for (int i = 0; bm[i].b >= 0; i++) { |
| 216 InstructionDesc* id = &instructions_[bm[i].b]; | 216 InstructionDesc* id = &instructions_[bm[i].b]; |
| 217 id->mnem = bm[i].mnem; | 217 id->mnem = bm[i].mnem; |
| 218 OperandType op_order = bm[i].op_order_; | 218 OperandType op_order = bm[i].op_order_; |
| 219 id->op_order_ = | 219 id->op_order_ = |
| 220 static_cast<OperandType>(op_order & ~BYTE_SIZE_OPERAND_FLAG); | 220 static_cast<OperandType>(op_order & ~BYTE_SIZE_OPERAND_FLAG); |
| 221 assert(id->type == NO_INSTR); // Information not already entered | 221 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered |
| 222 id->type = type; | 222 id->type = type; |
| 223 id->byte_size_operation = ((op_order & BYTE_SIZE_OPERAND_FLAG) != 0); | 223 id->byte_size_operation = ((op_order & BYTE_SIZE_OPERAND_FLAG) != 0); |
| 224 } | 224 } |
| 225 } | 225 } |
| 226 | 226 |
| 227 | 227 |
| 228 void InstructionTable::SetTableRange(InstructionType type, | 228 void InstructionTable::SetTableRange(InstructionType type, |
| 229 byte start, | 229 byte start, |
| 230 byte end, | 230 byte end, |
| 231 bool byte_size, | 231 bool byte_size, |
| 232 const char* mnem) { | 232 const char* mnem) { |
| 233 for (byte b = start; b <= end; b++) { | 233 for (byte b = start; b <= end; b++) { |
| 234 InstructionDesc* id = &instructions_[b]; | 234 InstructionDesc* id = &instructions_[b]; |
| 235 assert(id->type == NO_INSTR); // Information already entered | 235 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered |
| 236 id->mnem = mnem; | 236 id->mnem = mnem; |
| 237 id->type = type; | 237 id->type = type; |
| 238 id->byte_size_operation = byte_size; | 238 id->byte_size_operation = byte_size; |
| 239 } | 239 } |
| 240 } | 240 } |
| 241 | 241 |
| 242 | 242 |
| 243 void InstructionTable::AddJumpConditionalShort() { | 243 void InstructionTable::AddJumpConditionalShort() { |
| 244 for (byte b = 0x70; b <= 0x7F; b++) { | 244 for (byte b = 0x70; b <= 0x7F; b++) { |
| 245 InstructionDesc* id = &instructions_[b]; | 245 InstructionDesc* id = &instructions_[b]; |
| 246 assert(id->type == NO_INSTR); // Information already entered | 246 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered |
| 247 id->mnem = NULL; // Computed depending on condition code. | 247 id->mnem = NULL; // Computed depending on condition code. |
| 248 id->type = JUMP_CONDITIONAL_SHORT_INSTR; | 248 id->type = JUMP_CONDITIONAL_SHORT_INSTR; |
| 249 } | 249 } |
| 250 } | 250 } |
| 251 | 251 |
| 252 | 252 |
| 253 static InstructionTable instruction_table; | 253 static InstructionTable instruction_table; |
| 254 | 254 |
| 255 static InstructionDesc cmov_instructions[16] = { | 255 static InstructionDesc cmov_instructions[16] = { |
| 256 {"cmovo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, | 256 {"cmovo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 386 *index = ((data >> 3) & 7) | (rex_x() ? 8 : 0); | 386 *index = ((data >> 3) & 7) | (rex_x() ? 8 : 0); |
| 387 *base = (data & 7) | (rex_b() ? 8 : 0); | 387 *base = (data & 7) | (rex_b() ? 8 : 0); |
| 388 } | 388 } |
| 389 | 389 |
| 390 typedef const char* (DisassemblerX64::*RegisterNameMapping)(int reg) const; | 390 typedef const char* (DisassemblerX64::*RegisterNameMapping)(int reg) const; |
| 391 | 391 |
| 392 int PrintRightOperandHelper(byte* modrmp, | 392 int PrintRightOperandHelper(byte* modrmp, |
| 393 RegisterNameMapping register_name); | 393 RegisterNameMapping register_name); |
| 394 int PrintRightOperand(byte* modrmp); | 394 int PrintRightOperand(byte* modrmp); |
| 395 int PrintRightByteOperand(byte* modrmp); | 395 int PrintRightByteOperand(byte* modrmp); |
| 396 int PrintRightXMMOperand(byte* modrmp); |
| 396 int PrintOperands(const char* mnem, | 397 int PrintOperands(const char* mnem, |
| 397 OperandType op_order, | 398 OperandType op_order, |
| 398 byte* data); | 399 byte* data); |
| 399 int PrintImmediate(byte* data, OperandSize size); | 400 int PrintImmediate(byte* data, OperandSize size); |
| 400 int PrintImmediateOp(byte* data); | 401 int PrintImmediateOp(byte* data); |
| 401 const char* TwoByteMnemonic(byte opcode); | 402 const char* TwoByteMnemonic(byte opcode); |
| 402 int TwoByteOpcodeInstruction(byte* data); | 403 int TwoByteOpcodeInstruction(byte* data); |
| 403 int F7Instruction(byte* data); | 404 int F6F7Instruction(byte* data); |
| 404 int ShiftInstruction(byte* data); | 405 int ShiftInstruction(byte* data); |
| 405 int JumpShort(byte* data); | 406 int JumpShort(byte* data); |
| 406 int JumpConditional(byte* data); | 407 int JumpConditional(byte* data); |
| 407 int JumpConditionalShort(byte* data); | 408 int JumpConditionalShort(byte* data); |
| 408 int SetCC(byte* data); | 409 int SetCC(byte* data); |
| 409 int FPUInstruction(byte* data); | 410 int FPUInstruction(byte* data); |
| 411 int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start); |
| 412 int RegisterFPUInstruction(int escape_opcode, byte modrm_byte); |
| 410 void AppendToBuffer(const char* format, ...); | 413 void AppendToBuffer(const char* format, ...); |
| 411 | 414 |
| 412 void UnimplementedInstruction() { | 415 void UnimplementedInstruction() { |
| 413 if (abort_on_unimplemented_) { | 416 if (abort_on_unimplemented_) { |
| 414 CHECK(false); | 417 CHECK(false); |
| 415 } else { | 418 } else { |
| 416 AppendToBuffer("'Unimplemented Instruction'"); | 419 AppendToBuffer("'Unimplemented Instruction'"); |
| 417 } | 420 } |
| 418 } | 421 } |
| 419 }; | 422 }; |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 561 &DisassemblerX64::NameOfCPURegister); | 564 &DisassemblerX64::NameOfCPURegister); |
| 562 } | 565 } |
| 563 | 566 |
| 564 | 567 |
| 565 int DisassemblerX64::PrintRightByteOperand(byte* modrmp) { | 568 int DisassemblerX64::PrintRightByteOperand(byte* modrmp) { |
| 566 return PrintRightOperandHelper(modrmp, | 569 return PrintRightOperandHelper(modrmp, |
| 567 &DisassemblerX64::NameOfByteCPURegister); | 570 &DisassemblerX64::NameOfByteCPURegister); |
| 568 } | 571 } |
| 569 | 572 |
| 570 | 573 |
| 574 int DisassemblerX64::PrintRightXMMOperand(byte* modrmp) { |
| 575 return PrintRightOperandHelper(modrmp, |
| 576 &DisassemblerX64::NameOfXMMRegister); |
| 577 } |
| 578 |
| 579 |
| 571 // Returns number of bytes used including the current *data. | 580 // Returns number of bytes used including the current *data. |
| 572 // Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'. | 581 // Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'. |
| 573 int DisassemblerX64::PrintOperands(const char* mnem, | 582 int DisassemblerX64::PrintOperands(const char* mnem, |
| 574 OperandType op_order, | 583 OperandType op_order, |
| 575 byte* data) { | 584 byte* data) { |
| 576 byte modrm = *data; | 585 byte modrm = *data; |
| 577 int mod, regop, rm; | 586 int mod, regop, rm; |
| 578 get_modrm(modrm, &mod, ®op, &rm); | 587 get_modrm(modrm, &mod, ®op, &rm); |
| 579 int advance = 0; | 588 int advance = 0; |
| 580 const char* register_name = | 589 const char* register_name = |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 641 AppendToBuffer("%s%c ", mnem, operand_size_code()); | 650 AppendToBuffer("%s%c ", mnem, operand_size_code()); |
| 642 int count = PrintRightOperand(data + 1); | 651 int count = PrintRightOperand(data + 1); |
| 643 AppendToBuffer(",0x"); | 652 AppendToBuffer(",0x"); |
| 644 OperandSize immediate_size = byte_size_immediate ? BYTE_SIZE : operand_size(); | 653 OperandSize immediate_size = byte_size_immediate ? BYTE_SIZE : operand_size(); |
| 645 count += PrintImmediate(data + 1 + count, immediate_size); | 654 count += PrintImmediate(data + 1 + count, immediate_size); |
| 646 return 1 + count; | 655 return 1 + count; |
| 647 } | 656 } |
| 648 | 657 |
| 649 | 658 |
| 650 // Returns number of bytes used, including *data. | 659 // Returns number of bytes used, including *data. |
| 651 int DisassemblerX64::F7Instruction(byte* data) { | 660 int DisassemblerX64::F6F7Instruction(byte* data) { |
| 652 assert(*data == 0xF7); | 661 ASSERT(*data == 0xF7 || *data == 0xF6); |
| 653 byte modrm = *(data + 1); | 662 byte modrm = *(data + 1); |
| 654 int mod, regop, rm; | 663 int mod, regop, rm; |
| 655 get_modrm(modrm, &mod, ®op, &rm); | 664 get_modrm(modrm, &mod, ®op, &rm); |
| 656 if (mod == 3 && regop != 0) { | 665 if (mod == 3 && regop != 0) { |
| 657 const char* mnem = NULL; | 666 const char* mnem = NULL; |
| 658 switch (regop) { | 667 switch (regop) { |
| 659 case 2: | 668 case 2: |
| 660 mnem = "not"; | 669 mnem = "not"; |
| 661 break; | 670 break; |
| 662 case 3: | 671 case 3: |
| 663 mnem = "neg"; | 672 mnem = "neg"; |
| 664 break; | 673 break; |
| 665 case 4: | 674 case 4: |
| 666 mnem = "mul"; | 675 mnem = "mul"; |
| 667 break; | 676 break; |
| 668 case 7: | 677 case 7: |
| 669 mnem = "idiv"; | 678 mnem = "idiv"; |
| 670 break; | 679 break; |
| 671 default: | 680 default: |
| 672 UnimplementedInstruction(); | 681 UnimplementedInstruction(); |
| 673 } | 682 } |
| 674 AppendToBuffer("%s%c %s", | 683 AppendToBuffer("%s%c %s", |
| 675 mnem, | 684 mnem, |
| 676 operand_size_code(), | 685 operand_size_code(), |
| 677 NameOfCPURegister(rm)); | 686 NameOfCPURegister(rm)); |
| 678 return 2; | 687 return 2; |
| 679 } else if (mod == 3 && regop == 0) { | |
| 680 int32_t imm = *reinterpret_cast<int32_t*>(data + 2); | |
| 681 AppendToBuffer("test%c %s,0x%x", | |
| 682 operand_size_code(), | |
| 683 NameOfCPURegister(rm), | |
| 684 imm); | |
| 685 return 6; | |
| 686 } else if (regop == 0) { | 688 } else if (regop == 0) { |
| 687 AppendToBuffer("test%c ", operand_size_code()); | 689 AppendToBuffer("test%c ", operand_size_code()); |
| 688 int count = PrintRightOperand(data + 1); | 690 int count = PrintRightOperand(data + 1); // Use name of 64-bit register. |
| 689 int32_t imm = *reinterpret_cast<int32_t*>(data + 1 + count); | 691 AppendToBuffer(",0x"); |
| 690 AppendToBuffer(",0x%x", imm); | 692 count += PrintImmediate(data + 1 + count, operand_size()); |
| 691 return 1 + count + 4 /*int32_t*/; | 693 return 1 + count; |
| 692 } else { | 694 } else { |
| 693 UnimplementedInstruction(); | 695 UnimplementedInstruction(); |
| 694 return 2; | 696 return 2; |
| 695 } | 697 } |
| 696 } | 698 } |
| 697 | 699 |
| 698 | 700 |
| 699 int DisassemblerX64::ShiftInstruction(byte* data) { | 701 int DisassemblerX64::ShiftInstruction(byte* data) { |
| 700 byte op = *data & (~1); | 702 byte op = *data & (~1); |
| 701 if (op != 0xD0 && op != 0xD2 && op != 0xC0) { | 703 if (op != 0xD0 && op != 0xD2 && op != 0xC0) { |
| (...skipping 30 matching lines...) Expand all Loading... |
| 732 case 5: | 734 case 5: |
| 733 mnem = "shr"; | 735 mnem = "shr"; |
| 734 break; | 736 break; |
| 735 case 7: | 737 case 7: |
| 736 mnem = "sar"; | 738 mnem = "sar"; |
| 737 break; | 739 break; |
| 738 default: | 740 default: |
| 739 UnimplementedInstruction(); | 741 UnimplementedInstruction(); |
| 740 return num_bytes; | 742 return num_bytes; |
| 741 } | 743 } |
| 742 assert(mnem != NULL); | 744 ASSERT_NE(NULL, mnem); |
| 743 if (op == 0xD0) { | 745 if (op == 0xD0) { |
| 744 imm8 = 1; | 746 imm8 = 1; |
| 745 } else if (op == 0xC0) { | 747 } else if (op == 0xC0) { |
| 746 imm8 = *(data + 2); | 748 imm8 = *(data + 2); |
| 747 num_bytes = 3; | 749 num_bytes = 3; |
| 748 } | 750 } |
| 749 AppendToBuffer("%s%c %s,", | 751 AppendToBuffer("%s%c %s,", |
| 750 mnem, | 752 mnem, |
| 751 operand_size_code(), | 753 operand_size_code(), |
| 752 byte_size_operand_ ? NameOfByteCPURegister(rm) | 754 byte_size_operand_ ? NameOfByteCPURegister(rm) |
| 753 : NameOfCPURegister(rm)); | 755 : NameOfCPURegister(rm)); |
| 754 if (op == 0xD2) { | 756 if (op == 0xD2) { |
| 755 AppendToBuffer("cl"); | 757 AppendToBuffer("cl"); |
| 756 } else { | 758 } else { |
| 757 AppendToBuffer("%d", imm8); | 759 AppendToBuffer("%d", imm8); |
| 758 } | 760 } |
| 759 return num_bytes; | 761 return num_bytes; |
| 760 } | 762 } |
| 761 | 763 |
| 762 | 764 |
| 763 // Returns number of bytes used, including *data. | 765 // Returns number of bytes used, including *data. |
| 764 int DisassemblerX64::JumpShort(byte* data) { | 766 int DisassemblerX64::JumpShort(byte* data) { |
| 765 assert(*data == 0xEB); | 767 ASSERT_EQ(0xEB, *data); |
| 766 byte b = *(data + 1); | 768 byte b = *(data + 1); |
| 767 byte* dest = data + static_cast<int8_t>(b) + 2; | 769 byte* dest = data + static_cast<int8_t>(b) + 2; |
| 768 AppendToBuffer("jmp %s", NameOfAddress(dest)); | 770 AppendToBuffer("jmp %s", NameOfAddress(dest)); |
| 769 return 2; | 771 return 2; |
| 770 } | 772 } |
| 771 | 773 |
| 772 | 774 |
| 773 // Returns number of bytes used, including *data. | 775 // Returns number of bytes used, including *data. |
| 774 int DisassemblerX64::JumpConditional(byte* data) { | 776 int DisassemblerX64::JumpConditional(byte* data) { |
| 775 assert(*data == 0x0F); | 777 ASSERT_EQ(0x0F, *data); |
| 776 byte cond = *(data + 1) & 0x0F; | 778 byte cond = *(data + 1) & 0x0F; |
| 777 byte* dest = data + *reinterpret_cast<int32_t*>(data + 2) + 6; | 779 byte* dest = data + *reinterpret_cast<int32_t*>(data + 2) + 6; |
| 778 const char* mnem = conditional_code_suffix[cond]; | 780 const char* mnem = conditional_code_suffix[cond]; |
| 779 AppendToBuffer("j%s %s", mnem, NameOfAddress(dest)); | 781 AppendToBuffer("j%s %s", mnem, NameOfAddress(dest)); |
| 780 return 6; // includes 0x0F | 782 return 6; // includes 0x0F |
| 781 } | 783 } |
| 782 | 784 |
| 783 | 785 |
| 784 // Returns number of bytes used, including *data. | 786 // Returns number of bytes used, including *data. |
| 785 int DisassemblerX64::JumpConditionalShort(byte* data) { | 787 int DisassemblerX64::JumpConditionalShort(byte* data) { |
| 786 byte cond = *data & 0x0F; | 788 byte cond = *data & 0x0F; |
| 787 byte b = *(data + 1); | 789 byte b = *(data + 1); |
| 788 byte* dest = data + static_cast<int8_t>(b) + 2; | 790 byte* dest = data + static_cast<int8_t>(b) + 2; |
| 789 const char* mnem = conditional_code_suffix[cond]; | 791 const char* mnem = conditional_code_suffix[cond]; |
| 790 AppendToBuffer("j%s %s", mnem, NameOfAddress(dest)); | 792 AppendToBuffer("j%s %s", mnem, NameOfAddress(dest)); |
| 791 return 2; | 793 return 2; |
| 792 } | 794 } |
| 793 | 795 |
| 794 | 796 |
| 795 // Returns number of bytes used, including *data. | 797 // Returns number of bytes used, including *data. |
| 796 int DisassemblerX64::SetCC(byte* data) { | 798 int DisassemblerX64::SetCC(byte* data) { |
| 797 assert(*data == 0x0F); | 799 ASSERT_EQ(0x0F, *data); |
| 798 byte cond = *(data + 1) & 0x0F; | 800 byte cond = *(data + 1) & 0x0F; |
| 799 const char* mnem = conditional_code_suffix[cond]; | 801 const char* mnem = conditional_code_suffix[cond]; |
| 800 AppendToBuffer("set%s%c ", mnem, operand_size_code()); | 802 AppendToBuffer("set%s%c ", mnem, operand_size_code()); |
| 801 PrintRightByteOperand(data + 2); | 803 PrintRightByteOperand(data + 2); |
| 802 return 3; // includes 0x0F | 804 return 3; // includes 0x0F |
| 803 } | 805 } |
| 804 | 806 |
| 805 | 807 |
| 806 // Returns number of bytes used, including *data. | 808 // Returns number of bytes used, including *data. |
| 807 int DisassemblerX64::FPUInstruction(byte* data) { | 809 int DisassemblerX64::FPUInstruction(byte* data) { |
| 808 byte b1 = *data; | 810 byte escape_opcode = *data; |
| 809 byte b2 = *(data + 1); | 811 ASSERT_EQ(0xD8, escape_opcode & 0xF8); |
| 810 if (b1 == 0xD9) { | 812 byte modrm_byte = *(data+1); |
| 811 const char* mnem = NULL; | 813 |
| 812 switch (b2) { | 814 if (modrm_byte >= 0xC0) { |
| 813 case 0xE0: | 815 return RegisterFPUInstruction(escape_opcode, modrm_byte); |
| 814 mnem = "fchs"; | 816 } else { |
| 815 break; | 817 return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1); |
| 816 case 0xE1: | 818 } |
| 817 mnem = "fabs"; | 819 } |
| 818 break; | 820 |
| 819 case 0xE4: | 821 int DisassemblerX64::MemoryFPUInstruction(int escape_opcode, |
| 820 mnem = "ftst"; | 822 int modrm_byte, |
| 821 break; | 823 byte* modrm_start) { |
| 822 case 0xF5: | 824 const char* mnem = "?"; |
| 823 mnem = "fprem1"; | 825 int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte. |
| 824 break; | 826 switch (escape_opcode) { |
| 825 case 0xF7: | 827 case 0xD9: switch (regop) { |
| 826 mnem = "fincstp"; | 828 case 0: mnem = "fld_s"; break; |
| 827 break; | 829 case 3: mnem = "fstp_s"; break; |
| 828 case 0xE8: | 830 case 7: mnem = "fstcw"; break; |
| 829 mnem = "fld1"; | 831 default: UnimplementedInstruction(); |
| 830 break; | 832 } |
| 831 case 0xEE: | 833 break; |
| 832 mnem = "fldz"; | 834 |
| 833 break; | 835 case 0xDB: switch (regop) { |
| 834 case 0xF8: | 836 case 0: mnem = "fild_s"; break; |
| 835 mnem = "fprem"; | 837 case 1: mnem = "fisttp_s"; break; |
| 836 break; | 838 case 2: mnem = "fist_s"; break; |
| 837 } | 839 case 3: mnem = "fistp_s"; break; |
| 838 if (mnem != NULL) { | 840 default: UnimplementedInstruction(); |
| 839 AppendToBuffer("%s", mnem); | 841 } |
| 840 return 2; | 842 break; |
| 841 } else if ((b2 & 0xF8) == 0xC8) { | 843 |
| 842 AppendToBuffer("fxch st%d", b2 & 0x7); | 844 case 0xDD: switch (regop) { |
| 843 return 2; | 845 case 0: mnem = "fld_d"; break; |
| 844 } else { | 846 case 3: mnem = "fstp_d"; break; |
| 845 int mod, regop, rm; | 847 default: UnimplementedInstruction(); |
| 846 get_modrm(*(data + 1), &mod, ®op, &rm); | 848 } |
| 847 const char* mnem = "?"; | 849 break; |
| 848 switch (regop) { | 850 |
| 849 case 0: | 851 case 0xDF: switch (regop) { |
| 850 mnem = "fld_s"; | 852 case 5: mnem = "fild_d"; break; |
| 851 break; | 853 case 7: mnem = "fistp_d"; break; |
| 852 case 3: | 854 default: UnimplementedInstruction(); |
| 853 mnem = "fstp_s"; | 855 } |
| 856 break; |
| 857 |
| 858 default: UnimplementedInstruction(); |
| 859 } |
| 860 AppendToBuffer("%s ", mnem); |
| 861 int count = PrintRightOperand(modrm_start); |
| 862 return count + 1; |
| 863 } |
| 864 |
| 865 int DisassemblerX64::RegisterFPUInstruction(int escape_opcode, |
| 866 byte modrm_byte) { |
| 867 bool has_register = false; // Is the FPU register encoded in modrm_byte? |
| 868 const char* mnem = "?"; |
| 869 |
| 870 switch (escape_opcode) { |
| 871 case 0xD8: |
| 872 UnimplementedInstruction(); |
| 873 break; |
| 874 |
| 875 case 0xD9: |
| 876 switch (modrm_byte & 0xF8) { |
| 877 case 0xC8: |
| 878 mnem = "fxch"; |
| 879 has_register = true; |
| 854 break; | 880 break; |
| 855 default: | 881 default: |
| 856 UnimplementedInstruction(); | 882 switch (modrm_byte) { |
| 883 case 0xE0: mnem = "fchs"; break; |
| 884 case 0xE1: mnem = "fabs"; break; |
| 885 case 0xE4: mnem = "ftst"; break; |
| 886 case 0xE8: mnem = "fld1"; break; |
| 887 case 0xEE: mnem = "fldz"; break; |
| 888 case 0xF5: mnem = "fprem1"; break; |
| 889 case 0xF7: mnem = "fincstp"; break; |
| 890 case 0xF8: mnem = "fprem"; break; |
| 891 case 0xFE: mnem = "fsin"; break; |
| 892 case 0xFF: mnem = "fcos"; break; |
| 893 default: UnimplementedInstruction(); |
| 894 } |
| 857 } | 895 } |
| 858 AppendToBuffer("%s ", mnem); | 896 break; |
| 859 int count = PrintRightOperand(data + 1); | 897 |
| 860 return count + 1; | 898 case 0xDA: |
| 861 } | 899 if (modrm_byte == 0xE9) { |
| 862 } else if (b1 == 0xDD) { | 900 mnem = "fucompp"; |
| 863 int mod, regop, rm; | 901 } else { |
| 864 get_modrm(*(data + 1), &mod, ®op, &rm); | 902 UnimplementedInstruction(); |
| 865 if (mod == 3) { | |
| 866 switch (regop) { | |
| 867 case 0: | |
| 868 AppendToBuffer("ffree st%d", rm & 7); | |
| 869 break; | |
| 870 case 2: | |
| 871 AppendToBuffer("fstp st%d", rm & 7); | |
| 872 break; | |
| 873 default: | |
| 874 UnimplementedInstruction(); | |
| 875 break; | |
| 876 } | 903 } |
| 877 return 2; | 904 break; |
| 878 } else { | 905 |
| 879 const char* mnem = "?"; | 906 case 0xDB: |
| 880 switch (regop) { | 907 if ((modrm_byte & 0xF8) == 0xE8) { |
| 881 case 0: | 908 mnem = "fucomi"; |
| 882 mnem = "fld_d"; | 909 has_register = true; |
| 883 break; | 910 } else if (modrm_byte == 0xE2) { |
| 884 case 3: | 911 mnem = "fclex"; |
| 885 mnem = "fstp_d"; | 912 } else { |
| 886 break; | 913 UnimplementedInstruction(); |
| 887 default: | |
| 888 UnimplementedInstruction(); | |
| 889 } | 914 } |
| 890 AppendToBuffer("%s ", mnem); | 915 break; |
| 891 int count = PrintRightOperand(data + 1); | 916 |
| 892 return count + 1; | 917 case 0xDC: |
| 893 } | 918 has_register = true; |
| 894 } else if (b1 == 0xDB) { | 919 switch (modrm_byte & 0xF8) { |
| 895 int mod, regop, rm; | 920 case 0xC0: mnem = "fadd"; break; |
| 896 get_modrm(*(data + 1), &mod, ®op, &rm); | 921 case 0xE8: mnem = "fsub"; break; |
| 897 const char* mnem = "?"; | 922 case 0xC8: mnem = "fmul"; break; |
| 898 switch (regop) { | 923 case 0xF8: mnem = "fdiv"; break; |
| 899 case 0: | 924 default: UnimplementedInstruction(); |
| 900 mnem = "fild_s"; | 925 } |
| 901 break; | 926 break; |
| 902 case 2: | 927 |
| 903 mnem = "fist_s"; | 928 case 0xDD: |
| 904 break; | 929 has_register = true; |
| 905 case 3: | 930 switch (modrm_byte & 0xF8) { |
| 906 mnem = "fistp_s"; | 931 case 0xC0: mnem = "ffree"; break; |
| 907 break; | 932 case 0xD8: mnem = "fstp"; break; |
| 908 default: | 933 default: UnimplementedInstruction(); |
| 909 UnimplementedInstruction(); | 934 } |
| 910 } | 935 break; |
| 911 AppendToBuffer("%s ", mnem); | 936 |
| 912 int count = PrintRightOperand(data + 1); | 937 case 0xDE: |
| 913 return count + 1; | 938 if (modrm_byte == 0xD9) { |
| 914 } else if (b1 == 0xDF) { | 939 mnem = "fcompp"; |
| 915 if (b2 == 0xE0) { | 940 } else { |
| 916 AppendToBuffer("fnstsw_ax"); | 941 has_register = true; |
| 917 return 2; | 942 switch (modrm_byte & 0xF8) { |
| 918 } | 943 case 0xC0: mnem = "faddp"; break; |
| 919 int mod, regop, rm; | 944 case 0xE8: mnem = "fsubp"; break; |
| 920 get_modrm(*(data + 1), &mod, ®op, &rm); | 945 case 0xC8: mnem = "fmulp"; break; |
| 921 const char* mnem = "?"; | 946 case 0xF8: mnem = "fdivp"; break; |
| 922 switch (regop) { | 947 default: UnimplementedInstruction(); |
| 923 case 5: | 948 } |
| 924 mnem = "fild_d"; | 949 } |
| 925 break; | 950 break; |
| 926 case 7: | 951 |
| 927 mnem = "fistp_d"; | 952 case 0xDF: |
| 928 break; | 953 if (modrm_byte == 0xE0) { |
| 929 default: | 954 mnem = "fnstsw_ax"; |
| 930 UnimplementedInstruction(); | 955 } else if ((modrm_byte & 0xF8) == 0xE8) { |
| 931 } | 956 mnem = "fucomip"; |
| 932 AppendToBuffer("%s ", mnem); | 957 has_register = true; |
| 933 int count = PrintRightOperand(data + 1); | 958 } |
| 934 return count + 1; | 959 break; |
| 935 } else if (b1 == 0xDC || b1 == 0xDE) { | 960 |
| 936 bool is_pop = (b1 == 0xDE); | 961 default: UnimplementedInstruction(); |
| 937 if (is_pop && b2 == 0xD9) { | 962 } |
| 938 AppendToBuffer("fcompp"); | 963 |
| 939 return 2; | 964 if (has_register) { |
| 940 } | 965 AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7); |
| 941 const char* mnem = "FP0xDC"; | 966 } else { |
| 942 switch (b2 & 0xF8) { | |
| 943 case 0xC0: | |
| 944 mnem = "fadd"; | |
| 945 break; | |
| 946 case 0xE8: | |
| 947 mnem = "fsub"; | |
| 948 break; | |
| 949 case 0xC8: | |
| 950 mnem = "fmul"; | |
| 951 break; | |
| 952 case 0xF8: | |
| 953 mnem = "fdiv"; | |
| 954 break; | |
| 955 default: | |
| 956 UnimplementedInstruction(); | |
| 957 } | |
| 958 AppendToBuffer("%s%s st%d", mnem, is_pop ? "p" : "", b2 & 0x7); | |
| 959 return 2; | |
| 960 } else if (b1 == 0xDA && b2 == 0xE9) { | |
| 961 const char* mnem = "fucompp"; | |
| 962 AppendToBuffer("%s", mnem); | 967 AppendToBuffer("%s", mnem); |
| 963 return 2; | |
| 964 } | 968 } |
| 965 AppendToBuffer("Unknown FP instruction"); | |
| 966 return 2; | 969 return 2; |
| 967 } | 970 } |
| 968 | 971 |
| 969 | 972 |
| 973 |
| 970 // Handle all two-byte opcodes, which start with 0x0F. | 974 // Handle all two-byte opcodes, which start with 0x0F. |
| 971 // These instructions may be affected by an 0x66, 0xF2, or 0xF3 prefix. | 975 // These instructions may be affected by an 0x66, 0xF2, or 0xF3 prefix. |
| 972 // We do not use any three-byte opcodes, which start with 0x0F38 or 0x0F3A. | 976 // We do not use any three-byte opcodes, which start with 0x0F38 or 0x0F3A. |
| 973 int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { | 977 int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { |
| 974 byte opcode = *(data + 1); | 978 byte opcode = *(data + 1); |
| 975 byte* current = data + 2; | 979 byte* current = data + 2; |
| 976 // At return, "current" points to the start of the next instruction. | 980 // At return, "current" points to the start of the next instruction. |
| 977 const char* mnemonic = TwoByteMnemonic(opcode); | 981 const char* mnemonic = TwoByteMnemonic(opcode); |
| 978 if (opcode == 0x1F) { | 982 if (opcode == 0x1F) { |
| 979 // NOP | 983 // NOP |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1038 AppendToBuffer(",%s", NameOfXMMRegister(regop)); | 1042 AppendToBuffer(",%s", NameOfXMMRegister(regop)); |
| 1039 } else { | 1043 } else { |
| 1040 AppendToBuffer("%s,", NameOfXMMRegister(regop)); | 1044 AppendToBuffer("%s,", NameOfXMMRegister(regop)); |
| 1041 current += PrintRightOperand(current); | 1045 current += PrintRightOperand(current); |
| 1042 } | 1046 } |
| 1043 } else if (opcode == 0x2A) { | 1047 } else if (opcode == 0x2A) { |
| 1044 // CVTSI2SD: integer to XMM double conversion. | 1048 // CVTSI2SD: integer to XMM double conversion. |
| 1045 int mod, regop, rm; | 1049 int mod, regop, rm; |
| 1046 get_modrm(*current, &mod, ®op, &rm); | 1050 get_modrm(*current, &mod, ®op, &rm); |
| 1047 AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop)); | 1051 AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop)); |
| 1048 data += PrintRightOperand(data); | 1052 current += PrintRightOperand(current); |
| 1049 } else if ((opcode & 0xF8) == 0x58) { | 1053 } else if ((opcode & 0xF8) == 0x58) { |
| 1050 // XMM arithmetic. Mnemonic was retrieved at the start of this function. | 1054 // XMM arithmetic. Mnemonic was retrieved at the start of this function. |
| 1051 int mod, regop, rm; | 1055 int mod, regop, rm; |
| 1052 get_modrm(*current, &mod, ®op, &rm); | 1056 get_modrm(*current, &mod, ®op, &rm); |
| 1053 AppendToBuffer("%s %s,%s", mnemonic, NameOfXMMRegister(regop), | 1057 AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop)); |
| 1054 NameOfXMMRegister(rm)); | 1058 current += PrintRightXMMOperand(current); |
| 1055 } else { | 1059 } else { |
| 1056 UnimplementedInstruction(); | 1060 UnimplementedInstruction(); |
| 1057 } | 1061 } |
| 1058 } else if (opcode == 0x2C && group_1_prefix_ == 0xF3) { | 1062 } else if (opcode == 0x2C && group_1_prefix_ == 0xF3) { |
| 1059 // Instruction with prefix 0xF3. | 1063 // Instruction with prefix 0xF3. |
| 1060 | 1064 |
| 1061 // CVTTSS2SI: Convert scalar single-precision FP to dword integer. | 1065 // CVTTSS2SI: Convert scalar single-precision FP to dword integer. |
| 1062 // Assert that mod is not 3, so source is memory, not an XMM register. | 1066 // Assert that mod is not 3, so source is memory, not an XMM register. |
| 1063 ASSERT((*current & 0xC0) != 0xC0); | 1067 ASSERT_NE(0xC0, *current & 0xC0); |
| 1064 current += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, current); | 1068 current += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, current); |
| 1065 } else { | 1069 } else { |
| 1066 UnimplementedInstruction(); | 1070 UnimplementedInstruction(); |
| 1067 } | 1071 } |
| 1068 return current - data; | 1072 return current - data; |
| 1069 } | 1073 } |
| 1070 | 1074 |
| 1071 | 1075 |
| 1072 // Mnemonics for two-byte opcode instructions starting with 0x0F. | 1076 // Mnemonics for two-byte opcode instructions starting with 0x0F. |
| 1073 // The argument is the second byte of the two-byte opcode. | 1077 // The argument is the second byte of the two-byte opcode. |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1229 int mod, regop, rm; | 1233 int mod, regop, rm; |
| 1230 get_modrm(*(data + 1), &mod, ®op, &rm); | 1234 get_modrm(*(data + 1), &mod, ®op, &rm); |
| 1231 int32_t imm = *data == 0x6B ? *(data + 2) | 1235 int32_t imm = *data == 0x6B ? *(data + 2) |
| 1232 : *reinterpret_cast<int32_t*>(data + 2); | 1236 : *reinterpret_cast<int32_t*>(data + 2); |
| 1233 AppendToBuffer("imul %s,%s,0x%x", NameOfCPURegister(regop), | 1237 AppendToBuffer("imul %s,%s,0x%x", NameOfCPURegister(regop), |
| 1234 NameOfCPURegister(rm), imm); | 1238 NameOfCPURegister(rm), imm); |
| 1235 data += 2 + (*data == 0x6B ? 1 : 4); | 1239 data += 2 + (*data == 0x6B ? 1 : 4); |
| 1236 break; | 1240 break; |
| 1237 } | 1241 } |
| 1238 | 1242 |
| 1239 case 0xF6: { | |
| 1240 int mod, regop, rm; | |
| 1241 get_modrm(*(data + 1), &mod, ®op, &rm); | |
| 1242 if (mod == 3 && regop == 0) { | |
| 1243 AppendToBuffer("testb %s,%d", NameOfCPURegister(rm), *(data + 2)); | |
| 1244 } else { | |
| 1245 UnimplementedInstruction(); | |
| 1246 } | |
| 1247 data += 3; | |
| 1248 break; | |
| 1249 } | |
| 1250 | |
| 1251 case 0x81: // fall through | 1243 case 0x81: // fall through |
| 1252 case 0x83: // 0x81 with sign extension bit set | 1244 case 0x83: // 0x81 with sign extension bit set |
| 1253 data += PrintImmediateOp(data); | 1245 data += PrintImmediateOp(data); |
| 1254 break; | 1246 break; |
| 1255 | 1247 |
| 1256 case 0x0F: | 1248 case 0x0F: |
| 1257 data += TwoByteOpcodeInstruction(data); | 1249 data += TwoByteOpcodeInstruction(data); |
| 1258 break; | 1250 break; |
| 1259 | 1251 |
| 1260 case 0x8F: { | 1252 case 0x8F: { |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1337 break; | 1329 break; |
| 1338 | 1330 |
| 1339 case 0x90: | 1331 case 0x90: |
| 1340 case 0x91: | 1332 case 0x91: |
| 1341 case 0x92: | 1333 case 0x92: |
| 1342 case 0x93: | 1334 case 0x93: |
| 1343 case 0x94: | 1335 case 0x94: |
| 1344 case 0x95: | 1336 case 0x95: |
| 1345 case 0x96: | 1337 case 0x96: |
| 1346 case 0x97: { | 1338 case 0x97: { |
| 1347 int reg = (current & 0x7) | (rex_b() ? 8 : 0); | 1339 int reg = (*data & 0x7) | (rex_b() ? 8 : 0); |
| 1348 if (reg == 0) { | 1340 if (reg == 0) { |
| 1349 AppendToBuffer("nop"); // Common name for xchg rax,rax. | 1341 AppendToBuffer("nop"); // Common name for xchg rax,rax. |
| 1350 } else { | 1342 } else { |
| 1351 AppendToBuffer("xchg%c rax, %s", | 1343 AppendToBuffer("xchg%c rax, %s", |
| 1352 operand_size_code(), | 1344 operand_size_code(), |
| 1353 NameOfCPURegister(reg)); | 1345 NameOfCPURegister(reg)); |
| 1354 } | 1346 } |
| 1347 data++; |
| 1355 } | 1348 } |
| 1356 | 1349 break; |
| 1357 | 1350 |
| 1358 case 0xFE: { | 1351 case 0xFE: { |
| 1359 data++; | 1352 data++; |
| 1360 int mod, regop, rm; | 1353 int mod, regop, rm; |
| 1361 get_modrm(*data, &mod, ®op, &rm); | 1354 get_modrm(*data, &mod, ®op, &rm); |
| 1362 if (mod == 3 && regop == 1) { | 1355 if (mod == 3 && regop == 1) { |
| 1363 AppendToBuffer("decb %s", NameOfCPURegister(rm)); | 1356 AppendToBuffer("decb %s", NameOfCPURegister(rm)); |
| 1364 } else { | 1357 } else { |
| 1365 UnimplementedInstruction(); | 1358 UnimplementedInstruction(); |
| 1366 } | 1359 } |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1458 case 0xDD: // fall through | 1451 case 0xDD: // fall through |
| 1459 case 0xDE: // fall through | 1452 case 0xDE: // fall through |
| 1460 case 0xDF: | 1453 case 0xDF: |
| 1461 data += FPUInstruction(data); | 1454 data += FPUInstruction(data); |
| 1462 break; | 1455 break; |
| 1463 | 1456 |
| 1464 case 0xEB: | 1457 case 0xEB: |
| 1465 data += JumpShort(data); | 1458 data += JumpShort(data); |
| 1466 break; | 1459 break; |
| 1467 | 1460 |
| 1461 case 0xF6: |
| 1462 byte_size_operand_ = true; // fall through |
| 1468 case 0xF7: | 1463 case 0xF7: |
| 1469 data += F7Instruction(data); | 1464 data += F6F7Instruction(data); |
| 1470 break; | 1465 break; |
| 1471 | 1466 |
| 1472 default: | 1467 default: |
| 1473 UnimplementedInstruction(); | 1468 UnimplementedInstruction(); |
| 1474 data += 1; | 1469 data += 1; |
| 1475 } | 1470 } |
| 1476 } // !processed | 1471 } // !processed |
| 1477 | 1472 |
| 1478 if (tmp_buffer_pos_ < sizeof tmp_buffer_) { | 1473 if (tmp_buffer_pos_ < sizeof tmp_buffer_) { |
| 1479 tmp_buffer_[tmp_buffer_pos_] = '\0'; | 1474 tmp_buffer_[tmp_buffer_pos_] = '\0'; |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1592 fprintf(f, "%02x", *bp); | 1587 fprintf(f, "%02x", *bp); |
| 1593 } | 1588 } |
| 1594 for (int i = 6 - (pc - prev_pc); i >= 0; i--) { | 1589 for (int i = 6 - (pc - prev_pc); i >= 0; i--) { |
| 1595 fprintf(f, " "); | 1590 fprintf(f, " "); |
| 1596 } | 1591 } |
| 1597 fprintf(f, " %s\n", buffer.start()); | 1592 fprintf(f, " %s\n", buffer.start()); |
| 1598 } | 1593 } |
| 1599 } | 1594 } |
| 1600 | 1595 |
| 1601 } // namespace disasm | 1596 } // namespace disasm |
| OLD | NEW |