add x64 stub generator support

This commit is contained in:
Kenzzer 2024-04-20 14:10:59 +02:00 committed by Nick Hastings
parent f5f14786b9
commit 222c174f0d
12 changed files with 2846 additions and 20 deletions

View File

@ -178,7 +178,8 @@ class MMSConfig(object):
]
cxx.cflags += [
'/W3',
'/Zi',
'/Z7',
'/EHsc',
'/std:c++17',
]
cxx.cxxflags += ['/TP']

View File

@ -16,6 +16,7 @@ for sdk_target in MMS.sdk_targets:
'metamod_util.cpp',
'provider/provider_base.cpp',
'sourcehook/sourcehook.cpp',
'sourcehook/sourcehook_hookmangen_test.cpp',
'sourcehook/sourcehook_impl_chookidman.cpp',
'sourcehook/sourcehook_impl_chookmaninfo.cpp',
'sourcehook/sourcehook_impl_cproto.cpp',
@ -41,5 +42,7 @@ for sdk_target in MMS.sdk_targets:
if cxx.target.arch == 'x86':
binary.sources += ['sourcehook/sourcehook_hookmangen.cpp']
elif binary.compiler.target.arch == 'x86_64':
binary.sources += ['sourcehook/sourcehook_hookmangen_x86_64.cpp']
nodes = builder.Add(binary)
MMS.binaries += [nodes]

View File

@ -34,6 +34,7 @@
#include "metamod_util.h"
#include "metamod_console.h"
#include "provider/provider_base.h"
#include "sourcehook/sourcehook_hookmangen_test.h"
#include <sys/stat.h>
#define X64_SUFFIX ".x64"
@ -84,9 +85,7 @@ static MetamodSourceConVar *mm_basedir = NULL;
static CreateInterfaceFn engine_factory = NULL;
static CreateInterfaceFn physics_factory = NULL;
static CreateInterfaceFn filesystem_factory = NULL;
#if !defined( _WIN64 ) && !defined( __amd64__ )
static CHookManagerAutoGen g_SH_HookManagerAutoGen(&g_SourceHook);
#endif
static META_RES last_meta_res;
static IServerPluginCallbacks *vsp_callbacks = NULL;
static bool were_plugins_loaded = false;
@ -222,6 +221,7 @@ mm_InitializeForLoad()
in_first_level = true;
provider->SetCallbacks(&s_ProviderCallbacks);
SourceHook::Impl::run_tests();
}
bool
@ -420,6 +420,7 @@ mm_LogMessage(const char *msg, ...)
va_start(ap, msg);
size_t len = vsnprintf(buffer, sizeof(buffer) - 2, msg, ap);
len = min(len, 2046);
va_end(ap);
buffer[len++] = '\n';
@ -429,6 +430,7 @@ mm_LogMessage(const char *msg, ...)
{
fprintf(stdout, "%s", buffer);
}
provider->ConsolePrint(buffer);
}
static void
@ -814,19 +816,11 @@ void *MetamodSource::MetaFactory(const char *iface, int *ret, PluginId *id)
}
else if (strcmp(iface, MMIFACE_SH_HOOKMANAUTOGEN) == 0)
{
#if defined( _WIN64 ) || defined( __amd64__ )
if (ret)
{
*ret = META_IFACE_FAILED;
}
return nullptr;
#else
if (ret)
{
*ret = META_IFACE_OK;
}
return static_cast<void *>(static_cast<SourceHook::IHookManagerAutoGen *>(&g_SH_HookManagerAutoGen));
#endif
}
CPluginManager::CPlugin *pl;

View File

