mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2024-12-01 13:24:25 +01:00
2041 lines
63 KiB
C++
2041 lines
63 KiB
C++
/* ======== 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 <stdio.h>
|
|
#include "sourcehook_impl.h"
|
|
#include "sourcehook_hookmangen.h"
|
|
#include "sourcehook_hookmangen_x86.h"
|
|
#include "sh_memory.h"
|
|
#include <stdarg.h> // 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 <class T>
|
|
jit_int32_t DownCastPtr(T ptr)
|
|
{
|
|
return reinterpret_cast<jit_int32_t>(ptr);
|
|
}
|
|
|
|
jit_uint32_t DownCastSize(size_t size)
|
|
{
|
|
return static_cast<jit_uint32_t>(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<jit_int32_t>(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, <dwords>
|
|
// rep movsd
|
|
//if bytes
|
|
// mov ecx, <bytes>
|
|
// 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<jit_int32_t>(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+<offset>]
|
|
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+<offset>]
|
|
//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+<offset>]
|
|
//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+<offset>]
|
|
//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+<offset>+4]
|
|
//mov reg2, DWORD PTR [ebp+<offset>]
|
|
//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+<offset>]
|
|
//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+<offset>]
|
|
//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, <size>
|
|
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+<offs>]
|
|
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<x<8 sizes up to 8
|
|
|
|
// mov [ebp + v_plugin_ret], eax
|
|
// mov [ebp + v_plugin_ret + 4], edx
|
|
IA32_Mov_Rm_Reg_DispAuto(&m_HookFunc, REG_EBP, REG_EAX, v_where);
|
|
IA32_Mov_Rm_Reg_DispAuto(&m_HookFunc, REG_EBP, REG_EDX, v_where + 4);
|
|
}
|
|
}
|
|
else if (m_Proto.GetRet().type == PassInfo::PassType_Object)
|
|
{
|
|
if (m_Proto.GetRet().flags & PassInfo::PassFlag_RetReg)
|
|
{
|
|
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<x<8 sizes up to 8
|
|
|
|
// mov [ebp + v_plugin_ret], eax
|
|
// mov [ebp + v_plugin_ret + 4], edx
|
|
IA32_Mov_Rm_Reg_DispAuto(&m_HookFunc, REG_EBP, REG_EAX, v_where);
|
|
IA32_Mov_Rm_Reg_DispAuto(&m_HookFunc, REG_EBP, REG_EDX, v_where + 4);
|
|
}
|
|
else
|
|
{
|
|
SH_ASSERT(0, ("RetReg and size > 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<my_rettype*>(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<jit_uint8_t>(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 4<x<8 sizes up to 8
|
|
|
|
// mov eax, [ecx]
|
|
// mov edx, [ecx+4]
|
|
IA32_Mov_Reg_Rm(&m_HookFunc, REG_EAX, REG_ECX, MOD_MEM_REG);
|
|
IA32_Mov_Reg_Rm_DispAuto(&m_HookFunc, REG_EDX, REG_ECX, 4);
|
|
}
|
|
else
|
|
{
|
|
// size >8: 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<jit_uint8_t>(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<jit_int32_t>(loop_begin_counter));
|
|
|
|
m_HookFunc.end_count(counter);
|
|
m_HookFunc.rewrite(tmppos, static_cast<jit_int32_t>(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<jit_uint32_t>(counter));
|
|
|
|
m_HookFunc.end_count(counter2);
|
|
m_HookFunc.rewrite(tmppos2, static_cast<jit_uint32_t>(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<jit_uint32_t>(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<void*>(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<jit_uint32_t**>(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<jit_uint32_t**>(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<void*>(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<jit_uint8_t>(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<jit_uint8_t>(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<HookManagerPubFunc>(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<StoredContext>::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<StoredContext>::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<StoredContext>::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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|