pred@2504: /** pred@2504: * vim: set ts=4 : pred@2504: * ============================================================================= pred@2504: * SourceMod BinTools Extension ds@3104: * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. pred@2504: * ============================================================================= pred@2504: * pred@2504: * This program is free software; you can redistribute it and/or modify it under pred@2504: * the terms of the GNU General Public License, version 3.0, as published by the pred@2504: * Free Software Foundation. pred@2504: * pred@2504: * This program is distributed in the hope that it will be useful, but WITHOUT pred@2504: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS pred@2504: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more pred@2504: * details. pred@2504: * pred@2504: * You should have received a copy of the GNU General Public License along with pred@2504: * this program. If not, see . pred@2504: * pred@2504: * As a special exception, AlliedModders LLC gives you permission to link the pred@2504: * code of this program (as well as its derivative works) to "Half-Life 2," the pred@2504: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software pred@2504: * by the Valve Corporation. You must obey the GNU General Public License in pred@2504: * all respects for all other code used. Additionally, AlliedModders LLC grants pred@2504: * this exception to all derivative works. AlliedModders LLC defines further pred@2504: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), pred@2504: * or . pred@2504: * pred@2504: * Version: $Id: CallMaker.h 1964 2008-03-27 04:54:56Z damagedsoul $ pred@2504: */ pred@2504: pred@2504: #if defined HOOKING_ENABLED pred@2504: pred@2504: #include "HookWrapper.h" pred@2504: #include "jit_compile.h" pred@2504: pred@2504: /****************************** pred@2504: * HookWrapper Implementation * pred@2504: ******************************/ pred@2504: pred@2504: HookWrapper::HookWrapper(SourceHook::ISourceHook *pSH, pred@2504: const SourceHook::ProtoInfo *proto, pred@2504: SourceHook::MemFuncInfo *memfunc, pred@2504: void *addr) : m_ParamOffs(NULL), m_ParamSize(0), m_RetSize(0) pred@2504: { pred@2504: unsigned int argnum = proto->numOfParams; pred@2504: pred@2504: m_pSH = pSH; pred@2504: m_ProtoInfo = *proto; pred@2504: m_ProtoInfo.paramsPassInfo = new SourceHook::PassInfo[argnum + 1]; pred@2504: memcpy((void *)m_ProtoInfo.paramsPassInfo, proto->paramsPassInfo, sizeof(SourceHook::PassInfo) * (argnum+1)); pred@2504: m_MemFuncInfo = *memfunc; pred@2504: m_FuncAddr = addr; pred@2504: m_HookWrapper = NULL; pred@2504: pred@2504: /* If the function has parameters calculate the parameter buffer size and offsets */ pred@2504: if (argnum > 0) pred@2504: { pred@2504: const SourceHook::PassInfo *info = m_ProtoInfo.paramsPassInfo; pred@2504: m_ParamOffs = new unsigned int[argnum]; pred@2504: m_ParamOffs[0] = 0; pred@2504: pred@2504: for (unsigned int i=1; i<=argnum; i++) pred@2504: { pred@2504: /* The stack should be at least aligned to a 4 byte boundary pred@2504: * and byref params have the size of pointers pred@2504: */ pred@2504: if (((info[i].type == SourceHook::PassInfo::PassType_Basic) && pred@2504: (info[i].flags & SourceHook::PassInfo::PassFlag_ByVal) && pred@2504: (info[i].size < sizeof(void *))) || pred@2504: (info[i].flags & SourceHook::PassInfo::PassFlag_ByRef)) pred@2504: { pred@2504: m_ParamSize += sizeof(void *); pred@2504: } pred@2504: else pred@2504: { pred@2504: m_ParamSize += info[i].size; pred@2504: } pred@2504: pred@2504: //:TODO: fix this! pred@2504: if (i < argnum) pred@2504: { pred@2504: m_ParamOffs[i] = m_ParamSize; pred@2504: } pred@2504: } pred@2504: } pred@2504: pred@2504: /* Finally calculate the retval size if any */ pred@2504: size_t retsize = m_ProtoInfo.retPassInfo.size; pred@2504: if (retsize != 0) pred@2504: { pred@2504: if (m_ProtoInfo.retPassInfo.flags & SourceHook::PassInfo::PassFlag_ByRef) pred@2504: { pred@2504: m_RetSize = sizeof(void *); pred@2504: } pred@2504: else pred@2504: { pred@2504: m_RetSize = (retsize < sizeof(void *)) ? sizeof(void *) : retsize; pred@2504: } pred@2504: } pred@2504: } pred@2504: pred@2504: ISMDelegate *HookWrapper::CreateDelegate(void *data) pred@2504: { pred@2504: SMDelegate *pDeleg = new SMDelegate(data); pred@2504: pDeleg->PatchVtable(m_HookWrapper); pred@2504: pred@2504: return pDeleg; pred@2504: } pred@2504: pred@2504: unsigned int HookWrapper::GetParamCount() pred@2504: { pred@2504: return m_ProtoInfo.numOfParams; pred@2504: } pred@2504: pred@2504: unsigned int HookWrapper::GetParamOffset(unsigned int argnum, unsigned int *size) pred@2504: { pred@2504: assert(m_ParamOffs && argnum < (unsigned int)m_ProtoInfo.numOfParams); pred@2504: pred@2504: if (size) pred@2504: { pred@2504: *size = m_ProtoInfo.paramsPassInfo[argnum+1].size; pred@2504: } pred@2504: pred@2504: return m_ParamOffs[argnum]; pred@2504: } pred@2504: pred@2504: void HookWrapper::PerformRecall(void *params, void *retval) pred@2504: { pred@2504: /* Notify SourceHook of the upcoming recall */ ds@3104: m_pSH->DoRecall(); pred@2504: pred@2504: /* Add thisptr into params buffer */ pred@2504: unsigned char *newparams = new unsigned char[sizeof(void *) + m_ParamSize]; ds@3104: *(void **)newparams = m_pSH->GetIfacePtr(); pred@2504: memcpy(newparams + sizeof(void *), params, m_ParamSize); pred@2504: pred@2504: /* Execute the call */ pred@2504: m_CallWrapper->Execute(newparams, retval); pred@2504: ds@3104: m_pSH->SetRes(MRES_SUPERCEDE); pred@2504: pred@2504: return; pred@2504: } pred@2504: pred@2504: void HookWrapper::Destroy() pred@2504: { pred@2504: if (m_HookWrapper != NULL) pred@2504: { pred@2504: JIT_FreeHook(m_HookWrapper); pred@2504: } pred@2504: pred@2504: if (m_CallWrapper != NULL) pred@2504: { pred@2504: m_CallWrapper->Destroy(); pred@2504: } pred@2504: pred@2504: delete this; pred@2504: } pred@2504: pred@2504: unsigned int HookWrapper::GetParamSize() pred@2504: { pred@2504: return m_ParamSize; pred@2504: } pred@2504: pred@2504: SourceHook::ProtoInfo * HookWrapper::GetProtoInfo() pred@2504: { pred@2504: return &m_ProtoInfo; pred@2504: } pred@2504: pred@2504: unsigned int HookWrapper::GetRetSize() pred@2504: { pred@2504: return m_RetSize; pred@2504: } pred@2504: pred@2504: void * HookWrapper::GetHandlerAddr() pred@2504: { pred@2504: return m_FuncAddr; pred@2504: } pred@2504: pred@2504: void HookWrapper::SetCallWrapperAddr( ICallWrapper *wrap ) pred@2504: { pred@2504: m_CallWrapper = wrap; pred@2504: } pred@2504: pred@2504: void HookWrapper::SetHookWrpAddr( void *addr ) pred@2504: { pred@2504: m_HookWrapper = addr; pred@2504: } pred@2504: pred@2504: HookWrapper::~HookWrapper() pred@2504: { pred@2504: delete [] m_ProtoInfo.paramsPassInfo; pred@2504: delete [] m_ParamOffs; pred@2504: } pred@2504: /***************************** pred@2504: * SMDelegate Implementation * pred@2504: *****************************/ pred@2504: pred@2504: SMDelegate::SMDelegate(void *data) pred@2504: { pred@2504: m_Data = data; pred@2504: } pred@2504: pred@2504: bool SMDelegate::IsEqual(ISHDelegate *pOtherDeleg) pred@2504: { pred@2504: return false; pred@2504: } pred@2504: pred@2504: void SMDelegate::DeleteThis() pred@2504: { pred@2504: /* Remove our allocated vtable */ pred@2504: delete [] *reinterpret_cast(this); pred@2504: pred@2504: delete this; pred@2504: } pred@2504: pred@2504: void SMDelegate::Call() pred@2504: { pred@2504: } pred@2504: pred@2504: void *SMDelegate::GetUserData() pred@2504: { pred@2504: return m_Data; pred@2504: } pred@2504: pred@2504: /** pred@2504: * This duplicates the vtable (so each instance has a unique table) and patches SMDelgate::Call to point to our allocated function pred@2504: */ pred@2504: void SMDelegate::PatchVtable(void *addr) pred@2504: { pred@2504: void **new_vtptr = new void *[4]; pred@2504: void **cur_vtptr = *reinterpret_cast(this); pred@2504: memcpy(new_vtptr, cur_vtptr, sizeof(void *)*4); pred@2504: pred@2504: *reinterpret_cast(this) = new_vtptr; pred@2504: pred@2504: void *cur_vfnptr = reinterpret_cast(new_vtptr + VTABLE_PATCH_OFFS); pred@2504: pred@2504: *reinterpret_cast(cur_vfnptr) = addr; pred@2504: } pred@2504: pred@2504: #endif