mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2024-11-29 11:24:19 +01:00
e19413dd5b
--HG-- rename : sourcemm/IPluginManager.h => core/IPluginManager.h rename : sourcemm/ISmmAPI.h => core/ISmmAPI.h rename : sourcemm/ISmmPlugin.h => core/ISmmPlugin.h rename : sourcemm/ISmmPluginExt.h => core/ISmmPluginExt.h rename : sourcemm/LICENSE.txt => core/LICENSE.txt rename : sourcemm/Makefile => core/Makefile rename : sourcemm/changelog.txt => core/changelog.txt rename : sourcemm/episode1/console.cpp => core/episode1/console.cpp rename : sourcemm/episode1/console.h => core/episode1/console.h rename : sourcemm/episode1/convar_smm.h => core/episode1/convar_smm.h rename : sourcemm/episode1/msvc8/sourcemm.sln => core/episode1/msvc8/sourcemm.sln rename : sourcemm/episode1/msvc8/sourcemm.vcproj => core/episode1/msvc8/sourcemm.vcproj rename : sourcemm/episode1/provider_ep1.cpp => core/episode1/provider_ep1.cpp rename : sourcemm/episode1/provider_ep1.h => core/episode1/provider_ep1.h rename : sourcemm/episode1/vsp_listener.cpp => core/episode1/vsp_listener.cpp rename : sourcemm/episode1/vsp_listener.h => core/episode1/vsp_listener.h rename : sourcemm/metamod.cpp => core/metamod.cpp rename : sourcemm/metamod.h => core/metamod.h rename : sourcemm/metamod_console.cpp => core/metamod_console.cpp rename : sourcemm/metamod_console.h => core/metamod_console.h rename : sourcemm/metamod_oslink.cpp => core/metamod_oslink.cpp rename : sourcemm/metamod_oslink.h => core/metamod_oslink.h rename : sourcemm/metamod_plugins.cpp => core/metamod_plugins.cpp rename : sourcemm/metamod_plugins.h => core/metamod_plugins.h rename : sourcemm/metamod_provider.h => core/metamod_provider.h rename : sourcemm/metamod_util.cpp => core/metamod_util.cpp rename : sourcemm/metamod_util.h => core/metamod_util.h rename : sourcemm/episode2/console.cpp => core/provider/console.cpp rename : sourcemm/episode2/console.h => core/provider/console.h rename : sourcemm/episode2/msvc8/sourcemm.sln => core/provider/msvc8/sourcemm.sln rename : sourcemm/episode2/msvc8/sourcemm.vcproj => core/provider/msvc8/sourcemm.vcproj rename : sourcemm/episode2/msvc9/sourcemm.sln => core/provider/msvc9/sourcemm.sln rename : sourcemm/episode2/msvc9/sourcemm.vcproj => core/provider/msvc9/sourcemm.vcproj rename : sourcemm/episode2/provider_ep2.cpp => core/provider/provider_ep2.cpp rename : sourcemm/episode2/provider_ep2.h => core/provider/provider_ep2.h rename : sourcemm/episode2/vsp_listener.cpp => core/provider/vsp_listener.cpp rename : sourcemm/episode2/vsp_listener.h => core/provider/vsp_listener.h rename : sourcehook/FastDelegate.h => core/sourcehook/FastDelegate.h rename : sourcehook/generate/FastDelegate.h => core/sourcehook/generate/FastDelegate.h rename : sourcehook/generate/FastDelegate.hxx => core/sourcehook/generate/FastDelegate.hxx rename : sourcehook/generate/generate => core/sourcehook/generate/generate rename : sourcehook/generate/generate.bat => core/sourcehook/generate/generate.bat rename : sourcehook/generate/sh_memfuncinfo.h => core/sourcehook/generate/sh_memfuncinfo.h rename : sourcehook/generate/sh_memfuncinfo.hxx => core/sourcehook/generate/sh_memfuncinfo.hxx rename : sourcehook/generate/shworker.bin => core/sourcehook/generate/shworker.bin rename : sourcehook/generate/shworker.exe => core/sourcehook/generate/shworker.exe rename : sourcehook/generate/shworker/Makefile => core/sourcehook/generate/shworker/Makefile rename : sourcehook/generate/shworker/fd_hopter.cpp => core/sourcehook/generate/shworker/fd_hopter.cpp rename : sourcehook/generate/shworker/msvc7/shworker.vcproj => core/sourcehook/generate/shworker/msvc7/shworker.vcproj rename : sourcehook/generate/shworker/msvc8/shworker.vcproj => core/sourcehook/generate/shworker/msvc8/shworker.vcproj rename : sourcehook/generate/shworker/shworker.cpp => core/sourcehook/generate/shworker/shworker.cpp rename : sourcehook/generate/sourcehook.h => core/sourcehook/generate/sourcehook.h rename : sourcehook/generate/sourcehook.hxx => core/sourcehook/generate/sourcehook.hxx rename : sourcehook/sh_list.h => core/sourcehook/sh_list.h rename : sourcehook/sh_memfuncinfo.h => core/sourcehook/sh_memfuncinfo.h rename : sourcehook/sh_memory.h => core/sourcehook/sh_memory.h rename : sourcehook/sh_pagealloc.h => core/sourcehook/sh_pagealloc.h rename : sourcehook/sh_stack.h => core/sourcehook/sh_stack.h rename : sourcehook/sh_string.h => core/sourcehook/sh_string.h rename : sourcehook/sh_tinyhash.h => core/sourcehook/sh_tinyhash.h rename : sourcehook/sh_vector.h => core/sourcehook/sh_vector.h rename : sourcehook/sourcehook.cpp => core/sourcehook/sourcehook.cpp rename : sourcehook/sourcehook.h => core/sourcehook/sourcehook.h rename : sourcehook/sourcehook_hookmangen.cpp => core/sourcehook/sourcehook_hookmangen.cpp rename : sourcehook/sourcehook_hookmangen.h => core/sourcehook/sourcehook_hookmangen.h rename : sourcehook/sourcehook_hookmangen_x86.h => core/sourcehook/sourcehook_hookmangen_x86.h rename : sourcehook/sourcehook_impl.h => core/sourcehook/sourcehook_impl.h rename : sourcehook/sourcehook_impl_chook.h => core/sourcehook/sourcehook_impl_chook.h rename : sourcehook/sourcehook_impl_chookidman.h => core/sourcehook/sourcehook_impl_chookidman.h rename : sourcehook/sourcehook_impl_chookmaninfo.h => core/sourcehook/sourcehook_impl_chookmaninfo.h rename : sourcehook/sourcehook_impl_ciface.h => core/sourcehook/sourcehook_impl_ciface.h rename : sourcehook/sourcehook_impl_cproto.h => core/sourcehook/sourcehook_impl_cproto.h rename : sourcehook/sourcehook_impl_cvfnptr.h => core/sourcehook/sourcehook_impl_cvfnptr.h rename : sourcehook/sourcehook_pibuilder.h => core/sourcehook/sourcehook_pibuilder.h rename : sourcehook/test/Makefile => core/sourcehook/test/Makefile rename : sourcehook/test/generate.bat => core/sourcehook/test/generate.bat rename : sourcehook/test/main.cpp => core/sourcehook/test/main.cpp rename : sourcehook/test/msvc7/test.vcproj => core/sourcehook/test/msvc7/test.vcproj rename : sourcehook/test/msvc8/test.vcproj => core/sourcehook/test/msvc8/test.vcproj rename : sourcehook/test/sourcehook_test.h => core/sourcehook/test/sourcehook_test.h rename : sourcehook/test/test1.cpp => core/sourcehook/test/test1.cpp rename : sourcehook/test/test2.cpp => core/sourcehook/test/test2.cpp rename : sourcehook/test/test3.cpp => core/sourcehook/test/test3.cpp rename : sourcehook/test/test4.cpp => core/sourcehook/test/test4.cpp rename : sourcehook/test/testbail.cpp => core/sourcehook/test/testbail.cpp rename : sourcehook/test/testbail.h => core/sourcehook/test/testbail.h rename : sourcehook/test/testbail2.cpp => core/sourcehook/test/testbail2.cpp rename : sourcehook/test/testevents.h => core/sourcehook/test/testevents.h rename : sourcehook/test/testhookmangen.cpp => core/sourcehook/test/testhookmangen.cpp rename : sourcehook/test/testhookmangen.h => core/sourcehook/test/testhookmangen.h rename : sourcehook/test/testhookmangen.hxx => core/sourcehook/test/testhookmangen.hxx rename : sourcehook/test/testlist.cpp => core/sourcehook/test/testlist.cpp rename : sourcehook/test/testmanual.cpp => core/sourcehook/test/testmanual.cpp rename : sourcehook/test/testmulti.cpp => core/sourcehook/test/testmulti.cpp rename : sourcehook/test/testrecall.cpp => core/sourcehook/test/testrecall.cpp rename : sourcehook/test/testreentr.cpp => core/sourcehook/test/testreentr.cpp rename : sourcehook/test/testref.cpp => core/sourcehook/test/testref.cpp rename : sourcehook/test/testrefret.cpp => core/sourcehook/test/testrefret.cpp rename : sourcehook/test/testvphooks.cpp => core/sourcehook/test/testvphooks.cpp rename : sourcemm/svn_version.h => core/svn_version.h rename : sourcemm/svn_version.tpl => core/svn_version.tpl rename : sourcemm/version.rc => core/version.rc
333 lines
9.9 KiB
C++
333 lines
9.9 KiB
C++
/* ======== SourceHook ========
|
|
* Copyright (C) 2004-2008 Metamod:Source Development Team
|
|
* No warranties of any kind
|
|
*
|
|
* License: zlib/libpng
|
|
*
|
|
* Author(s): Pavol "PM OnoTo" Marko
|
|
* ============================
|
|
*/
|
|
|
|
/**
|
|
* @brief This file provides a way for getting information about a member function.
|
|
* @file sh_memfuncinfo.h
|
|
*/
|
|
|
|
#ifndef __SHINT_MEMFUNC_INFO_H__
|
|
#define __SHINT_MEMFUNC_INFO_H__
|
|
|
|
namespace SourceHook
|
|
{
|
|
|
|
// Don Clugston:
|
|
// implicit_cast< >
|
|
// I believe this was originally going to be in the C++ standard but
|
|
// was left out by accident. It's even milder than static_cast.
|
|
// I use it instead of static_cast<> to emphasize that I'm not doing
|
|
// anything nasty.
|
|
// Usage is identical to static_cast<>
|
|
template <class OutputClass, class InputClass>
|
|
inline OutputClass implicit_cast(InputClass input){
|
|
return input;
|
|
}
|
|
|
|
|
|
struct MemFuncInfo
|
|
{
|
|
bool isVirtual; // Is the function virtual?
|
|
int thisptroffs; // The this pointer the function expects to be called with
|
|
// If -1, you need to call the GetFuncInfo_GetThisPtr function
|
|
int vtblindex; // The function's index in the vtable (0-based, 1=second entry, 2=third entry, ...)
|
|
int vtbloffs; // The vtable pointer
|
|
};
|
|
|
|
// Ideas by Don Clugston.
|
|
// Check out his excellent paper: http://www.codeproject.com/cpp/FastDelegate.asp
|
|
|
|
template<int N> struct MFI_Impl
|
|
{
|
|
template<class MFP> static inline void GetFuncInfo(MFP *mfp, MemFuncInfo &out)
|
|
{
|
|
static char weird_memfunc_pointer_exclamation_mark_arrow_error[N-1000];
|
|
}
|
|
};
|
|
|
|
# if SH_COMP == SH_COMP_GCC
|
|
|
|
template<> struct MFI_Impl<2*SH_PTRSIZE> // All of these have size==8/16
|
|
{
|
|
struct GCC_MemFunPtr
|
|
{
|
|
union
|
|
{
|
|
void *funcadr; // always even
|
|
intptr_t vtable_index_plus1; // = vindex+1, always odd
|
|
};
|
|
intptr_t delta;
|
|
};
|
|
template<class MFP> static inline void GetFuncInfo(MFP mfp, MemFuncInfo &out)
|
|
{
|
|
GCC_MemFunPtr *mfp_detail = (GCC_MemFunPtr*)&mfp;
|
|
out.thisptroffs = mfp_detail->delta;
|
|
if (mfp_detail->vtable_index_plus1 & 1)
|
|
{
|
|
out.vtblindex = (mfp_detail->vtable_index_plus1 - 1) / SH_PTRSIZE;
|
|
out.vtbloffs = 0;
|
|
out.isVirtual = true;
|
|
}
|
|
else
|
|
out.isVirtual = false;
|
|
}
|
|
};
|
|
|
|
# elif SH_COMP == SH_COMP_MSVC
|
|
|
|
namespace
|
|
{
|
|
int MFI_GetVtblOffset(void *mfp)
|
|
{
|
|
unsigned char *addr = (unsigned char*)mfp;
|
|
if (*addr == 0xE9) // Jmp
|
|
{
|
|
// May or may not be!
|
|
// Check where it'd jump
|
|
addr += 5 /*size of the instruction*/ + *(unsigned long*)(addr + 1);
|
|
}
|
|
|
|
// Check whether it's a virtual function call
|
|
// They look like this:
|
|
// 004125A0 8B 01 mov eax,dword ptr [ecx]
|
|
// 004125A2 FF 60 04 jmp dword ptr [eax+4]
|
|
// ==OR==
|
|
// 00411B80 8B 01 mov eax,dword ptr [ecx]
|
|
// 00411B82 FF A0 18 03 00 00 jmp dword ptr [eax+318h]
|
|
|
|
// However, for vararg functions, they look like this:
|
|
// 0048F0B0 8B 44 24 04 mov eax,dword ptr [esp+4]
|
|
// 0048F0B4 8B 00 mov eax,dword ptr [eax]
|
|
// 0048F0B6 FF 60 08 jmp dword ptr [eax+8]
|
|
// ==OR==
|
|
// 0048F0B0 8B 44 24 04 mov eax,dword ptr [esp+4]
|
|
// 0048F0B4 8B 00 mov eax,dword ptr [eax]
|
|
// 00411B82 FF A0 18 03 00 00 jmp dword ptr [eax+318h]
|
|
|
|
// With varargs, the this pointer is passed as if it was the first argument
|
|
|
|
bool ok = false;
|
|
if (addr[0] == 0x8B && addr[1] == 0x44 && addr[2] == 0x24 && addr[3] == 0x04 &&
|
|
addr[4] == 0x8B && addr[5] == 0x00)
|
|
{
|
|
addr += 6;
|
|
ok = true;
|
|
}
|
|
else if (addr[0] == 0x8B && addr[1] == 0x01)
|
|
{
|
|
addr += 2;
|
|
ok = true;
|
|
}
|
|
if (!ok)
|
|
return -1;
|
|
|
|
if (*addr++ == 0xFF)
|
|
{
|
|
if (*addr == 0x60)
|
|
{
|
|
return *++addr / 4;
|
|
}
|
|
else if (*addr == 0xA0)
|
|
{
|
|
return *((unsigned int*)++addr) / 4;
|
|
}
|
|
else if (*addr == 0x20)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
template<> struct MFI_Impl<1*SH_PTRSIZE> // simple ones
|
|
{
|
|
template<class MFP> static inline void GetFuncInfo(MFP mfp, MemFuncInfo &out)
|
|
{
|
|
out.vtblindex = MFI_GetVtblOffset(*(void**)&mfp);
|
|
out.isVirtual = out.vtblindex >= 0 ? true : false;
|
|
out.thisptroffs = 0;
|
|
out.vtbloffs = 0;
|
|
}
|
|
};
|
|
|
|
template<> struct MFI_Impl<2*SH_PTRSIZE> // more complicated ones!
|
|
{
|
|
struct MSVC_MemFunPtr2
|
|
{
|
|
void *funcadr;
|
|
int delta;
|
|
};
|
|
template<class MFP> static inline void GetFuncInfo(MFP mfp, MemFuncInfo &out)
|
|
{
|
|
out.vtblindex = MFI_GetVtblOffset(*(void**)&mfp);
|
|
out.isVirtual = out.vtblindex >= 0 ? true : false;
|
|
out.thisptroffs = reinterpret_cast<MSVC_MemFunPtr2*>(&mfp)->delta;
|
|
out.vtbloffs = 0;
|
|
}
|
|
};
|
|
|
|
// By Don Clugston, adapted
|
|
template<> struct MFI_Impl<3*SH_PTRSIZE> // WOW IT"S GETTING BIGGER OMGOMOGMG
|
|
{
|
|
class __single_inheritance GenericClass;
|
|
class GenericClass {};
|
|
|
|
struct MicrosoftVirtualMFP {
|
|
void (GenericClass::*codeptr)(); // points to the actual member function
|
|
int delta; // #bytes to be added to the 'this' pointer
|
|
int vtable_index; // or 0 if no virtual inheritance
|
|
};
|
|
|
|
struct GenericVirtualClass : virtual public GenericClass
|
|
{
|
|
typedef GenericVirtualClass * (GenericVirtualClass::*ProbePtrType)();
|
|
GenericVirtualClass * GetThis() { return this; }
|
|
};
|
|
|
|
template<class MFP> static inline void GetFuncInfo(MFP mfp, MemFuncInfo &out)
|
|
{
|
|
out.vtblindex = MFI_GetVtblOffset(*(void**)&mfp);
|
|
out.isVirtual = out.vtblindex >= 0 ? true : false;
|
|
// This pointer
|
|
/*
|
|
union {
|
|
MFP func;
|
|
GenericClass* (T::*ProbeFunc)();
|
|
MicrosoftVirtualMFP s;
|
|
} u;
|
|
u.func = mfp;
|
|
union {
|
|
GenericVirtualClass::ProbePtrType virtfunc;
|
|
MicrosoftVirtualMFP s;
|
|
} u2;
|
|
|
|
// Check that the horrible_cast<>s will work
|
|
typedef int ERROR_CantUsehorrible_cast[sizeof(mfp)==sizeof(u.s)
|
|
&& sizeof(mfp)==sizeof(u.ProbeFunc)
|
|
&& sizeof(u2.virtfunc)==sizeof(u2.s) ? 1 : -1];
|
|
// Unfortunately, taking the address of a MF prevents it from being inlined, so
|
|
// this next line can't be completely optimised away by the compiler.
|
|
u2.virtfunc = &GenericVirtualClass::GetThis;
|
|
u.s.codeptr = u2.s.codeptr;
|
|
out.thisptroffs = (reinterpret_cast<T*>(NULL)->*u.ProbeFunc)();
|
|
*/
|
|
out.thisptroffs = -1;
|
|
out.vtbloffs = 0;
|
|
}
|
|
};
|
|
|
|
// Don: Nasty hack for Microsoft and Intel (IA32 and Itanium)
|
|
// unknown_inheritance classes go here
|
|
// This is probably the ugliest bit of code I've ever written. Look at the casts!
|
|
// There is a compiler bug in MSVC6 which prevents it from using this code.
|
|
template<> struct MFI_Impl<4*SH_PTRSIZE> // THE BIGGEST ONE!!!1GABEN
|
|
{
|
|
template<class MFP> static inline void GetFuncInfo(MFP mfp, MemFuncInfo &out)
|
|
{
|
|
out.vtblindex = MFI_GetVtblOffset(*(void**)&mfp);
|
|
out.isVirtual = out.vtblindex >= 0 ? true : false;
|
|
|
|
// The member function pointer is 16 bytes long. We can't use a normal cast, but
|
|
// we can use a union to do the conversion.
|
|
union {
|
|
MFP func;
|
|
// In VC++ and ICL, an unknown_inheritance member pointer
|
|
// is internally defined as:
|
|
struct {
|
|
void *m_funcaddress; // points to the actual member function
|
|
int delta; // #bytes to be added to the 'this' pointer
|
|
int vtordisp; // #bytes to add to 'this' to find the vtable
|
|
int vtable_index; // or 0 if no virtual inheritance
|
|
} s;
|
|
} u;
|
|
// Check that the horrible_cast will work
|
|
typedef int ERROR_CantUsehorrible_cast[sizeof(u.func)==sizeof(u.s)? 1 : -1];
|
|
u.func = mfp;
|
|
int virtual_delta = 0;
|
|
if (u.s.vtable_index) { // Virtual inheritance is used
|
|
/*
|
|
// First, get to the vtable.
|
|
// It is 'vtordisp' bytes from the start of the class.
|
|
int * vtable = *reinterpret_cast<int **>(
|
|
reinterpret_cast<char *>(thisptr) + u.s.vtordisp );
|
|
|
|
// 'vtable_index' tells us where in the table we should be looking.
|
|
virtual_delta = u.s.vtordisp + *reinterpret_cast<const int *>(
|
|
reinterpret_cast<const char *>(vtable) + u.s.vtable_index);
|
|
// The int at 'virtual_delta' gives us the amount to add to 'this'.
|
|
// Finally we can add the three components together. Phew!
|
|
out.thisptr = reinterpret_cast<void *>(
|
|
reinterpret_cast<char *>(thisptr) + u.s.delta + virtual_delta);
|
|
*/
|
|
out.vtbloffs = u.s.vtordisp;
|
|
out.thisptroffs = -1;
|
|
}
|
|
else
|
|
{
|
|
out.vtbloffs = out.vtblindex < 0 ? 0 : u.s.delta;
|
|
out.thisptroffs = u.s.delta;
|
|
}
|
|
};
|
|
};
|
|
# else
|
|
# error Unsupported compiler
|
|
# endif
|
|
|
|
// This version does not take a this pointer
|
|
// Useful for hookdecls, as they ensure that mfp is correct through a static_cast
|
|
template<class X> inline void GetFuncInfo(X mfp, MemFuncInfo &out)
|
|
{
|
|
MFI_Impl<sizeof(mfp)>::GetFuncInfo(mfp, out);
|
|
}
|
|
|
|
// Versions which do take a this
|
|
@[$1,0,$a:
|
|
template<class X, class Y, class RetType@[$1!=0:, @]@[$2,1,$1|, :class Param$2@]>
|
|
inline void GetFuncInfo(Y *ptr, RetType(X::*mfp)(@[$2,1,$1|, :Param$2@]), MemFuncInfo &out)
|
|
{
|
|
RetType(Y::*mfp2)(@[$2,1,$1|, :Param$2@]) = mfp;
|
|
MFI_Impl<sizeof(mfp2)>::GetFuncInfo(mfp2, out);
|
|
}
|
|
|
|
template<class X, class Y, class RetType@[$1!=0:, @]@[$2,1,$1|, :class Param$2@]>
|
|
inline void GetFuncInfo(Y *ptr, RetType(X::*mfp)(@[$2,1,$1|, :Param$2@]) const, MemFuncInfo &out)
|
|
{
|
|
RetType(Y::*mfp2)(@[$2,1,$1|, :Param$2@]) const = mfp;
|
|
MFI_Impl<sizeof(mfp2)>::GetFuncInfo(mfp2, out);
|
|
}
|
|
@]
|
|
|
|
// GCC & MSVC 7.1 need this, MSVC 7.0 doesn't like it
|
|
#if SH_COMP != SH_COMP_MSVC || _MSC_VER > 1300
|
|
|
|
@[$1,0,$a:
|
|
template<class X, class Y, class RetType@[$1!=0:, @]@[$2,1,$1|, :class Param$2@]>
|
|
inline void GetFuncInfo(Y *ptr, RetType(X::*mfp)(@[$2,1,$1|, :Param$2@]@[$1!=0:, @]...), MemFuncInfo &out)
|
|
{
|
|
RetType(Y::*mfp2)(@[$2,1,$1|, :Param$2@]@[$1!=0:, @]...) = mfp;
|
|
MFI_Impl<sizeof(mfp2)>::GetFuncInfo(mfp2, out);
|
|
}
|
|
|
|
template<class X, class Y, class RetType@[$1!=0:, @]@[$2,1,$1|, :class Param$2@]>
|
|
inline void GetFuncInfo(Y *ptr, RetType(X::*mfp)(@[$2,1,$1|, :Param$2@]@[$1!=0:, @]...) const, MemFuncInfo &out)
|
|
{
|
|
RetType(Y::*mfp2)(@[$2,1,$1|, :Param$2@]@[$1!=0:, @]...) const = mfp;
|
|
MFI_Impl<sizeof(mfp2)>::GetFuncInfo(mfp2, out);
|
|
}
|
|
|
|
@]
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif
|