/* This file is part of the dynarmic project. * Copyright (c) 2016 MerryMage * This software may be used and distributed according to the terms of the GNU * General Public License version 2 or any later version. */ #include #include #define WIN32_LEAN_AND_MEAN #include #include "backend_x64/block_of_code.h" #include "common/assert.h" #include "common/common_types.h" using UBYTE = u8; enum UNWIND_REGISTER_CODES { UWRC_RAX, UWRC_RCX, UWRC_RDX, UWRC_RBX, UWRC_RSP, UWRC_RBP, UWRC_RSI, UWRC_RDI, UWRC_R8, UWRC_R9, UWRC_R10, UWRC_R11, UWRC_R12, UWRC_R13, UWRC_R14, UWRC_R15, }; enum UNWIND_OPCODE { UWOP_PUSH_NONVOL = 0, UWOP_ALLOC_LARGE = 1, UWOP_ALLOC_SMALL = 2, UWOP_SET_FPREG = 3, UWOP_SAVE_NONVOL = 4, UWOP_SAVE_NONVOL_FAR = 5, UWOP_SAVE_XMM128 = 8, UWOP_SAVE_XMM128_FAR = 9, UWOP_PUSH_MACHFRAME = 10, }; union UNWIND_CODE { struct { UBYTE CodeOffset; UBYTE UnwindOp : 4; UBYTE OpInfo : 4; } code; USHORT FrameOffset; }; // UNWIND_INFO is a tail-padded structure struct UNWIND_INFO { UBYTE Version : 3; UBYTE Flags : 5; UBYTE SizeOfProlog; UBYTE CountOfCodes; UBYTE FrameRegister : 4; UBYTE FrameOffset : 4; // UNWIND_CODE UnwindCode[]; // With Flags == 0 there are no additional fields. }; namespace Dynarmic { namespace BackendX64 { struct PrologueInformation { std::vector unwind_code; size_t number_of_unwind_code_entries; u8 prolog_size; }; static PrologueInformation GetPrologueInformation() { PrologueInformation ret; const auto next_entry = [&]() -> UNWIND_CODE& { ret.unwind_code.emplace_back(); return ret.unwind_code.back(); }; const auto push_nonvol = [&](u8 offset, UNWIND_REGISTER_CODES reg) { auto& entry = next_entry(); entry.code.CodeOffset = offset; entry.code.UnwindOp = UWOP_PUSH_NONVOL; entry.code.OpInfo = reg; }; const auto alloc_large = [&](u8 offset, size_t size) { ASSERT(size % 8 == 0); size /= 8; auto& entry = next_entry(); entry.code.CodeOffset = offset; entry.code.UnwindOp = UWOP_ALLOC_LARGE; if (size <= 0xFFFF) { entry.code.OpInfo = 0; auto& size_entry = next_entry(); size_entry.FrameOffset = static_cast(size); } else { entry.code.OpInfo = 1; auto& size_entry_1 = next_entry(); size_entry_1.FrameOffset = static_cast(size); auto& size_entry_2 = next_entry(); size_entry_2.FrameOffset = static_cast(size >> 16); } }; const auto save_xmm128 = [&](u8 offset, u8 reg, size_t frame_offset) { ASSERT(frame_offset % 16 == 0); auto& entry = next_entry(); entry.code.CodeOffset = offset; entry.code.UnwindOp = UWOP_SAVE_XMM128; entry.code.OpInfo = reg; auto& offset_entry = next_entry(); offset_entry.FrameOffset = static_cast(frame_offset / 16); }; // This is a list of operations that occur in the prologue. // The debugger uses this information to retrieve register values and // to calculate the size of the stack frame. ret.prolog_size = 89; save_xmm128(89, 15, 0xB0); // +050 44 0F 29 BC 24 B0 00 00 00 movaps xmmword ptr [rsp+0B0h],xmm15 save_xmm128(80, 14, 0xA0); // +047 44 0F 29 B4 24 A0 00 00 00 movaps xmmword ptr [rsp+0A0h],xmm14 save_xmm128(71, 13, 0x90); // +03E 44 0F 29 AC 24 90 00 00 00 movaps xmmword ptr [rsp+90h],xmm13 save_xmm128(62, 12, 0x80); // +035 44 0F 29 A4 24 80 00 00 00 movaps xmmword ptr [rsp+80h],xmm12 save_xmm128(53, 11, 0x70); // +02F 44 0F 29 5C 24 70 movaps xmmword ptr [rsp+70h],xmm11 save_xmm128(47, 10, 0x60); // +029 44 0F 29 54 24 60 movaps xmmword ptr [rsp+60h],xmm10 save_xmm128(41, 9, 0x50); // +023 44 0F 29 4C 24 50 movaps xmmword ptr [rsp+50h],xmm9 save_xmm128(35, 8, 0x40); // +01D 44 0F 29 44 24 40 movaps xmmword ptr [rsp+40h],xmm8 save_xmm128(29, 7, 0x30); // +018 0F 29 7C 24 30 movaps xmmword ptr [rsp+30h],xmm7 save_xmm128(24, 6, 0x20); // +013 0F 29 74 24 20 movaps xmmword ptr [rsp+20h],xmm6 alloc_large(19, 0xC8); // +00C 48 81 EC C8 00 00 00 sub rsp,0C8h push_nonvol(12, UWRC_R15); // +00A 41 57 push r15 push_nonvol(10, UWRC_R14); // +008 41 56 push r14 push_nonvol(8, UWRC_R13); // +006 41 55 push r13 push_nonvol(6, UWRC_R12); // +004 41 54 push r12 push_nonvol(4, UWRC_RBP); // +003 55 push rbp push_nonvol(3, UWRC_RDI); // +002 57 push rdi push_nonvol(2, UWRC_RSI); // +001 56 push rsi push_nonvol(1, UWRC_RBX); // +000 53 push rbx ret.number_of_unwind_code_entries = ret.unwind_code.size(); // The Windows API requires the size of the unwind_code array // to be a multiple of two for alignment reasons. if (ret.unwind_code.size() % 2 == 1) { auto& last_entry = next_entry(); last_entry.FrameOffset = 0; } ASSERT(ret.unwind_code.size() % 2 == 0); return ret; } struct BlockOfCode::ExceptionHandler::Impl final { Impl(RUNTIME_FUNCTION* rfuncs_, const u8* base_ptr) : rfuncs(rfuncs_) { RtlAddFunctionTable(rfuncs, 1, reinterpret_cast(base_ptr)); } ~Impl() { RtlDeleteFunctionTable(rfuncs); } private: RUNTIME_FUNCTION* rfuncs = nullptr; }; BlockOfCode::ExceptionHandler::ExceptionHandler() = default; BlockOfCode::ExceptionHandler::~ExceptionHandler() = default; void BlockOfCode::ExceptionHandler::Register(BlockOfCode& code) { const auto prolog_info = GetPrologueInformation(); code.align(16); UNWIND_INFO* unwind_info = static_cast(code.AllocateFromCodeSpace(sizeof(UNWIND_INFO))); unwind_info->Version = 1; unwind_info->Flags = 0; // No special exception handling required. unwind_info->SizeOfProlog = prolog_info.prolog_size; unwind_info->CountOfCodes = static_cast(prolog_info.number_of_unwind_code_entries); unwind_info->FrameRegister = 0; // No frame register present unwind_info->FrameOffset = 0; // Unused because FrameRegister == 0 // UNWIND_INFO::UnwindCode field: const size_t size_of_unwind_code = sizeof(UNWIND_CODE) * prolog_info.unwind_code.size(); UNWIND_CODE* unwind_code = static_cast(code.AllocateFromCodeSpace(size_of_unwind_code)); memcpy(unwind_code, prolog_info.unwind_code.data(), size_of_unwind_code); code.align(16); RUNTIME_FUNCTION* rfuncs = static_cast(code.AllocateFromCodeSpace(sizeof(RUNTIME_FUNCTION))); rfuncs->BeginAddress = static_cast(reinterpret_cast(code.run_code) - code.getCode()); rfuncs->EndAddress = static_cast(code.maxSize_); rfuncs->UnwindData = static_cast(reinterpret_cast(unwind_info) - code.getCode()); impl = std::make_unique(rfuncs, code.getCode()); } } // namespace BackendX64 } // namespace Dynarmic