@ -42,6 +42,9 @@ namespace SourceHook
namespace Impl
{
// Log level
SH_LOG sh_log_level = SH_LOG::TEST;
//////////////////////////////////////////////////////////////////////////
// CVfnPtrList
//////////////////////////////////////////////////////////////////////////
@ -488,6 +491,18 @@ namespace SourceHook
IHookContext *CSourceHookImpl::SetupHookLoop(IHookManagerInfo *hi, void *vfnptr, void *thisptr, void **origCallAddr, META_RES *statusPtr,
META_RES *prevResPtr, META_RES *curResPtr, const void *origRetPtr, void *overrideRetPtr)
{
SH_DEBUG_LOG(VERBOSE, "CSourceHookImpl(%p)::SetupHookLoop\n"
"- hi %p\n"
"- vfnptr %p\n"
"- thisptr %p\n"
"- origCallAddr %p\n"
"- statusPtr %p\n"
"- prevResPtr %p\n"
"- curResPtr %p\n"
"- origRetPtr %p\n"
"- overrideRetPtr %p",
(void*)this, (void*)hi, (void*)vfnptr, (void*)thisptr, (void*)origCallAddr, (void*)statusPtr, (void*)prevResPtr, (void*)curResPtr, (void*)origRetPtr, (void*)overrideRetPtr);
CHookContext *pCtx = NULL;
CHookContext *oldctx = m_ContextStack.empty() ? NULL : &m_ContextStack.front();
if (oldctx)
@ -568,6 +583,7 @@ namespace SourceHook
if (vfnptr_iter == vfnptr_list.end())
{
pCtx->m_State = CHookContext::State_Dead;
SH_DEBUG_LOG(VERBOSE, "CSourceHookImpl(%p)::SetupHookLoop\nThe hook is dead", this);
}
else
{
@ -696,6 +712,8 @@ namespace SourceHook
//////////////////////////////////////////////////////////////////////////
ISHDelegate *CHookContext::GetNext()
{
SH_DEBUG_LOG(VERBOSE, "CHookContext(%p)::GetNext\n- m_State %d\n",(void*)this, m_State);
CIface *pVPIface;
switch (m_State)
{
@ -753,6 +771,7 @@ namespace SourceHook
// end VP hooks -> orig call
m_State = State_OrigCall;
SH_DEBUG_LOG(VERBOSE, "CHookContext(%p)::GetNext\n- No more PRE handler!\n",(void*)this);
return NULL;
case State_OrigCall:
@ -801,6 +820,7 @@ namespace SourceHook
// end VP hooks -> done
m_State = State_Dead;
SH_DEBUG_LOG(VERBOSE, "CHookContext(%p)::GetNext\n- No more POST handler!\n",(void*)this);
return NULL;
case State_Recall_Post:
@ -813,6 +833,7 @@ namespace SourceHook
m_State = State_PostVP;
return NULL;
}
SH_DEBUG_LOG(VERBOSE, "CHookContext(%p)::GetNext\n- Unknown state we fell through!\n",(void*)this);
return NULL;
}

View File

@ -52,6 +52,7 @@
#endif
#ifdef SH_DEBUG
# include <stdio.h>
# include <stdlib.h>
@ -194,7 +195,7 @@ namespace SourceHook
// SH tries to auto-detect these
// If you want to override SH's auto-detection, pass them in yourself
PassFlag_RetMem = (1<<6), /**< Object is returned in memory (through hidden first param */
PassFlag_RetReg = (1<<7) /**< Object is returned in EAX(:EDX) */
PassFlag_RetReg = (1<<7) /**< Object is returned in EAX(:EDX)/RAX(x86_64) */
};
size_t size; //!< Size of the data being passed
@ -4928,4 +4929,4 @@ namespace SourceHook
}
#endif
// The pope is dead. -> :(
// The pope is dead. -> :(

View File

@ -12,6 +12,8 @@
#define __SOURCEHOOK_HOOKMANGEN_H__
#include "sh_pagealloc.h"
#include <list>
#include <memory>
namespace SourceHook
{
@ -38,6 +40,16 @@ namespace SourceHook
typedef unsigned int jitoffs_t;
typedef signed int jitrel_t;
class IGenContext
{
public:
virtual ~IGenContext() {};
virtual bool Equal(const CProto& proto, int vtbl_offs, int vtbl_idx) = 0;
virtual bool Equal(HookManagerPubFunc other) = 0;
virtual HookManagerPubFunc GetPubFunc() = 0;
};
class GenBuffer
{
@ -140,6 +152,9 @@ namespace SourceHook
void write_uint32(jit_uint32_t x) { push(x); }
void write_int32(jit_uint32_t x) { push(x); }
void write_uint64(jit_uint64_t x) { push(x); }
void write_int64(jit_int64_t x) { push(x); }
jitoffs_t get_outputpos()
{
return m_Size;
@ -155,7 +170,7 @@ namespace SourceHook
}
};
class GenContext
class GenContext : public IGenContext
{
const static int SIZE_MWORD = 4;
const static int SIZE_PTR = sizeof(void*);
@ -263,12 +278,12 @@ namespace SourceHook
public:
// Level 1 -> Public interface
GenContext(const ProtoInfo *proto, int vtbl_offs, int vtbl_idx, ISourceHook *pSHPtr);
~GenContext();
virtual ~GenContext();
bool Equal(const CProto &proto, int vtbl_offs, int vtbl_idx);
bool Equal(HookManagerPubFunc other);
virtual bool Equal(const CProto &proto, int vtbl_offs, int vtbl_idx) override;
virtual bool Equal(HookManagerPubFunc other) override;
HookManagerPubFunc GetPubFunc();
virtual HookManagerPubFunc GetPubFunc() override;
};
class CHookManagerAutoGen : public IHookManagerAutoGen
@ -276,9 +291,9 @@ namespace SourceHook
struct StoredContext
{
int m_RefCnt;
GenContext *m_GenContext;
std::unique_ptr<IGenContext> m_GenContext;
};
List<StoredContext> m_Contexts;
std::list<StoredContext> m_Contexts;
ISourceHook *m_pSHPtr;
public:

View File

@ -0,0 +1,546 @@
#include "sourcehook_pibuilder.h"
#include "sourcehook_hookmangen_test.h"
#include "metamod.h"
extern MetamodSource g_Metamod;
namespace SourceHook {
namespace Impl {
SourceHook::IHookManagerAutoGen* g_pHookManager = nullptr;
std::vector<SHT*> SHT::m_tests;
void run_tests() {
SH_DEBUG_LOG(TEST, "[SourceHook] Test Suite");
g_pHookManager = static_cast<IHookManagerAutoGen*>(g_Metamod.MetaFactory(MMIFACE_SH_HOOKMANAUTOGEN, NULL, NULL));
if (!g_SHPtr || !g_pHookManager) {
SH_DEBUG_LOG(TEST, "SourceHook is not initialised!");
return;
}
static std::uint32_t testIndex = 1;
for (auto lambdas : SHT::m_tests) {
try {
lambdas->Call();
SH_DEBUG_LOG(TEST, "TEST #%d \"%s\" - Passed", testIndex, lambdas->GetName().c_str());
}
catch (const SHTException& e) {
SH_DEBUG_LOG(TEST, "TEST #%d \"%s\" - Failed\nError: \"%s\"", testIndex, lambdas->GetName().c_str(), e.what());
}
catch (const std::exception& e) {
SH_DEBUG_LOG(TEST, "TEST #%d \"%s\" - Failed\nUnknown error: \"%s\"", testIndex, lambdas->GetName().c_str(), e.what());
}
catch (...) {
SH_DEBUG_LOG(TEST, "TEST #%d \"%s\" - Unknown exception during test", testIndex, lambdas->GetName().c_str());
}
testIndex++;
}
SH_DEBUG_LOG(TEST, "[SourceHook] Test Suite - Ended");
}
template<typename ReturnType, typename... Args>
class Function : public ISHDelegate {
public:
Function(CProtoInfoBuilder info, ReturnType (*virtualFunc)(void* thisPtr, Args... args), ReturnType (*hookFuncPre)(void* thisPtr, Args... args), ReturnType (*hookFuncPost)(void* thisPtr, Args... args)) :
m_virtualFunc(virtualFunc),
m_hookFuncPre(hookFuncPre),
m_hookFuncPost(hookFuncPost),
m_preCalled(false)
{
MemFuncInfo mfi = {false, -1, -1, -1};
GetFuncInfo(&Function<ReturnType, Args...>::Test, mfi);
if (!mfi.isVirtual || mfi.vtblindex <= 0) {
throw SHTException("Failed to initialise test class");
}
m_hookMan = g_pHookManager->MakeHookMan(info, mfi.vtbloffs, mfi.vtblindex);
m_hookIdPre = g_SHPtr->AddHook(g_PLID, ISourceHook::Hook_Normal, this, 0, m_hookMan, this, false);
m_hookIdPost = g_SHPtr->AddHook(g_PLID, ISourceHook::Hook_Normal, this, 0, m_hookMan, this, true);
SH_DEBUG_LOG(VERBOSE, "Setup Test Class %p - Vfnptr %p", this, (*(void***)this)[mfi.vtblindex]);
}
protected:
virtual bool IsEqual(ISHDelegate *pOtherDeleg) override { return false; };
virtual void DeleteThis() override { };
virtual ReturnType Call(Args... args) {
if (m_preCalled) {
return (ReturnType)m_hookFuncPost(this, args...);
}
else {
m_preCalled = true;
return (ReturnType)m_hookFuncPre(this, args...);
}
};
ReturnType (*m_virtualFunc)(void* thisPtr, Args... args);
ReturnType (*m_hookFuncPre)(void* thisPtr, Args... args);
ReturnType (*m_hookFuncPost)(void* thisPtr, Args... args);
HookManagerPubFunc m_hookMan;
int m_hookIdPre;
int m_hookIdPost;
bool m_preCalled;
public:
virtual ReturnType Test(Args... args) {
return (ReturnType)m_virtualFunc(this, args...);
};
};
void sht_assert(bool assert, const char* message) {
if (!assert) {
throw SHTException(message);
}
}
SHT test1("void_no_param", []() {
static bool g_called = false;
static bool g_pre_hook_called = false;
static bool g_post_hook_called = false;
static bool g_consistent_thisptr = true;
static void* testThisPtr = nullptr;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.SetReturnType(0, SourceHook::PassInfo::PassType_Unknown, 0, nullptr, nullptr, nullptr, nullptr);
auto test = new Function<void>(info,
[](void* thisPtr) {
// Virtual Function
g_called = true;
g_consistent_thisptr &= (thisPtr == testThisPtr);
},
[](void* thisPtr) {
// Pre Hook
g_pre_hook_called = true;
g_consistent_thisptr &= (thisPtr == testThisPtr);
RETURN_META(MRES_IGNORED);
},
[](void* thisPtr) {
// Post hook
g_post_hook_called = true;
g_consistent_thisptr &= (thisPtr == testThisPtr);
RETURN_META(MRES_IGNORED);
});
testThisPtr = test;
test->Test();
sht_assert(g_called, "Original function not called");
sht_assert(g_pre_hook_called, "Pre hook not called");
sht_assert(g_post_hook_called, "Post hook not called");
sht_assert(g_consistent_thisptr, "Inconsistent this ptr across hooks");
});
SHT test2("void_one_int_param", []() {
static bool g_called = false;
static bool g_pre_hook_called = false;
static bool g_post_hook_called = false;
static bool g_number_consistent = true;
static int g_test_number = 7680212;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.AddParam(sizeof(int), PassInfo::PassType_Basic, PassInfo::PassFlag_ByVal, nullptr, nullptr, nullptr, nullptr);
info.SetReturnType(0, PassInfo::PassType_Unknown, 0, nullptr, nullptr, nullptr, nullptr);
auto test = new Function<void, int>(info,
[](void* thisPtr, int param) {
// Virtual Function
g_called = true;
g_number_consistent &= (param == g_test_number);
},
[](void* thisPtr, int param) {
// Pre Hook
g_pre_hook_called = true;
g_number_consistent &= (param == g_test_number);
RETURN_META(MRES_IGNORED);
},
[](void* thisPtr, int param) {
// Post hook
g_post_hook_called = true;
g_number_consistent &= (param == g_test_number);
RETURN_META(MRES_IGNORED);
});
test->Test(g_test_number);
sht_assert(g_called, "Original function not called");
sht_assert(g_pre_hook_called, "Pre hook not called");
sht_assert(g_post_hook_called, "Post hook not called");
sht_assert(g_number_consistent, "Int parameter changed");
});
SHT test3("void_mres_supercede", []() {
static bool g_called = false;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.SetReturnType(0, PassInfo::PassType_Unknown, 0, nullptr, nullptr, nullptr, nullptr);
auto test = new Function<void>(info,
[](void* thisPtr) {
// Virtual Function
g_called = true;
},
[](void* thisPtr) {
// Pre Hook
RETURN_META(MRES_SUPERCEDE);
},
[](void* thisPtr) {
// Post hook
RETURN_META(MRES_IGNORED);
});
test->Test();
sht_assert(!g_called, "Original function was called");
});
SHT test4("return_mres_supercede", []() {
static bool g_called = false;
static bool g_valid_override = true;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.SetReturnType(sizeof(int), PassInfo::PassType_Basic, PassInfo::PassFlag_ByVal, nullptr, nullptr, nullptr, nullptr);
auto test = new Function<int>(info,
[](void* thisPtr) {
// Virtual Function
g_called = true;
return -1;
},
[](void* thisPtr) {
// Pre Hook
RETURN_META_VALUE(MRES_SUPERCEDE, 0);
},
[](void* thisPtr) {
// Post hook
int value = *static_cast<const int*>(g_SHPtr->GetOverrideRet());
g_valid_override &= (value == 0);
RETURN_META_VALUE(MRES_IGNORED, 0);
});
g_valid_override &= (test->Test() == 0);
sht_assert(!g_called, "Original function was called");
sht_assert(g_valid_override, "Return value wasn't not overridden");
});
SHT test5("void_mres_override", []() {
static bool g_called = false;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.SetReturnType(0, PassInfo::PassType_Unknown, 0, nullptr, nullptr, nullptr, nullptr);
auto test = new Function<void>(info,
[](void* thisPtr) {
// Virtual Function
g_called = true;
},
[](void* thisPtr) {
// Pre Hook
RETURN_META(MRES_OVERRIDE);
},
[](void* thisPtr) {
// Post hook
RETURN_META(MRES_IGNORED);
});
test->Test();
sht_assert(g_called, "Original function was not called");
});
SHT test6("void_mres_override", []() {
static bool g_called = false;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.SetReturnType(0, PassInfo::PassType_Unknown, 0, nullptr, nullptr, nullptr, nullptr);
auto test = new Function<void>(info,
[](void* thisPtr) {
// Virtual Function
g_called = true;
},
[](void* thisPtr) {
// Pre Hook
RETURN_META(MRES_IGNORED);
},
[](void* thisPtr) {
// Post hook
RETURN_META(MRES_OVERRIDE);
});
test->Test();
sht_assert(g_called, "Original function was not called");
});
SHT test7("void_mres_supercede_2", []() {
static bool g_called = false;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.SetReturnType(0, PassInfo::PassType_Unknown, 0, nullptr, nullptr, nullptr, nullptr);
auto test = new Function<void>(info,
[](void* thisPtr) {
// Virtual Function
g_called = true;
},
[](void* thisPtr) {
// Pre Hook
RETURN_META(MRES_IGNORED);
},
[](void* thisPtr) {
// Post hook
RETURN_META(MRES_SUPERCEDE);
});
test->Test();
sht_assert(g_called, "Original function was not called");
});
SHT test8("return_mres_supercede", []() {
static bool g_called = false;
static bool g_valid_override = true;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.SetReturnType(sizeof(int), PassInfo::PassType_Basic, PassInfo::PassFlag_ByVal, nullptr, nullptr, nullptr, nullptr);
auto test = new Function<int>(info,
[](void* thisPtr) {
// Virtual Function
g_called = true;
return -1;
},
[](void* thisPtr) {
// Pre Hook
RETURN_META_VALUE(MRES_OVERRIDE, 7);
},
[](void* thisPtr) {
// Post hook
int value = *static_cast<const int*>(g_SHPtr->GetOverrideRet());
g_valid_override &= (value == 7);
RETURN_META_VALUE(MRES_IGNORED, 5);
});
g_valid_override &= (test->Test() == 7);
sht_assert(g_called, "Original function was not called");
sht_assert(g_valid_override, "Return value wasn't not overridden");
});
SHT test9("return_vector_ref", []() {
static bool g_called = false;
static bool g_consistent_thisptr = true;
static void* testThisPtr = nullptr;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.SetReturnType(sizeof(SDKVector), PassInfo::PassType_Object, PassInfo::PassFlag_ByRef, nullptr, nullptr, nullptr, nullptr);
static SDKVector originalVector(1, 2, 3);
static SDKVector hookVector(4, 5, 6);
auto test = new Function<SDKVector&>(info,
[](void* thisPtr) -> SDKVector& {
// Virtual Function
g_called = true;
g_consistent_thisptr &= (thisPtr == testThisPtr);
return originalVector;
},
[](void* thisPtr) -> SDKVector& {
// Pre Hook
g_consistent_thisptr &= (thisPtr == testThisPtr);
RETURN_META_VALUE(MRES_IGNORED, hookVector);
},
[](void* thisPtr) -> SDKVector& {
// Post hook
g_consistent_thisptr &= (thisPtr == testThisPtr);
RETURN_META_VALUE(MRES_IGNORED, hookVector);
});
testThisPtr = test;
auto& vector = test->Test();
sht_assert(g_called, "Original function was not called");
sht_assert(g_consistent_thisptr, "The this ptr was not consistent");
sht_assert(&vector == &originalVector, "Original vector was not returned");
});
SHT test10("return_vector_ref_mres_supercede", []() {
static bool g_called = false;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.SetReturnType(sizeof(SDKVector), PassInfo::PassType_Object, PassInfo::PassFlag_ByRef, nullptr, nullptr, nullptr, nullptr);
static SDKVector originalVector(1, 2, 3);
static SDKVector hookVector(4, 5, 6);
auto test = new Function<SDKVector&>(info,
[](void* thisPtr) -> SDKVector& {
// Virtual Function
g_called = true;
return originalVector;
},
[](void* thisPtr) -> SDKVector& {
// Pre Hook
RETURN_META_VALUE(MRES_SUPERCEDE, hookVector);
},
[](void* thisPtr) -> SDKVector& {
// Post hook
RETURN_META_VALUE(MRES_IGNORED, originalVector);
});
auto& vector = test->Test();
sht_assert(!g_called, "Original function was called");
sht_assert(&vector == &hookVector, "Hook vector was not returned");
});
SHT test11("return_vector_ref_mres_supercede_2", []() {
static bool g_called = false;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.SetReturnType(sizeof(SDKVector), PassInfo::PassType_Object, PassInfo::PassFlag_ByRef, nullptr, nullptr, nullptr, nullptr);
static SDKVector originalVector(1, 2, 3);
static SDKVector hookVector(4, 5, 6);
auto test = new Function<SDKVector&>(info,
[](void* thisPtr) -> SDKVector& {
// Virtual Function
g_called = true;
return originalVector;
},
[](void* thisPtr) -> SDKVector& {
// Pre Hook
RETURN_META_VALUE(MRES_IGNORED, originalVector);
},
[](void* thisPtr) -> SDKVector& {
// Post hook
RETURN_META_VALUE(MRES_SUPERCEDE, hookVector);
});
auto& vector = test->Test();
sht_assert(g_called, "Original function was not called");
sht_assert(&vector == &hookVector, "Hook vector was not returned");
});
SHT test12("return_vector_ref_mres_override", []() {
static bool g_called = false;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.SetReturnType(sizeof(SDKVector), PassInfo::PassType_Object, PassInfo::PassFlag_ByRef, nullptr, nullptr, nullptr, nullptr);
static SDKVector originalVector(1, 2, 3);
static SDKVector hookVector(4, 5, 6);
auto test = new Function<SDKVector&>(info,
[](void* thisPtr) -> SDKVector& {
// Virtual Function
g_called = true;
return originalVector;
},
[](void* thisPtr) -> SDKVector& {
// Pre Hook
RETURN_META_VALUE(MRES_OVERRIDE, hookVector);
},
[](void* thisPtr) -> SDKVector& {
// Post hook
RETURN_META_VALUE(MRES_IGNORED, originalVector);
});
auto& vector = test->Test();
sht_assert(g_called, "Original function was not called");
sht_assert(&vector == &hookVector, "Hook vector was not returned");
});
SHT test13("return_vector_mres_override", []() {
static bool g_called = false;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.SetReturnType(sizeof(SDKVector), PassInfo::PassType_Object, PassInfo::PassFlag_ByVal, nullptr, nullptr, nullptr, nullptr);
static SDKVector originalVector(1, 2, 3);
static SDKVector hookVector(4, 5, 6);
auto test = new Function<SDKVector>(info,
[](void* thisPtr) -> SDKVector {
// Virtual Function
g_called = true;
return originalVector;
},
[](void* thisPtr) -> SDKVector {
// Pre Hook
RETURN_META_VALUE(MRES_OVERRIDE, hookVector);
},
[](void* thisPtr) -> SDKVector {
// Post hook
RETURN_META_VALUE(MRES_IGNORED, originalVector);
});
auto vector = test->Test();
sht_assert(g_called, "Original function was not called");
sht_assert(vector.x == 4.0 && vector.y == 5.0 && vector.z == 6.0, "Hook vector was not returned");
});
SHT test14("void_three_params_vector_int_vector", []() {
static bool g_called = false;
static bool g_hook_pre_called = false;
static bool g_hook_post_called = false;
static bool g_valid_arg[4] = { true, true, true, true };
static void* testThisPtr = nullptr;
CProtoInfoBuilder info(ProtoInfo::CallConv_ThisCall);
info.SetReturnType(0, PassInfo::PassType_Basic, PassInfo::PassFlag_ByVal, nullptr, nullptr, nullptr, nullptr);
info.AddParam(sizeof(SDKVector), PassInfo::PassType_Object, PassInfo::PassFlag_ByVal, nullptr, nullptr, nullptr, nullptr);
info.AddParam(sizeof(int), PassInfo::PassType_Basic, PassInfo::PassFlag_ByVal, nullptr, nullptr, nullptr, nullptr);
info.AddParam(sizeof(SDKVector), PassInfo::PassType_Object, PassInfo::PassFlag_ByVal, nullptr, nullptr, nullptr, nullptr);
auto test = new Function<void, SDKVector, int, SDKVector>(info,
[](void* thisPtr, SDKVector vec1, int arg2, SDKVector vec2) {
// Virtual Function
g_called = true;
g_valid_arg[0] &= (vec1.x == 1.0 && vec1.y == 2.0 && vec1.z == 3.0);
g_valid_arg[1] &= (arg2 == 777);
g_valid_arg[2] &= (vec2.x == 4.0 && vec2.y == 5.0 && vec2.z == 6.0);
g_valid_arg[3] &= (testThisPtr == thisPtr);
},
[](void* thisPtr, SDKVector vec1, int arg2, SDKVector vec2) {
// Pre Hook
g_valid_arg[0] &= (vec1.x == 1.0 && vec1.y == 2.0 && vec1.z == 3.0);
g_valid_arg[1] &= (arg2 == 777);
g_valid_arg[2] &= (vec2.x == 4.0 && vec2.y == 5.0 && vec2.z == 6.0);
g_valid_arg[3] &= (testThisPtr == thisPtr);
g_hook_pre_called = true;
RETURN_META(MRES_IGNORED);
},
[](void* thisPtr, SDKVector vec1, int arg2, SDKVector vec2) {
// Post hook
g_valid_arg[0] &= (vec1.x == 1.0 && vec1.y == 2.0 && vec1.z == 3.0);
g_valid_arg[1] &= (arg2 == 777);
g_valid_arg[2] &= (vec2.x == 4.0 && vec2.y == 5.0 && vec2.z == 6.0);
g_valid_arg[3] &= (testThisPtr == thisPtr);
g_hook_post_called = true;
RETURN_META(MRES_IGNORED);
});
testThisPtr = test;
test->Test(SDKVector(1, 2, 3), 777, SDKVector(4, 5, 6));
sht_assert(g_called, "Original function was not called");
sht_assert(g_hook_pre_called, "Pre hook was not called");
sht_assert(g_hook_pre_called, "Post hook was not called");
sht_assert(g_valid_arg[3], "This ptr was not consistent");
sht_assert(g_valid_arg[0] && g_valid_arg[1] && g_valid_arg[2], "Arguments were not valid");
});
}
}

View File

@ -0,0 +1,57 @@
#pragma once
#include "sourcehook_impl.h"
#include "sourcehook_hookmangen.h"
#include <vector>
#include <string>
#include <ISmmPluginExt.h>
extern SourceHook::ISourceHook* g_SHPtr;
extern SourceMM::PluginId g_PLID;
namespace SourceHook {
namespace Impl {
void run_tests();
class SDKVector
{
public:
SDKVector(float x1, float y1, float z1)
{
this->x = x1;
this->y = y1;
this->z = z1;
}
SDKVector(void)
{
this->x = 0.0;
this->y = 0.0;
this->z = 0.0;
}
float x;
float y;
float z;
};
class SHTException : public std::exception {
public:
SHTException(const char* message) : std::exception(message) {};
};
class SHT {
public:
SHT(const char* name, void (*func)(void)) noexcept : m_function(func), m_name(name) {
m_tests.push_back(this);
};
void Call() { m_function(); }
const std::string& GetName() { return m_name; }
static std::vector<SHT*> m_tests;
private:
void (*m_function)(void);
std::string m_name;
};
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,666 @@
/* ======== SourceHook ========
* vim: set ts=4 :
* Copyright (C) 2024 AlliedModders LLC. All rights reserved.
* No warranties of any kind
*
* License: zlib/libpng
*
* ============================
*/
#ifndef __SOURCEHOOK_HOOKMANGEN_X86_64_H__
#define __SOURCEHOOK_HOOKMANGEN_X86_64_H__
#include <cstdint>
#include <limits.h>
namespace SourceHook
{
namespace Impl
{
enum x8664Reg : std::uint8_t {
RAX = 0,
RCX = 1,
RDX = 2,
RBX = 3,
RSP = 4,
RBP = 5,
RSI = 6,
RDI = 7,
R8 = 8,
R9 = 9,
R10 = 10,
R11 = 11,
R12 = 12,
R13 = 13,
R14 = 14,
R15 = 15
};
enum x8664FloatReg : std::uint8_t {
XMM0 = 0,
XMM1 = 1,
XMM2 = 2,
XMM3 = 3,
XMM4 = 4,
XMM5 = 5,
XMM6 = 6,
XMM7 = 7,
XMM8 = 8,
XMM9 = 9,
XMM10 = 10,
XMM11 = 11,
XMM12 = 12,
XMM13 = 13,
XMM14 = 14,
XMM15 = 15
};
class x86_64_FloatReg {
public:
constexpr x86_64_FloatReg(x8664FloatReg op) : code(op) { }
constexpr bool operator==(x86_64_FloatReg a) const { return code == a.code; }
constexpr bool operator!=(x86_64_FloatReg a) const { return code != a.code; }
constexpr bool extended() const { return ((code & 0x8) == 0x8); }
constexpr std::uint8_t low() const { return code & 0x7; }
constexpr operator x8664FloatReg() const { return code; }
protected:
x8664FloatReg code;
};
static const x86_64_FloatReg xmm0 = { x8664FloatReg::XMM0 };
static const x86_64_FloatReg xmm1 = { x8664FloatReg::XMM1 };
static const x86_64_FloatReg xmm2 = { x8664FloatReg::XMM2 };
static const x86_64_FloatReg xmm3 = { x8664FloatReg::XMM3 };
static const x86_64_FloatReg xmm4 = { x8664FloatReg::XMM4 };
static const x86_64_FloatReg xmm5 = { x8664FloatReg::XMM5 };
static const x86_64_FloatReg xmm6 = { x8664FloatReg::XMM6 };
static const x86_64_FloatReg xmm7 = { x8664FloatReg::XMM7 };
static const x86_64_FloatReg xmm8 = { x8664FloatReg::XMM8 };
static const x86_64_FloatReg xmm9 = { x8664FloatReg::XMM9 };
static const x86_64_FloatReg xmm10 = { x8664FloatReg::XMM10 };
static const x86_64_FloatReg xmm11 = { x8664FloatReg::XMM11 };
static const x86_64_FloatReg xmm12 = { x8664FloatReg::XMM12 };
static const x86_64_FloatReg xmm13 = { x8664FloatReg::XMM13 };
static const x86_64_FloatReg xmm14 = { x8664FloatReg::XMM14 };
static const x86_64_FloatReg xmm15 = { x8664FloatReg::XMM15 };
enum MOD_MODRM : std::uint8_t {
DISP0 = 0b00,
DISP8 = 0b01,
DISP32 = 0b10,
REG = 0b11
};
class x86_64_RegRm {
public:
friend class x86_64_Reg;
inline std::uint8_t sib() {
// For the time being, we don't support multiple register
return (0 << 6) | (this->low() << 3) | this->low();
}
inline std::uint8_t modrm(x8664Reg reg) {
return (mod << 6) | ((reg & 0x7) << 3) | this->low();
}
inline std::uint8_t modrm(x8664FloatReg reg) {
return (mod << 6) | ((reg & 0x7) << 3) | this->low();
}
inline std::uint8_t modrm() {
return (mod << 6) | (0x0 << 3) | this->low();
}
void write_modrm(GenBuffer* buffer);
void write_modrm(GenBuffer* buffer, x8664Reg op);
void write_modrm(GenBuffer* buffer, x8664FloatReg op);
bool extended() const { return ((rm & 0x8) == 0x8); }
std::uint8_t low() const { return rm & 0x7; }
constexpr operator x8664Reg() const { return rm; }
protected:
x86_64_RegRm(x8664Reg reg, std::int32_t disp);
x86_64_RegRm(x8664Reg reg);
void Setup();
x8664Reg rm;
std::int32_t disp;
MOD_MODRM mod;
};
class x86_64_Reg {
public:
constexpr x86_64_Reg(x8664Reg op) : code(op) { }
constexpr bool operator==(x86_64_Reg a) const { return code == a.code; }
constexpr bool operator!=(x86_64_Reg a) const { return code != a.code; }
x86_64_RegRm operator()() const { return x86_64_RegRm(*this, 0); }
x86_64_RegRm operator()(std::int32_t disp) const { return x86_64_RegRm(*this, disp); }
constexpr bool extended() const { return ((code & 0x8) == 0x8); }
constexpr std::uint8_t low() const { return code & 0x7; }
constexpr operator x8664Reg() const { return code; }
protected:
x8664Reg code;
};
static const x86_64_Reg rax = { x8664Reg::RAX };
static const x86_64_Reg rcx = { x8664Reg::RCX };
static const x86_64_Reg rdx = { x8664Reg::RDX };
static const x86_64_Reg rbx = { x8664Reg::RBX };
static const x86_64_Reg rsp = { x8664Reg::RSP };
static const x86_64_Reg rbp = { x8664Reg::RBP };
static const x86_64_Reg rsi = { x8664Reg::RSI };
static const x86_64_Reg rdi = { x8664Reg::RDI };
static const x86_64_Reg r8 = { x8664Reg::R8 };
static const x86_64_Reg r9 = { x8664Reg::R9 };
static const x86_64_Reg r10 = { x8664Reg::R10 };
static const x86_64_Reg r11 = { x8664Reg::R11 };
static const x86_64_Reg r12 = { x8664Reg::R12 };
static const x86_64_Reg r13 = { x8664Reg::R13 };
static const x86_64_Reg r14 = { x8664Reg::R14 };
static const x86_64_Reg r15 = { x8664Reg::R15 };
enum REX : std::uint8_t {
BASE = 0x40,
B = 0x41,
X = 0x42,
XB = 0x43,
R = 0x44,
RB = 0x45,
RX = 0x46,
RXB = 0x47,
W = 0x48,
WB = 0x49,
WX = 0x4A,
WXB = 0x4B,
WR = 0x4C,
WRB = 0x4D,
WRX = 0x4E,
WRXB = 0x4F
};
constexpr inline std::uint8_t w_rex(x8664Reg reg, x8664Reg rm) {
return REX::W | ((static_cast<std::int8_t>((reg & 0x8) == 0x8) << 2) | static_cast<std::int8_t>((rm & 0x8) == 0x8));
}
constexpr inline std::uint8_t w_rex(x8664FloatReg reg, x8664Reg rm) {
return REX::W | ((static_cast<std::int8_t>((reg & 0x8) == 0x8) << 2) | static_cast<std::int8_t>((rm & 0x8) == 0x8));
}
constexpr inline std::uint8_t w_rex(x8664Reg reg, x8664FloatReg rm) {
return REX::W | ((static_cast<std::int8_t>((reg & 0x8) == 0x8) << 2) | static_cast<std::int8_t>((rm & 0x8) == 0x8));
}
constexpr inline std::uint8_t modrm(x8664Reg reg, x8664Reg rm) {
return (MOD_MODRM::REG << 6) | ((reg & 0x7) << 3) | (rm & 0x7);
}
constexpr inline std::uint8_t modrm_rm(x8664Reg rm, std::uint8_t base) {
return (MOD_MODRM::REG << 6) | (base << 3) | (rm & 0x7);
}
x86_64_RegRm::x86_64_RegRm(x8664Reg reg, std::int32_t disp) : rm(reg), disp(disp) {
Setup();
}
x86_64_RegRm::x86_64_RegRm(x8664Reg reg) : rm(reg), disp(0) {
Setup();
}
void x86_64_RegRm::Setup() {
if (disp == 0 && rm != x8664Reg::RBP && rm != x8664Reg::R13) {
mod = DISP0;
}
else if (disp >= SCHAR_MIN && disp <= SCHAR_MAX) {
mod = DISP8;
} else {
mod = DISP32;
}
}
void x86_64_RegRm::write_modrm(GenBuffer* buffer) {
// modrm
buffer->write_ubyte(modrm());
// Special register we need a sib byte
if (rm == x8664Reg::RSP || rm == x8664Reg::R12) { // rsp/r12
buffer->write_ubyte(sib());
}
// Special disp mod
if (mod != DISP0) {
if (mod == DISP8) {
buffer->write_byte(disp);
} else if (mod == DISP32) {
buffer->write_int32(disp);
}
}
}
void x86_64_RegRm::write_modrm(GenBuffer* buffer, x8664Reg reg) {
// modrm
buffer->write_ubyte(modrm(reg));
// Special register we need a sib byte
if (rm == x8664Reg::RSP || rm == x8664Reg::R12) { // rsp/r12
buffer->write_ubyte(sib());
}
// Special disp mod
if (mod != DISP0) {
if (mod == DISP8) {
buffer->write_byte(disp);
} else if (mod == DISP32) {
buffer->write_int32(disp);
}
}
}
void x86_64_RegRm::write_modrm(GenBuffer* buffer, x8664FloatReg reg) {
// modrm
buffer->write_ubyte(modrm(reg));
// Special register we need a sib byte
if (rm == x8664Reg::RSP || rm == x8664Reg::R12) { // rsp/r12
buffer->write_ubyte(sib());
}
// Special disp mod
if (mod != DISP0) {
if (mod == DISP8) {
buffer->write_byte(disp);
} else if (mod == DISP32) {
buffer->write_int32(disp);
}
}
}
class x64JitWriter : public GenBuffer {
public:
void breakpoint() {
this->write_ubyte(0xCC);
}
void rep_movs_bytes() {
this->write_ubyte(0xF3);
this->write_ubyte(0x48);
this->write_ubyte(0xA4);
}
void call(x86_64_Reg reg) {
if (reg.extended()) {
this->write_ubyte(REX::B);
}
this->write_ubyte(0xFF);
this->write_ubyte(0xD0 + reg.low());
}
void jump(x86_64_Reg reg) {
if (reg.extended()) {
this->write_ubyte(REX::B);
}
this->write_ubyte(0xFF);
this->write_ubyte(0xE0 + reg.low());
}
void jump(std::int32_t off) {
this->write_ubyte(0xE9);
this->write_int32(off);
}
void jz(std::int32_t off) {
this->write_ubyte(0x0F);
this->write_ubyte(0x84);
this->write_int32(off);
}
void jl(std::int32_t off) {
this->write_ubyte(0x0F);
this->write_ubyte(0x8C);
this->write_int32(off);
}
void jle(std::int32_t off) {
this->write_ubyte(0x0F);
this->write_ubyte(0x8E);
this->write_int32(off);
}
void je(std::int32_t off) {
this->write_ubyte(0x0F);
this->write_ubyte(0x84);
this->write_int32(off);
}
void jne(std::int32_t off) {
this->write_ubyte(0x0F);
this->write_ubyte(0x85);
this->write_int32(off);
}
void push(x86_64_Reg reg) {
if (reg.extended()) {
this->write_ubyte(REX::B);
}
this->write_ubyte(0x50 + reg.low());
}
void push(std::int32_t val) {
if (val >= SCHAR_MIN && val <= SCHAR_MAX) {
this->write_ubyte(0x6A);
this->write_byte(std::int8_t(val));
} else {
this->write_ubyte(0x68);
this->write_int32(val);
}
}
void pop(x86_64_Reg reg) {
if (reg.extended()) {
this->write_ubyte(REX::B);
}
this->write_ubyte(0x58 + reg.low());
}
// mov_rm
void mov(x86_64_Reg dst, x86_64_Reg src) {
this->write_ubyte(w_rex(src, dst));
this->write_ubyte(0x89);
this->write_ubyte(modrm(src, dst));
}
void mov(x86_64_RegRm rm, x86_64_Reg reg) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x89);
rm.write_modrm(this, reg);
}
void mov(x86_64_Reg reg, x86_64_RegRm rm) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x8B);
rm.write_modrm(this, reg);
}
void mov(x86_64_Reg dst, std::int32_t imm) {
if (dst.extended()) {
this->write_ubyte(REX::B);
}
this->write_ubyte(0xB8 + dst.low());
this->write_int32(imm);
}
void mov(x86_64_RegRm dst, std::int32_t imm) {
if (dst.extended()) {
this->write_ubyte(REX::WB);
} else {
this->write_ubyte(REX::W);
}
this->write_ubyte(0xC7);
dst.write_modrm(this);
this->write_int32(imm);
}
void mov(x86_64_Reg dst, std::uint64_t imm) {
if (imm <= UINT32_MAX) {
this->mov(dst, std::int32_t(imm));
return;
}
if (dst.extended()) {
this->write_ubyte(REX::WB);
} else {
this->write_ubyte(REX::W);
}
this->write_ubyte(0xB8 + dst.low());
this->write_uint64(imm);
}
void movsd(x86_64_FloatReg reg, x86_64_RegRm rm) {
this->write_ubyte(0xF2);
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x0F);
this->write_ubyte(0x10);
rm.write_modrm(this, reg);
}
void movsd(x86_64_RegRm rm, x86_64_FloatReg reg) {
this->write_ubyte(0xF2);
if (reg.extended() || rm.extended()) {
this->write_ubyte(w_rex(reg, rm));
}
this->write_ubyte(0x0F);
this->write_ubyte(0x11);
rm.write_modrm(this, reg);
}
void add(x86_64_Reg dst, x86_64_Reg src) {
this->write_ubyte(w_rex(src, dst));
this->write_ubyte(0x01);
this->write_ubyte(modrm(src, dst));
}
void add(x86_64_Reg dst, int32_t imm) {
if (dst.extended()) {
this->write_ubyte(REX::WB);
} else {
this->write_ubyte(REX::W);
}
this->write_ubyte(0x81);
this->write_ubyte(modrm_rm(dst, 0));
this->write_int32(imm);
}
void sub(x86_64_Reg dst, x86_64_Reg src) {
this->write_ubyte(w_rex(src, dst));
this->write_ubyte(0x29);
this->write_ubyte(modrm(src, dst));
}
void sub(x86_64_Reg dst, int32_t imm) {
if (dst.extended()) {
this->write_ubyte(REX::WB);
} else {
this->write_ubyte(REX::W);
}
this->write_ubyte(0x81);
this->write_ubyte(modrm_rm(dst, 5));
this->write_int32(imm);
}
void xor(x86_64_Reg dst, x86_64_Reg src) {
this->write_ubyte(w_rex(src, dst));
this->write_ubyte(0x31);
this->write_ubyte(modrm(src, dst));
}
void test(x86_64_Reg dst, x86_64_Reg src) {
this->write_ubyte(w_rex(src, dst));
this->write_ubyte(0x85);
this->write_ubyte(modrm(src, dst));
}
void test(x86_64_Reg reg, int32_t imm) {
if (reg.extended()) {
this->write_ubyte(REX::WB);
} else {
this->write_ubyte(REX::W);
}
this->write_ubyte(0xF7);
this->write_ubyte(modrm_rm(reg, 0));
this->write_int32(imm);
}
void cmovne(x86_64_Reg reg, x86_64_RegRm rm) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x0F);
this->write_ubyte(0x45);
rm.write_modrm(this, reg);
}
void cmovne(x86_64_Reg reg, x86_64_Reg rm) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x0F);
this->write_ubyte(0x45);
this->write_ubyte(modrm(reg, rm));
}
void cmovnz(x86_64_Reg reg, x86_64_RegRm rm) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x0F);
this->write_ubyte(0x45);
rm.write_modrm(this, reg);
}
void cmovnz(x86_64_Reg reg, x86_64_Reg rm) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x0F);
this->write_ubyte(0x45);
this->write_ubyte(modrm(reg, rm));
}
void cmovge(x86_64_Reg reg, x86_64_RegRm rm) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x0F);
this->write_ubyte(0x4D);
rm.write_modrm(this, reg);
}
void cmovge(x86_64_Reg reg, x86_64_Reg rm) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x0F);
this->write_ubyte(0x4D);
this->write_ubyte(modrm(reg, rm));
}
void cmovg(x86_64_Reg reg, x86_64_RegRm rm) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x0F);
this->write_ubyte(0x4F);
rm.write_modrm(this, reg);
}
void cmovg(x86_64_Reg reg, x86_64_Reg rm) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x0F);
this->write_ubyte(0x4F);
this->write_ubyte(modrm(reg, rm));
}
void lea(x86_64_Reg reg, x86_64_RegRm rm) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x8D);
rm.write_modrm(this, reg);
}
void cmp(x86_64_Reg reg, x86_64_RegRm rm) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x3B);
rm.write_modrm(this, reg);
}
void cmp(x86_64_RegRm rm, x86_64_Reg reg) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x39);
rm.write_modrm(this, reg);
}
void cmp(x86_64_Reg reg, x86_64_Reg rm) {
this->write_ubyte(w_rex(reg, rm));
this->write_ubyte(0x3B);
this->write_ubyte(modrm(reg, rm));
}
void cmp(x86_64_Reg dst, int32_t imm) {
if (dst.extended()) {
this->write_ubyte(REX::WB);
} else {
this->write_ubyte(REX::W);
}
this->write_ubyte(0x81);
this->write_ubyte(modrm_rm(dst, 7));
this->write_int32(imm);
}
void retn() {
this->write_ubyte(0xC3);
}
};
class x64GenContext : public IGenContext
{
public:
x64GenContext();
x64GenContext(const ProtoInfo *proto, int vtbl_offs, int vtbl_idx, ISourceHook *pSHPtr);
virtual ~x64GenContext();
virtual bool Equal(const CProto &proto, int vtbl_offs, int vtbl_idx) override;
virtual bool Equal(HookManagerPubFunc other) override;
virtual HookManagerPubFunc GetPubFunc() override;
HookManagerPubFunc Generate();
protected:
friend void foo_test();
static const std::int32_t SIZE_PTR = sizeof(void*);
std::int32_t AddVarToFrame(std::int32_t size);
std::int32_t ComputeVarsSize();
std::int32_t x64GenContext::GetRealSize(const IntPassInfo& info);
std::int32_t AlignSize(std::int32_t x, std::int32_t boundary);
std::int32_t GetParamStackSize(const IntPassInfo &info);
void Clear();
void AutoDetectRetType();
void AutoDetectParamFlags();
bool PassInfoSupported(const IntPassInfo& pi, bool is_ret);
void BuildProtoInfo();
bool MemRetWithTempObj();
void* GeneratePubFunc();
void* GenerateHookFunc();
void 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);
void GenerateCallHooks(int v_status, int v_prev_res, int v_cur_res, int v_iter,
int v_pContext, int v_plugin_ret, int v_mem_ret);
void GenerateCallOrig(int v_status, int v_pContext, int v_this, int v_vfnptr_origentry, int v_orig_ret, int v_override_ret, int v_place_for_memret);
void PrepareReturn(int v_status, int v_pContext, int v_retptr);
void CallEndContext(int v_pContext);
void DoReturn(int v_retptr, int v_memret_outaddr);
std::int32_t PushParameters(int v_this, int v_ret);
void SaveReturnValue(int v_mem_ret, int v_ret);
HookManagerPubFunc m_GeneratedPubFunc;
CProto m_OrigProto;
CProto m_Proto;
int m_VtblOffs;
int m_VtblIdx;
ISourceHook *m_SHPtr;
x64JitWriter m_HookFunc;
x64JitWriter m_PubFunc;
ProtoInfo *m_BuiltPI;
PassInfo *m_BuiltPI_Params;
PassInfo::V2Info *m_BuiltPI_Params2;
void **m_pHI;
void **m_HookfuncVfnptr;
std::int32_t m_HookFunc_FrameOffset;
std::int32_t m_HookFunc_FrameVarsSize;
};
}
}
#endif //__SOURCEHOOK_HOOKMANGEN_X86_64_H__

