mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2025-02-20 13:54:14 +01:00
add x64 stub generator support
This commit is contained in:
parent
f5f14786b9
commit
222c174f0d
@ -178,7 +178,8 @@ class MMSConfig(object):
|
||||
]
|
||||
cxx.cflags += [
|
||||
'/W3',
|
||||
'/Zi',
|
||||
'/Z7',
|
||||
'/EHsc',
|
||||
'/std:c++17',
|
||||
]
|
||||
cxx.cxxflags += ['/TP']
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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. -> :(
|
@ -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:
|
||||
|
546
core/sourcehook/sourcehook_hookmangen_test.cpp
Normal file
546
core/sourcehook/sourcehook_hookmangen_test.cpp
Normal 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");
|
||||
});
|
||||
}
|
||||
}
|
57
core/sourcehook/sourcehook_hookmangen_test.h
Normal file
57
core/sourcehook/sourcehook_hookmangen_test.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
1488
core/sourcehook/sourcehook_hookmangen_x86_64.cpp
Normal file
1488
core/sourcehook/sourcehook_hookmangen_x86_64.cpp
Normal file
@ -0,0 +1,1488 @@
|
||||
/* ======== SourceHook ========
|
||||
* Copyright (C) 2024 Metamod:Source Development Team
|
||||
* No warranties of any kind
|
||||
*
|
||||
* License: zlib/libpng
|
||||
*
|
||||
* Author(s): Andre "Kenzzer" Benoist
|
||||
* ============================
|
||||
*/
|
||||
|
||||
// recommended literature:
|
||||
// https://www.felixcloutier.com/x86/
|
||||
// http://ref.x86asm.net/coder64.html
|
||||
// https://defuse.ca/online-x86-assembler.htm
|
||||
// https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention
|
||||
// https://refspecs.linuxbase.orgz/elf/x86_64-abi-0.99.pdf
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include "sourcehook_impl.h"
|
||||
#include "sourcehook_hookmangen.h"
|
||||
#include "sourcehook_hookmangen_x86_64.h"
|
||||
#include "sourcehook_pibuilder.h"
|
||||
|
||||
#include "metamod_oslink.h"
|
||||
#include "metamod.h"
|
||||
#include <interface.h>
|
||||
#include <eiface.h>
|
||||
#include <metamod_version.h>
|
||||
#include <metamod_provider.h>
|
||||
|
||||
extern SourceHook::ISourceHook *g_SHPtr;
|
||||
extern SourceMM::IMetamodSourceProvider *provider;
|
||||
|
||||
#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
|
||||
|
||||
namespace SourceHook
|
||||
{
|
||||
namespace Impl
|
||||
{
|
||||
void PrintDebug(x64JitWriter& jit, const char* message) {
|
||||
static MemFuncInfo mfi = {false, -1, -1, -1};
|
||||
if (mfi.vtblindex == -1)
|
||||
{
|
||||
GetFuncInfo(&SourceMM::IMetamodSourceProvider::ConsolePrint, mfi);
|
||||
if (!mfi.isVirtual || mfi.thisptroffs != 0 || mfi.vtbloffs != 0 || mfi.vtblindex < 0)
|
||||
{
|
||||
mfi.vtblindex = -1;
|
||||
SH_ASSERT(0, ("Couldn't retrieve details of SourceMM::IMetamodSourceProvider::ConsolePrint!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static MemFuncInfo mfi2 = {false, -1, -1, -1};
|
||||
if (mfi2.vtblindex == -1)
|
||||
{
|
||||
GetFuncInfo(&SourceMM::IMetamodSourceProvider::LogMessage, mfi2);
|
||||
if (!mfi2.isVirtual || mfi2.thisptroffs != 0 || mfi2.vtbloffs != 0 || mfi2.vtblindex < 0)
|
||||
{
|
||||
mfi2.vtblindex = -1;
|
||||
SH_ASSERT(0, ("Couldn't retrieve details of SourceMM::IMetamodSourceProvider::ConsolePrint!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Shadow space
|
||||
MSVC_ONLY(jit.sub(rsp, 40));
|
||||
|
||||
MSVC_ONLY(jit.mov(rcx, reinterpret_cast<std::uint64_t>(provider)));
|
||||
GCC_ONLY(jit.mov(rdi, reinterpret_cast<std::uint64_t>(provider)));
|
||||
|
||||
MSVC_ONLY(jit.mov(rdx, reinterpret_cast<std::uint64_t>(message)));
|
||||
GCC_ONLY(jit.mov(rsi, reinterpret_cast<std::uint64_t>(message)));
|
||||
|
||||
jit.mov(rax, reinterpret_cast<std::uint64_t>(provider));
|
||||
jit.mov(rax, rax(mfi.vtbloffs));
|
||||
jit.mov(rax, rax(sizeof(void*) * mfi.vtblindex));
|
||||
jit.call(rax);
|
||||
|
||||
MSVC_ONLY(jit.mov(rcx, reinterpret_cast<std::uint64_t>(provider)));
|
||||
GCC_ONLY(jit.mov(rdi, reinterpret_cast<std::uint64_t>(provider)));
|
||||
|
||||
MSVC_ONLY(jit.mov(rdx, reinterpret_cast<std::uint64_t>(message)));
|
||||
GCC_ONLY(jit.mov(rsi, reinterpret_cast<std::uint64_t>(message)));
|
||||
|
||||
jit.mov(rax, reinterpret_cast<std::uint64_t>(provider));
|
||||
jit.mov(rax, rax(mfi2.vtbloffs));
|
||||
jit.mov(rax, rax(sizeof(void*) * mfi2.vtblindex));
|
||||
jit.call(rax);
|
||||
|
||||
// Free shadow space
|
||||
MSVC_ONLY(jit.add(rsp, 40));
|
||||
}
|
||||
|
||||
CPageAlloc GenBuffer::ms_Allocator(16);
|
||||
|
||||
x64GenContext::x64GenContext()
|
||||
: m_GeneratedPubFunc(nullptr), m_VtblOffs(0),
|
||||
m_VtblIdx(666), m_SHPtr((ISourceHook*)0x1122334455667788), m_pHI(nullptr), m_HookfuncVfnptr(nullptr), m_HookFunc_FrameOffset(0), m_HookFunc_FrameVarsSize(0) {
|
||||
m_pHI = new void*;
|
||||
*m_pHI = (void*)0x77777777;
|
||||
m_HookfuncVfnptr = new void*;
|
||||
m_BuiltPI = new ProtoInfo;
|
||||
m_BuiltPI_Params = nullptr;
|
||||
m_BuiltPI_Params2 = nullptr;
|
||||
}
|
||||
|
||||
x64GenContext::x64GenContext(const ProtoInfo *proto, int vtbl_offs, int vtbl_idx, ISourceHook *pSHPtr)
|
||||
: m_GeneratedPubFunc(nullptr), m_OrigProto(proto), m_Proto(proto), m_VtblOffs(vtbl_offs),
|
||||
m_VtblIdx(vtbl_idx), m_SHPtr(pSHPtr), m_pHI(nullptr), m_HookfuncVfnptr(nullptr), m_HookFunc_FrameOffset(0), m_HookFunc_FrameVarsSize(0)
|
||||
{
|
||||
m_pHI = new void*;
|
||||
*m_pHI = (void*)0x77777777; // Magic number for debugging
|
||||
m_HookfuncVfnptr = new void*;
|
||||
m_BuiltPI = new ProtoInfo;
|
||||
m_BuiltPI_Params = nullptr;
|
||||
m_BuiltPI_Params2 = nullptr;
|
||||
}
|
||||
|
||||
x64GenContext::~x64GenContext()
|
||||
{
|
||||
//Clear();
|
||||
delete m_pHI;
|
||||
delete m_HookfuncVfnptr;
|
||||
delete m_BuiltPI;
|
||||
}
|
||||
|
||||
void x64GenContext::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 x64GenContext::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;
|
||||
}
|
||||
|
||||
std::int32_t x64GenContext::AddVarToFrame(std::int32_t size)
|
||||
{
|
||||
m_HookFunc_FrameOffset -= size;
|
||||
m_HookFunc_FrameVarsSize += size;
|
||||
return m_HookFunc_FrameOffset;
|
||||
}
|
||||
|
||||
std::int32_t x64GenContext::ComputeVarsSize()
|
||||
{
|
||||
return m_HookFunc_FrameVarsSize;
|
||||
}
|
||||
|
||||
std::int32_t x64GenContext::GetRealSize(const IntPassInfo& info)
|
||||
{
|
||||
if ((info.flags & PassInfo::PassFlag_ByRef) == PassInfo::PassFlag_ByRef) {
|
||||
return SIZE_PTR;
|
||||
}
|
||||
return static_cast<std::int32_t>(info.size);
|
||||
}
|
||||
|
||||
std::int32_t x64GenContext::AlignSize(std::int32_t x, std::int32_t boundary)
|
||||
{
|
||||
if (x % boundary != 0)
|
||||
x = (x & ~(boundary-1)) + boundary;
|
||||
return x;
|
||||
}
|
||||
|
||||
// Computes size on the stack
|
||||
std::int32_t x64GenContext::GetParamStackSize(const IntPassInfo &info)
|
||||
{
|
||||
// Align up to 4 byte boundaries
|
||||
return AlignSize(GetRealSize(info), 8);
|
||||
}
|
||||
|
||||
HookManagerPubFunc x64GenContext::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 nullptr;
|
||||
}
|
||||
|
||||
// Detect the pass flags (if they're missing) for return and parameters type
|
||||
AutoDetectRetType();
|
||||
AutoDetectParamFlags();
|
||||
|
||||
// Calling conventions are gone on x86_64, there's only one to call all functions
|
||||
// however act as if they still exist to avoid code duplication on the user's side
|
||||
// TO-DO: Handle microsoft's vectorcall
|
||||
if ((m_Proto.GetConvention() & (~ProtoInfo::CallConv_HasVafmt)) != ProtoInfo::CallConv_ThisCall)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Non void return, ensure we support it
|
||||
if (m_Proto.GetRet().size != 0 && !PassInfoSupported(m_Proto.GetRet(), true))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure we support each param
|
||||
for (int i = 0; i < m_Proto.GetNumOfParams(); ++i)
|
||||
{
|
||||
if (!PassInfoSupported(m_Proto.GetParam(i), false))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
BuildProtoInfo();
|
||||
GenerateHookFunc();
|
||||
return fastdelegate::detail::horrible_cast<HookManagerPubFunc>(GeneratePubFunc());
|
||||
}
|
||||
|
||||
bool x64GenContext::PassInfoSupported(const IntPassInfo& pi, bool is_ret)
|
||||
{
|
||||
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* x64GenContext::GenerateHookFunc()
|
||||
{
|
||||
const auto& retInfo = m_Proto.GetRet();
|
||||
m_HookFunc.breakpoint();
|
||||
|
||||
// For the time being, we only consider xmm0-xmm15 registers
|
||||
// are only used to store 64bits worth of data, despite being
|
||||
// able to store up to 128bits
|
||||
|
||||
// RBP is a general purpose register on x86_64
|
||||
// we will therefore use it on both linux and windows
|
||||
// to refer to our space in the stack where we grew
|
||||
|
||||
// *********** stack frame *************
|
||||
|
||||
// MSVC ONLY START
|
||||
// rbp + 40 end of shadow space
|
||||
// rbp + 8 start of shadow space
|
||||
// MSVC ONLY END
|
||||
//
|
||||
// rbp - 0 begining of (old) rsp
|
||||
// rbp - 8 saved old rbp value
|
||||
// rbp - 16 vfnptr_origentry
|
||||
// rbp - 24 status
|
||||
// rbp - 32 prev_res
|
||||
// rbp - 40 cur_res
|
||||
// rbp - 48 iter
|
||||
// rbp - 56 context
|
||||
// rbp - 64 this ptr
|
||||
// [Non void functions:]
|
||||
// rbp - 64 - sizeof(returntype) original return
|
||||
// rbp - 64 - sizeof(returntype) * 2 override return
|
||||
// rbp - 64 - sizeof(returntype) * 3 plugin return
|
||||
|
||||
const std::int8_t v_original_rbp = AddVarToFrame(SIZE_PTR); // -8
|
||||
const std::int8_t v_vfnptr_origentry = AddVarToFrame(SIZE_PTR); // -16
|
||||
const std::int8_t v_status = AddVarToFrame(SIZE_PTR /*sizeof(META_RES)*/); // -24
|
||||
const std::int8_t v_prev_res = AddVarToFrame(SIZE_PTR /*sizeof(META_RES)*/); // -32
|
||||
const std::int8_t v_cur_res = AddVarToFrame(SIZE_PTR /*sizeof(META_RES)*/); // -40
|
||||
const std::int8_t v_iter = AddVarToFrame(SIZE_PTR); // -48
|
||||
const std::int8_t v_pContext = AddVarToFrame(SIZE_PTR); // -56
|
||||
const std::int8_t v_this = AddVarToFrame(SIZE_PTR); // -64
|
||||
|
||||
// Non void return, track the values
|
||||
std::int32_t v_ret_ptr = 0;
|
||||
std::int32_t v_memret_ptr = 0;
|
||||
std::int32_t v_orig_ret = 0;
|
||||
std::int32_t v_override_ret = 0;
|
||||
std::int32_t v_plugin_ret = 0;
|
||||
std::int32_t v_mem_ret = 0;
|
||||
if (m_Proto.GetRet().size != 0)
|
||||
{
|
||||
v_ret_ptr = AddVarToFrame(SIZE_PTR);
|
||||
v_memret_ptr = AddVarToFrame(SIZE_PTR);
|
||||
v_orig_ret = AddVarToFrame(AlignSize(GetParamStackSize(retInfo), 16)); // 16 bytes aligned
|
||||
v_override_ret = AddVarToFrame(AlignSize(GetParamStackSize(retInfo), 16));
|
||||
v_plugin_ret = AddVarToFrame(AlignSize(GetParamStackSize(retInfo), 16));
|
||||
v_mem_ret = AddVarToFrame(AlignSize(GetParamStackSize(retInfo), 16));
|
||||
}
|
||||
|
||||
std::int32_t stack_frame_size = ComputeVarsSize();
|
||||
m_HookFunc.sub(rsp, stack_frame_size);
|
||||
|
||||
// Store rbp where it should be
|
||||
m_HookFunc.mov(rsp(stack_frame_size - SIZE_PTR), rbp);
|
||||
m_HookFunc.lea(rbp, rsp(stack_frame_size));
|
||||
|
||||
// MSVC ONLY - Save the registers into shadow space
|
||||
#if SH_COMP == SH_COMP_MSVC
|
||||
const x86_64_Reg params_reg[] = { rcx, rdx, r8, r9 };
|
||||
const x86_64_FloatReg params_floatreg[] = { xmm0, xmm1, xmm2, xmm3 };
|
||||
|
||||
int reg_index = 0;
|
||||
|
||||
// retrieve this ptr
|
||||
m_HookFunc.mov(rbp(v_this), params_reg[reg_index]);
|
||||
m_HookFunc.mov(rbp(reg_index * 8 + 8), params_reg[reg_index]);
|
||||
reg_index++;
|
||||
|
||||
// Non standard return size, a ptr has been passed into rcx. Shifting all the parameters
|
||||
if ((retInfo.flags & PassInfo::PassFlag_RetMem) == PassInfo::PassFlag_RetMem) {
|
||||
m_HookFunc.mov(rbp(reg_index * 8 + 8), params_reg[reg_index]);
|
||||
m_HookFunc.mov(rbp(v_memret_ptr), params_reg[reg_index]);
|
||||
reg_index++;
|
||||
}
|
||||
|
||||
m_HookFunc.mov(rax, m_Proto.GetNumOfParams());
|
||||
m_HookFunc.mov(rax, reg_index);
|
||||
m_HookFunc.mov(rax, retInfo.size);
|
||||
|
||||
for (int i = 0; i < m_Proto.GetNumOfParams() && reg_index < 4; reg_index++, i++) {
|
||||
auto& info = m_Proto.GetParam(i);
|
||||
if (info.type == PassInfo::PassType_Float && (info.flags & PassInfo::PassFlag_ByRef) != PassInfo::PassFlag_ByRef) {
|
||||
m_HookFunc.movsd(rbp(reg_index * 8 + 8), params_floatreg[reg_index]);
|
||||
} else {
|
||||
m_HookFunc.mov(rbp(reg_index * 8 + 8), params_reg[reg_index]);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static_assert(false, "Missing registers saving for linux");
|
||||
#endif
|
||||
|
||||
// From this point on, no matter what. RSP should be aligned on 16 bytes boundary
|
||||
|
||||
// If return value has a constructor, call it
|
||||
if ((retInfo.flags & PassInfo::PassFlag_ByVal) && retInfo.pNormalCtor != nullptr)
|
||||
{
|
||||
std::int32_t v_ret_vals[] = {v_orig_ret, v_override_ret, v_plugin_ret};
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Shadow space
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 40));
|
||||
|
||||
// First param is this
|
||||
MSVC_ONLY(m_HookFunc.lea(rcx, rbp(v_ret_vals[i])));
|
||||
GCC_ONLY(m_HookFunc.lea(rdi, rbp(v_ret_vals[i])));
|
||||
|
||||
// We've saved (or not) r8 value, use the freed register to store function ptr
|
||||
m_HookFunc.mov(r8, reinterpret_cast<std::uint64_t>(retInfo.pNormalCtor));
|
||||
m_HookFunc.call(r8);
|
||||
|
||||
// Free shadow space
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 40));
|
||||
}
|
||||
}
|
||||
|
||||
m_HookFunc.mov(rbp(v_status), MRES_IGNORED);
|
||||
m_HookFunc.mov(rbp(v_cur_res), MRES_IGNORED);
|
||||
m_HookFunc.mov(rbp(v_prev_res), MRES_IGNORED);
|
||||
|
||||
// ********************** SetupHookLoop **********************
|
||||
//PrintDebug(m_HookFunc, "Call - SetupHookLoop\n");
|
||||
|
||||
CallSetupHookLoop(v_orig_ret, v_override_ret, v_cur_res, v_prev_res, v_status, v_vfnptr_origentry,
|
||||
v_this, v_pContext);
|
||||
|
||||
//PrintDebug(m_HookFunc, "Call - SetupHookLoop - END\n");
|
||||
|
||||
// ********************** call pre hooks **********************
|
||||
//PrintDebug(m_HookFunc, "Call - CallHooks [PRE]\n");
|
||||
|
||||
GenerateCallHooks(v_status, v_prev_res, v_cur_res, v_iter, v_pContext, v_plugin_ret, v_mem_ret);
|
||||
|
||||
//PrintDebug(m_HookFunc, "Call - CallHooks [PRE] - END\n");
|
||||
|
||||
// ********************** call orig func **********************
|
||||
//PrintDebug(m_HookFunc, "Call - CallOrig\n");
|
||||
|
||||
GenerateCallOrig(v_status, v_pContext, v_this, v_vfnptr_origentry, v_orig_ret, v_override_ret, v_mem_ret);
|
||||
|
||||
//PrintDebug(m_HookFunc, "Call - CallOrig - END\n");
|
||||
|
||||
// ********************** call post hooks **********************
|
||||
//PrintDebug(m_HookFunc, "Call - Hooks [POST]\n");
|
||||
|
||||
GenerateCallHooks(v_status, v_prev_res, v_cur_res, v_iter, v_pContext, v_plugin_ret, v_mem_ret);
|
||||
|
||||
//PrintDebug(m_HookFunc, "Call - Hooks [POST] - END\n");
|
||||
|
||||
// ********************** 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
|
||||
#if SH_COMP == SH_COMP_MSVC
|
||||
int stack_index = 1; // account this pointer
|
||||
if ((retInfo.flags & PassInfo::PassFlag_RetMem) == PassInfo::PassFlag_RetMem) {
|
||||
// Non trivial return value
|
||||
stack_index++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_Proto.GetNumOfParams(); ++i, ++stack_index) {
|
||||
// Shadow space
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 40));
|
||||
|
||||
const IntPassInfo &pi = m_Proto.GetParam(i);
|
||||
if (pi.type == PassInfo::PassType_Object && (pi.flags & PassInfo::PassFlag_ODtor) &&
|
||||
(pi.flags & PassInfo::PassFlag_ByVal)) {
|
||||
// Every non trivial types are passed as a pointer to a special dedicated space
|
||||
MSVC_ONLY(m_HookFunc.mov(rcx, rbp(8 + stack_index * 8)));
|
||||
GCC_ONLY(m_HookFunc.mov(rdi, rbp(8 + stack_index * 8)));
|
||||
|
||||
m_HookFunc.mov(rax, reinterpret_cast<std::uint64_t>(pi.pDtor));
|
||||
m_HookFunc.call(rax);
|
||||
}
|
||||
|
||||
// Free shadow space
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 40));
|
||||
}
|
||||
#else
|
||||
static_assert(false, "Missing parameters destruction for linux");
|
||||
#endif
|
||||
|
||||
DoReturn(v_ret_ptr, v_memret_ptr);
|
||||
// From then on, rax cannot be used as a general register
|
||||
// Use r8 or r9 instead
|
||||
|
||||
// If return value type has a destructor, call it
|
||||
if ((retInfo.flags & PassInfo::PassFlag_ByVal) && retInfo.pDtor != nullptr)
|
||||
{
|
||||
std::int32_t v_ret_vals[] = {v_orig_ret, v_override_ret, v_plugin_ret};
|
||||
|
||||
// Shadow space
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 40));
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// First param is this
|
||||
MSVC_ONLY(m_HookFunc.lea(rcx, rbp(v_ret_vals[i])));
|
||||
GCC_ONLY(m_HookFunc.lea(rdi, rbp(v_ret_vals[i])));
|
||||
|
||||
m_HookFunc.mov(r8, reinterpret_cast<std::uint64_t>(retInfo.pDtor));
|
||||
m_HookFunc.call(r8);
|
||||
}
|
||||
// Free shadow space
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 40));
|
||||
}
|
||||
|
||||
// Restore rbp
|
||||
m_HookFunc.mov(rbp, rbp(v_original_rbp));
|
||||
// Free the stack frame
|
||||
m_HookFunc.add(rsp, stack_frame_size);
|
||||
|
||||
m_HookFunc.retn();
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
void x64GenContext::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)
|
||||
{
|
||||
// IHookContext *shptr->SetupHookLoop(IHookManagerInfo *hi, void *vfnptr, void *thisptr, void **origCallAddr, META_RES *statusPtr,
|
||||
// META_RES *prevResPtr, META_RES *curResPtr, const void *origRetPtr, void *overrideRetPtr);
|
||||
|
||||
static MemFuncInfo mfi = {false, -1, -1, -1};
|
||||
if (mfi.vtblindex == -1)
|
||||
{
|
||||
GetFuncInfo(&ISourceHook::SetupHookLoop, mfi);
|
||||
// The function is somehow not virtual, or has a non trivial this ptr
|
||||
if (!mfi.isVirtual || mfi.thisptroffs != 0 || mfi.vtbloffs != 0 || mfi.vtblindex < 0)
|
||||
{
|
||||
mfi.vtblindex = -1; // Ensure we go through there again on subsequent calls
|
||||
SH_ASSERT(0, ("Couldn't retrieve details of ISourceHook::SetupHookLoop!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate the necessary stack space
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 88)); // shadow space (32 bytes) + 6 stack arguments (48 bytes) + 8 bytes
|
||||
|
||||
// 1st parameter (this)
|
||||
GCC_ONLY(m_HookFunc.mov(rdi, reinterpret_cast<std::uintptr_t>(m_SHPtr)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rcx, reinterpret_cast<std::uintptr_t>(m_SHPtr)));
|
||||
// 2nd parameter - IHookManagerInfo* hi
|
||||
GCC_ONLY(m_HookFunc.mov(rsi, reinterpret_cast<std::uintptr_t>(m_pHI)));
|
||||
GCC_ONLY(m_HookFunc.mov(rsi, rsi()));
|
||||
MSVC_ONLY(m_HookFunc.mov(rdx, reinterpret_cast<std::uintptr_t>(m_pHI)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rdx, rdx()));
|
||||
// 3rd parameter - void* vfnptr
|
||||
GCC_ONLY(m_HookFunc.mov(rdx, rbp(v_this)));
|
||||
GCC_ONLY(m_HookFunc.mov(rdx, rdx(m_VtblOffs))); // *(this + m_VtblOffs)
|
||||
GCC_ONLY(m_HookFunc.add(rdx, SIZE_PTR * m_VtblIdx)); // vtable + m_VtblIdx
|
||||
|
||||
MSVC_ONLY(m_HookFunc.mov(r8, rbp(v_this)));
|
||||
MSVC_ONLY(m_HookFunc.mov(r8, r8(m_VtblOffs))); // *(this + m_VtblOffs)
|
||||
MSVC_ONLY(m_HookFunc.add(r8, SIZE_PTR * m_VtblIdx)); // vtable + m_VtblIdx
|
||||
// 4th parameter - void* thisptr
|
||||
GCC_ONLY(m_HookFunc.mov(rcx, rbp(v_this)));
|
||||
MSVC_ONLY(m_HookFunc.mov(r9, rbp(v_this)));
|
||||
// 5th argument - void** original call address
|
||||
GCC_ONLY(m_HookFunc.lea(r8, rbp(v_vfnptr_origentry)));
|
||||
MSVC_ONLY(m_HookFunc.lea(rax, rbp(v_vfnptr_origentry)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rsp(0x20), rax));
|
||||
// 6th argument - META_RES* statusPtr
|
||||
GCC_ONLY(m_HookFunc.lea(r9, rbp(v_status)));
|
||||
MSVC_ONLY(m_HookFunc.lea(rax, rbp(v_status)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rsp(0x28), rax));
|
||||
// 7th argument - META_RES* prevResPtr
|
||||
MSVC_ONLY(m_HookFunc.lea(rax, rbp(v_prev_res)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rsp(0x30), rax));
|
||||
// 8th argument - META_RES* curResPtr
|
||||
MSVC_ONLY(m_HookFunc.lea(rax, rbp(v_cur_res)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rsp(0x38), rax));
|
||||
if (m_Proto.GetRet().size == 0) // void return function
|
||||
{
|
||||
// nullptr
|
||||
m_HookFunc.xor(rax, rax);
|
||||
// 9th argument - const void* origRetPtr
|
||||
MSVC_ONLY(m_HookFunc.mov(rsp(0x40), rax));
|
||||
// 10th argument - void* overrideRetPtr
|
||||
MSVC_ONLY(m_HookFunc.mov(rsp(0x48), rax));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 9th argument - const void* origRetPtr
|
||||
MSVC_ONLY(m_HookFunc.lea(rax, rbp(v_orig_ret)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rsp(0x40), rax));
|
||||
// 10th argument - void* overrideRetPtr
|
||||
MSVC_ONLY(m_HookFunc.lea(rax, rbp(v_override_ret)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rsp(0x48), rax));
|
||||
}
|
||||
|
||||
// Retrieve the function address
|
||||
m_HookFunc.mov(rax, (*reinterpret_cast<std::uintptr_t**>(m_SHPtr))[mfi.vtblindex]);
|
||||
m_HookFunc.call(rax);
|
||||
|
||||
// Store the return value
|
||||
m_HookFunc.mov(rbp(v_pContext), rax);
|
||||
|
||||
// Restore the rsp value
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 88));
|
||||
}
|
||||
|
||||
// Extension of MAKE_DELEG macro
|
||||
struct IMyDelegate : ::SourceHook::ISHDelegate { virtual void Call() = 0; };
|
||||
|
||||
void x64GenContext::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)
|
||||
{
|
||||
static MemFuncInfo getNext = {false, -1, -1, -1};
|
||||
if (getNext.vtblindex == -1)
|
||||
{
|
||||
GetFuncInfo(&IHookContext::GetNext, getNext);
|
||||
// The function is somehow not virtual, or has a non trivial this ptr
|
||||
if (!getNext.isVirtual || getNext.thisptroffs != 0 || getNext.vtbloffs != 0 || getNext.vtblindex < 0)
|
||||
{
|
||||
getNext.vtblindex = -1; // Ensure we go through there again on subsequent calls
|
||||
SH_ASSERT(0, ("Unexpected compilation of IHookContext::GetNext!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static MemFuncInfo callMfi = {false, -1, -1, -1};
|
||||
if (callMfi.vtblindex == -1)
|
||||
{
|
||||
GetFuncInfo(&IMyDelegate::Call, callMfi);
|
||||
// The function is somehow not virtual, or has a non trivial this ptr
|
||||
if (!callMfi.isVirtual || callMfi.thisptroffs != 0 || callMfi.vtbloffs != 0 || callMfi.vtblindex < 0)
|
||||
{
|
||||
callMfi.vtblindex = -1; // Ensure we go through there again on subsequent calls
|
||||
SH_ASSERT(0, ("Unexpected compilation of IMyDelegate::Call!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static MemFuncInfo getOverrideRetPtrMfi = {false, -1, -1, -1};
|
||||
if (getOverrideRetPtrMfi.vtblindex == -1)
|
||||
{
|
||||
GetFuncInfo(&IHookContext::GetOverrideRetPtr, getOverrideRetPtrMfi);
|
||||
// The function is somehow not virtual, or has a non trivial this ptr
|
||||
if (!getOverrideRetPtrMfi.isVirtual || getOverrideRetPtrMfi.thisptroffs != 0 || getOverrideRetPtrMfi.vtbloffs != 0 || getOverrideRetPtrMfi.vtblindex < 0)
|
||||
{
|
||||
getOverrideRetPtrMfi.vtblindex = -1; // Ensure we go through there again on subsequent calls
|
||||
SH_ASSERT(0, ("Unexpected compilation of IHookContext::GetOverrideRetPtr!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//prev_res = MRES_IGNORED;
|
||||
//while ( (iter = static_cast<IMyDelegate*>(pContext->GetNext())) )
|
||||
//{
|
||||
// cur_res = MRES_IGNORED;
|
||||
// plugin_ret = iter->Call params;
|
||||
// prev_res = cur_res;
|
||||
// if (cur_res > status)
|
||||
// status = cur_res;
|
||||
// if (cur_res >= MRES_OVERRIDE)
|
||||
// *reinterpret_cast<my_rettype*>(pContext->GetOverrideRetPtr()) = plugin_ret;
|
||||
//}
|
||||
|
||||
// prev_res = MRES_IGNORED;
|
||||
//m_HookFunc.breakpoint();
|
||||
|
||||
m_HookFunc.mov(rbp(v_prev_res), MRES_IGNORED);
|
||||
|
||||
auto startLoop = m_HookFunc.get_outputpos();
|
||||
// while ( (iter = static_cast<IMyDelegate*>(pContext->GetNext())) )
|
||||
m_HookFunc.mov(rax, rbp(v_pContext));
|
||||
m_HookFunc.mov(rax, rax()); // *this (vtable)
|
||||
m_HookFunc.mov(rax, rax(getNext.vtblindex * SIZE_PTR)); // vtable[vtblindex]
|
||||
|
||||
// Shadow space 32 bytes + 8 bytes
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 40));
|
||||
|
||||
GCC_ONLY(m_HookFunc.mov(rdi, rbp(v_pContext)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rcx, rbp(v_pContext)));
|
||||
m_HookFunc.call(rax); // pContext->GetNext()
|
||||
|
||||
// store into iter
|
||||
m_HookFunc.mov(rbp(v_iter), rax);
|
||||
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 40));
|
||||
|
||||
// null check iter
|
||||
m_HookFunc.test(rax, rax);
|
||||
m_HookFunc.jz(0x0); // Leave loop if nullptr
|
||||
std::int32_t jumpOff = m_HookFunc.get_outputpos();
|
||||
|
||||
// cur_res = MRES_IGNORED;
|
||||
m_HookFunc.mov(rbp(v_cur_res), MRES_IGNORED);
|
||||
|
||||
// prev_res = cur_res;
|
||||
m_HookFunc.mov(rax, rbp(v_cur_res));
|
||||
m_HookFunc.mov(rbp(v_prev_res), rax);
|
||||
|
||||
// call
|
||||
std::int32_t stackSpace = PushParameters(v_iter, MemRetWithTempObj() ? v_mem_ret : v_plugin_ret);
|
||||
m_HookFunc.mov(rax, rbp(v_iter));
|
||||
m_HookFunc.mov(rax, rax()); // *this (vtable)
|
||||
m_HookFunc.mov(rax, rax(callMfi.vtblindex * SIZE_PTR)); // vtable[vtblindex] iter -> Call
|
||||
m_HookFunc.call(rax);
|
||||
// epilog free the stack
|
||||
m_HookFunc.add(rsp, stackSpace);
|
||||
|
||||
SaveReturnValue(v_mem_ret, v_plugin_ret);
|
||||
|
||||
// if (cur_res > status)
|
||||
m_HookFunc.mov(rax, rbp(v_cur_res));
|
||||
m_HookFunc.cmp(rax, rbp(v_status));
|
||||
// status = cur_res;
|
||||
m_HookFunc.mov(rax, rbp(v_status));
|
||||
m_HookFunc.cmovg(rax, rbp(v_cur_res));
|
||||
m_HookFunc.mov(rbp(v_status), rax);
|
||||
|
||||
// Are we dealing with a non void function ?
|
||||
auto retInfo = m_Proto.GetRet();
|
||||
if (retInfo.size != 0)
|
||||
{
|
||||
// if (cur_res >= MRES_OVERRIDE)
|
||||
m_HookFunc.mov(rax, MRES_OVERRIDE);
|
||||
m_HookFunc.cmp(rbp(v_cur_res), rax);
|
||||
|
||||
m_HookFunc.jl(0x0);
|
||||
std::int32_t earlyLoopBack = m_HookFunc.get_outputpos() - startLoop;
|
||||
m_HookFunc.rewrite<std::int32_t>(m_HookFunc.get_outputpos() - sizeof(std::int32_t), -earlyLoopBack);
|
||||
|
||||
// Shadow space 32 bytes + 8 bytes
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 40));
|
||||
|
||||
m_HookFunc.mov(rax, rbp(v_pContext));
|
||||
m_HookFunc.mov(rax, rax()); // *this (vtable)
|
||||
m_HookFunc.mov(rax, rax(getOverrideRetPtrMfi.vtblindex * SIZE_PTR)); // vtable[vtblindex]
|
||||
|
||||
GCC_ONLY(m_HookFunc.mov(rdi, rbp(v_pContext)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rcx, rbp(v_pContext)));
|
||||
m_HookFunc.call(rax); // pContext->GetOverrideRetPtr()
|
||||
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 40));
|
||||
|
||||
// *reinterpret_cast<my_rettype*>(pContext->GetOverrideRetPtr()) = plugin_ret;
|
||||
|
||||
// byref is always a pointer underneath
|
||||
if (retInfo.flags & PassInfo::PassFlag_ByRef)
|
||||
{
|
||||
m_HookFunc.mov(r8, rbp(v_plugin_ret));
|
||||
m_HookFunc.mov(rax(), r8);
|
||||
}
|
||||
else
|
||||
{
|
||||
// custom assignment operator, so call it
|
||||
if (retInfo.pAssignOperator)
|
||||
{
|
||||
// Shadow space 32 bytes + 8 bytes
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 40));
|
||||
|
||||
// 1st parameter (this)
|
||||
GCC_ONLY(m_HookFunc.mov(rdi, rax));
|
||||
MSVC_ONLY(m_HookFunc.mov(rcx, rax));
|
||||
|
||||
// 2nd parameter (copy)
|
||||
GCC_ONLY(m_HookFunc.lea(rsi, rbp(v_plugin_ret)));
|
||||
MSVC_ONLY(m_HookFunc.lea(rdx, rbp(v_plugin_ret)));
|
||||
|
||||
// Move address and call
|
||||
m_HookFunc.mov(rax, reinterpret_cast<std::uint64_t>(retInfo.pAssignOperator));
|
||||
m_HookFunc.call(rax);
|
||||
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 40));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HookFunc.push(rdi);
|
||||
m_HookFunc.push(rsi);
|
||||
m_HookFunc.push(rcx);
|
||||
|
||||
m_HookFunc.mov(rcx, retInfo.size);
|
||||
m_HookFunc.mov(rdi, rax);
|
||||
m_HookFunc.lea(rsi, rbp(v_plugin_ret));
|
||||
|
||||
m_HookFunc.rep_movs_bytes();
|
||||
|
||||
m_HookFunc.pop(rcx);
|
||||
m_HookFunc.pop(rsi);
|
||||
m_HookFunc.pop(rdi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_HookFunc.jump(0x0);
|
||||
std::int32_t loopBack = m_HookFunc.get_outputpos() - startLoop;
|
||||
m_HookFunc.rewrite<std::int32_t>(m_HookFunc.get_outputpos() - sizeof(std::int32_t), -loopBack);
|
||||
|
||||
m_HookFunc.rewrite<std::int32_t>(jumpOff - sizeof(std::int32_t), m_HookFunc.get_outputpos() - jumpOff);
|
||||
}
|
||||
|
||||
void x64GenContext::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)
|
||||
{
|
||||
static MemFuncInfo shouldCallOrigMfi = {false, -1, -1, -1};
|
||||
if (shouldCallOrigMfi.vtblindex == -1)
|
||||
{
|
||||
GetFuncInfo(&IHookContext::ShouldCallOrig, shouldCallOrigMfi);
|
||||
// The function is somehow not virtual, or has a non trivial this ptr
|
||||
if (!shouldCallOrigMfi.isVirtual || shouldCallOrigMfi.thisptroffs != 0 || shouldCallOrigMfi.vtbloffs != 0 || shouldCallOrigMfi.vtblindex < 0)
|
||||
{
|
||||
shouldCallOrigMfi.vtblindex = -1; // Ensure we go through there again on subsequent calls
|
||||
SH_ASSERT(0, ("Unexpected compilation of IHookContext::ShouldCallOrig!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//if (status != MRES_SUPERCEDE && pContext->ShouldCallOrig())
|
||||
//{
|
||||
// rettype (EmptyClass::*mfp)paramtypes;
|
||||
// SH_SETUP_MFP(mfp);
|
||||
// orig_ret = (reinterpret_cast<EmptyClass*>(this)->*mfp)params;
|
||||
//}
|
||||
//else
|
||||
// orig_ret = override_ret;
|
||||
//m_HookFunc.breakpoint();
|
||||
|
||||
m_HookFunc.mov(rax, rbp(v_status));
|
||||
m_HookFunc.cmp(rax, MRES_SUPERCEDE);
|
||||
m_HookFunc.je(0x0);
|
||||
auto statusCmpOff = m_HookFunc.get_outputpos();
|
||||
|
||||
// Shadow space 32 bytes + 8 bytes
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 40));
|
||||
|
||||
m_HookFunc.mov(rax, rbp(v_pContext));
|
||||
|
||||
// 1st parameter (this)
|
||||
GCC_ONLY(m_HookFunc.mov(rdi, rax));
|
||||
MSVC_ONLY(m_HookFunc.mov(rcx, rax));
|
||||
|
||||
m_HookFunc.mov(rax, rax());
|
||||
m_HookFunc.mov(rax, rax(SIZE_PTR * shouldCallOrigMfi.vtblindex));
|
||||
|
||||
m_HookFunc.call(rax); // pContext->ShouldCallOrig()
|
||||
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 40));
|
||||
|
||||
// Don't have the lower register yet, so this will do for now
|
||||
m_HookFunc.test(rax, 0x1);
|
||||
m_HookFunc.jz(0x0);
|
||||
auto shouldCallOff = m_HookFunc.get_outputpos();
|
||||
|
||||
// original call
|
||||
std::int32_t stackSpace = PushParameters(v_this, MemRetWithTempObj() ? v_place_for_memret : v_orig_ret);
|
||||
m_HookFunc.mov(rax, rbp(v_vfnptr_origentry));
|
||||
m_HookFunc.call(rax);
|
||||
// epilog free the stack
|
||||
m_HookFunc.add(rsp, stackSpace);
|
||||
|
||||
SaveReturnValue(v_place_for_memret, v_orig_ret);
|
||||
|
||||
m_HookFunc.jump(0x0);
|
||||
auto callOriginalOff = m_HookFunc.get_outputpos();
|
||||
|
||||
// else
|
||||
auto elseStartOff = m_HookFunc.get_outputpos();
|
||||
m_HookFunc.rewrite(statusCmpOff - sizeof(std::int32_t), static_cast<std::int32_t>(elseStartOff - statusCmpOff));
|
||||
m_HookFunc.rewrite(shouldCallOff - sizeof(std::int32_t), static_cast<std::int32_t>(elseStartOff - shouldCallOff));
|
||||
|
||||
auto retInfo = m_Proto.GetRet();
|
||||
if (retInfo.size != 0)
|
||||
{
|
||||
if (retInfo.flags & PassInfo::PassFlag_ByRef)
|
||||
{
|
||||
m_HookFunc.mov(rax, rbp(v_override_ret));
|
||||
m_HookFunc.mov(rbp(v_orig_ret), rax);
|
||||
}
|
||||
else
|
||||
{
|
||||
// custom assignment operator, so call it
|
||||
if (retInfo.pAssignOperator)
|
||||
{
|
||||
// Shadow space 32 bytes + 8 bytes
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 40));
|
||||
|
||||
// 1st parameter (this)
|
||||
GCC_ONLY(m_HookFunc.lea(rdi, rbp(v_orig_ret)));
|
||||
MSVC_ONLY(m_HookFunc.lea(rcx, rbp(v_orig_ret)));
|
||||
|
||||
// 2nd parameter (copy)
|
||||
GCC_ONLY(m_HookFunc.lea(rsi, rbp(v_override_ret)));
|
||||
MSVC_ONLY(m_HookFunc.lea(rdx, rbp(v_override_ret)));
|
||||
|
||||
// Move address and call
|
||||
m_HookFunc.mov(rax, reinterpret_cast<std::uint64_t>(retInfo.pAssignOperator));
|
||||
m_HookFunc.call(rax);
|
||||
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 40));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HookFunc.push(rdi);
|
||||
m_HookFunc.push(rsi);
|
||||
m_HookFunc.push(rcx);
|
||||
|
||||
m_HookFunc.mov(rcx, retInfo.size);
|
||||
m_HookFunc.lea(rdi, rbp(v_orig_ret));
|
||||
m_HookFunc.lea(rsi, rbp(v_override_ret));
|
||||
|
||||
m_HookFunc.rep_movs_bytes();
|
||||
|
||||
m_HookFunc.pop(rcx);
|
||||
m_HookFunc.pop(rsi);
|
||||
m_HookFunc.pop(rdi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_HookFunc.rewrite(callOriginalOff - sizeof(std::int32_t), static_cast<std::int32_t>(m_HookFunc.get_outputpos() - callOriginalOff));
|
||||
}
|
||||
|
||||
std::int32_t x64GenContext::PushParameters(int v_this, int v_ret)
|
||||
{
|
||||
auto retInfo = m_Proto.GetRet();
|
||||
std::int32_t stackSpace = 0;
|
||||
|
||||
#if SH_COMP == SH_COMP_MSVC
|
||||
const x86_64_Reg params_reg[] = { rcx, rdx, r8, r9 };
|
||||
const x86_64_FloatReg params_floatreg[] = { xmm0, xmm1, xmm2, xmm3 };
|
||||
|
||||
int reg_index = 0;
|
||||
|
||||
// setup this parameter
|
||||
m_HookFunc.mov(params_reg[reg_index], rbp(v_this));
|
||||
reg_index++;
|
||||
|
||||
// Non standard return
|
||||
if (retInfo.size != 0 && (retInfo.flags & PassInfo::PassFlag_RetMem) == PassInfo::PassFlag_RetMem) {
|
||||
m_HookFunc.lea(params_reg[reg_index], rbp(v_ret));
|
||||
reg_index++;
|
||||
}
|
||||
|
||||
// We've backed up the parameters into the shadow space
|
||||
int parameter_index = 0;
|
||||
for (; parameter_index < m_Proto.GetNumOfParams() && reg_index < 4; reg_index++, parameter_index++) {
|
||||
auto& info = m_Proto.GetParam(parameter_index);
|
||||
|
||||
if (info.type == PassInfo::PassType_Float && (info.flags & PassInfo::PassFlag_ByRef) != PassInfo::PassFlag_ByRef) {
|
||||
m_HookFunc.movsd(params_floatreg[reg_index], rbp(reg_index * 8 + 8));
|
||||
} else {
|
||||
m_HookFunc.mov(params_reg[reg_index], rbp(reg_index * 8 + 8));
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate the shadow space
|
||||
m_HookFunc.sub(rsp, 32);
|
||||
stackSpace += 32;
|
||||
|
||||
int parameters_on_stack = m_Proto.GetNumOfParams() - parameter_index;
|
||||
m_HookFunc.sub(rsp, parameters_on_stack * 8);
|
||||
stackSpace += parameters_on_stack * 8;
|
||||
|
||||
// If this number is even we need to allocate an extra 8 bytes
|
||||
if (parameters_on_stack % 2 == 0) {
|
||||
m_HookFunc.sub(rsp, 8);
|
||||
stackSpace += 8;
|
||||
}
|
||||
|
||||
for (int i = 0; parameter_index < m_Proto.GetNumOfParams(); parameter_index++, i++) {
|
||||
m_HookFunc.mov(rax, rbp(40 + (8 * i))); // We need to skip the shadow space + return address
|
||||
m_HookFunc.mov(rsp(32 + (8 * i)), rax);
|
||||
}
|
||||
|
||||
return stackSpace;
|
||||
#else
|
||||
static_assert(false, "Missing registers saving for linux");
|
||||
#endif
|
||||
}
|
||||
|
||||
void x64GenContext::SaveReturnValue(int v_mem_ret, int v_ret) {
|
||||
const auto& retInfo = m_Proto.GetRet();
|
||||
// void, early return
|
||||
if (retInfo.size == 0) {
|
||||
return;
|
||||
}
|
||||
#if SH_COMP == SH_COMP_MSVC
|
||||
if ((retInfo.flags & PassInfo::PassFlag_ByRef) == PassInfo::PassFlag_ByRef) {
|
||||
m_HookFunc.mov(rbp(v_ret), rax);
|
||||
return;
|
||||
}
|
||||
|
||||
// ByVal
|
||||
|
||||
if (retInfo.type == PassInfo::PassType_Float) {
|
||||
m_HookFunc.movsd(rbp(v_ret), xmm0);
|
||||
} else if (retInfo.type == PassInfo::PassType_Basic) {
|
||||
m_HookFunc.mov(rbp(v_ret), rax);
|
||||
} else if ((retInfo.flags & PassInfo::PassFlag_RetMem) == PassInfo::PassFlag_RetMem) {
|
||||
if (MemRetWithTempObj()) {
|
||||
if (retInfo.pAssignOperator) {
|
||||
// Shadow space 32 bytes + 8 bytes
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 40));
|
||||
|
||||
// 1st parameter (this)
|
||||
GCC_ONLY(m_HookFunc.lea(rdi, rbp(v_ret)));
|
||||
MSVC_ONLY(m_HookFunc.lea(rcx, rbp(v_ret)));
|
||||
|
||||
// 2nd parameter (copy)
|
||||
GCC_ONLY(m_HookFunc.lea(rsi, rbp(v_mem_ret)));
|
||||
MSVC_ONLY(m_HookFunc.lea(rdx, rbp(v_mem_ret)));
|
||||
|
||||
// Move address and call
|
||||
m_HookFunc.mov(rax, reinterpret_cast<std::uint64_t>(retInfo.pAssignOperator));
|
||||
m_HookFunc.call(rax);
|
||||
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 40));
|
||||
}
|
||||
else {
|
||||
m_HookFunc.push(rdi);
|
||||
m_HookFunc.push(rsi);
|
||||
m_HookFunc.push(rcx);
|
||||
|
||||
m_HookFunc.mov(rcx, retInfo.size);
|
||||
m_HookFunc.lea(rdi, rbp(v_ret));
|
||||
m_HookFunc.lea(rsi, rbp(v_mem_ret));
|
||||
|
||||
m_HookFunc.rep_movs_bytes();
|
||||
|
||||
m_HookFunc.pop(rcx);
|
||||
m_HookFunc.pop(rsi);
|
||||
m_HookFunc.pop(rdi);
|
||||
}
|
||||
|
||||
if (retInfo.pDtor) {
|
||||
// Shadow space 32 bytes + 8 bytes
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 40));
|
||||
|
||||
// 1st parameter (this)
|
||||
GCC_ONLY(m_HookFunc.lea(rdi, rbp(v_mem_ret)));
|
||||
MSVC_ONLY(m_HookFunc.lea(rcx, rbp(v_mem_ret)));
|
||||
|
||||
// Move address and call
|
||||
m_HookFunc.mov(rax, reinterpret_cast<std::uint64_t>(retInfo.pDtor));
|
||||
m_HookFunc.call(rax);
|
||||
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 40));
|
||||
}
|
||||
|
||||
} else {
|
||||
// Already copied in the proper location
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
SH_ASSERT(0, ("Unknown handling of return type!"));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
static_assert(false, "Missing SaveReturnValue for linux");
|
||||
#endif
|
||||
}
|
||||
|
||||
void x64GenContext::PrepareReturn(int v_status, int v_pContext, int v_retptr)
|
||||
{
|
||||
const auto& retInfo = m_Proto.GetRet();
|
||||
if (retInfo.size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
static MemFuncInfo getOverrideRetPtrMfi = {false, -1, -1, -1};
|
||||
if (getOverrideRetPtrMfi.vtblindex == -1) {
|
||||
GetFuncInfo(&IHookContext::GetOverrideRetPtr, getOverrideRetPtrMfi);
|
||||
if (!getOverrideRetPtrMfi.isVirtual || getOverrideRetPtrMfi.thisptroffs != 0 || getOverrideRetPtrMfi.vtbloffs != 0 || getOverrideRetPtrMfi.vtblindex < 0) {
|
||||
getOverrideRetPtrMfi.vtblindex = -1;
|
||||
SH_ASSERT(0, ("Unexpected compilation of IHookContext::GetOverrideRetPtr!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static MemFuncInfo getOrigRetPtrMfi = {false, -1, -1, -1};
|
||||
if (getOrigRetPtrMfi.vtblindex == -1) {
|
||||
GetFuncInfo(&IHookContext::GetOrigRetPtr, getOrigRetPtrMfi);
|
||||
if (!getOrigRetPtrMfi.isVirtual || getOrigRetPtrMfi.thisptroffs != 0 || getOrigRetPtrMfi.vtbloffs != 0 || getOrigRetPtrMfi.vtblindex < 0) {
|
||||
getOrigRetPtrMfi.vtblindex = -1;
|
||||
SH_ASSERT(0, ("Unexpected compilation of IHookContext::GetOverrideRetPtr!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//const my_rettype *retptr = reinterpret_cast<const my_rettype*>(
|
||||
//(status >= MRES_OVERRIDE) ? pContext->GetOverrideRetPtr() : pContext->GetOrigRetPtr());
|
||||
|
||||
m_HookFunc.mov(rax, rbp(v_pContext));
|
||||
m_HookFunc.mov(rax, rax());
|
||||
m_HookFunc.mov(r8, rax);
|
||||
|
||||
m_HookFunc.mov(rax, rax(getOrigRetPtrMfi.vtblindex * SIZE_PTR));
|
||||
m_HookFunc.mov(r8, r8(getOverrideRetPtrMfi.vtblindex * SIZE_PTR));
|
||||
|
||||
m_HookFunc.xor(r9, r9);
|
||||
m_HookFunc.mov(r9, rbp(v_status));
|
||||
m_HookFunc.cmp(r9, MRES_OVERRIDE);
|
||||
|
||||
m_HookFunc.cmovge(rax, r8);
|
||||
|
||||
// Shadow space 32 bytes + 8 bytes
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 40));
|
||||
|
||||
// 1st parameter (this)
|
||||
GCC_ONLY(m_HookFunc.mov(rdi, rbp(v_pContext)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rcx, rbp(v_pContext)));
|
||||
|
||||
m_HookFunc.call(rax);
|
||||
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 40));
|
||||
|
||||
m_HookFunc.mov(rbp(v_retptr), rax);
|
||||
}
|
||||
|
||||
void x64GenContext::DoReturn(int v_retptr, int v_memret_outaddr)
|
||||
{
|
||||
const auto& retInfo = m_Proto.GetRet();
|
||||
if (retInfo.size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_HookFunc.mov(r8, rbp(v_retptr));
|
||||
|
||||
if (retInfo.flags & PassInfo::PassFlag_ByRef) {
|
||||
m_HookFunc.mov(rax, r8());
|
||||
return;
|
||||
}
|
||||
// else: byval
|
||||
|
||||
if (retInfo.type == PassInfo::PassType_Float) {
|
||||
m_HookFunc.movsd(xmm0, r8());
|
||||
}
|
||||
else if (retInfo.type == PassInfo::PassType_Basic ||
|
||||
((retInfo.type == PassInfo::PassType_Object) && (retInfo.flags & PassInfo::PassFlag_RetReg)) ) {
|
||||
m_HookFunc.mov(rax, r8());
|
||||
}
|
||||
|
||||
if (retInfo.flags & PassInfo::PassFlag_RetMem)
|
||||
{
|
||||
// *memret_outaddr = plugin_ret
|
||||
if (retInfo.pCopyCtor)
|
||||
{
|
||||
// Shadow space 32 bytes + 8 bytes
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 40));
|
||||
|
||||
// 1st parameter (this)
|
||||
GCC_ONLY(m_HookFunc.mov(rdi, rbp(v_memret_outaddr)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rcx, rbp(v_memret_outaddr)));
|
||||
|
||||
// 2nd parameter (copy)
|
||||
GCC_ONLY(m_HookFunc.mov(rsi, r8));
|
||||
MSVC_ONLY(m_HookFunc.mov(rdx, r8));
|
||||
|
||||
// Move address and call
|
||||
m_HookFunc.mov(rax, reinterpret_cast<std::uint64_t>(retInfo.pCopyCtor));
|
||||
m_HookFunc.call(rax);
|
||||
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 40));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HookFunc.push(rdi);
|
||||
m_HookFunc.push(rsi);
|
||||
m_HookFunc.push(rcx);
|
||||
|
||||
m_HookFunc.mov(rcx, retInfo.size);
|
||||
m_HookFunc.mov(rdi, rbp(v_memret_outaddr));
|
||||
m_HookFunc.mov(rsi, r8);
|
||||
|
||||
m_HookFunc.rep_movs_bytes();
|
||||
|
||||
m_HookFunc.pop(rcx);
|
||||
m_HookFunc.pop(rsi);
|
||||
m_HookFunc.pop(rdi);
|
||||
}
|
||||
m_HookFunc.mov(rax, rbp(v_memret_outaddr));
|
||||
}
|
||||
}
|
||||
|
||||
void x64GenContext::CallEndContext(int v_pContext) {
|
||||
static MemFuncInfo mfi = {false, -1, -1, -1};
|
||||
if (mfi.vtblindex == -1)
|
||||
{
|
||||
GetFuncInfo(&ISourceHook::EndContext, mfi);
|
||||
// The function is somehow not virtual, or has a non trivial this ptr
|
||||
if (!mfi.isVirtual || mfi.thisptroffs != 0 || mfi.vtbloffs != 0 || mfi.vtblindex < 0)
|
||||
{
|
||||
mfi.vtblindex = -1; // Ensure we go through there again on subsequent calls
|
||||
SH_ASSERT(0, ("Couldn't retrieve details of ISourceHook::EndContext!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Shadow space 32 bytes + 8 bytes
|
||||
MSVC_ONLY(m_HookFunc.sub(rsp, 40));
|
||||
|
||||
// 1st parameter (this)
|
||||
GCC_ONLY(m_HookFunc.mov(rdi, reinterpret_cast<std::uintptr_t>(m_SHPtr)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rcx, reinterpret_cast<std::uintptr_t>(m_SHPtr)));
|
||||
|
||||
// 2nd param
|
||||
GCC_ONLY(m_HookFunc.mov(rsi, rbp(v_pContext)));
|
||||
MSVC_ONLY(m_HookFunc.mov(rdx, rbp(v_pContext)));
|
||||
|
||||
// Move address and call
|
||||
m_HookFunc.mov(rax, (*reinterpret_cast<std::uintptr_t**>(m_SHPtr))[mfi.vtblindex]);
|
||||
m_HookFunc.call(rax);
|
||||
|
||||
MSVC_ONLY(m_HookFunc.add(rsp, 40));
|
||||
}
|
||||
|
||||
bool x64GenContext::MemRetWithTempObj() {
|
||||
const auto& retInfo = m_Proto.GetRet();
|
||||
// Memory return AND (has destructor OR has assign operator)
|
||||
return ((retInfo.flags & PassInfo::PassFlag_RetMem)
|
||||
&& (retInfo.flags & (PassInfo::PassFlag_ODtor | PassInfo::PassFlag_AssignOp)));
|
||||
}
|
||||
|
||||
void x64GenContext::AutoDetectRetType() {
|
||||
auto& pi = m_Proto.GetRet();
|
||||
// Void return, ignore
|
||||
if (pi.size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 has various criteria for passing in memory
|
||||
// if object doesn't fit on 8, 16, 32, or 64 bits. It's in memory
|
||||
// if object has a constructor or destructor. It's in memory
|
||||
bool unconventionalsize = (pi.size == 3 || (pi.size != 8 && pi.size > 4));
|
||||
bool hasSpecialFunctions = (pi.flags & PassInfo::PassFlag_OCtor|PassInfo::PassFlag_ODtor|PassInfo::PassFlag_CCtor) != 0;
|
||||
|
||||
if (unconventionalsize || hasSpecialFunctions) {
|
||||
pi.flags |= PassInfo::PassFlag_RetMem;
|
||||
} else {
|
||||
pi.flags |= PassInfo::PassFlag_RetReg;
|
||||
}
|
||||
#elif SH_COMP == SH_COMP_GCC
|
||||
static_assert(false, "Missing auto-detect type for linux!");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// byref: make sure that the flag is _not_ set
|
||||
pi.flags &= ~PassInfo::PassFlag_RetMem;
|
||||
pi.flags |= PassInfo::PassFlag_RetReg;
|
||||
}
|
||||
}
|
||||
|
||||
void x64GenContext::AutoDetectParamFlags()
|
||||
{
|
||||
}
|
||||
|
||||
void* x64GenContext::GeneratePubFunc()
|
||||
{
|
||||
// The pubfunc is a static cdecl function.
|
||||
// C Code:
|
||||
// int HookManPubFunc(
|
||||
// bool store, // rdi (AMD) rcx (microsoft)
|
||||
// IHookManagerInfo *hi // rsi (AMD) rdx (microsoft)
|
||||
// )
|
||||
// {
|
||||
// if (store)
|
||||
// *m_pHI = hi;
|
||||
// if (hi)
|
||||
// hi->SetInfo(HOOKMAN_VERSION, m_VtblOffs, m_VtblIdx, m_Proto.GetProto(), m_HookfuncVfnptr)
|
||||
// }
|
||||
|
||||
// prologue
|
||||
MSVC_ONLY(m_PubFunc.sub(rsp, 0x38)); // Shadow space 32 bytes + 2 * 8 bytes (for our parameters) + 8 bytes
|
||||
|
||||
// Unnecessary according to AMD manual (Section 3.2.2 The Stack Frame)
|
||||
// but GCC still does it anyways, so let's do it as well
|
||||
GCC_ONLY(m_PubFunc.push(rbp));
|
||||
GCC_ONLY(m_PubFunc.mov(rbp, rsp));
|
||||
|
||||
// Both Microsoft and AMD uses r8 and r9 as argument parameters
|
||||
// Therefore they need not to be preserved across function calls
|
||||
// Let's use them as local variables, this will make writing the
|
||||
// rest of the function much easier
|
||||
|
||||
// Store 'store' into r8
|
||||
GCC_ONLY(m_PubFunc.mov(r8, rdi));
|
||||
MSVC_ONLY(m_PubFunc.mov(r8, rcx));
|
||||
|
||||
// Store 'hi' into r9
|
||||
GCC_ONLY(m_PubFunc.mov(r9, rsi));
|
||||
MSVC_ONLY(m_PubFunc.mov(r9, rdx));
|
||||
|
||||
// If 'store' is true, store hi into rax
|
||||
m_PubFunc.test(r8, 0x1);
|
||||
m_PubFunc.jz(0x0);
|
||||
|
||||
auto storeOff = m_PubFunc.get_outputpos();
|
||||
m_PubFunc.mov(rax, reinterpret_cast<std::uint64_t>(m_pHI));
|
||||
m_PubFunc.mov(rax(), r9);
|
||||
|
||||
m_PubFunc.rewrite<std::int32_t>(storeOff - sizeof(std::int32_t), m_PubFunc.get_outputpos() - storeOff);
|
||||
|
||||
// If 'hi' is not null, call SetInfo
|
||||
m_PubFunc.test(r9, r9);
|
||||
m_PubFunc.jz(0x0); // We will write the real offset later
|
||||
auto jumpOff = m_PubFunc.get_outputpos();
|
||||
|
||||
static MemFuncInfo mfi = {false, -1, -1, -1};
|
||||
if (mfi.vtblindex == -1)
|
||||
{
|
||||
GetFuncInfo(&IHookManagerInfo::SetInfo, mfi);
|
||||
// The function is somehow not virtual, or has a non trivial this ptr
|
||||
if (!mfi.isVirtual || mfi.thisptroffs != 0 || mfi.vtbloffs != 0 || mfi.vtblindex < 0)
|
||||
{
|
||||
mfi.vtblindex = -1; // Ensure we go through there again on subsequent calls
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain the vtable
|
||||
m_PubFunc.mov(rax, r9());
|
||||
m_PubFunc.mov(rax, rax(SIZE_PTR * mfi.vtblindex));
|
||||
|
||||
// 1st parameter (this)
|
||||
GCC_ONLY(m_PubFunc.mov(rdi, r9));
|
||||
MSVC_ONLY(m_PubFunc.mov(rcx, r9));
|
||||
// 2nd parameter
|
||||
GCC_ONLY(m_PubFunc.mov(rsi, SH_HOOKMAN_VERSION));
|
||||
MSVC_ONLY(m_PubFunc.mov(rdx, SH_HOOKMAN_VERSION));
|
||||
// 3rd parameter
|
||||
GCC_ONLY(m_PubFunc.mov(rdx, m_VtblOffs));
|
||||
MSVC_ONLY(m_PubFunc.mov(r8, m_VtblOffs));
|
||||
// 4th parameter
|
||||
GCC_ONLY(m_PubFunc.mov(rcx, m_VtblIdx));
|
||||
MSVC_ONLY(m_PubFunc.mov(r9, m_VtblIdx));
|
||||
// 5th argument
|
||||
GCC_ONLY(m_PubFunc.mov(r8, reinterpret_cast<std::uint64_t>(m_BuiltPI)));
|
||||
MSVC_ONLY(m_PubFunc.mov(r10, reinterpret_cast<std::uint64_t>(m_BuiltPI)));
|
||||
MSVC_ONLY(m_PubFunc.mov(rsp(0x20), r10));
|
||||
// 6th argument
|
||||
GCC_ONLY(m_PubFunc.mov(r9, reinterpret_cast<std::uint64_t>(m_HookfuncVfnptr)));
|
||||
MSVC_ONLY(m_PubFunc.mov(r10, reinterpret_cast<std::uint64_t>(m_HookfuncVfnptr)));
|
||||
MSVC_ONLY(m_PubFunc.mov(rsp(0x28), r10));
|
||||
|
||||
m_PubFunc.call(rax);
|
||||
|
||||
// Now that we've written the conditional branch
|
||||
// we can move set the offset at our earlier jump
|
||||
std::int32_t endOff = static_cast<std::int32_t>(m_PubFunc.get_outputpos()) - jumpOff;
|
||||
m_PubFunc.rewrite<std::int32_t>(jumpOff - sizeof(std::int32_t), endOff);
|
||||
|
||||
// epilogue
|
||||
|
||||
MSVC_ONLY(m_PubFunc.add(rsp, 0x38));
|
||||
|
||||
GCC_ONLY(m_PubFunc.pop(rbp));
|
||||
|
||||
// Return 0
|
||||
m_PubFunc.xor(rax, rax);
|
||||
|
||||
m_PubFunc.retn();
|
||||
|
||||
m_PubFunc.SetRE();
|
||||
return m_PubFunc;
|
||||
}
|
||||
|
||||
HookManagerPubFunc x64GenContext::GetPubFunc()
|
||||
{
|
||||
if (m_GeneratedPubFunc == nullptr)
|
||||
{
|
||||
// Try generating the function
|
||||
m_GeneratedPubFunc = Generate();
|
||||
}
|
||||
return m_GeneratedPubFunc;
|
||||
}
|
||||
|
||||
bool x64GenContext::Equal(const CProto &proto, int vtbl_offs, int vtbl_idx)
|
||||
{
|
||||
return (m_OrigProto.ExactlyEqual(proto) && m_VtblOffs == vtbl_offs && m_VtblIdx == vtbl_idx);
|
||||
}
|
||||
|
||||
bool x64GenContext::Equal(HookManagerPubFunc other)
|
||||
{
|
||||
return m_GeneratedPubFunc == other;
|
||||
}
|
||||
|
||||
CHookManagerAutoGen::CHookManagerAutoGen(ISourceHook *pSHPtr) : m_pSHPtr(pSHPtr) { }
|
||||
|
||||
CHookManagerAutoGen::~CHookManagerAutoGen() { }
|
||||
|
||||
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 (auto 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 = std::make_unique<x64GenContext>(proto, vtbl_offs, vtbl_idx, m_pSHPtr);
|
||||
|
||||
auto pubFunc = sctx.m_GenContext->GetPubFunc();
|
||||
if (pubFunc != nullptr)
|
||||
{
|
||||
m_Contexts.emplace_back(std::move(sctx));
|
||||
}
|
||||
return pubFunc;
|
||||
}
|
||||
|
||||
void CHookManagerAutoGen::ReleaseHookMan(HookManagerPubFunc pubFunc)
|
||||
{
|
||||
for (auto iter = m_Contexts.begin(); iter != m_Contexts.end(); ++iter)
|
||||
{
|
||||
if (iter->m_GenContext->Equal(pubFunc))
|
||||
{
|
||||
iter->m_RefCnt--;
|
||||
if (iter->m_RefCnt == 0)
|
||||
{
|
||||
iter = m_Contexts.erase(iter);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
666
core/sourcehook/sourcehook_hookmangen_x86_64.h
Normal file
666
core/sourcehook/sourcehook_hookmangen_x86_64.h
Normal 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__
|
@ -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)
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user