/* ======== SourceHook ======== * Copyright (C) 2004-2010 Metamod:Source Development Team * No warranties of any kind * * License: zlib/libpng * * Author(s): Pavol "PM OnoTo" Marko * Contributor(s): Borja "faluco" Ferav (many thanks for assitance!) * David "BAILOPAN" Anderson * ============================ */ // recommended literature: // http://www.cs.umbc.edu/~chang/cs313.s02/stack.shtml // http://www.angelcode.com/dev/callconv/callconv.html // http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/Chapter_6/CH06-1.html #include #include "sourcehook_impl.h" #include "sourcehook_hookmangen.h" #include "sourcehook_hookmangen_x86.h" #include "sh_memory.h" #include // we might need the address of vsnprintf #if SH_COMP == SH_COMP_MSVC # define GCC_ONLY(x) # define MSVC_ONLY(x) x #elif SH_COMP == SH_COMP_GCC # define GCC_ONLY(x) x # define MSVC_ONLY(x) #endif // :TODO: test BIG vtable indices namespace SourceHook { namespace Impl { CPageAlloc GenBuffer::ms_Allocator(16); template jit_int32_t DownCastPtr(T ptr) { return reinterpret_cast(ptr); } jit_uint32_t DownCastSize(size_t size) { return static_cast(size); } GenContext::GenContext(const ProtoInfo *proto, int vtbl_offs, int vtbl_idx, ISourceHook *pSHPtr) : m_GeneratedPubFunc(NULL), m_OrigProto(proto), m_Proto(proto), m_VtblOffs(vtbl_offs), m_VtblIdx(vtbl_idx), m_SHPtr(pSHPtr), m_pHI(NULL), m_HookfuncVfnptr(NULL), m_RegCounter(0) { m_pHI = new void*; m_HookfuncVfnptr = new void*; m_BuiltPI = new ProtoInfo; m_BuiltPI_Params = NULL; m_BuiltPI_Params2 = NULL; } GenContext::~GenContext() { Clear(); delete m_pHI; delete m_HookfuncVfnptr; delete m_BuiltPI; } void GenContext::Clear() { m_HookFunc.clear(); m_PubFunc.clear(); if (m_BuiltPI_Params) { delete [] m_BuiltPI_Params; m_BuiltPI_Params = NULL; } if (m_BuiltPI_Params2) { delete [] m_BuiltPI_Params2; m_BuiltPI_Params2 = NULL; } } void GenContext::BuildProtoInfo() { m_BuiltPI->convention = m_Proto.GetConvention(); m_BuiltPI->numOfParams = m_Proto.GetNumOfParams(); m_BuiltPI->retPassInfo.size = m_Proto.GetRet().size; m_BuiltPI->retPassInfo.type = m_Proto.GetRet().type; m_BuiltPI->retPassInfo.flags = m_Proto.GetRet().flags; m_BuiltPI->retPassInfo2.pNormalCtor = m_Proto.GetRet().pNormalCtor; m_BuiltPI->retPassInfo2.pCopyCtor = m_Proto.GetRet().pCopyCtor; m_BuiltPI->retPassInfo2.pDtor = m_Proto.GetRet().pDtor; m_BuiltPI->retPassInfo2.pAssignOperator = m_Proto.GetRet().pAssignOperator; if (m_BuiltPI_Params) delete m_BuiltPI_Params; m_BuiltPI_Params = new PassInfo[m_BuiltPI->numOfParams + 1]; if (m_BuiltPI_Params2) delete m_BuiltPI_Params2; m_BuiltPI_Params2 = new PassInfo::V2Info[m_BuiltPI->numOfParams + 1]; m_BuiltPI_Params[0].size = 1; // Version 1 m_BuiltPI_Params[0].type = 0; m_BuiltPI_Params[0].flags = 0; for (int i = 0; i < m_Proto.GetNumOfParams(); ++i) { m_BuiltPI_Params[i+1].size = m_Proto.GetParam(i).size; m_BuiltPI_Params[i+1].type = m_Proto.GetParam(i).type; m_BuiltPI_Params[i+1].flags = m_Proto.GetParam(i).flags; m_BuiltPI_Params2[i+1].pNormalCtor = m_Proto.GetParam(i).pNormalCtor; m_BuiltPI_Params2[i+1].pCopyCtor = m_Proto.GetParam(i).pCopyCtor; m_BuiltPI_Params2[i+1].pDtor = m_Proto.GetParam(i).pDtor; m_BuiltPI_Params2[i+1].pAssignOperator = m_Proto.GetParam(i).pAssignOperator; } m_BuiltPI->paramsPassInfo = m_BuiltPI_Params; m_BuiltPI->paramsPassInfo2 = m_BuiltPI_Params2; } jit_int32_t GenContext::GetRealSize(const IntPassInfo &info) { if (info.flags & (PassInfo::PassFlag_ByRef | PassFlag_ForcedByRef)) { return SIZE_PTR; } return static_cast(info.size); } jit_int32_t GenContext::AlignSize(jit_int32_t x, jit_int32_t boundary) { if (x % boundary != 0) x = (x & ~(boundary-1)) + boundary; return x; } // Computes size on the stack jit_int32_t GenContext::GetParamStackSize(const IntPassInfo &info) { // Align up to 4 byte boundaries return AlignSize(GetRealSize(info), 4); } jit_int8_t GenContext::NextRegEBX_ECX_EDX() { switch ((m_RegCounter++) % 3) { case 0: return REG_EBX; case 1: return REG_ECX; case 2: default: m_RegCounter = 0; return REG_EDX; } } void GenContext::BitwiseCopy_Setup() { //cld //push edi //push esi IA32_Cld(&m_HookFunc); IA32_Push_Reg(&m_HookFunc, REG_EDI); IA32_Push_Reg(&m_HookFunc, REG_ESI); } void GenContext::BitwiseCopy_Do(size_t size) { jit_uint32_t dwords = DownCastSize(size) / 4; jit_uint32_t bytes = DownCastSize(size) % 4; //if dwords // mov ecx, // rep movsd //if bytes // mov ecx, // rep movsb //pop esi //pop edi if (dwords) { IA32_Mov_Reg_Imm32(&m_HookFunc, REG_ECX, dwords); IA32_Rep(&m_HookFunc); IA32_Movsd(&m_HookFunc); } if (bytes) { IA32_Mov_Reg_Imm32(&m_HookFunc, REG_ECX, bytes); IA32_Rep(&m_HookFunc); IA32_Movsb(&m_HookFunc); } IA32_Pop_Reg(&m_HookFunc, REG_ESI); IA32_Pop_Reg(&m_HookFunc, REG_EDI); } jit_int32_t GenContext::AlignStackBeforeCall(int paramsize, int flags) { paramsize += GCC_ONLY( ((flags & AlignStack_GCC_ThisOnStack)!=0 ? SIZE_PTR : 0) + ) MSVC_ONLY( ((flags & AlignStack_MSVC_ThisOnStack)!=0 ? SIZE_PTR : 0) + ) ((flags & AlignStack_MemRet)!=0 ? SIZE_PTR : 0); // At the beginning of the hookfunc, the stack is aligned to a 16 bytes boundary. // Then, m_BytesPushedAfterInitialAlignment were pushed (can also be 0). // After this function is called, paramsize bytes will be pushed onto the stack // After that, the alignment has to be a 16 bytes boundary again. // How many bytes we would subtract if the alignment was alright now: int subtractFromEsp = 16 - (paramsize % 16); if (subtractFromEsp == 16) subtractFromEsp = 0; // But: there might be bytes pushed alreay! subtractFromEsp -= m_BytesPushedAfterInitialAlignment; // For example: paramsize was 0 and m_BytesPushedAfterInitialAlignment was 4. // we then have to push another 12 bytes to reach 16 bytes alignment again. if (subtractFromEsp < 0) subtractFromEsp = 16 - ((-subtractFromEsp) % 16); if (subtractFromEsp != 0) IA32_Sub_Rm_ImmAuto(&m_HookFunc, REG_ESP, subtractFromEsp, MOD_REG); return subtractFromEsp; } void GenContext::AlignStackAfterCall(jit_int32_t numofbytes) { if (numofbytes != 0) IA32_Add_Rm_ImmAuto(&m_HookFunc, REG_ESP, numofbytes, MOD_REG); } void GenContext::CheckAlignmentBeforeCall() { #if 0 IA32_Test_Rm_Imm32(&m_HookFunc, REG_ESP, 15, MOD_REG); IA32_Jump_Cond_Imm8(&m_HookFunc, CC_Z, 1); IA32_Int3(&m_HookFunc); #endif } short GenContext::GetParamsTotalStackSize() { short acc = 0; for (int i = 0; i < m_Proto.GetNumOfParams(); ++i) { acc += GetParamStackSize(m_Proto.GetParam(i)); } return acc; } short GenContext::GetForcedByRefParamOffset(int p) { short off = 0; for (int i = 0; i < p; ++i) { if (m_Proto.GetParam(i).flags & PassFlag_ForcedByRef) off += AlignSize(static_cast(m_Proto.GetParam(i).size), 4); } return off; } short GenContext::GetForcedByRefParamsSize() { return GetForcedByRefParamOffset(m_Proto.GetNumOfParams()); } jit_int32_t GenContext::PushRef(jit_int32_t param_offset, const IntPassInfo &pi) { // push [ebp+] IA32_Push_Rm_DispAuto(&m_HookFunc, REG_EBP, param_offset); return SIZE_PTR; } jit_int32_t GenContext::PushBasic(jit_int32_t param_offset, const IntPassInfo &pi) { int reg; int reg2; switch (pi.size) { default: SH_ASSERT(0, ("Unsupported!")); return 0; case 1: reg = NextRegEBX_ECX_EDX(); //movzx reg, BYTE PTR [ebp+] //push reg IA32_Movzx_Reg32_Rm8_DispAuto(&m_HookFunc, reg, REG_EBP, param_offset); IA32_Push_Reg(&m_HookFunc, reg); return 4; case 2: reg = NextRegEBX_ECX_EDX(); //movzx reg, WORD PTR [ebp+] //push reg m_HookFunc.write_ubyte(IA32_16BIT_PREFIX); IA32_Movzx_Reg32_Rm16_DispAuto(&m_HookFunc, reg, REG_EBP, param_offset); IA32_Push_Reg(&m_HookFunc, reg); return 4; case 4: reg = NextRegEBX_ECX_EDX(); //mov reg, DWORD PTR [ebp+] //push reg IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, reg, REG_EBP, param_offset); IA32_Push_Reg(&m_HookFunc, reg); return 4; case 8: reg = NextRegEBX_ECX_EDX(); reg2 = NextRegEBX_ECX_EDX(); //mov reg, DWORD PTR [ebp++4] //mov reg2, DWORD PTR [ebp+] //push reg //push reg2 IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, reg, REG_EBP, param_offset+4); IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, reg2, REG_EBP, param_offset); IA32_Push_Reg(&m_HookFunc, reg); IA32_Push_Reg(&m_HookFunc, reg2); return 8; } } jit_int32_t GenContext::PushFloat(jit_int32_t param_offset, const IntPassInfo &pi) { switch (pi.size) { default: SH_ASSERT(0, ("Unsupported!")); return 0; case 4: //fld DWORD PTR [ebp+] //push reg //fstp DWORD PTR [esp] IA32_Fld_Mem32_DispAuto(&m_HookFunc, REG_EBP, param_offset); IA32_Push_Reg(&m_HookFunc, NextRegEBX_ECX_EDX()); IA32_Fstp_Mem32_ESP(&m_HookFunc); return 4; case 8: //fld QWORD PTR [ebp+] //sub esp, 8 //fstp QWORD PTR [esp] IA32_Fld_Mem64_DispAuto(&m_HookFunc, REG_EBP, param_offset); IA32_Sub_Rm_Imm8(&m_HookFunc, REG_ESP, 8, MOD_REG); IA32_Fstp_Mem64_ESP(&m_HookFunc); return 8; } } jit_int32_t GenContext::PushObject(jit_int32_t param_offset, const IntPassInfo &pi, jit_int32_t place_fbrr) { if ((pi.flags & PassFlag_ForcedByRef) == 0) { // make room on the stack // sub esp, IA32_Sub_Rm_ImmAuto(&m_HookFunc, REG_ESP, GetParamStackSize(pi), MOD_REG); } // if there is a copy constructor.. if (pi.pCopyCtor) { // :TODO: alignment here? // can't use normal alignment methods // because an unknown number of bytes has been pushed already (the other params) // save eax IA32_Push_Reg(&m_HookFunc, REG_EAX); // compute dest addr to ECX // = forcedbyref ? ebp+place_fbrr : esp+12 if (pi.flags & PassFlag_ForcedByRef) IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, place_fbrr); else IA32_Lea_Reg_DispRegMultImm8(&m_HookFunc, REG_ECX, REG_NOIDX, REG_ESP, NOSCALE, 4); // compute src addr to EAX IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EAX, REG_EBP, param_offset); // push params (gcc: also this) IA32_Push_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); // call IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EDX, DownCastPtr(pi.pCopyCtor)); IA32_Call_Reg(&m_HookFunc, REG_EDX); // gcc: clean up stack GCC_ONLY(IA32_Add_Rm_ImmAuto(&m_HookFunc, REG_ESP, 2 * SIZE_PTR, MOD_REG)); // restore eax IA32_Pop_Reg(&m_HookFunc, REG_EAX); } else { // bitwise copy BitwiseCopy_Setup(); //if forcedbyref: // lea edi, [ebp_place_fbrr] //else // lea edi, [esp+8] - bc_setup pushed two regs onto the stack! //lea esi, [ebp+] if (pi.flags & PassFlag_ForcedByRef) IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EDI, REG_EBP, place_fbrr); else IA32_Lea_Reg_DispRegMultImm8(&m_HookFunc, REG_EDI, REG_NOIDX, REG_ESP, NOSCALE, 8); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ESI, REG_EBP, param_offset); BitwiseCopy_Do(pi.size); } // forcedref: push reference to ebp+place_fbrr if (pi.flags & PassFlag_ForcedByRef) { IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, place_fbrr); IA32_Push_Reg(&m_HookFunc, REG_ECX); return SIZE_PTR; } return GetParamStackSize(pi); } void GenContext::DestroyParams(jit_int32_t fbrr_base) { for (int i = m_Proto.GetNumOfParams() - 1; i >= 0; --i) { const IntPassInfo &pi = m_Proto.GetParam(i); if (pi.type == PassInfo::PassType_Object && (pi.flags & PassInfo::PassFlag_ODtor) && (pi.flags & PassInfo::PassFlag_ByVal) && (pi.flags & PassFlag_ForcedByRef)) { // Actually, this is only for GCC (see line above: ForcedByRef) jit_int32_t tmpAlign = AlignStackBeforeCall(0, AlignStack_GCC_ThisOnStack); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, fbrr_base + GetForcedByRefParamOffset(i)); IA32_Push_Reg(&m_HookFunc, REG_ECX); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(pi.pDtor)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); IA32_Pop_Reg(&m_HookFunc, REG_ECX); AlignStackAfterCall(tmpAlign); } } } // May not touch eax! jit_int32_t GenContext::PushParams(jit_int32_t param_base_offset, jit_int32_t save_ret_to, int v_place_for_memret, jit_int32_t v_place_fbrr_base) { jit_int32_t added_to_stack = 0; jit_int32_t ret = 0; // compute the offset _after_ the last parameter jit_int32_t cur_offset = param_base_offset; for (int i = 0; i < m_Proto.GetNumOfParams(); ++i) { cur_offset += GetParamStackSize(m_Proto.GetParam(i)); } // push parameters in reverse order for (int i = m_Proto.GetNumOfParams() - 1; i >= 0; --i) { const IntPassInfo &pi = m_Proto.GetParam(i); cur_offset -= GetParamStackSize(pi); if (pi.flags & PassInfo::PassFlag_ByVal) { switch (pi.type) { case PassInfo::PassType_Basic: ret = PushBasic(cur_offset, pi); break; case PassInfo::PassType_Float: ret = PushFloat(cur_offset, pi); break; case PassInfo::PassType_Object: ret = PushObject(cur_offset, pi, v_place_fbrr_base + GetForcedByRefParamOffset(i)); break; } } else if (pi.flags & PassInfo::PassFlag_ByRef) { ret = PushRef(cur_offset, pi); } else { SH_ASSERT(0, ("Unsupported!")); } added_to_stack += ret; } return added_to_stack; } // It is IMPORTANT that PushMemRetPtr doesn't touch ecx and eax jit_int32_t GenContext::PushMemRetPtr(jit_int32_t save_ret_to, jit_int32_t v_place_for_memret) { // Memory return support if (m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) { // push address where to save it! int reg = REG_EDX; IA32_Lea_DispRegImmAuto(&m_HookFunc, reg, REG_EBP, MemRetWithTempObj() ? v_place_for_memret : save_ret_to); IA32_Push_Reg(&m_HookFunc, reg); return (SH_COMP==SH_COMP_MSVC) ? 4 : 0; // varargs funcs on msvc might need this. // gcc doesn't: callee cleans the memret ptr, caller the other params :s } return 0; } void GenContext::SaveRetVal(int v_where, int v_place_for_memret) { size_t size = GetRealSize(m_Proto.GetRet()); if (size == 0) { // No return value -> nothing return; } if (m_Proto.GetRet().flags & PassInfo::PassFlag_ByRef) { // mov [ebp + v_plugin_ret], eax IA32_Mov_Rm_Reg_DispAuto(&m_HookFunc, REG_EBP, REG_EAX, v_where); return; } // else: ByVal // Memory return: if (m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) { if (MemRetWithTempObj()) { // *v_where = *v_place_for_memret // if we have an assign operator, use that if (m_Proto.GetRet().pAssignOperator) { // lea edx, [ebp + v_place_for_memret] <-- src addr // lea ecx, [ebp + v_where] <-- dest addr // push edx // gcc: push ecx // call it // gcc: clean up jit_int32_t tmpAlign = AlignStackBeforeCall(SIZE_PTR, AlignStack_GCC_ThisOnStack); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EDX, REG_EBP, v_place_for_memret); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_where); IA32_Push_Reg(&m_HookFunc, REG_EDX); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pAssignOperator)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Add_Rm_ImmAuto(&m_HookFunc, REG_ESP, 2 * SIZE_PTR, MOD_REG)); AlignStackAfterCall(tmpAlign); } else { // bitwise copy BitwiseCopy_Setup(); //lea edi, [evp+v_where] <-- destination //lea esi, [ebp+v_place_for_memret] <-- src IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EDI, REG_EBP, v_where); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ESI, REG_EBP, v_place_for_memret); BitwiseCopy_Do(m_Proto.GetRet().size); } // Then: destruct *v_place_for_memret if required if (m_Proto.GetRet().pDtor) { //lea ecx, [ebp+v_place_for_memret] //gcc: push ecx //call it //gcc: clean up jit_int32_t tmpAlign = AlignStackBeforeCall(0, AlignStack_GCC_ThisOnStack); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_place_for_memret); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pDtor)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Pop_Reg(&m_HookFunc, REG_ECX)); AlignStackAfterCall(tmpAlign); } } else { // Already copied to correct address -> we're done return; } } if (m_Proto.GetRet().type == PassInfo::PassType_Float) { if (size == 4) IA32_Fstp_Mem32_DispAuto(&m_HookFunc, REG_EBP, v_where); else if (size == 8) IA32_Fstp_Mem64_DispAuto(&m_HookFunc, REG_EBP, v_where); } else if (m_Proto.GetRet().type == PassInfo::PassType_Basic) { if (size <= 4) { // size <= 4: return in EAX // We align <4 sizes up to 4 // mov [ebp + v_plugin_ret], eax IA32_Mov_Rm_Reg_DispAuto(&m_HookFunc, REG_EBP, REG_EAX, v_where); } else if (size <= 8) { // size <= 4: return in EAX:EDX // We align 4 8 !")); } } } } bool GenContext::MemRetWithTempObj() { // Memory return AND (has destructor OR has assign operator) return ((m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) && (m_Proto.GetRet().flags & (PassInfo::PassFlag_ODtor | PassInfo::PassFlag_AssignOp))); } void GenContext::ProcessPluginRetVal(int v_cur_res, int v_pContext, int v_plugin_ret) { // only for non-void functions! if (m_Proto.GetRet().size == 0) return; // if (cur_res >= MRES_OVERRIDE) // *reinterpret_cast(pContext->GetOverrideRetPtr()) = plugin_ret; // eax = cur_res // cmp eax,MRES_OVERRIDE // jnge thelabel // pContext->GetOverrideRetPtr() --> overrideretptr in eax // *eax = plugin_ret // thelabel: // jitoffs_t tmppos, counter; IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_cur_res); IA32_Cmp_Rm_Imm32(&m_HookFunc, MOD_REG, REG_EAX, MRES_OVERRIDE); tmppos = IA32_Jump_Cond_Imm8(&m_HookFunc, CC_NGE, 0); m_HookFunc.start_count(counter); // eax = pContext->GetOverrideRetPtr() no alignment needs // ECX = pContext // gcc: push ecx // eax = [ecx] // eax = [eax + 4] // call eax // gcc: clean up IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_pContext); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); // vtbloffs=0, vtblidx=1 IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EAX, 4); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Pop_Reg(&m_HookFunc, REG_ECX)); // *eax = plugin_ret if (m_Proto.GetRet().flags & PassInfo::PassFlag_ByRef) { // mov ecx, [ebp+v_plugin_ret] // mov [eax], ecx IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_plugin_ret); IA32_Mov_Rm_Reg(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); } else { if (m_Proto.GetRet().pAssignOperator) { // lea edx, [ebp + v_plugin_ret] // push edx <-- src addr // msvc: ecx = eax <-- dest addr // gcc: push eax <-- dest addr // call it // gcc: clean up jit_int32_t tmpAlign = AlignStackBeforeCall(SIZE_PTR, AlignStack_GCC_ThisOnStack); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EDX, REG_EBP, v_plugin_ret); IA32_Push_Reg(&m_HookFunc, REG_EDX); #if SH_COMP == SH_COMP_MSVC IA32_Mov_Reg_Rm(&m_HookFunc, REG_ECX, REG_EAX, MOD_REG); #elif SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_HookFunc, REG_EAX); #endif IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pAssignOperator)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Add_Rm_ImmAuto(&m_HookFunc, REG_ESP, 2 * SIZE_PTR, MOD_REG)); AlignStackAfterCall(tmpAlign); } else { // bitwise copy BitwiseCopy_Setup(); //mov edi, eax <-- destination //lea esi, [ebp+v_plugin_ret] <-- src IA32_Mov_Reg_Rm(&m_HookFunc, REG_EDI, REG_EAX, MOD_REG); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ESI, REG_EBP, v_plugin_ret); BitwiseCopy_Do(m_Proto.GetRet().size); } } m_HookFunc.end_count(counter); m_HookFunc.rewrite(tmppos, static_cast(counter)); } void GenContext::PrepareReturn(int v_status, int v_pContext, int v_retptr) { // only for non-void functions! if (m_Proto.GetRet().size == 0) return; // retptr = status >= MRES_OVERRIDE ? pContext->GetOverrideRetPtr() : pContext->GetOrigRetPtr() // OverrideRetPtr: vtblidx = 1 // OrigRetPtr: vtbldix = 2 // vtblidx = (status >= MRES_OVERRIDE) ? 1 : 2 // // eax = pContext->GetOverrideRetPtr() // ECX = pContext // gcc: push ecx // eax = (status < MRES_OVERRIDE) ? 1 : 0 // xor eax, eax // cmp [ebp + v_status], MRES_OVERRIDE // setl al <-- setcc optimization for ternary operators, // lea eax, [4*eax + 0x4] // edx = [ecx] // add edx, eax // mov edx, [edx] // call edx // gcc: clean up IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_pContext); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); IA32_Xor_Reg_Rm(&m_HookFunc, REG_EAX, REG_EAX, MOD_REG); IA32_Cmp_Rm_Disp8_Imm8(&m_HookFunc, REG_EBP, v_status, MRES_OVERRIDE); IA32_SetCC_Rm8(&m_HookFunc, REG_EAX, CC_L); IA32_Lea_Reg_RegMultImm32(&m_HookFunc, REG_EAX, REG_EAX, SCALE4, 4); IA32_Mov_Reg_Rm(&m_HookFunc, REG_EDX, REG_ECX, MOD_MEM_REG); IA32_Add_Reg_Rm(&m_HookFunc, REG_EDX, REG_EAX, MOD_REG); IA32_Mov_Reg_Rm(&m_HookFunc, REG_EDX, REG_EDX, MOD_MEM_REG); IA32_Call_Reg(&m_HookFunc, REG_EDX); GCC_ONLY(IA32_Pop_Reg(&m_HookFunc, REG_ECX)); IA32_Mov_Rm_Reg_DispAuto(&m_HookFunc, REG_EBP, REG_EAX, v_retptr); } void GenContext::DoReturn(int v_retptr, int v_memret_outaddr) { size_t size = m_Proto.GetRet().size; if (!size) return; // Get real ret pointer into ecx // mov ecx, [ebp + v_ret_ptr] IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_retptr); if (m_Proto.GetRet().flags & PassInfo::PassFlag_ByRef) { // mov eax, [ecx] IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); return; } // else: byval if (m_Proto.GetRet().type == PassInfo::PassType_Float) { if (size == 4) IA32_Fld_Mem32(&m_HookFunc, REG_ECX); else if (size == 8) IA32_Fld_Mem64(&m_HookFunc, REG_ECX); } else if (m_Proto.GetRet().type == PassInfo::PassType_Basic || ((m_Proto.GetRet().type == PassInfo::PassType_Object) && (m_Proto.GetRet().flags & PassInfo::PassFlag_RetReg)) ) { if (size <= 4) { // size <= 4: return in EAX // We align <4 sizes up to 4 // mov eax, [ecx] IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); } else if (size <= 8) { // size <= 4: return in EAX:EDX // We align 48: return in memory // handled later } } if (m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) { // *memret_outaddr = plugin_ret if (m_Proto.GetRet().pCopyCtor) { // mov edx, ecx <-- src ( we set ecx to [ebp+v_retptr] before ) // push edx <-- src addr // msvc: ecx = [ebp + v_memret_outaddr] <-- dest addr // gcc: push [ebp + v_memret_outaddr] <-- dest addr // call it // gcc: clean up jit_int32_t tmpAlign = AlignStackBeforeCall(SIZE_PTR, AlignStack_GCC_ThisOnStack); IA32_Mov_Reg_Rm(&m_HookFunc, REG_EDX, REG_ECX, MOD_REG); IA32_Push_Reg(&m_HookFunc, REG_EDX); #if SH_COMP == SH_COMP_MSVC IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_memret_outaddr); #elif SH_COMP == SH_COMP_GCC IA32_Push_Rm_DispAuto(&m_HookFunc, REG_EBP, v_memret_outaddr); #endif IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pCopyCtor)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Add_Rm_ImmAuto(&m_HookFunc, REG_ESP, 2 * SIZE_PTR, MOD_REG)); AlignStackAfterCall(tmpAlign); } else { // bitwise copy BitwiseCopy_Setup(); //mov edi, [ebp+v_memret_outaddr] <-- destination //mov esi, ecx <-- src ( we set ecx to [ebp+v_retptr] before ) IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EDI, REG_EBP, v_memret_outaddr); IA32_Mov_Reg_Rm(&m_HookFunc, REG_ESI, REG_ECX, MOD_REG); BitwiseCopy_Do(m_Proto.GetRet().size); } // In both cases: return the pointer in EAX // mov eax, [ebp + v_memret_outaddr] IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_memret_outaddr); } } void GenContext::GenerateCallHooks(int v_status, int v_prev_res, int v_cur_res, int v_iter, int v_pContext, int base_param_offset, int v_plugin_ret, int v_place_for_memret, jit_int32_t v_place_fbrr_base, jit_int32_t v_va_buf) { jitoffs_t counter, tmppos; jitoffs_t counter2, tmppos2; jitoffs_t loop_begin_counter; // prev_res = MRES_IGNORED IA32_Mov_Rm_Imm32_Disp8(&m_HookFunc, REG_EBP, MRES_IGNORED, v_prev_res); m_HookFunc.start_count(loop_begin_counter); // eax = pContext->GetNext() // ECX = pContext // gcc: push ecx // eax = [ecx] // eax = [eax] // call eax // gcc: clean up IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_pContext); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); // vtbloffs=0, vtblidx=0 IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_EAX, MOD_MEM_REG); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Pop_Reg(&m_HookFunc, REG_ECX)); // quit on zero // test eax, eax // jz exit IA32_Test_Rm_Reg(&m_HookFunc, REG_EAX, REG_EAX, MOD_REG); tmppos = IA32_Jump_Cond_Imm32(&m_HookFunc, CC_Z, 0); m_HookFunc.start_count(counter); // prev_res = MRES_IGNORED IA32_Mov_Rm_Imm32_Disp8(&m_HookFunc, REG_EBP, MRES_IGNORED, v_cur_res); // iter->call() // push params // ecx = eax // gcc: push ecx // eax = [ecx] // eax = [eax+2*SIZE_PTR] // call eax // gcc: clean up jit_int32_t caller_clean_bytes = 0; // gcc always, msvc never (hooks never have varargs!) jit_int32_t alignBytes = AlignStackBeforeCall( GetParamsTotalStackSize() + ((m_Proto.GetConvention() & ProtoInfo::CallConv_HasVafmt)!=0 ? SIZE_PTR : 0), AlignStack_GCC_ThisOnStack | ((m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) == 0 ? 0 : AlignStack_MemRet) ); // vafmt: push va_buf if (m_Proto.GetConvention() & ProtoInfo::CallConv_HasVafmt) { IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_va_buf); IA32_Push_Reg(&m_HookFunc, REG_ECX); caller_clean_bytes += SIZE_PTR; } caller_clean_bytes += PushParams(base_param_offset, v_plugin_ret, v_place_for_memret, v_place_fbrr_base); IA32_Mov_Reg_Rm(&m_HookFunc, REG_ECX, REG_EAX, MOD_REG); if (SH_COMP == SH_COMP_GCC) IA32_Push_Reg(&m_HookFunc, REG_ECX); caller_clean_bytes += PushMemRetPtr(v_plugin_ret, v_place_for_memret); IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EAX, 2*SIZE_PTR); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); AlignStackAfterCall(alignBytes); // cleanup (gcc only) // params + thisptr if (SH_COMP == SH_COMP_GCC) IA32_Add_Rm_ImmAuto(&m_HookFunc, REG_ESP, caller_clean_bytes + SIZE_PTR, MOD_REG); DestroyParams(v_place_fbrr_base); SaveRetVal(v_plugin_ret, v_place_for_memret); // process meta return: // prev_res = cur_res // if (cur_res > status) status = cur_res; // // eax = cur_res // edx = status // prev_res = eax // cmp eax,edx // jng thelabel // status = eax // thelabel: // IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_cur_res); IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EDX, REG_EBP, v_status); IA32_Mov_Rm_Reg_Disp8(&m_HookFunc, REG_EBP, REG_EAX, v_prev_res); IA32_Cmp_Reg_Rm(&m_HookFunc, REG_EAX, REG_EDX, MOD_REG); tmppos2 = IA32_Jump_Cond_Imm8(&m_HookFunc, CC_NG, 0); m_HookFunc.start_count(counter2); IA32_Mov_Rm_Reg_Disp8(&m_HookFunc, REG_EBP, REG_EAX, v_status); m_HookFunc.end_count(counter2); m_HookFunc.rewrite(tmppos2, static_cast(counter2)); // process retval for non-void functions ProcessPluginRetVal(v_cur_res, v_pContext, v_plugin_ret); // jump back to loop begin tmppos2 = IA32_Jump_Imm32(&m_HookFunc, 0); m_HookFunc.end_count(loop_begin_counter); m_HookFunc.rewrite(tmppos2, -static_cast(loop_begin_counter)); m_HookFunc.end_count(counter); m_HookFunc.rewrite(tmppos, static_cast(counter)); } void GenContext::GenerateCallOrig(int v_status, int v_pContext, int param_base_offs, int v_this, int v_vfnptr_origentry, int v_orig_ret, int v_override_ret, int v_place_for_memret, jit_int32_t v_place_fbrr_base, jit_int32_t v_va_buf) { jitoffs_t counter, tmppos; jitoffs_t counter2, tmppos2; jitoffs_t counter3, tmppos3; // if (status != MRES_SUPERCEDE && pConteext->ShouldCallOrig()) // *v_orig_ret = orig_call() // else // *v_orig_ret = *v_override_ret // mov eax, status // cmp eax, MRES_SUPERCEDE // je dont_call // call pContext->ShouldCallOrig() // test al, al !! important: al, not eax! bool is only stored in the LSbyte // jz dont_call // // orig_call() // SaveRet(v_orig_ret) // jmp skip_dont_call: // // dont_call: // *v_orig_ret = *v_override_ret // skip_dont_call: IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_status); IA32_Cmp_Rm_Imm32(&m_HookFunc, MOD_REG, REG_EAX, MRES_SUPERCEDE); tmppos = IA32_Jump_Cond_Imm32(&m_HookFunc, CC_E, 0); m_HookFunc.start_count(counter); // eax = pContext->ShouldCallOrig() // ECX = pContext // gcc: push ecx // eax = [ecx] // eax = [eax + 3*SIZE_PTR] // call eax // gcc: clean up IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_pContext); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); // vtbloffs=0, vtblidx=3 IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG); IA32_Mov_Reg_Rm_Disp8(&m_HookFunc, REG_EAX, REG_EAX, 3*SIZE_PTR); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Pop_Reg(&m_HookFunc, REG_ECX)); IA32_Test_Rm_Reg8(&m_HookFunc, REG_EAX, REG_EAX, MOD_REG); tmppos2 = IA32_Jump_Cond_Imm32(&m_HookFunc, CC_Z, 0); m_HookFunc.start_count(counter2); jit_int32_t caller_clean_bytes = 0; // gcc always, msvc when cdecl-like (varargs) jit_int32_t alignBytes = AlignStackBeforeCall( GetParamsTotalStackSize() + ((m_Proto.GetConvention() & ProtoInfo::CallConv_HasVafmt)!=0 ? 2*SIZE_PTR : 0), AlignStack_GCC_ThisOnStack | ((m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) == 0 ? 0 : AlignStack_MemRet) | ((m_Proto.GetConvention() & ProtoInfo::CallConv_HasVarArgs) == 0 ? 0 : AlignStack_MSVC_ThisOnStack) ); // vafmt: push va_buf, then "%s" if (m_Proto.GetConvention() & ProtoInfo::CallConv_HasVafmt) { IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_va_buf); IA32_Push_Reg(&m_HookFunc, REG_ECX); IA32_Push_Imm32(&m_HookFunc, DownCastPtr("%s")); caller_clean_bytes += 2*SIZE_PTR; } // push params caller_clean_bytes += PushParams(param_base_offs, v_orig_ret, v_place_for_memret, v_place_fbrr_base); // thisptr IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_this); if (SH_COMP == SH_COMP_GCC) { // on gcc/mingw, this is the first parameter IA32_Push_Reg(&m_HookFunc, REG_ECX); // on msvc without varargs, simply leave it in ecx // actually, if we're returning in memory, this pointer is the second param // and the memret pointer is the real first parameter caller_clean_bytes += PushMemRetPtr(v_orig_ret, v_place_for_memret); } else { // On msvc, if we're returning in memory, the memret pointer is the first parameter caller_clean_bytes += PushMemRetPtr(v_orig_ret, v_place_for_memret); // actually, with varargs, the this pointer is the first param and the memret ptr // is the second one if (m_Proto.GetConvention() & ProtoInfo::CallConv_HasVarArgs) { IA32_Push_Reg(&m_HookFunc, REG_ECX); } } // call IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_vfnptr_origentry); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); AlignStackAfterCall(alignBytes); // cleanup if (SH_COMP == SH_COMP_GCC || (m_Proto.GetConvention() & ProtoInfo::CallConv_HasVarArgs)) IA32_Add_Rm_ImmAuto(&m_HookFunc, REG_ESP, caller_clean_bytes + SIZE_PTR, MOD_REG); DestroyParams(v_place_fbrr_base); // save retval SaveRetVal(v_orig_ret, v_place_for_memret); // Skip don't call variant tmppos3 = IA32_Jump_Imm32(&m_HookFunc, 0); m_HookFunc.start_count(counter3); // don't call: m_HookFunc.end_count(counter); m_HookFunc.rewrite(tmppos, static_cast(counter)); m_HookFunc.end_count(counter2); m_HookFunc.rewrite(tmppos2, static_cast(counter2)); // *v_orig_ret = *v_override_ret if (m_Proto.GetRet().flags & PassInfo::PassFlag_ByRef) { // mov ecx, [ebp + v_override_ret] // mov [ebp + v_orig_ret], ecx IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_override_ret); IA32_Mov_Rm_Reg_DispAuto(&m_HookFunc, REG_EBP, REG_ECX, v_orig_ret); } else { if (m_Proto.GetRet().pAssignOperator) { // lea edx, [ebp + v_override_ret] <-- src addr // lea ecx, [ebp + v_orig_ret] <-- dest addr // push edx <-- src addr // gcc: push ecx // call it // gcc: clean up jit_int32_t tmpAlign = AlignStackBeforeCall(SIZE_PTR, AlignStack_GCC_ThisOnStack); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EDX, REG_EBP, v_override_ret); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_orig_ret); IA32_Push_Reg(&m_HookFunc, REG_EDX); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pAssignOperator)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Add_Rm_ImmAuto(&m_HookFunc, REG_ESP, 2*SIZE_PTR, MOD_REG)); AlignStackAfterCall(tmpAlign); } else { // bitwise copy BitwiseCopy_Setup(); //lea edi, [ebp+v_orig_ret] <-- destination //lea esi, [ebp+v_override_ret] <-- src IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EDI, REG_EBP, v_orig_ret); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ESI, REG_EBP, v_override_ret); BitwiseCopy_Do(m_Proto.GetRet().size); } } // skip don't call label target: m_HookFunc.end_count(counter3); m_HookFunc.rewrite(tmppos3, static_cast(counter3)); } // Sets *v_pContext to return value void GenContext::CallSetupHookLoop(int v_orig_ret, int v_override_ret, int v_cur_res, int v_prev_res, int v_status, int v_vfnptr_origentry, int v_this, int v_pContext) { // call shptr->SetupHookLoop(ms_HI, ourvfnptr, reinterpret_cast(this), // &vfnptr_origentry, &status, &prev_res, &cur_res, &orig_ret, &override_ret); // The last two params are null for void funcs. if (m_Proto.GetRet().size == 0) { // void IA32_Push_Imm8(&m_HookFunc, 0); // orig_ret IA32_Push_Imm8(&m_HookFunc, 0); // override_ret } else { // orig_ret and override_ret IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EAX, REG_EBP, v_override_ret); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EDX, REG_EBP, v_orig_ret); IA32_Push_Reg(&m_HookFunc, REG_EAX); IA32_Push_Reg(&m_HookFunc, REG_EDX); } // cur_res and prev_res IA32_Lea_DispRegImm8(&m_HookFunc, REG_EAX, REG_EBP, v_cur_res); IA32_Lea_DispRegImm8(&m_HookFunc, REG_EDX, REG_EBP, v_prev_res); IA32_Push_Reg(&m_HookFunc, REG_EAX); IA32_Push_Reg(&m_HookFunc, REG_EDX); // status and vfnptr_origentry IA32_Lea_DispRegImm8(&m_HookFunc, REG_EAX, REG_EBP, v_status); IA32_Lea_DispRegImm8(&m_HookFunc, REG_EDX, REG_EBP, v_vfnptr_origentry); IA32_Push_Reg(&m_HookFunc, REG_EAX); IA32_Push_Reg(&m_HookFunc, REG_EDX); // this IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_this); IA32_Push_Reg(&m_HookFunc, REG_EAX); // our vfn ptr // *(this + vtbloffs) + SIZE_PTR*vtblidx IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_ECX, REG_EBP, v_this); // get this into ecx (gcc!) IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_ECX, m_VtblOffs); IA32_Add_Rm_ImmAuto(&m_HookFunc, REG_EAX, m_VtblIdx * SIZE_PTR, MOD_REG); IA32_Push_Reg(&m_HookFunc, REG_EAX); // *m_pHI IA32_Mov_Rm_Imm32(&m_HookFunc, REG_EDX, DownCastPtr(m_pHI), MOD_REG); IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_EDX, MOD_MEM_REG); IA32_Push_Reg(&m_HookFunc, REG_EAX); // set up thisptr #if SH_COMP == SH_COMP_GCC // on gcc/mingw, this is the first parameter GCC_ONLY(IA32_Push_Imm32(&m_HookFunc, DownCastPtr(m_SHPtr))); #elif SH_COMP == SH_COMP_MSVC // on msvc, it's ecx IA32_Mov_Reg_Imm32(&m_HookFunc, REG_ECX, DownCastPtr(m_SHPtr)); #endif // call the function. vtbloffs = 0, vtblidx = 19 // get vtptr into edx -- we know shptr on jit time -> dereference it here! IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, (*reinterpret_cast(m_SHPtr))[19]); IA32_Call_Reg(&m_HookFunc, REG_EAX); // on gcc/mingw, we have to clean up after the call // 9 params + hidden thisptr param GCC_ONLY(IA32_Add_Rm_Imm8(&m_HookFunc, REG_ESP, 10*SIZE_PTR, MOD_REG)); // store return value IA32_Mov_Rm_Reg_Disp8(&m_HookFunc, REG_EBP, REG_EAX, v_pContext); } void GenContext::CallEndContext(int v_pContext) { // call endcontext: // shptr->EndContext(pContex) IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EAX, REG_EBP, v_pContext); IA32_Push_Reg(&m_HookFunc, REG_EAX); // thisptr #if SH_COMP == SH_COMP_GCC // on gcc/mingw, this is the first parameter IA32_Push_Imm32(&m_HookFunc, DownCastPtr(m_SHPtr)); #elif SH_COMP == SH_COMP_MSVC // on msvc, it's ecx IA32_Mov_Reg_Imm32(&m_HookFunc, REG_ECX, DownCastPtr(m_SHPtr)); #endif // get vtptr into edx -- we know shptr on jit time -> dereference it here! IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, (*reinterpret_cast(m_SHPtr))[20]); IA32_Call_Reg(&m_HookFunc, REG_EAX); // on gcc/mingw, we have to clean up after the call // 1 param + hidden thisptr param GCC_ONLY(IA32_Add_Rm_Imm8(&m_HookFunc, REG_ESP, 2*SIZE_PTR, MOD_REG)); } void GenContext::ResetFrame(jit_int32_t startOffset) { m_HookFunc_FrameOffset = startOffset; m_HookFunc_FrameVarsSize = 0; } jit_int32_t GenContext::AddVarToFrame(jit_int32_t size) { m_HookFunc_FrameOffset -= size; m_HookFunc_FrameVarsSize += size; return m_HookFunc_FrameOffset; } jit_int32_t GenContext::ComputeVarsSize() { return m_HookFunc_FrameVarsSize; } void * GenContext::GenerateHookFunc() { // prologue IA32_Push_Reg(&m_HookFunc, REG_EBP); IA32_Push_Reg(&m_HookFunc, REG_EBX); IA32_Mov_Reg_Rm(&m_HookFunc, REG_EBP, REG_ESP, MOD_REG); jit_int32_t v_this = 0; jit_int32_t param_base_offs = 0; if (SH_COMP == SH_COMP_GCC || (m_Proto.GetConvention() & ProtoInfo::CallConv_HasVarArgs)) { // gcc or msvc with varargs: v_this = 12; // first param param_base_offs = 16; ResetFrame(0); } else { // on msvc without varargs, save thisptr v_this = -4; param_base_offs = 12; IA32_Push_Reg(&m_HookFunc, REG_ECX); ResetFrame(-4); // start placing local vars on offset -4 // because there already is the thisptr variable } // ********************** stack frame ********************** // MSVC without varargs // second param (gcc: first real param) ebp + 16 // first param (gcc: thisptr) ebp + 12 // ret address: ebp + 8 // caller's ebp ebp + 4 // saved ebx ebp // MSVC ONLY: current this ebp - 4 // void *vfnptr_origentry ebp - 4 -4 // META_RES status = MRES_IGNORED ebp - 8 -4 // META_RES prev_res ebp - 12 -4 // META_RES cur_res ebp - 16 -4 // IMyDelegate *iter ebp - 20 -4 // IHookContext *pContext ebp - 24 -4 // == 3 ptrs + 3 enums = 24 bytes // // non-void: add: // my_rettype *ret_ptr ebp - 28 -4 // my_rettype orig_ret ebp - 28 - sizeof(my_rettype) -4 // my_rettype override_ret ebp - 28 - sizeof(my_rettype)*2 -4 // my_rettype plugin_ret ebp - 28 - sizeof(my_rettype)*3 -4 // == + 3 * sizeof(my_rettype) bytes // if required: // my_rettype place_for_memret ebp - 28 - sizeof(my_rettype)*4 -4 // gcc only: if required: // place forced byref params ebp - 28 - sizeof(my_rettype)*{4 or 5} // // varargs: // va_list argptr // char va_buf[something]; const jit_int8_t v_vfnptr_origentry = AddVarToFrame(SIZE_PTR); const jit_int8_t v_status = AddVarToFrame(sizeof(META_RES)); const jit_int8_t v_prev_res = AddVarToFrame(sizeof(META_RES)); const jit_int8_t v_cur_res = AddVarToFrame(sizeof(META_RES)); const jit_int8_t v_iter = AddVarToFrame(SIZE_PTR); const jit_int8_t v_pContext = AddVarToFrame(SIZE_PTR); // Memory return: first param is the address jit_int32_t v_memret_addr = 0; if (m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) { if (SH_COMP == SH_COMP_GCC) { // gcc: now: first param = mem ret addr // second param = this pointer // third param = actual first param v_memret_addr = 12; v_this += 4; param_base_offs += SIZE_PTR; } else // MSVC { if (m_Proto.GetConvention() & ProtoInfo::CallConv_HasVarArgs) { // varargs -> cdecl // msvc: now: // first param = this pointer // second param = mem ret addr // third param = actual first param // params_base_offs is already updated to point to after the this pointer v_memret_addr = param_base_offs; param_base_offs += SIZE_PTR; } else { v_memret_addr = param_base_offs; param_base_offs += SIZE_PTR; } } } jit_int32_t v_ret_ptr = 0; jit_int32_t v_orig_ret = 0; jit_int32_t v_override_ret = 0; jit_int32_t v_plugin_ret = 0; if (m_Proto.GetRet().size != 0) { v_ret_ptr = AddVarToFrame(SIZE_PTR); v_orig_ret = AddVarToFrame(GetParamStackSize(m_Proto.GetRet())); v_override_ret = AddVarToFrame(GetParamStackSize(m_Proto.GetRet())); v_plugin_ret = AddVarToFrame(GetParamStackSize(m_Proto.GetRet())); } jit_int32_t v_place_for_memret = 0; if (MemRetWithTempObj()) { v_place_for_memret = AddVarToFrame(GetParamStackSize(m_Proto.GetRet())); } jit_int32_t v_place_fbrr_base = 0; if (SH_COMP == SH_COMP_GCC && GetForcedByRefParamsSize()) { v_place_fbrr_base = AddVarToFrame(GetForcedByRefParamsSize()); } // Only exists for varargs functions jit_int32_t v_va_argptr = 0; if (m_Proto.GetConvention() & ProtoInfo::CallConv_HasVarArgs) { v_va_argptr = AddVarToFrame(SIZE_PTR); } jit_int32_t v_va_buf = 0; if (m_Proto.GetConvention() & ProtoInfo::CallConv_HasVafmt) { v_va_buf = AddVarToFrame(SourceHook::STRBUF_LEN); } IA32_Sub_Rm_Imm32(&m_HookFunc, REG_ESP, ComputeVarsSize(), MOD_REG); // Initial stack alignment IA32_And_Rm_Imm32(&m_HookFunc, REG_ESP, MOD_REG, -16); m_BytesPushedAfterInitialAlignment = 0; // init status localvar IA32_Mov_Rm_Imm32_Disp8(&m_HookFunc, REG_EBP, MRES_IGNORED, v_status); // VarArgs: init argptr & format if (m_Proto.GetConvention() & ProtoInfo::CallConv_HasVarArgs) { // argptr = first vararg param // lea eax, [ebp + param_base_offs + paramssize] // mov argptr, eax IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_EAX, REG_EBP, param_base_offs + GetParamsTotalStackSize() + SIZE_PTR); // +SIZE_PTR: last const char * is not in protoinfo IA32_Mov_Rm_Reg_DispAuto(&m_HookFunc, REG_EBP, REG_EAX, v_va_argptr); } if (m_Proto.GetConvention() & ProtoInfo::CallConv_HasVafmt) { // vsnprintf jit_int32_t tmpAlign = AlignStackBeforeCall(SIZE_PTR*3 + sizeof(size_t), 0); // push valist, fmt param, maxsize, buffer IA32_Push_Reg(&m_HookFunc, REG_EAX); IA32_Push_Rm_DispAuto(&m_HookFunc, REG_EBP, param_base_offs + GetParamsTotalStackSize()); // last given param (+4-4, see above) IA32_Push_Imm32(&m_HookFunc, SourceHook::STRBUF_LEN - 1); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_va_buf); IA32_Push_Reg(&m_HookFunc, REG_ECX); // call IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(&vsnprintf)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); // Clean up (cdecl) IA32_Add_Rm_Imm32(&m_HookFunc, REG_ESP, 0x10, MOD_REG); AlignStackAfterCall(tmpAlign); // Set trailing zero IA32_Xor_Reg_Rm(&m_HookFunc, REG_EDX, REG_EDX, MOD_REG); IA32_Mov_Rm8_Reg8_DispAuto(&m_HookFunc, REG_EBP, REG_EDX, v_va_buf + SourceHook::STRBUF_LEN - 1); } // Call constructors for ret vars if required if((m_Proto.GetRet().flags & PassInfo::PassFlag_ByVal) && m_Proto.GetRet().pNormalCtor) { jit_int32_t tmpAlign = AlignStackBeforeCall(0, AlignStack_GCC_ThisOnStack); // orig_reg IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_orig_ret); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pNormalCtor)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Pop_Reg(&m_HookFunc, REG_ECX)); // override_reg IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_override_ret); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pNormalCtor)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Pop_Reg(&m_HookFunc, REG_ECX)); // plugin_ret IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_plugin_ret); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pNormalCtor)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Pop_Reg(&m_HookFunc, REG_ECX)); AlignStackAfterCall(tmpAlign); // _don't_ call a constructor for v_place_for_memret ! } // ********************** SetupHookLoop ********************** CallSetupHookLoop(v_orig_ret, v_override_ret, v_cur_res, v_prev_res, v_status, v_vfnptr_origentry, v_this, v_pContext); // ********************** call pre hooks ********************** GenerateCallHooks(v_status, v_prev_res, v_cur_res, v_iter, v_pContext, param_base_offs, v_plugin_ret, v_place_for_memret, v_place_fbrr_base, v_va_buf); // ********************** call orig func ********************** GenerateCallOrig(v_status, v_pContext, param_base_offs, v_this, v_vfnptr_origentry, v_orig_ret, v_override_ret, v_place_for_memret, v_place_fbrr_base, v_va_buf); // ********************** call post hooks ********************** GenerateCallHooks(v_status, v_prev_res, v_cur_res, v_iter, v_pContext, param_base_offs, v_plugin_ret, v_place_for_memret, v_place_fbrr_base, v_va_buf); // ********************** end context and return ********************** PrepareReturn(v_status, v_pContext, v_ret_ptr); CallEndContext(v_pContext); // Call destructors of byval object params which have a destructor jit_int32_t tmpAlign = AlignStackBeforeCall(0, AlignStack_GCC_ThisOnStack); jit_int32_t cur_param_pos = param_base_offs; for (int i = 0; i < m_Proto.GetNumOfParams(); ++i) { const IntPassInfo &pi = m_Proto.GetParam(i); // GCC: NOT of forced byref params. the caller destructs those. if (pi.type == PassInfo::PassType_Object && (pi.flags & PassInfo::PassFlag_ODtor) && (pi.flags & PassInfo::PassFlag_ByVal) && !(pi.flags & PassFlag_ForcedByRef)) { IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, cur_param_pos); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(pi.pDtor)); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Pop_Reg(&m_HookFunc, REG_ECX)); } cur_param_pos += GetParamStackSize(pi); } AlignStackAfterCall(tmpAlign); DoReturn(v_ret_ptr, v_memret_addr); // Call destructors of orig_ret/ ... if((m_Proto.GetRet().flags & PassInfo::PassFlag_ByVal) && m_Proto.GetRet().pDtor) { // Preserve return value in EAX(:EDX) IA32_Push_Reg(&m_HookFunc, REG_EAX); IA32_Push_Reg(&m_HookFunc, REG_EDX); m_BytesPushedAfterInitialAlignment += 8; jit_int32_t tmpAlign = AlignStackBeforeCall(0, AlignStack_GCC_ThisOnStack); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_plugin_ret); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pDtor)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Pop_Reg(&m_HookFunc, REG_ECX)); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_override_ret); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pDtor)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Pop_Reg(&m_HookFunc, REG_ECX)); IA32_Lea_DispRegImmAuto(&m_HookFunc, REG_ECX, REG_EBP, v_orig_ret); GCC_ONLY(IA32_Push_Reg(&m_HookFunc, REG_ECX)); IA32_Mov_Reg_Imm32(&m_HookFunc, REG_EAX, DownCastPtr(m_Proto.GetRet().pDtor)); CheckAlignmentBeforeCall(); IA32_Call_Reg(&m_HookFunc, REG_EAX); GCC_ONLY(IA32_Pop_Reg(&m_HookFunc, REG_ECX)); AlignStackAfterCall(tmpAlign); IA32_Pop_Reg(&m_HookFunc, REG_EDX); IA32_Pop_Reg(&m_HookFunc, REG_EAX); m_BytesPushedAfterInitialAlignment -= 8; } // epilogue IA32_Mov_Reg_Rm(&m_HookFunc, REG_ESP, REG_EBP, MOD_REG); IA32_Pop_Reg(&m_HookFunc, REG_EBX); IA32_Pop_Reg(&m_HookFunc, REG_EBP); if (SH_COMP == SH_COMP_MSVC && !(m_Proto.GetConvention() & ProtoInfo::CallConv_HasVarArgs)) { // msvc without varargs: // callee cleans the stack short cleansize = GetParamsTotalStackSize(); // Memory return: address is first param if (m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem) cleansize += SIZE_PTR; IA32_Return_Popstack(&m_HookFunc, cleansize); } else { // gcc or msvc with varargs: caller cleans the stack // exception: gcc removes the memret addr on memret: if (SH_COMP == SH_COMP_GCC && (m_Proto.GetRet().flags & PassInfo::PassFlag_RetMem)) IA32_Return_Popstack(&m_HookFunc, SIZE_PTR); else IA32_Return(&m_HookFunc); } // Store pointer for later use // m_HookfuncVfnPtr is a pointer to a void* because SH expects a pointer // into the hookman's vtable *m_HookfuncVfnptr = reinterpret_cast(m_HookFunc.GetData()); m_HookFunc.SetRE(); return m_HookFunc.GetData(); } // Pre-condition: GenerateHookFunc() has been called! void * GenContext::GeneratePubFunc() { jitoffs_t counter, tmppos; // The pubfunc is a static cdecl function. // C Code: // int HookManPubFunc( // bool store, ebp + 8 // IHookManagerInfo *hi ebp + 12 // ) // { // if (store) // *m_pHI = hi; // if (hi) // hi->SetInfo(HOOKMAN_VERSION, m_VtblOffs, m_VtblIdx, m_Proto.GetProto(), m_HookfuncVfnptr) // } // prologue IA32_Push_Reg(&m_PubFunc, REG_EBP); IA32_Mov_Reg_Rm(&m_PubFunc, REG_EBP, REG_ESP, MOD_REG); // save store in eax, hi in ecx IA32_Movzx_Reg32_Rm8_Disp8(&m_PubFunc, REG_EAX, REG_EBP, 8); IA32_Mov_Reg_Rm_DispAuto(&m_PubFunc, REG_ECX, REG_EBP, 12); // Check for store == 0 IA32_Test_Rm_Reg8(&m_PubFunc, REG_EAX, REG_EAX, MOD_REG); tmppos = IA32_Jump_Cond_Imm8(&m_PubFunc, CC_Z, 0); m_PubFunc.start_count(counter); // nonzero -> store hi IA32_Mov_Rm_Imm32(&m_PubFunc, REG_EDX, DownCastPtr(m_pHI), MOD_REG); IA32_Mov_Rm_Reg(&m_PubFunc, REG_EDX, REG_ECX, MOD_MEM_REG); // zero m_PubFunc.end_count(counter); m_PubFunc.rewrite(tmppos, static_cast(counter)); // check for hi == 0 IA32_Test_Rm_Reg(&m_PubFunc, REG_ECX, REG_ECX, MOD_REG); tmppos = IA32_Jump_Cond_Imm8(&m_PubFunc, CC_Z, 0); m_PubFunc.start_count(counter); // nonzero -> call vfunc // push params in reverse order IA32_Push_Imm32(&m_PubFunc, DownCastPtr(m_HookfuncVfnptr)); IA32_Push_Imm32(&m_PubFunc, DownCastPtr(m_BuiltPI)); IA32_Push_Imm32(&m_PubFunc, m_VtblIdx); IA32_Push_Imm32(&m_PubFunc, m_VtblOffs); IA32_Push_Imm32(&m_PubFunc, SH_HOOKMAN_VERSION); // hi == this is in ecx // on gcc/mingw, ecx is the first parameter #if SH_COMP == SH_COMP_GCC IA32_Push_Reg(&m_PubFunc, REG_ECX); #endif // call the function. vtbloffs = 0, vtblidx = 0 // get vtptr into edx IA32_Mov_Reg_Rm(&m_PubFunc, REG_EDX, REG_ECX, MOD_MEM_REG); // get funcptr into eax IA32_Mov_Reg_Rm(&m_PubFunc, REG_EAX, REG_EDX, MOD_MEM_REG); IA32_Call_Reg(&m_PubFunc, REG_EAX); // on gcc/mingw, we have to clean up after the call #if SH_COMP == SH_COMP_GCC // 5 params + hidden thisptr param IA32_Add_Rm_Imm8(&m_PubFunc, REG_ESP, 6*SIZE_MWORD, MOD_REG); #endif // zero m_PubFunc.end_count(counter); m_PubFunc.rewrite(tmppos, static_cast(counter)); // return value IA32_Xor_Reg_Rm(&m_PubFunc, REG_EAX, REG_EAX, MOD_REG); // epilogue IA32_Mov_Reg_Rm(&m_PubFunc, REG_ESP, REG_EBP, MOD_REG); IA32_Pop_Reg(&m_PubFunc, REG_EBP); IA32_Return(&m_PubFunc); m_PubFunc.SetRE(); return m_PubFunc; } bool GenContext::PassInfoSupported(const IntPassInfo &pi, bool is_ret) { // :TODO: Error returns if (pi.type != PassInfo::PassType_Basic && pi.type != PassInfo::PassType_Float && pi.type != PassInfo::PassType_Object) { return false; } if (pi.type == PassInfo::PassType_Object && (pi.flags & PassInfo::PassFlag_ByVal)) { if ((pi.flags & PassInfo::PassFlag_CCtor) && !pi.pCopyCtor) { return false; } if ((pi.flags & PassInfo::PassFlag_ODtor) && !pi.pDtor) { return false; } if ((pi.flags & PassInfo::PassFlag_AssignOp) && !pi.pAssignOperator) { return false; } if ((pi.flags & PassInfo::PassFlag_OCtor) && !pi.pNormalCtor) { return false; } } if ((pi.flags & (PassInfo::PassFlag_ByVal | PassInfo::PassFlag_ByRef)) == 0) { return false; // Neither byval nor byref! } return true; } void GenContext::AutoDetectRetType() { IntPassInfo &pi = m_Proto.GetRet(); // Only relevant for byval types if (pi.flags & PassInfo::PassFlag_ByVal) { // Basic + float: if (pi.type == PassInfo::PassType_Basic || pi.type == PassInfo::PassType_Float) { // <= 8 bytes: // _always_ in registers, no matter what the user says if (pi.size <= 8) { pi.flags &= ~PassInfo::PassFlag_RetMem; pi.flags |= PassInfo::PassFlag_RetReg; } else { // Does this even exist? No idea, if it does: in memory! pi.flags &= ~PassInfo::PassFlag_RetReg; pi.flags |= PassInfo::PassFlag_RetMem; } } // Object: else if (pi.type == PassInfo::PassType_Object) { // If the user says nothing, auto-detect if ((pi.flags & (PassInfo::PassFlag_RetMem | PassInfo::PassFlag_RetReg)) == 0) { #if SH_COMP == SH_COMP_MSVC // MSVC seems to return _all_ structs, classes, unions in memory pi.flags |= PassInfo::PassFlag_RetMem; #elif SH_COMP == SH_COMP_GCC #if SH_SYS == SH_SYS_APPLE // Apple GCC returns in memory if size isn't a power of 2 or > 8 if ((pi.size & (pi.size - 1)) == 0 && pi.size <= 8) { pi.flags |= PassInfo::PassFlag_RetReg; } else #endif { // GCC on Linux does same thing as MSVC pi.flags |= PassInfo::PassFlag_RetMem; } #endif } } } else { // byref: make sure that the flag is _not_ set pi.flags &= ~PassInfo::PassFlag_RetMem; pi.flags |= PassInfo::PassFlag_RetReg; } } void GenContext::AutoDetectParamFlags() { #if SH_COMP == SH_COMP_GCC // On GCC, all objects are passed by reference if they have a destructor for (int i = 0; i < m_Proto.GetNumOfParams(); ++i) { IntPassInfo &pi = m_Proto.GetParam(i); if (pi.type == PassInfo::PassType_Object && (pi.flags & PassInfo::PassFlag_ByVal) && (pi.flags & PassInfo::PassFlag_ODtor)) { pi.flags |= PassFlag_ForcedByRef; } } #endif } HookManagerPubFunc GenContext::Generate() { Clear(); // Check conditions: // -1) good proto version // 0) we don't support unknown passtypes, convention, ... // 1) we don't support functions which return objects by value or take parameters by value // that have a constructor, a destructor or an overloaded assignment op // (we wouldn't know how to call it!) if (m_Proto.GetVersion() < 1) { return NULL; } AutoDetectRetType(); AutoDetectParamFlags(); // Basically, we only support ThisCall/thiscall with varargs if ((m_Proto.GetConvention() & (~ProtoInfo::CallConv_HasVafmt)) != ProtoInfo::CallConv_ThisCall) { return NULL; } if (m_Proto.GetRet().size != 0 && !PassInfoSupported(m_Proto.GetRet(), true)) { return NULL; } for (int i = 0; i < m_Proto.GetNumOfParams(); ++i) { if (!PassInfoSupported(m_Proto.GetParam(i), false)) return NULL; } BuildProtoInfo(); GenerateHookFunc(); return fastdelegate::detail::horrible_cast(GeneratePubFunc()); } HookManagerPubFunc GenContext::GetPubFunc() { if (m_GeneratedPubFunc == 0) m_GeneratedPubFunc = Generate(); return m_GeneratedPubFunc; } bool GenContext::Equal(const CProto &proto, int vtbl_offs, int vtbl_idx) { return (m_OrigProto.ExactlyEqual(proto) && m_VtblOffs == vtbl_offs && m_VtblIdx == vtbl_idx); } bool GenContext::Equal(HookManagerPubFunc other) { return m_GeneratedPubFunc == other; } // *********************************** class GenContextContainer CHookManagerAutoGen::CHookManagerAutoGen(ISourceHook *pSHPtr) : m_pSHPtr(pSHPtr) { } CHookManagerAutoGen::~CHookManagerAutoGen() { for (List::iterator iter = m_Contexts.begin(); iter != m_Contexts.end(); ++iter) { delete iter->m_GenContext; } } int CHookManagerAutoGen::GetIfaceVersion() { return SH_HOOKMANAUTOGEN_IFACE_VERSION; } int CHookManagerAutoGen::GetImplVersion() { return SH_HOOKMANAUTOGEN_IMPL_VERSION; } HookManagerPubFunc CHookManagerAutoGen::MakeHookMan(const ProtoInfo *proto, int vtbl_offs, int vtbl_idx) { CProto mproto(proto); for (List::iterator iter = m_Contexts.begin(); iter != m_Contexts.end(); ++iter) { if (iter->m_GenContext->Equal(mproto, vtbl_offs, vtbl_idx)) { iter->m_RefCnt++; return iter->m_GenContext->GetPubFunc(); } } // Not found yet -> new one StoredContext sctx; sctx.m_RefCnt = 1; sctx.m_GenContext = new GenContext(proto, vtbl_offs, vtbl_idx, m_pSHPtr); if (sctx.m_GenContext->GetPubFunc() == NULL) { return NULL; } else { m_Contexts.push_back(sctx); return sctx.m_GenContext->GetPubFunc(); } } void CHookManagerAutoGen::ReleaseHookMan(HookManagerPubFunc pubFunc) { for (List::iterator iter = m_Contexts.begin(); iter != m_Contexts.end(); ++iter) { if (iter->m_GenContext->Equal(pubFunc)) { iter->m_RefCnt--; if (iter->m_RefCnt == 0) { delete iter->m_GenContext; m_Contexts.erase(iter); } break; } } } } }