View File

@ -179,6 +179,9 @@ New SH_CALL
#include "sourcehook_impl_ciface.h"
#include "sourcehook_impl_cvfnptr.h"
#include "sourcehook_impl_chookidman.h"
#include <stdarg.h>
void mm_LogMessage(const char* msg, ...);
namespace SourceHook
{
@ -188,6 +191,24 @@ namespace SourceHook
namespace Impl
{
enum SH_LOG {
VERBOSE,
NORMAL,
TEST,
NONE,
};
extern SH_LOG sh_log_level;
template<typename... Args>
inline void SH_DEBUG_LOG(SH_LOG log_level, const char* message, Args... args)
{
if (log_level < sh_log_level) {
return;
}
mm_LogMessage(message, args...);
}
struct CHookContext : IHookContext
{
CHookContext() : m_CleanupTask(NULL)

View File

@ -39,6 +39,13 @@ namespace SourceHook
void CHookManager::Register()
{
SH_DEBUG_LOG(VERBOSE, "CHookManager(%p)::SetInfo\n"
"- hookman_version %d\n"
"- vtbloffs %d\n"
"- vtblidx %d\n"
"- hookfunc_vfnptr %p\n\n",
(void*)this, m_Version, m_VtblOffs, m_VtblIdx, m_HookfuncVfnptr);
m_PubFunc(true, this);
}
@ -49,6 +56,9 @@ namespace SourceHook
void CHookManager::IncrRef(CVfnPtr *pVfnPtr)
{
SH_DEBUG_LOG(VERBOSE, "CHookManager(%p)::IncrRef\n",
(void*)this);
m_VfnPtrs.push_back(pVfnPtr);
if (m_VfnPtrs.size() == 1)
Register();
@ -56,6 +66,9 @@ namespace SourceHook
void CHookManager::DecrRef(CVfnPtr *pVfnPtr)
{
SH_DEBUG_LOG(VERBOSE, "CHookManager(%p)::DecrRef\n",
(void*)this);
m_VfnPtrs.remove(pVfnPtr);
if (m_VfnPtrs.empty())
Unregister();