diff --git a/extensions/bintools/CallMaker.cpp b/extensions/bintools/CallMaker.cpp index 2084a478..5d25c6a8 100644 --- a/extensions/bintools/CallMaker.cpp +++ b/extensions/bintools/CallMaker.cpp @@ -29,9 +29,54 @@ * Version: $Id$ */ +#include #include "CallMaker.h" -#include "CallWrapper.h" -#include "jit_call.h" +#include "jit_compile.h" +#include "extension.h" + +/* SourceMod <==> SourceHook type conversion functions */ + +SourceHook::ProtoInfo::CallConvention GetSHCallConvention(SourceMod::CallConvention cv) +{ + if (cv < SourceMod::CallConv_ThisCall || cv > SourceMod::CallConv_Cdecl) + { + return SourceHook::ProtoInfo::CallConv_Unknown; + } + + return (SourceHook::ProtoInfo::CallConvention)(cv + 1); +} + +SourceMod::CallConvention GetSMCallConvention(SourceHook::ProtoInfo::CallConvention cv) +{ + assert(cv >= SourceHook::ProtoInfo::CallConv_ThisCall || cv <= SourceHook::ProtoInfo::CallConv_Cdecl); + + return (SourceMod::CallConvention)(cv - 1); +} + +SourceHook::PassInfo::PassType GetSHPassType(SourceMod::PassType type) +{ + if (type < SourceMod::PassType_Basic || type > SourceMod::PassType_Object) + { + return SourceHook::PassInfo::PassType_Unknown; + } + + return (SourceHook::PassInfo::PassType)(type + 1); +} + +SourceMod::PassType GetSMPassType(int type) +{ + /* SourceMod doesn't provide an Unknown type so we it must be an error */ + assert(type >= SourceHook::PassInfo::PassType_Basic && type <= SourceHook::PassInfo::PassType_Object); + + return (SourceMod::PassType)(type - 1); +} + +void GetSMPassInfo(SourceMod::PassInfo *out, const SourceHook::PassInfo *in) +{ + out->size = in->size; + out->flags = in->flags; + out->type = GetSMPassType(in->type); +} ICallWrapper *CallMaker::CreateCall(void *address, CallConvention cv, @@ -39,12 +84,26 @@ ICallWrapper *CallMaker::CreateCall(void *address, const PassInfo paramInfo[], unsigned int numParams) { - CallWrapper *pWrapper = new CallWrapper(cv, paramInfo, retInfo, numParams); - pWrapper->m_Addrs[ADDR_CALLEE] = address; + SourceHook::CProtoInfoBuilder protoInfo(GetSHCallConvention(cv)); - JIT_Compile(pWrapper, FuncAddr_Direct); + for (unsigned int i=0; isize, GetSHPassType(retInfo->type), retInfo->flags, + NULL, NULL, NULL, NULL); + } + else + { + protoInfo.SetReturnType(0, SourceHook::PassInfo::PassType_Unknown, 0, + NULL, NULL, NULL, NULL); + } + + return g_CallMaker2.CreateCall(address, &(*protoInfo)); } ICallWrapper *CallMaker::CreateVCall(unsigned int vtblIdx, @@ -54,12 +113,73 @@ ICallWrapper *CallMaker::CreateVCall(unsigned int vtblIdx, const PassInfo paramInfo[], unsigned int numParams) { - CallWrapper *pWrapper = new CallWrapper(CallConv_ThisCall, paramInfo, retInfo, numParams); - pWrapper->m_VtInfo.thisOffs = thisOffs; - pWrapper->m_VtInfo.vtblIdx = vtblIdx; - pWrapper->m_VtInfo.vtblOffs = vtblOffs; + SourceHook::MemFuncInfo info; + info.isVirtual = true; + info.vtblindex = vtblIdx; + info.vtbloffs = vtblOffs; + info.thisptroffs = thisOffs; - JIT_Compile(pWrapper, FuncAddr_VTable); + SourceHook::CProtoInfoBuilder protoInfo(SourceHook::ProtoInfo::CallConv_ThisCall); + + for (unsigned int i=0; isize, GetSHPassType(retInfo->type), retInfo->flags, + NULL, NULL, NULL, NULL); + } + else + { + protoInfo.SetReturnType(0, SourceHook::PassInfo::PassType_Unknown, 0, + NULL, NULL, NULL, NULL); + } + + + return g_CallMaker2.CreateVirtualCall(&(*protoInfo), &info); +} + +ICallWrapper *CallMaker2::CreateCall(void *address, const SourceHook::ProtoInfo *protoInfo) +{ + CallWrapper *pWrapper = new CallWrapper(protoInfo); + pWrapper->SetCalleeAddr(address); + + void *addr = JIT_CallCompile(pWrapper, FuncAddr_Direct); + pWrapper->SetCodeBaseAddr(addr); return pWrapper; } + +ICallWrapper *CallMaker2::CreateVirtualCall(const SourceHook::ProtoInfo *protoInfo, + const SourceHook::MemFuncInfo *info) +{ + CallWrapper *pWrapper = new CallWrapper(protoInfo); + pWrapper->SetMemFuncInfo(info); + + void *addr = JIT_CallCompile(pWrapper, FuncAddr_VTable); + pWrapper->SetCodeBaseAddr(addr); + + return pWrapper; +} + +#if defined HOOKING_ENABLED +IHookWrapper *CallMaker2::CreateVirtualHook(SourceHook::ISourceHook *pSH, + const SourceHook::ProtoInfo *protoInfo, + const SourceHook::MemFuncInfo *info, + VIRTUAL_HOOK_PROTO f) +{ + HookWrapper *pWrapper = new HookWrapper(pSH, protoInfo, const_cast(info), (void *)f); + + void *addr = JIT_HookCompile(pWrapper); + pWrapper->SetHookWrpAddr(addr); + + ICallWrapper *callWrap = CreateVirtualCall(protoInfo, info); + pWrapper->SetCallWrapperAddr(callWrap); + + return pWrapper; +} +#endif + diff --git a/extensions/bintools/CallMaker.h b/extensions/bintools/CallMaker.h index 2ed0eeeb..c1ea7385 100644 --- a/extensions/bintools/CallMaker.h +++ b/extensions/bintools/CallMaker.h @@ -32,16 +32,12 @@ #ifndef _INCLUDE_SOURCEMOD_CALLMAKER_H_ #define _INCLUDE_SOURCEMOD_CALLMAKER_H_ -#include + +#include "CallWrapper.h" +#include "HookWrapper.h" using namespace SourceMod; -enum FuncAddrMethod -{ - FuncAddr_Direct, - FuncAddr_VTable -}; - class CallMaker : public IBinTools { public: //IBinTools @@ -58,4 +54,32 @@ public: //IBinTools unsigned int numParams); }; +class CallMaker2 +#if defined HOOKING_ENABLED + : public IBinTools2 +#endif +{ +public: //IBinTools2 + virtual ICallWrapper *CreateCall(void *address, + const SourceHook::ProtoInfo *protoInfo); + virtual ICallWrapper *CreateVirtualCall(const SourceHook::ProtoInfo *protoInfo, + const SourceHook::MemFuncInfo *info); +#if defined HOOKING_ENABLED + virtual IHookWrapper *CreateVirtualHook(SourceHook::ISourceHook *pSH, + const SourceHook::ProtoInfo *protoInfo, + const SourceHook::MemFuncInfo *info, + VIRTUAL_HOOK_PROTO f); +#endif +}; + +extern CallMaker2 g_CallMaker2; + +SourceHook::ProtoInfo::CallConvention GetSHCallConvention(SourceMod::CallConvention cv); +SourceMod::CallConvention GetSMCallConvention(SourceHook::ProtoInfo::CallConvention cv); +SourceHook::PassInfo::PassType GetSHPassType(SourceMod::PassType type); +SourceMod::PassType GetSMPassType(int type); +void GetSMPassInfo(SourceMod::PassInfo *out, const SourceHook::PassInfo *in); + + + #endif //_INCLUDE_SOURCEMOD_CALLMAKER_H_ diff --git a/extensions/bintools/CallWrapper.cpp b/extensions/bintools/CallWrapper.cpp index 5f87de5d..50150861 100644 --- a/extensions/bintools/CallWrapper.cpp +++ b/extensions/bintools/CallWrapper.cpp @@ -31,40 +31,46 @@ #include "extension.h" #include "CallWrapper.h" +#include "CallMaker.h" -CallWrapper::CallWrapper(CallConvention cv, const PassInfo *paramInfo, const PassInfo *retInfo, unsigned int numParams) +CallWrapper::CallWrapper(const SourceHook::ProtoInfo *protoInfo) { - m_Cv = cv; + m_AddrCodeBase = NULL; + m_AddrCallee = NULL; - if (numParams) + unsigned int argnum = protoInfo->numOfParams; + + m_Info = *protoInfo; + m_Info.paramsPassInfo = new SourceHook::PassInfo[argnum + 1]; + memcpy((void *)m_Info.paramsPassInfo, protoInfo->paramsPassInfo, sizeof(SourceHook::PassInfo) * (argnum+1)); + + if (argnum) { - m_Params = new PassEncode[numParams]; - for (size_t i=0; iFreePageMemory(m_Addrs[ADDR_CODEBASE]); + delete [] m_Info.paramsPassInfo; } void CallWrapper::Destroy() { + if (m_AddrCodeBase != NULL) + { + g_SPEngine->FreePageMemory(m_AddrCodeBase); + m_AddrCodeBase = NULL; + } + delete this; } CallConvention CallWrapper::GetCallConvention() { - return m_Cv; + /* Need to convert to a SourceMod convention for bcompat */ + return GetSMCallConvention((SourceHook::ProtoInfo::CallConvention)m_Info.convention); } const PassEncode *CallWrapper::GetParamInfo(unsigned int num) { - return (num+1 > m_NumParams) ? NULL : &m_Params[num]; + if (num + 1 > GetParamCount() || num < 0) + { + return NULL; + } + + return &m_Params[num]; } const PassInfo *CallWrapper::GetReturnInfo() @@ -100,12 +118,69 @@ const PassInfo *CallWrapper::GetReturnInfo() unsigned int CallWrapper::GetParamCount() { - return m_NumParams; + return m_Info.numOfParams; } void CallWrapper::Execute(void *vParamStack, void *retBuffer) { typedef void (*CALL_EXECUTE)(void *, void *); - CALL_EXECUTE fn = (CALL_EXECUTE)m_Addrs[ADDR_CODEBASE]; + CALL_EXECUTE fn = (CALL_EXECUTE)GetCodeBaseAddr(); fn(vParamStack, retBuffer); } + +void CallWrapper::SetMemFuncInfo(const SourceHook::MemFuncInfo *funcInfo) +{ + m_FuncInfo = *funcInfo; +} + +SourceHook::MemFuncInfo *CallWrapper::GetMemFuncInfo() +{ + return &m_FuncInfo; +} + +void CallWrapper::SetCalleeAddr(void *addr) +{ + m_AddrCallee = addr; +} + +void CallWrapper::SetCodeBaseAddr(void *addr) +{ + m_AddrCodeBase = addr; +} + +void *CallWrapper::GetCalleeAddr() +{ + return m_AddrCallee; +} + +void *CallWrapper::GetCodeBaseAddr() +{ + return m_AddrCodeBase; +} + +const SourceHook::PassInfo *CallWrapper::GetSHReturnInfo() +{ + return &(m_Info.retPassInfo); +} + +SourceHook::ProtoInfo::CallConvention CallWrapper::GetSHCallConvention() +{ + return (SourceHook::ProtoInfo::CallConvention)m_Info.convention; +} + +const SourceHook::PassInfo * CallWrapper::GetSHParamInfo(unsigned int num) +{ + if (num + 1 > GetParamCount() || num < 0) + { + return NULL; + } + + return &(m_Info.paramsPassInfo[num+1]); +} + +unsigned int CallWrapper::GetParamOffset(unsigned int num) +{ + assert(num <= GetParamCount() && num > 0); + + return m_Params[num].offset; +} diff --git a/extensions/bintools/CallWrapper.h b/extensions/bintools/CallWrapper.h index 1ca78bc4..a6e5848f 100644 --- a/extensions/bintools/CallWrapper.h +++ b/extensions/bintools/CallWrapper.h @@ -29,27 +29,24 @@ * Version: $Id$ */ -#ifndef _INCLUDE_SOURCEMOD_CCALLWRAPPER_H_ -#define _INCLUDE_SOURCEMOD_CCALLWRAPPER_H_ +#ifndef _INCLUDE_SOURCEMOD_CALLWRAPPER_H_ +#define _INCLUDE_SOURCEMOD_CALLWRAPPER_H_ #include +#include using namespace SourceMod; -#define ADDR_CALLEE 0 -#define ADDR_CODEBASE 1 - -struct VtableInfo +enum FuncAddrMethod { - unsigned int vtblIdx; - unsigned int vtblOffs; - unsigned int thisOffs; + FuncAddr_Direct, + FuncAddr_VTable }; class CallWrapper : public ICallWrapper { public: - CallWrapper(CallConvention cv, const PassInfo *paramInfo, const PassInfo *retInfo, unsigned int numParams); + CallWrapper(const SourceHook::ProtoInfo *protoInfo); ~CallWrapper(); public: //ICallWrapper CallConvention GetCallConvention(); @@ -58,15 +55,25 @@ public: //ICallWrapper unsigned int GetParamCount(); void Execute(void *vParamStack, void *retBuffer); void Destroy(); + const SourceHook::PassInfo *GetSHReturnInfo(); + SourceHook::ProtoInfo::CallConvention GetSHCallConvention(); + const SourceHook::PassInfo *GetSHParamInfo(unsigned int num); + unsigned int GetParamOffset(unsigned int num); public: - inline void deleteThis() { delete this; } - void *m_Addrs[4]; - VtableInfo m_VtInfo; + void SetCalleeAddr(void *addr); + void SetCodeBaseAddr(void *addr); + void *GetCalleeAddr(); + void *GetCodeBaseAddr(); + + void SetMemFuncInfo(const SourceHook::MemFuncInfo *funcInfo); + SourceHook::MemFuncInfo *GetMemFuncInfo(); private: - CallConvention m_Cv; PassEncode *m_Params; + SourceHook::ProtoInfo m_Info; PassInfo *m_RetParam; - unsigned int m_NumParams; + void *m_AddrCallee; + void *m_AddrCodeBase; + SourceHook::MemFuncInfo m_FuncInfo; }; -#endif //_INCLUDE_SOURCEMOD_CCALLWRAPPER_H_ +#endif //_INCLUDE_SOURCEMOD_CALLWRAPPER_H_ diff --git a/extensions/bintools/HookWrapper.cpp b/extensions/bintools/HookWrapper.cpp new file mode 100644 index 00000000..f3aa5456 --- /dev/null +++ b/extensions/bintools/HookWrapper.cpp @@ -0,0 +1,243 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod BinTools Extension + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: CallMaker.h 1964 2008-03-27 04:54:56Z damagedsoul $ + */ + +#if defined HOOKING_ENABLED + +#include "HookWrapper.h" +#include "jit_compile.h" + +/****************************** + * HookWrapper Implementation * + ******************************/ + +HookWrapper::HookWrapper(SourceHook::ISourceHook *pSH, + const SourceHook::ProtoInfo *proto, + SourceHook::MemFuncInfo *memfunc, + void *addr) : m_ParamOffs(NULL), m_ParamSize(0), m_RetSize(0) +{ + unsigned int argnum = proto->numOfParams; + + m_pSH = pSH; + m_ProtoInfo = *proto; + m_ProtoInfo.paramsPassInfo = new SourceHook::PassInfo[argnum + 1]; + memcpy((void *)m_ProtoInfo.paramsPassInfo, proto->paramsPassInfo, sizeof(SourceHook::PassInfo) * (argnum+1)); + m_MemFuncInfo = *memfunc; + m_FuncAddr = addr; + m_HookWrapper = NULL; + + /* If the function has parameters calculate the parameter buffer size and offsets */ + if (argnum > 0) + { + const SourceHook::PassInfo *info = m_ProtoInfo.paramsPassInfo; + m_ParamOffs = new unsigned int[argnum]; + m_ParamOffs[0] = 0; + + for (unsigned int i=1; i<=argnum; i++) + { + /* The stack should be at least aligned to a 4 byte boundary + * and byref params have the size of pointers + */ + if (((info[i].type == SourceHook::PassInfo::PassType_Basic) && + (info[i].flags & SourceHook::PassInfo::PassFlag_ByVal) && + (info[i].size < sizeof(void *))) || + (info[i].flags & SourceHook::PassInfo::PassFlag_ByRef)) + { + m_ParamSize += sizeof(void *); + } + else + { + m_ParamSize += info[i].size; + } + + //:TODO: fix this! + if (i < argnum) + { + m_ParamOffs[i] = m_ParamSize; + } + } + } + + /* Finally calculate the retval size if any */ + size_t retsize = m_ProtoInfo.retPassInfo.size; + if (retsize != 0) + { + if (m_ProtoInfo.retPassInfo.flags & SourceHook::PassInfo::PassFlag_ByRef) + { + m_RetSize = sizeof(void *); + } + else + { + m_RetSize = (retsize < sizeof(void *)) ? sizeof(void *) : retsize; + } + } +} + +ISMDelegate *HookWrapper::CreateDelegate(void *data) +{ + SMDelegate *pDeleg = new SMDelegate(data); + pDeleg->PatchVtable(m_HookWrapper); + + return pDeleg; +} + +unsigned int HookWrapper::GetParamCount() +{ + return m_ProtoInfo.numOfParams; +} + +unsigned int HookWrapper::GetParamOffset(unsigned int argnum, unsigned int *size) +{ + assert(m_ParamOffs && argnum < (unsigned int)m_ProtoInfo.numOfParams); + + if (size) + { + *size = m_ProtoInfo.paramsPassInfo[argnum+1].size; + } + + return m_ParamOffs[argnum]; +} + +void HookWrapper::PerformRecall(void *params, void *retval) +{ + /* Notify SourceHook of the upcoming recall */ + SH_GLOB_SHPTR->DoRecall(); + + /* Add thisptr into params buffer */ + unsigned char *newparams = new unsigned char[sizeof(void *) + m_ParamSize]; + *(void **)newparams = META_IFACEPTR(void); + memcpy(newparams + sizeof(void *), params, m_ParamSize); + + /* Execute the call */ + m_CallWrapper->Execute(newparams, retval); + + SET_META_RESULT(MRES_SUPERCEDE); + + return; +} + +void HookWrapper::Destroy() +{ + if (m_HookWrapper != NULL) + { + JIT_FreeHook(m_HookWrapper); + } + + if (m_CallWrapper != NULL) + { + m_CallWrapper->Destroy(); + } + + delete this; +} + +unsigned int HookWrapper::GetParamSize() +{ + return m_ParamSize; +} + +SourceHook::ProtoInfo * HookWrapper::GetProtoInfo() +{ + return &m_ProtoInfo; +} + +unsigned int HookWrapper::GetRetSize() +{ + return m_RetSize; +} + +void * HookWrapper::GetHandlerAddr() +{ + return m_FuncAddr; +} + +void HookWrapper::SetCallWrapperAddr( ICallWrapper *wrap ) +{ + m_CallWrapper = wrap; +} + +void HookWrapper::SetHookWrpAddr( void *addr ) +{ + m_HookWrapper = addr; +} + +HookWrapper::~HookWrapper() +{ + delete [] m_ProtoInfo.paramsPassInfo; + delete [] m_ParamOffs; +} +/***************************** + * SMDelegate Implementation * + *****************************/ + +SMDelegate::SMDelegate(void *data) +{ + m_Data = data; +} + +bool SMDelegate::IsEqual(ISHDelegate *pOtherDeleg) +{ + return false; +} + +void SMDelegate::DeleteThis() +{ + /* Remove our allocated vtable */ + delete [] *reinterpret_cast(this); + + delete this; +} + +void SMDelegate::Call() +{ +} + +void *SMDelegate::GetUserData() +{ + return m_Data; +} + +/** + * This duplicates the vtable (so each instance has a unique table) and patches SMDelgate::Call to point to our allocated function + */ +void SMDelegate::PatchVtable(void *addr) +{ + void **new_vtptr = new void *[4]; + void **cur_vtptr = *reinterpret_cast(this); + memcpy(new_vtptr, cur_vtptr, sizeof(void *)*4); + + *reinterpret_cast(this) = new_vtptr; + + void *cur_vfnptr = reinterpret_cast(new_vtptr + VTABLE_PATCH_OFFS); + + *reinterpret_cast(cur_vfnptr) = addr; +} + +#endif diff --git a/extensions/bintools/HookWrapper.h b/extensions/bintools/HookWrapper.h new file mode 100644 index 00000000..e5294cb2 --- /dev/null +++ b/extensions/bintools/HookWrapper.h @@ -0,0 +1,97 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod BinTools Extension + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: CallMaker.h 1964 2008-03-27 04:54:56Z damagedsoul $ + */ + +#ifndef _INCLUDE_SOURCEMOD_HOOKWRAPPER_H_ +#define _INCLUDE_SOURCEMOD_HOOKWRAPPER_H_ + +#if defined HOOKING_ENABLED + +#include "smsdk_ext.h" +#include "sourcehook.h" +#include "IBinTools.h" + +#define VTABLE_PATCH_OFFS 2 + +using namespace SourceMod; + +class HookWrapper : public IHookWrapper +{ +public: + HookWrapper(SourceHook::ISourceHook *pSH, + const SourceHook::ProtoInfo *proto, + SourceHook::MemFuncInfo *memfunc, + void *addr + ); + ~HookWrapper(); +public: //IHookWrapper + ISMDelegate *CreateDelegate(void *data); + unsigned int GetParamCount(); + unsigned int GetParamOffset(unsigned int argnum, unsigned int *size); + void PerformRecall(void *params, void *retval); + void Destroy(); +public: + unsigned int GetParamSize(); + unsigned int GetRetSize(); + SourceHook::ProtoInfo *GetProtoInfo(); + void *GetHandlerAddr(); + void SetHookWrpAddr(void *addr); + void SetCallWrapperAddr(ICallWrapper *wrap); +private: + void *m_FuncAddr; + void *m_HookWrapper; + SourceHook::ISourceHook *m_pSH; + unsigned int *m_ParamOffs; + unsigned int m_ParamSize; + unsigned int m_RetSize; + SourceHook::MemFuncInfo m_MemFuncInfo; + SourceHook::ProtoInfo m_ProtoInfo; + ICallWrapper *m_CallWrapper; +}; + +class SMDelegate : public ISMDelegate +{ +public: + SMDelegate(void *data); +public: //SourceHook::ISHDelegate + bool IsEqual(ISHDelegate *pOtherDeleg); + void DeleteThis(); + void Call(); +public: //ISMDelegate + void *GetUserData(); +public: + void PatchVtable(void *addr); +private: + void *m_Data; +}; + +#endif + +#endif //_INCLUDE_SOURCEMOD_HOOKWRAPPER_H_ diff --git a/extensions/bintools/Makefile b/extensions/bintools/Makefile index 02a1e87e..f801aeb0 100644 --- a/extensions/bintools/Makefile +++ b/extensions/bintools/Makefile @@ -14,10 +14,7 @@ MMSOURCE17 = ../../../mmsource-1.7 PROJECT = bintools -#Uncomment for Metamod: Source enabled extension -#USEMETA = true - -OBJECTS = sdk/smsdk_ext.cpp extension.cpp jit_call.cpp CallWrapper.cpp CallMaker.cpp +OBJECTS = sdk/smsdk_ext.cpp extension.cpp jit_call.cpp CallWrapper.cpp CallMaker.cpp HookWrapper.cpp jit_hook.cpp ############################################## ### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### @@ -49,6 +46,8 @@ ifeq "$(ENGINE)" "orangebox" INCLUDE += -I$(HL2SDK)/public/game/server SRCDS = $(SRCDS_BASE)/orangebox override ENGSET = true + USEMETA = true + CFLAGS += -DHOOKING_ENABLED endif ifeq "$(ENGINE)" "left4dead" HL2SDK = $(HL2SDK_L4D) @@ -59,6 +58,8 @@ ifeq "$(ENGINE)" "left4dead" INCLUDE += -I$(HL2SDK)/public/game/server SRCDS = $(SRCDS_BASE)/l4d override ENGSET = true + USEMETA = true + CFLAGS += -DHOOKING_ENABLED endif ifeq "$(USEMETA)" "true" diff --git a/extensions/bintools/extension.cpp b/extensions/bintools/extension.cpp index b62d0287..276e1f5e 100644 --- a/extensions/bintools/extension.cpp +++ b/extensions/bintools/extension.cpp @@ -39,6 +39,7 @@ BinTools g_BinTools; /**< Global singleton for extension's main interface */ CallMaker g_CallMaker; +CallMaker2 g_CallMaker2; ISourcePawnEngine *g_SPEngine; SMEXT_LINK(&g_BinTools); @@ -47,6 +48,9 @@ bool BinTools::SDK_OnLoad(char *error, size_t maxlength, bool late) { g_SPEngine = g_pSM->GetScriptingEngine(); g_pShareSys->AddInterface(myself, &g_CallMaker); +#if defined METAMOD_PLAPI_VERSION + g_pShareSys->AddInterface(myself, &g_CallMaker2); +#endif return true; } diff --git a/extensions/bintools/jit_call.cpp b/extensions/bintools/jit_call.cpp index ea0755c4..576b3b67 100644 --- a/extensions/bintools/jit_call.cpp +++ b/extensions/bintools/jit_call.cpp @@ -33,38 +33,12 @@ #include "extension.h" #include #include -#include "jit_call.h" +#include "jit_compile.h" jit_uint32_t g_StackUsage = 0; jit_uint32_t g_StackAlign = 0; jit_uint32_t g_RegDecoder = 0; -/******************** - * Assembly Helpers * - ********************/ - -inline jit_uint8_t _DecodeRegister3(jit_uint32_t val) -{ - switch (val % 3) - { - case 0: - { - return REG_EAX; - } - case 1: - { - return REG_EDX; - } - case 2: - { - return REG_ECX; - } - } - - /* Should never happen */ - return 0xFF; -} - /******************** * Assembly Opcodes * ********************/ @@ -137,25 +111,25 @@ inline void Write_Function_Epilogue(JitWriter *jit, bool is_void, bool has_param IA32_Return(jit); } -inline void Write_PushPOD(JitWriter *jit, const PassEncode *pEnc) +inline void Write_PushPOD(JitWriter *jit, const SourceHook::PassInfo *info, unsigned int offset) { jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++); - if (pEnc->info.flags & PASSFLAG_BYVAL) + if (info->flags & PASSFLAG_BYVAL) { - switch (pEnc->info.size) + switch (info->size) { case 1: { //movzx reg, BYTE PTR [ebx+] //push reg - if (pEnc->offset < SCHAR_MAX) + if (offset < SCHAR_MAX) { - IA32_Movzx_Reg32_Rm8_Disp8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset); - } else if (!pEnc->offset) { + IA32_Movzx_Reg32_Rm8_Disp8(jit, reg, REG_EBX, (jit_int8_t)offset); + } else if (!offset) { IA32_Movzx_Reg32_Rm8(jit, reg, REG_EBX, MOD_MEM_REG); } else { - IA32_Movzx_Reg32_Rm8_Disp32(jit, reg, REG_EBX, pEnc->offset); + IA32_Movzx_Reg32_Rm8_Disp32(jit, reg, REG_EBX, offset); } IA32_Push_Reg(jit, reg); @@ -167,13 +141,13 @@ inline void Write_PushPOD(JitWriter *jit, const PassEncode *pEnc) //movzx reg, WORD PTR [ebx+] //push reg jit->write_ubyte(IA32_16BIT_PREFIX); - if (pEnc->offset < SCHAR_MAX) + if (offset < SCHAR_MAX) { - IA32_Movzx_Reg32_Rm16_Disp8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset); - } else if (!pEnc->offset) { + IA32_Movzx_Reg32_Rm16_Disp8(jit, reg, REG_EBX, (jit_int8_t)offset); + } else if (!offset) { IA32_Movzx_Reg32_Rm16(jit, reg, REG_EBX, MOD_MEM_REG); } else { - IA32_Movzx_Reg32_Rm16_Disp32(jit, reg, REG_EBX, pEnc->offset); + IA32_Movzx_Reg32_Rm16_Disp32(jit, reg, REG_EBX, offset); } IA32_Push_Reg(jit, reg); @@ -184,13 +158,13 @@ inline void Write_PushPOD(JitWriter *jit, const PassEncode *pEnc) { //mov reg, DWORD PTR [ebx+] //push reg - if (pEnc->offset < SCHAR_MAX) + if (offset < SCHAR_MAX) { - IA32_Mov_Reg_Rm_Disp8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset); - } else if (!pEnc->offset) { + IA32_Mov_Reg_Rm_Disp8(jit, reg, REG_EBX, (jit_int8_t)offset); + } else if (!offset) { IA32_Mov_Reg_Rm(jit, reg, REG_EBX, MOD_MEM_REG); } else { - IA32_Mov_Reg_Rm_Disp32(jit, reg, REG_EBX, pEnc->offset); + IA32_Mov_Reg_Rm_Disp32(jit, reg, REG_EBX, offset); } IA32_Push_Reg(jit, reg); @@ -205,19 +179,19 @@ inline void Write_PushPOD(JitWriter *jit, const PassEncode *pEnc) //push reg2 jit_uint8_t reg2 = _DecodeRegister3(g_RegDecoder++); - if (pEnc->offset+4 < SCHAR_MAX) + if (offset+4 < SCHAR_MAX) { - IA32_Mov_Reg_Rm_Disp8(jit, reg, REG_EBX, (jit_int8_t)(pEnc->offset+4)); + IA32_Mov_Reg_Rm_Disp8(jit, reg, REG_EBX, (jit_int8_t)(offset+4)); } else { - IA32_Mov_Reg_Rm_Disp32(jit, reg, REG_EBX, pEnc->offset+4); + IA32_Mov_Reg_Rm_Disp32(jit, reg, REG_EBX, offset+4); } - if (pEnc->offset < SCHAR_MAX) + if (offset < SCHAR_MAX) { - IA32_Mov_Reg_Rm_Disp8(jit, reg2, REG_EBX, (jit_int8_t)pEnc->offset); - } else if (!pEnc->offset) { + IA32_Mov_Reg_Rm_Disp8(jit, reg2, REG_EBX, (jit_int8_t)offset); + } else if (!offset) { IA32_Mov_Reg_Rm(jit, reg2, REG_EBX, MOD_MEM_REG); } else { - IA32_Mov_Reg_Rm_Disp32(jit, reg2, REG_EBX, pEnc->offset); + IA32_Mov_Reg_Rm_Disp32(jit, reg2, REG_EBX, offset); } IA32_Push_Reg(jit, reg); IA32_Push_Reg(jit, reg2); @@ -226,20 +200,20 @@ inline void Write_PushPOD(JitWriter *jit, const PassEncode *pEnc) break; } } - } else if (pEnc->info.flags & PASSFLAG_BYREF) { + } else if (info->flags & PASSFLAG_BYREF) { //lea reg, [ebx+] //push reg - if (!pEnc->offset) + if (!offset) { IA32_Push_Reg(jit, REG_EBX); g_StackUsage += 4; return; } - if (pEnc->offset < SCHAR_MAX) + if (offset < SCHAR_MAX) { - IA32_Lea_DispRegImm8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset); + IA32_Lea_DispRegImm8(jit, reg, REG_EBX, (jit_int8_t)offset); } else { - IA32_Lea_DispRegImm32(jit, reg, REG_EBX, pEnc->offset); + IA32_Lea_DispRegImm32(jit, reg, REG_EBX, offset); } IA32_Push_Reg(jit, reg); @@ -247,24 +221,24 @@ inline void Write_PushPOD(JitWriter *jit, const PassEncode *pEnc) } } -inline void Write_PushFloat(JitWriter *jit, const PassEncode *pEnc) +inline void Write_PushFloat(JitWriter *jit, const SourceHook::PassInfo *info, unsigned int offset) { - if (pEnc->info.flags & PASSFLAG_BYVAL) + if (info->flags & PASSFLAG_BYVAL) { - switch (pEnc->info.size) + switch (info->size) { case 4: { //fld DWORD PTR [ebx+] //push reg //fstp DWORD PTR [esp] - if (pEnc->offset < SCHAR_MAX) + if (offset < SCHAR_MAX) { - IA32_Fld_Mem32_Disp8(jit, REG_EBX, (jit_int8_t)pEnc->offset); - } else if (!pEnc->offset) { + IA32_Fld_Mem32_Disp8(jit, REG_EBX, (jit_int8_t)offset); + } else if (!offset) { IA32_Fld_Mem32(jit, REG_EBX); } else { - IA32_Fld_Mem32_Disp32(jit, REG_EBX, pEnc->offset); + IA32_Fld_Mem32_Disp32(jit, REG_EBX, offset); } IA32_Push_Reg(jit, _DecodeRegister3(g_RegDecoder++)); IA32_Fstp_Mem32_ESP(jit); @@ -276,13 +250,13 @@ inline void Write_PushFloat(JitWriter *jit, const PassEncode *pEnc) //fld QWORD PTR [ebx+] //sub esp, 8 //fstp QWORD PTR [esp] - if (pEnc->offset < SCHAR_MAX) + if (offset < SCHAR_MAX) { - IA32_Fld_Mem64_Disp8(jit, REG_EBX, (jit_int8_t)pEnc->offset); - } else if (!pEnc->offset) { + IA32_Fld_Mem64_Disp8(jit, REG_EBX, (jit_int8_t)offset); + } else if (!offset) { IA32_Fld_Mem64(jit, REG_EBX); } else { - IA32_Fld_Mem64_Disp32(jit, REG_EBX, pEnc->offset); + IA32_Fld_Mem64_Disp32(jit, REG_EBX, offset); } IA32_Sub_Rm_Imm8(jit, REG_ESP, 8, MOD_REG); IA32_Fstp_Mem64_ESP(jit); @@ -290,10 +264,10 @@ inline void Write_PushFloat(JitWriter *jit, const PassEncode *pEnc) break; } } - } else if (pEnc->info.flags & PASSFLAG_BYREF) { + } else if (info->flags & PASSFLAG_BYREF) { //lea reg, [ebx+] //push reg - if (!pEnc->offset) + if (!offset) { IA32_Push_Reg(jit, REG_EBX); g_StackUsage += 4; @@ -301,11 +275,11 @@ inline void Write_PushFloat(JitWriter *jit, const PassEncode *pEnc) } jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++); - if (pEnc->offset < SCHAR_MAX) + if (offset < SCHAR_MAX) { - IA32_Lea_DispRegImm8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset); + IA32_Lea_DispRegImm8(jit, reg, REG_EBX, (jit_int8_t)offset); } else { - IA32_Lea_DispRegImm32(jit, reg, REG_EBX, pEnc->offset); + IA32_Lea_DispRegImm32(jit, reg, REG_EBX, offset); } IA32_Push_Reg(jit, reg); @@ -313,18 +287,18 @@ inline void Write_PushFloat(JitWriter *jit, const PassEncode *pEnc) } } -inline void Write_PushObject(JitWriter *jit, const PassEncode *pEnc) +inline void Write_PushObject(JitWriter *jit, const SourceHook::PassInfo *info, unsigned int offset) { - if (pEnc->info.flags & PASSFLAG_BYVAL) + if (info->flags & PASSFLAG_BYVAL) { #ifdef PLATFORM_POSIX - if (pEnc->info.flags & PASSFLAG_ODTOR) + if (info->flags & PASSFLAG_ODTOR) { goto push_byref; } #endif - jit_uint32_t dwords = pEnc->info.size >> 2; - jit_uint32_t bytes = pEnc->info.size & 0x3; + jit_uint32_t dwords = info->size >> 2; + jit_uint32_t bytes = info->size & 0x3; //sub esp, //cld @@ -340,23 +314,23 @@ inline void Write_PushObject(JitWriter *jit, const PassEncode *pEnc) // rep movsb //pop esi //pop edi - if (pEnc->info.size < SCHAR_MAX) + if (info->size < SCHAR_MAX) { - IA32_Sub_Rm_Imm8(jit, REG_ESP, (jit_int8_t)pEnc->info.size, MOD_REG); + IA32_Sub_Rm_Imm8(jit, REG_ESP, (jit_int8_t)info->size, MOD_REG); } else { - IA32_Sub_Rm_Imm32(jit, REG_ESP, pEnc->info.size, MOD_REG); + IA32_Sub_Rm_Imm32(jit, REG_ESP, info->size, MOD_REG); } IA32_Cld(jit); IA32_Push_Reg(jit, REG_EDI); IA32_Push_Reg(jit, REG_ESI); IA32_Lea_Reg_DispRegMultImm8(jit, REG_EDI, REG_NOIDX, REG_ESP, NOSCALE, 8); - if (pEnc->offset < SCHAR_MAX) + if (offset < SCHAR_MAX) { - IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBX, (jit_int8_t)pEnc->offset); - } else if (!pEnc->offset) { + IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBX, (jit_int8_t)offset); + } else if (!offset) { IA32_Mov_Reg_Rm(jit, REG_ESI, REG_EBX, MOD_REG); } else { - IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBX, pEnc->offset); + IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBX, offset); } if (dwords) { @@ -373,12 +347,12 @@ inline void Write_PushObject(JitWriter *jit, const PassEncode *pEnc) IA32_Pop_Reg(jit, REG_ESI); IA32_Pop_Reg(jit, REG_EDI); - g_StackUsage += pEnc->info.size; - } else if (pEnc->info.flags & PASSFLAG_BYREF) { + g_StackUsage += info->size; + } else if (info->flags & PASSFLAG_BYREF) { #ifdef PLATFORM_POSIX push_byref: #endif - if (!pEnc->offset) + if (!offset) { IA32_Push_Reg(jit, REG_EBX); g_StackUsage += 4; @@ -388,11 +362,11 @@ push_byref: //lea reg, [ebx+] //push reg jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++); - if (pEnc->offset < SCHAR_MAX) + if (offset < SCHAR_MAX) { - IA32_Lea_DispRegImm8(jit, reg, REG_EBX, (jit_int8_t)pEnc->offset); + IA32_Lea_DispRegImm8(jit, reg, REG_EBX, (jit_int8_t)offset); } else { - IA32_Lea_DispRegImm32(jit, reg, REG_EBX, pEnc->offset); + IA32_Lea_DispRegImm32(jit, reg, REG_EBX, offset); } IA32_Push_Reg(jit, reg); @@ -429,15 +403,16 @@ inline void Write_CallFunction(JitWriter *jit, FuncAddrMethod method, CallWrappe { //call jitoffs_t call = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32_Abs(jit, call, pWrapper->m_Addrs[ADDR_CALLEE]); + IA32_Write_Jump32_Abs(jit, call, pWrapper->GetCalleeAddr()); } else if (method == FuncAddr_VTable) { //*(this + thisOffs + vtblOffs)[vtblIdx] //mov edx, [ebx] //mov eax, [edx++] //mov edx, [eax+*4] //call edx - jit_uint32_t total_offs = pWrapper->m_VtInfo.thisOffs + pWrapper->m_VtInfo.vtblOffs; - jit_uint32_t vfunc_pos = pWrapper->m_VtInfo.vtblIdx * 4; + SourceHook::MemFuncInfo *funcInfo = pWrapper->GetMemFuncInfo(); + jit_uint32_t total_offs = funcInfo->thisptroffs + funcInfo->vtbloffs; + jit_uint32_t vfunc_pos = funcInfo->vtblindex * 4; IA32_Mov_Reg_Rm(jit, REG_EDX, REG_EBX, MOD_MEM_REG); if (total_offs < SCHAR_MAX) @@ -529,7 +504,7 @@ inline void Write_MovRet2Buf(JitWriter *jit, const PassInfo *pRet) * Assembly Compiler Function * ******************************/ -void JIT_Compile(CallWrapper *pWrapper, FuncAddrMethod method) +void *JIT_CallCompile(CallWrapper *pWrapper, FuncAddrMethod method) { JitWriter writer; JitWriter *jit = &writer; @@ -553,22 +528,25 @@ jit_rewind: /* Write parameter push code */ for (jit_int32_t i=ParamCount-1; i>=0; i--) { - const PassEncode *pEnc = pWrapper->GetParamInfo(i); - switch (pEnc->info.type) + unsigned int offset = pWrapper->GetParamOffset(i); + const SourceHook::PassInfo *info = pWrapper->GetSHParamInfo(i); + assert(info != NULL); + + switch (info->type) { - case PassType_Basic: + case SourceHook::PassInfo::PassType_Basic: { - Write_PushPOD(jit, pEnc); + Write_PushPOD(jit, info, offset); break; } - case PassType_Float: + case SourceHook::PassInfo::PassType_Float: { - Write_PushFloat(jit, pEnc); + Write_PushFloat(jit, info, offset); break; } - case PassType_Object: + case SourceHook::PassInfo::PassType_Object: { - Write_PushObject(jit, pEnc); + Write_PushObject(jit, info, offset); break; } } @@ -640,7 +618,7 @@ skip_retbuffer: writer.outbase = (jitcode_t)g_SPEngine->AllocatePageMemory(CodeSize); g_SPEngine->SetReadWrite(writer.outbase); writer.outptr = writer.outbase; - pWrapper->m_Addrs[ADDR_CODEBASE] = writer.outbase; + pWrapper->SetCodeBaseAddr(writer.outbase); g_StackAlign = (g_StackUsage) ? ((g_StackUsage & 0xFFFFFFF0) + 16) - g_StackUsage : 0; g_StackUsage = 0; g_RegDecoder = 0; @@ -648,4 +626,5 @@ skip_retbuffer: goto jit_rewind; } g_SPEngine->SetReadExecute(writer.outbase); + return writer.outbase; } diff --git a/extensions/bintools/jit_call.h b/extensions/bintools/jit_compile.h similarity index 65% rename from extensions/bintools/jit_call.h rename to extensions/bintools/jit_compile.h index 39d46412..b1d42c60 100644 --- a/extensions/bintools/jit_call.h +++ b/extensions/bintools/jit_compile.h @@ -29,12 +29,47 @@ * Version: $Id$ */ -#ifndef _INCLUDE_SOURCEMOD_JIT_CALL_H_ -#define _INCLUDE_SOURCEMOD_JIT_CALL_H_ +#ifndef _INCLUDE_SOURCEMOD_JIT_COMPILE_H_ +#define _INCLUDE_SOURCEMOD_JIT_COMPILE_H_ -#include "CallMaker.h" +#include +#include #include "CallWrapper.h" +#include "HookWrapper.h" -void JIT_Compile(CallWrapper *pWrapper, FuncAddrMethod method); +void *JIT_CallCompile(CallWrapper *pWrapper, FuncAddrMethod method); +#if defined HOOKING_ENABLED +void *JIT_HookCompile(HookWrapper *pWrapper); +void JIT_FreeHook(void *addr); +#endif -#endif //_INCLUDE_SOURCEMOD_JIT_CALL_H_ +/******************** + * Assembly Helpers * + ********************/ + +inline jit_uint8_t _DecodeRegister3(jit_uint32_t val) +{ + switch (val % 3) + { + case 0: + { + return REG_EAX; + } + case 1: + { + return REG_EDX; + } + case 2: + { + return REG_ECX; + } + } + + /* Should never happen */ + assert(false); + return 0xFF; +} + +extern jit_uint32_t g_RegDecoder; + +#endif //_INCLUDE_SOURCEMOD_JIT_COMPILE_H_ diff --git a/extensions/bintools/jit_hook.cpp b/extensions/bintools/jit_hook.cpp new file mode 100644 index 00000000..deb4b8bc --- /dev/null +++ b/extensions/bintools/jit_hook.cpp @@ -0,0 +1,430 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod BinTools Extension + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id: CallMaker.h 1964 2008-03-27 04:54:56Z damagedsoul $ + */ + +#if defined HOOKING_ENABLED + +#include "jit_compile.h" +#include "sm_platform.h" +#include "extension.h" + +/******************** + * Assembly Opcodes * + ********************/ + +inline void Write_Function_Prologue(JitWriter *jit, bool RetInMemory) +{ + //push ebp + //push ebx + //mov ebp, esp + IA32_Push_Reg(jit, REG_EBP); + IA32_Push_Reg(jit, REG_EBX); + IA32_Mov_Reg_Rm(jit, REG_EBP, REG_ESP, MOD_REG); +#if defined PLATFORM_WINDOWS + //mov ebx, ecx + IA32_Mov_Reg_Rm(jit, REG_EBX, REG_ECX, MOD_REG); +#elif defined PLATFORM_LINUX + //mov ebx, [ebp+12+(RetInMemory)?4:0] + IA32_Mov_Reg_Rm_Disp8(jit, REG_EBX, REG_EBP, 12+((RetInMemory)?4:0)); +#endif +} + +inline void Write_Function_Epilogue(JitWriter *jit, unsigned short size) +{ + //mov esp, ebp + //pop ebx + //pop ebp + //ret + IA32_Mov_Reg_Rm(jit, REG_ESP, REG_EBP, MOD_REG); + IA32_Pop_Reg(jit, REG_EBX); + IA32_Pop_Reg(jit, REG_EBP); + if (size == 0) + { + IA32_Return(jit); + } + else + { + IA32_Return_Popstack(jit, size); + } +} + +inline void Write_Stack_Alloc(JitWriter *jit, jit_uint32_t size) +{ + //sub esp, + if (size <= SCHAR_MAX) + { + IA32_Sub_Rm_Imm8(jit, REG_ESP, (jit_int8_t)size, MOD_REG); + } + else + { + IA32_Sub_Rm_Imm32(jit, REG_ESP, size, MOD_REG); + } +} + +inline void Write_Copy_Params(JitWriter *jit, bool RetInMemory, jit_uint32_t retsize, jit_uint32_t paramsize) +{ + //:TODO: inline this memcpy!! - For small numbers of params mov's (with clever reg allocation?) would be faster + + //cld + //push edi + //push esi + //lea edi, [ebp--] + //lea esi, [ebp+12+(RetInMemory)?4:0] + //if dwords + // mov ecx, + // rep movsd + //if bytes + // mov ecx, + // rep movsb + //pop esi + //pop edi + + jit_int32_t offs; + jit_uint32_t dwords = paramsize >> 2; + jit_uint32_t bytes = paramsize & 0x3; + + IA32_Cld(jit); + IA32_Push_Reg(jit, REG_EDI); + IA32_Push_Reg(jit, REG_ESI); + offs = -(jit_int32_t)retsize - paramsize; + + if (offs > SCHAR_MIN) + { + IA32_Lea_DispRegImm8(jit, REG_EDI, REG_EBP, (jit_int8_t)offs); + } + else + { + IA32_Lea_DispRegImm32(jit, REG_EDI, REG_EBP, offs); + } + offs = 12 + ((RetInMemory) ? sizeof(void *) : 0); + +#if defined PLATFORM_LINUX + offs += 4; +#endif + + if (offs < SCHAR_MAX) + { + IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBP, (jit_int8_t)offs); + } + else + { + IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBP, offs); + } + if (dwords) + { + IA32_Mov_Reg_Imm32(jit, REG_ECX, dwords); + IA32_Rep(jit); + IA32_Movsd(jit); + } + if (bytes) + { + IA32_Mov_Reg_Imm32(jit, REG_ECX, bytes); + IA32_Rep(jit); + IA32_Movsb(jit); + } + IA32_Pop_Reg(jit, REG_ESI); + IA32_Pop_Reg(jit, REG_EDI); +} + +inline void Write_Push_Params(JitWriter *jit, + bool isretvoid, + bool isvoid, + jit_uint32_t retsize, + jit_uint32_t paramsize, + HookWrapper *pWrapper) +{ + //and esp, 0xFFFFFFF0 + IA32_And_Rm_Imm8(jit, REG_ESP, MOD_REG, -16); + + //if retvoid + // push 0 + //else + // lea reg, [ebp-] + // push reg + if (isretvoid) + { + IA32_Push_Imm8(jit, 0); + } + else + { + jit_int32_t offs = -(jit_int32_t)retsize; + if (offs >= SCHAR_MIN) + { + IA32_Lea_DispRegImm8(jit, REG_EAX, REG_EBP, (jit_int8_t)offs); + } + else + { + IA32_Lea_DispRegImm32(jit, REG_EAX, REG_EBP, offs); + } + IA32_Push_Reg(jit, REG_EAX); + } + + //if void + // push 0 + //else + // lea reg, [ebp--] + // push reg + if (isvoid) + { + IA32_Push_Imm8(jit, 0); + } + else + { + jit_int32_t offs = -(jit_int32_t)retsize - paramsize; + if (offs > SCHAR_MIN) + { + IA32_Lea_DispRegImm8(jit, REG_EDX, REG_EBP, (jit_int8_t)offs); + } else { + IA32_Lea_DispRegImm32(jit, REG_EDX, REG_EBP, offs); + } + IA32_Push_Reg(jit, REG_EDX); + } + + //push eax (thisptr) + //IA32_Push_Reg(jit, REG_ECX); + + //push ebx + IA32_Push_Reg(jit, REG_EBX); + + //push + IA32_Push_Imm32(jit, (jit_int32_t)pWrapper); +} + +inline void Write_Call_Handler(JitWriter *jit, void *addr) +{ + //call + jitoffs_t call = IA32_Call_Imm32(jit, 0); + IA32_Write_Jump32_Abs(jit, call, addr); +} + +inline void Write_Copy_RetVal(JitWriter *jit, SourceHook::PassInfo *pRetInfo) +{ + /* If the return value is a reference the size will probably be >sizeof(void *) + * for objects, we need to fix that so we can actually return the reference. + */ + size_t size = pRetInfo->size; + if (pRetInfo->flags & PASSFLAG_BYREF) + { + size = sizeof(void *); + } + + if (pRetInfo->type == SourceHook::PassInfo::PassType_Float && + pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal) + { + switch (size) + { + case 4: + { + //fld DWORD PTR [ebp-4] + IA32_Fld_Mem32_Disp8(jit, REG_EBP, -4); + break; + } + case 8: + { + //fld QWORD PTR [ebp-8] + IA32_Fld_Mem64_Disp8(jit, REG_EBP, -8); + break; + } + } + } + else if (pRetInfo->type == SourceHook::PassInfo::PassType_Object && + pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal) + { + //cld + //push edi + //push esi + //mov edi, [ebp+12] + //lea esi, [ebp-] + //if dwords + // mov ecx, + // rep movsd + //if bytes + // mov ecx, + // rep movsb + //pop esi + //pop edi + + jit_int32_t offs; + jit_uint32_t dwords = size >> 2; + jit_uint32_t bytes = size & 0x3; + + IA32_Cld(jit); + IA32_Push_Reg(jit, REG_EDI); + IA32_Push_Reg(jit, REG_ESI); + + IA32_Mov_Reg_Rm_Disp8(jit, REG_EDI, REG_EBP, 12); + + offs = -(jit_int32_t)pRetInfo->size; + if (offs >= SCHAR_MIN) + { + IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBP, (jit_int8_t)offs); + } + else + { + IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBP, offs); + } + IA32_Mov_Reg_Rm(jit, REG_EAX, REG_EDI, MOD_REG); + + if (dwords) + { + IA32_Mov_Reg_Imm32(jit, REG_ECX, dwords); + IA32_Rep(jit); + IA32_Movsd(jit); + } + if (bytes) + { + IA32_Mov_Reg_Imm32(jit, REG_ECX, bytes); + IA32_Rep(jit); + IA32_Movsb(jit); + } + IA32_Pop_Reg(jit, REG_ESI); + IA32_Pop_Reg(jit, REG_EDI); + } + else + { + switch (size) + { + case 1: + { + //mov al, BYTE PTR [ebp-4] + IA32_Mov_Reg8_Rm8_Disp8(jit, REG_EAX, REG_EBP, -4); + break; + } + case 2: + { + //mov ax, WORD PTR [ebp-4] + jit->write_ubyte(IA32_16BIT_PREFIX); + IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, -4); + break; + } + case 4: + { + //mov eax, DWORD PTR [ebp-4] + IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, -4); + break; + } + case 8: + { + //mov eax, DWORD PTR [ebp-8] + //mov edx, DWORD PTR [ebp-4] + //:TODO: this is broken due to SH + IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, -8); + IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_EBP, -4); + break; + } + } + } +} + +/****************************** + * Assembly Compiler Function * + ******************************/ + +void *JIT_HookCompile(HookWrapper *pWrapper) +{ + JitWriter writer; + JitWriter *jit = &writer; + + jit_uint32_t CodeSize = 0; + jit_uint32_t ParamSize = pWrapper->GetParamSize(); + jit_uint32_t RetSize = pWrapper->GetRetSize(); + + /* Local variable size allocated in the stack for the param and retval buffers */ + jit_uint32_t LocalVarSize = ParamSize + RetSize; + + /* Check if the return value is returned in memory */ + bool RetInMemory = false; + SourceHook::PassInfo *pRetInfo = &pWrapper->GetProtoInfo()->retPassInfo; + if ((pRetInfo->type == SourceHook::PassInfo::PassType_Object) && + (pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal)) + { + RetInMemory = true; + } + + writer.outbase = NULL; + writer.outptr = NULL; + +jit_rewind: + /* Write the function prologue */ + Write_Function_Prologue(jit, RetInMemory); + + /* Allocate the local variables into the stack */ + if (LocalVarSize) + { + Write_Stack_Alloc(jit, LocalVarSize); + } + + /* Copy all the parameters into the buffer */ + if (ParamSize) + { + Write_Copy_Params(jit, RetInMemory, RetSize, ParamSize); + } + + /* Push the parameters into the handler */ + Write_Push_Params(jit, !RetSize, !ParamSize, RetSize, ParamSize, pWrapper); + + /* Call the handler function */ + Write_Call_Handler(jit, pWrapper->GetHandlerAddr()); + + /* Copy back the return value into eax or the hidden return buffer */ + if (RetSize) + { + Write_Copy_RetVal(jit, pRetInfo); + } + + /* Write the function epilogue */ + Write_Function_Epilogue(jit, +#if defined PLATFORM_WINDOWS + ParamSize + ((RetInMemory) ? sizeof(void *) : 0) +#elif defined PLATFORM_LINUX + (RetInMemory) ? sizeof(void *) : 0 +#endif + ); + + if (writer.outbase == NULL) + { + CodeSize = writer.get_outputpos(); + writer.outbase = (jitcode_t)g_SPEngine->AllocatePageMemory(CodeSize); + g_SPEngine->SetReadWrite(writer.outbase); + writer.outptr = writer.outbase; + g_RegDecoder = 0; + goto jit_rewind; + } + g_SPEngine->SetReadExecute(writer.outbase); + + return writer.outbase; +} + +void JIT_FreeHook(void *addr) +{ + g_SPEngine->FreePageMemory(addr); +} + +#endif diff --git a/extensions/bintools/msvc9/bintools.sln b/extensions/bintools/msvc9/bintools.sln index eae3aa15..48d57f9b 100644 --- a/extensions/bintools/msvc9/bintools.sln +++ b/extensions/bintools/msvc9/bintools.sln @@ -5,14 +5,32 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bintools", "bintools.vcproj EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 + Debug - Episode 1|Win32 = Debug - Episode 1|Win32 + Debug - Left 4 Dead|Win32 = Debug - Left 4 Dead|Win32 + Debug - Old Metamod|Win32 = Debug - Old Metamod|Win32 + Debug - Orange Box|Win32 = Debug - Orange Box|Win32 + Release - Episode 1|Win32 = Release - Episode 1|Win32 + Release - Left 4 Dead|Win32 = Release - Left 4 Dead|Win32 + Release - Old Metamod|Win32 = Release - Old Metamod|Win32 + Release - Orange Box|Win32 = Release - Orange Box|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Debug|Win32.ActiveCfg = Debug|Win32 - {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Debug|Win32.Build.0 = Debug|Win32 - {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Release|Win32.ActiveCfg = Release|Win32 - {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Release|Win32.Build.0 = Release|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Debug - Episode 1|Win32.ActiveCfg = Debug - Episode 1|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Debug - Episode 1|Win32.Build.0 = Debug - Episode 1|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Debug - Left 4 Dead|Win32.ActiveCfg = Debug - Left 4 Dead|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Debug - Left 4 Dead|Win32.Build.0 = Debug - Left 4 Dead|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Debug - Old Metamod|Win32.ActiveCfg = Debug - Old Metamod|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Debug - Old Metamod|Win32.Build.0 = Debug - Old Metamod|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Debug - Orange Box|Win32.ActiveCfg = Debug - Orange Box|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Debug - Orange Box|Win32.Build.0 = Debug - Orange Box|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Release - Episode 1|Win32.ActiveCfg = Release - Episode 1|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Release - Episode 1|Win32.Build.0 = Release - Episode 1|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Release - Left 4 Dead|Win32.ActiveCfg = Release - Left 4 Dead|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Release - Left 4 Dead|Win32.Build.0 = Release - Left 4 Dead|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Release - Old Metamod|Win32.ActiveCfg = Release - Old Metamod|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Release - Old Metamod|Win32.Build.0 = Release - Old Metamod|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Release - Orange Box|Win32.ActiveCfg = Release - Orange Box|Win32 + {E38F65D9-74B2-4373-B46A-DBB76F579F98}.Release - Orange Box|Win32.Build.0 = Release - Orange Box|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/extensions/bintools/msvc9/bintools.vcproj b/extensions/bintools/msvc9/bintools.vcproj index 69321889..0ce5cd5b 100644 --- a/extensions/bintools/msvc9/bintools.vcproj +++ b/extensions/bintools/msvc9/bintools.vcproj @@ -17,7 +17,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -244,10 +717,18 @@ RelativePath="..\CallWrapper.cpp" > + + + + #endif +#if defined SMEXT_ENABLE_PLUGINSYS +#include +#endif +#if defined SMEXT_ENABLE_MENUS +#include +#endif +#if defined SMEXT_ENABLE_ADMINSYS +#include +#endif +#if defined SMEXT_ENABLE_TEXTPARSERS +#include +#endif +#if defined SMEXT_ENABLE_USERMSGS +#include +#endif +#if defined SMEXT_ENABLE_TRANSLATOR +#include +#endif #if defined SMEXT_CONF_METAMOD #include @@ -256,6 +274,21 @@ extern IThreader *threader; #if defined SMEXT_ENABLE_LIBSYS extern ILibrarySys *libsys; #endif +#if defined SMEXT_ENABLE_PLUGINSYS +extern SourceMod::IPluginManager *plsys; +#endif +#if defined SMEXT_ENABLE_MENUS +extern IMenuManager *menus; +#endif +#if defined SMEXT_ENABLE_ADMINSYS +extern IAdminSystem *adminsys; +#endif +#if defined SMEXT_ENABLE_USERMSGS +extern IUserMessages *usermsgs; +#endif +#if defined SMEXT_ENABLE_TRANSLATOR +extern ITranslator *translator; +#endif #if defined SMEXT_CONF_METAMOD PLUGIN_GLOBALVARS(); diff --git a/public/extensions/IBinTools.h b/public/extensions/IBinTools.h index f8334de3..66ae0a3e 100644 --- a/public/extensions/IBinTools.h +++ b/public/extensions/IBinTools.h @@ -34,8 +34,16 @@ #include + #define SMINTERFACE_BINTOOLS_NAME "IBinTools" -#define SMINTERFACE_BINTOOLS_VERSION 2 +#define SMINTERFACE_BINTOOLS_VERSION 3 + +#if defined HOOKING_ENABLED +#include + +#define SMINTERFACE_BINTOOLS2_NAME "IBinTools2" +#define SMINTERFACE_BINTOOLS2_VERSION 1 +#endif /** * @brief Function calling encoding utilities @@ -135,8 +143,109 @@ namespace SourceMod * @brief Destroys all resources used by this object. */ virtual void Destroy() =0; + +#if defined HOOKING_ENABLED + + /** + * @brief Gets the Return type info. + * + * @return A PassInfo pointer. + */ + virtual const SourceHook::PassInfo *GetSHReturnInfo() =0; + + /** + * @brief Returns the calling convention. + * + * @return CallConvention value. + */ + virtual SourceHook::ProtoInfo::CallConvention GetSHCallConvention() =0; + + /** + * @brief Returns parameter info. + * + * @param num Parameter number to get (starting from 0). + * @return A PassInfo pointer. + */ + virtual const SourceHook::PassInfo *GetSHParamInfo(unsigned int num) =0; + + /** + * @brief Returns the offset of a given param. + * + * @param num Parameter number to get (starting from 0). + * @return Parameter offset. + */ + virtual unsigned int GetParamOffset(unsigned int num) =0; + +#endif }; +#if defined HOOKING_ENABLED + + /** + * @brief Delegate object that intermediates between SourceHook and the callback function. + */ + class ISMDelegate : public SourceHook::ISHDelegate + { + private: + /** + * @brief Internally used callback function - Do not call! + */ + virtual void Call() =0; /**< Do not call */ + public: + /** + * @brief Retrieves the User data buffer. + * + * @return User data pointer. + */ + virtual void *GetUserData() =0; + }; + + /** + * @brief Wrapper around a virtual hook. + */ + class IHookWrapper + { + public: + /** + * @brief Creates a hook delegate to pass to SourceHook. + * + * @param data User data pointer. + * @return A new ISMDelegate for the hook. + */ + virtual ISMDelegate *CreateDelegate(void *data) =0; + + /** + * @brief Gets the number of params in the hooked function. + * + * @return Number of params. + */ + virtual unsigned int GetParamCount() =0; + + /** + * @brief Returns the offset of a given param. + * + * @param argnum Parameter number from 0 to GetParamCount-1. + * @param size Optional buffer to store the size of the param. + * @return Parameter offset or -1 on error. + */ + virtual unsigned int GetParamOffset(unsigned int argnum, unsigned int *size) =0; + + /** + * @brief Initiates a recall on the function. + * + * @param params Parameter buffer. + * @param retval Buffer to store the return value in. + */ + virtual void PerformRecall(void *params, void *retval) =0; + + /** + * @brief Destroys this HookWrapper. + */ + virtual void Destroy() =0; + }; + +#endif + /** * @brief Binary tools interface. */ @@ -196,6 +305,82 @@ namespace SourceMod const PassInfo paramInfo[], unsigned int numParams) =0; }; + +#if defined HOOKING_ENABLED + + /** + * @brief Binary tools interface. + */ + class IBinTools2 : public SMInterface + { + public: + virtual const char *GetInterfaceName() + { + return SMINTERFACE_BINTOOLS2_NAME; + } + virtual unsigned int GetInterfaceVersion() + { + return SMINTERFACE_BINTOOLS2_VERSION; + } + public: + + /** + * @brief Creates a call decoder. + * + * Note: CallConv_ThisCall requires an implicit first parameter + * of PassType_Basic / PASSFLAG_BYVAL / sizeof(void *). However, + * this should only be given to the Execute() function, and never + * listed in the paramInfo array. + * + * @param address Address to use as a call. + * @param protoInfo Parameter type information. + * @return A new ICallWrapper function. + */ + virtual ICallWrapper *CreateCall(void *address, + const SourceHook::ProtoInfo *protoInfo) =0; + + /** + * @brief Creates a vtable call decoder. + * + * Note: CallConv_ThisCall requires an implicit first parameter + * of PassType_Basic / PASSFLAG_BYVAL / sizeof(void *). However, + * this should only be given to the Execute() function, and never + * listed in the paramInfo array. + * + * @param protoInfo Parameter type information. + * @param info Function offset information. + * @return A new ICallWrapper function. + */ + virtual ICallWrapper *CreateVirtualCall(const SourceHook::ProtoInfo *protoInfo, + const SourceHook::MemFuncInfo *info) =0; + + + /** + * @brief Callback function pointer for Virtual Hooks. + * + * @param wrapper Call wrapper for this hook. + * @param deleg Delegate for this call. + * @param params Array of parameters. + * @param ret Storage buffer for the return value. + */ + typedef void (*VIRTUAL_HOOK_PROTO)(IHookWrapper *wrapper, ISMDelegate *deleg, void *params, void *ret); + + /** + * @brief Creates a hook on a virtual function. + * + * @param pSH Global SourceHook pointer. + * @param protoInfo Parameter type information. + * @param info Function offset information. + * @param f Callback function pointer. + */ + virtual IHookWrapper *CreateVirtualHook(SourceHook::ISourceHook *pSH, + const SourceHook::ProtoInfo *protoInfo, + const SourceHook::MemFuncInfo *info, + VIRTUAL_HOOK_PROTO f) =0; + }; + +#endif + } #endif //_INCLUDE_SMEXT_BINTOOLS_H_ diff --git a/tools/builder/PkgCore.cs b/tools/builder/PkgCore.cs index 5e945c32..6df1af0d 100644 --- a/tools/builder/PkgCore.cs +++ b/tools/builder/PkgCore.cs @@ -202,12 +202,37 @@ namespace builder libraries.Add(lib); lib = new Library(); - lib.package_path = "addons/sourcemod/extensions"; + lib.package_path = "addons/sourcemod/extensions/auto.1.ep1"; lib.source_path = "extensions/bintools"; lib.binary_name = "bintools.ext"; lib.vcproj_name = "bintools"; + lib.build_mode = BuildMode.BuildMode_OldMetamod; libraries.Add(lib); + lib = new Library(); + lib.package_path = "addons/sourcemod/extensions/auto.2.ep1"; + lib.source_path = "extensions/bintools"; + lib.binary_name = "bintools.ext"; + lib.vcproj_name = "bintools"; + lib.build_mode = BuildMode.BuildMode_Episode1; + libraries.Add(lib); + + lib = new Library(); + lib.package_path = "addons/sourcemod/extensions/auto.2.ep2"; + lib.source_path = "extensions/bintools"; + lib.binary_name = "bintools.ext"; + lib.vcproj_name = "bintools"; + lib.build_mode = BuildMode.BuildMode_Episode2; + libraries.Add(lib); + + lib = new Library(); + lib.package_path = "addons/sourcemod/extensions/auto.2.l4d"; + lib.source_path = "extensions/bintools"; + lib.binary_name = "bintools.ext"; + lib.vcproj_name = "bintools"; + lib.build_mode = BuildMode.BuildMode_Left4Dead; + libraries.Add(lib); + lib = new Library(); lib.package_path = "addons/sourcemod/extensions"; lib.source_path = "extensions/mysql";