// Copyright (C) 2003-2008 Dolphin Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ #include #include "Common.h" #include "Thunk.h" #include "x64Emitter.h" #include "MemoryUtil.h" #include "ABI.h" using namespace Gen; #define THUNK_ARENA_SIZE 1024*1024*1 namespace { static std::map thunks; u8 GC_ALIGNED32(saved_fp_state[16 * 4 * 4]); u8 GC_ALIGNED32(saved_gpr_state[16 * 8]); } static u8 *thunk_memory; static u8 *thunk_code; static const u8 *save_regs; static const u8 *load_regs; u32 saved_return; void Thunk_Init() { thunk_memory = (u8 *)AllocateExecutableMemory(THUNK_ARENA_SIZE); thunk_code = thunk_memory; GenContext ctx(&thunk_code); save_regs = GetCodePtr(); for (int i = 2; i < ABI_GetNumXMMRegs(); i++) MOVAPS(M(saved_fp_state + i * 16), (X64Reg)(XMM0 + i)); #ifdef _M_X64 MOV(64, M(saved_gpr_state + 0 ), R(RCX)); MOV(64, M(saved_gpr_state + 8 ), R(RDX)); MOV(64, M(saved_gpr_state + 16), R(R8) ); MOV(64, M(saved_gpr_state + 24), R(R9) ); MOV(64, M(saved_gpr_state + 32), R(R10)); MOV(64, M(saved_gpr_state + 40), R(R11)); #ifndef _WIN32 MOV(64, M(saved_gpr_state + 48), R(RSI)); MOV(64, M(saved_gpr_state + 56), R(RDI)); #endif MOV(64, M(saved_gpr_state + 64), R(RBX)); #else MOV(32, M(saved_gpr_state + 0 ), R(RCX)); MOV(32, M(saved_gpr_state + 4 ), R(RDX)); #endif RET(); load_regs = GetCodePtr(); for (int i = 2; i < ABI_GetNumXMMRegs(); i++) MOVAPS((X64Reg)(XMM0 + i), M(saved_fp_state + i * 16)); #ifdef _M_X64 MOV(64, R(RCX), M(saved_gpr_state + 0 )); MOV(64, R(RDX), M(saved_gpr_state + 8 )); MOV(64, R(R8) , M(saved_gpr_state + 16)); MOV(64, R(R9) , M(saved_gpr_state + 24)); MOV(64, R(R10), M(saved_gpr_state + 32)); MOV(64, R(R11), M(saved_gpr_state + 40)); #ifndef _WIN32 MOV(64, R(RSI), M(saved_gpr_state + 48)); MOV(64, R(RDI), M(saved_gpr_state + 56)); #endif MOV(64, R(RBX), M(saved_gpr_state + 64)); #else MOV(32, R(RCX), M(saved_gpr_state + 0 )); MOV(32, R(RDX), M(saved_gpr_state + 4 )); #endif RET(); } void Thunk_Reset() { thunks.clear(); thunk_code = thunk_memory; } void Thunk_Shutdown() { Thunk_Reset(); FreeMemoryPages(thunk_memory, THUNK_ARENA_SIZE); thunk_memory = 0; thunk_code = 0; } void *ProtectFunction(void *function, int num_params) { std::map::iterator iter; iter = thunks.find(function); if (iter != thunks.end()) return (void *)iter->second; if (!thunk_memory) PanicAlert("Trying to protect functions before the emu is started. Bad bad bad."); GenContext gen(&thunk_code); const u8 *call_point = GetCodePtr(); // Make sure to align stack. #ifdef _M_X64 #ifdef _WIN32 SUB(64, R(ESP), Imm8(0x28)); #else SUB(64, R(ESP), Imm8(0x8)); #endif CALL((void*)save_regs); CALL((void*)function); CALL((void*)load_regs); #ifdef _WIN32 ADD(64, R(ESP), Imm8(0x28)); #else ADD(64, R(ESP), Imm8(0x8)); #endif RET(); #else CALL((void*)save_regs); // Re-push parameters from previous stack frame for (int i = 0; i < num_params; i++) { // ESP is changing, so we do not need i PUSH(32, MDisp(ESP, (num_params) * 4)); } CALL(function); if (num_params) ADD(32, R(ESP), Imm8(num_params * 4)); CALL((void*)load_regs); RET(); #endif thunks[function] = call_point; return (void *)call_point; }