2008-11-14 11:04:02 +01:00
/* ======== SourceHook ========
2010-10-17 08:21:58 +02:00
* Copyright ( C ) 2004 - 2010 Metamod : Source Development Team
2008-11-14 11:04:02 +01:00
* 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 ) ;
2009-11-24 14:33:52 +01:00
IA32_Mov_Reg_Rm_DispAuto ( & m_HookFunc , REG_EDX , REG_ECX , 4 ) ;
2008-11-14 11:04:02 +01:00
}
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
2009-11-24 14:33:52 +01:00
# 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 ;
}
2008-11-14 11:04:02 +01:00
# 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 ;
}
}
}
}
}