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