extensions/bintools/HookWrapper.cpp
author David Anderson <danderson@mozilla.com>
Thu Aug 08 20:26:36 2013 -0700 (2013-08-08)
changeset 3979 bdad6d9c8351
parent 3104 b15fb3b476f1
permissions -rw-r--r--
Use SSE when available for floating-point operations (bug 5841, r=her).
pred@2504
     1
/**
pred@2504
     2
 * vim: set ts=4 :
pred@2504
     3
 * =============================================================================
pred@2504
     4
 * SourceMod BinTools Extension
ds@3104
     5
 * Copyright (C) 2004-2010 AlliedModders LLC.  All rights reserved.
pred@2504
     6
 * =============================================================================
pred@2504
     7
 *
pred@2504
     8
 * This program is free software; you can redistribute it and/or modify it under
pred@2504
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
pred@2504
    10
 * Free Software Foundation.
pred@2504
    11
 * 
pred@2504
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
pred@2504
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
pred@2504
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
pred@2504
    15
 * details.
pred@2504
    16
 *
pred@2504
    17
 * You should have received a copy of the GNU General Public License along with
pred@2504
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
pred@2504
    19
 *
pred@2504
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
pred@2504
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
pred@2504
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
pred@2504
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
pred@2504
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
pred@2504
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
pred@2504
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
pred@2504
    27
 * or <http://www.sourcemod.net/license.php>.
pred@2504
    28
 *
pred@2504
    29
 * Version: $Id: CallMaker.h 1964 2008-03-27 04:54:56Z damagedsoul $
pred@2504
    30
 */
pred@2504
    31
pred@2504
    32
#if defined HOOKING_ENABLED
pred@2504
    33
pred@2504
    34
#include "HookWrapper.h"
pred@2504
    35
#include "jit_compile.h"
pred@2504
    36
pred@2504
    37
/******************************
pred@2504
    38
 * HookWrapper Implementation *
pred@2504
    39
 ******************************/
pred@2504
    40
pred@2504
    41
HookWrapper::HookWrapper(SourceHook::ISourceHook *pSH, 
pred@2504
    42
						 const SourceHook::ProtoInfo *proto, 
pred@2504
    43
						 SourceHook::MemFuncInfo *memfunc, 
pred@2504
    44
						 void *addr) : m_ParamOffs(NULL), m_ParamSize(0), m_RetSize(0)
pred@2504
    45
{
pred@2504
    46
	unsigned int argnum = proto->numOfParams;
pred@2504
    47
pred@2504
    48
	m_pSH = pSH;
pred@2504
    49
	m_ProtoInfo = *proto;
pred@2504
    50
	m_ProtoInfo.paramsPassInfo = new SourceHook::PassInfo[argnum + 1];
pred@2504
    51
	memcpy((void *)m_ProtoInfo.paramsPassInfo, proto->paramsPassInfo, sizeof(SourceHook::PassInfo) * (argnum+1));
pred@2504
    52
	m_MemFuncInfo = *memfunc;
pred@2504
    53
	m_FuncAddr = addr;
pred@2504
    54
	m_HookWrapper = NULL;
pred@2504
    55
pred@2504
    56
	/* If the function has parameters calculate the parameter buffer size and offsets */
pred@2504
    57
	if (argnum > 0)
pred@2504
    58
	{
pred@2504
    59
		const SourceHook::PassInfo *info = m_ProtoInfo.paramsPassInfo;
pred@2504
    60
		m_ParamOffs = new unsigned int[argnum];
pred@2504
    61
		m_ParamOffs[0] = 0;
pred@2504
    62
pred@2504
    63
		for (unsigned int i=1; i<=argnum; i++)
pred@2504
    64
		{
pred@2504
    65
			/* The stack should be at least aligned to a 4 byte boundary 
pred@2504
    66
			* and byref params have the size of pointers 
pred@2504
    67
			*/
pred@2504
    68
			if (((info[i].type == SourceHook::PassInfo::PassType_Basic) &&
pred@2504
    69
				(info[i].flags & SourceHook::PassInfo::PassFlag_ByVal) && 
pred@2504
    70
				(info[i].size < sizeof(void *))) ||
pred@2504
    71
				(info[i].flags & SourceHook::PassInfo::PassFlag_ByRef))
pred@2504
    72
			{
pred@2504
    73
				m_ParamSize += sizeof(void *);
pred@2504
    74
			}
pred@2504
    75
			else
pred@2504
    76
			{
pred@2504
    77
				m_ParamSize += info[i].size;
pred@2504
    78
			}
pred@2504
    79
pred@2504
    80
			//:TODO: fix this!
pred@2504
    81
			if (i < argnum)
pred@2504
    82
			{
pred@2504
    83
				m_ParamOffs[i] = m_ParamSize;
pred@2504
    84
			}
pred@2504
    85
		}
pred@2504
    86
	}
pred@2504
    87
pred@2504
    88
	/* Finally calculate the retval size if any */
pred@2504
    89
	size_t retsize = m_ProtoInfo.retPassInfo.size;
pred@2504
    90
	if (retsize != 0)
pred@2504
    91
	{
pred@2504
    92
		if (m_ProtoInfo.retPassInfo.flags & SourceHook::PassInfo::PassFlag_ByRef)
pred@2504
    93
		{
pred@2504
    94
			m_RetSize = sizeof(void *);
pred@2504
    95
		}
pred@2504
    96
		else
pred@2504
    97
		{
pred@2504
    98
			m_RetSize = (retsize < sizeof(void *)) ? sizeof(void *) : retsize;
pred@2504
    99
		}
pred@2504
   100
	}
pred@2504
   101
}
pred@2504
   102
pred@2504
   103
ISMDelegate *HookWrapper::CreateDelegate(void *data)
pred@2504
   104
{
pred@2504
   105
	SMDelegate *pDeleg = new SMDelegate(data);
pred@2504
   106
	pDeleg->PatchVtable(m_HookWrapper);
pred@2504
   107
pred@2504
   108
	return pDeleg;
pred@2504
   109
}
pred@2504
   110
pred@2504
   111
unsigned int HookWrapper::GetParamCount()
pred@2504
   112
{
pred@2504
   113
	return m_ProtoInfo.numOfParams;
pred@2504
   114
}
pred@2504
   115
pred@2504
   116
unsigned int HookWrapper::GetParamOffset(unsigned int argnum, unsigned int *size)
pred@2504
   117
{
pred@2504
   118
	assert(m_ParamOffs && argnum < (unsigned int)m_ProtoInfo.numOfParams);
pred@2504
   119
pred@2504
   120
	if (size)
pred@2504
   121
	{
pred@2504
   122
		*size = m_ProtoInfo.paramsPassInfo[argnum+1].size;
pred@2504
   123
	}
pred@2504
   124
pred@2504
   125
	return m_ParamOffs[argnum];
pred@2504
   126
}
pred@2504
   127
pred@2504
   128
void HookWrapper::PerformRecall(void *params, void *retval)
pred@2504
   129
{
pred@2504
   130
	/* Notify SourceHook of the upcoming recall */
ds@3104
   131
	m_pSH->DoRecall();
pred@2504
   132
pred@2504
   133
	/* Add thisptr into params buffer */
pred@2504
   134
	unsigned char *newparams = new unsigned char[sizeof(void *) + m_ParamSize];
ds@3104
   135
	*(void **)newparams = m_pSH->GetIfacePtr();
pred@2504
   136
	memcpy(newparams + sizeof(void *), params, m_ParamSize);
pred@2504
   137
pred@2504
   138
	/* Execute the call */
pred@2504
   139
	m_CallWrapper->Execute(newparams, retval);
pred@2504
   140
ds@3104
   141
	m_pSH->SetRes(MRES_SUPERCEDE);
pred@2504
   142
pred@2504
   143
	return;
pred@2504
   144
}
pred@2504
   145
pred@2504
   146
void HookWrapper::Destroy()
pred@2504
   147
{
pred@2504
   148
	if (m_HookWrapper != NULL)
pred@2504
   149
	{
pred@2504
   150
		JIT_FreeHook(m_HookWrapper);
pred@2504
   151
	}
pred@2504
   152
pred@2504
   153
	if (m_CallWrapper != NULL)
pred@2504
   154
	{
pred@2504
   155
		m_CallWrapper->Destroy();
pred@2504
   156
	}
pred@2504
   157
pred@2504
   158
	delete this;
pred@2504
   159
}
pred@2504
   160
pred@2504
   161
unsigned int HookWrapper::GetParamSize()
pred@2504
   162
{
pred@2504
   163
	return m_ParamSize;
pred@2504
   164
}
pred@2504
   165
pred@2504
   166
SourceHook::ProtoInfo * HookWrapper::GetProtoInfo()
pred@2504
   167
{
pred@2504
   168
	return &m_ProtoInfo;
pred@2504
   169
}
pred@2504
   170
pred@2504
   171
unsigned int HookWrapper::GetRetSize()
pred@2504
   172
{
pred@2504
   173
	return m_RetSize;
pred@2504
   174
}
pred@2504
   175
pred@2504
   176
void * HookWrapper::GetHandlerAddr()
pred@2504
   177
{
pred@2504
   178
	return m_FuncAddr;
pred@2504
   179
}
pred@2504
   180
pred@2504
   181
void HookWrapper::SetCallWrapperAddr( ICallWrapper *wrap )
pred@2504
   182
{
pred@2504
   183
	m_CallWrapper = wrap;
pred@2504
   184
}
pred@2504
   185
pred@2504
   186
void HookWrapper::SetHookWrpAddr( void *addr )
pred@2504
   187
{
pred@2504
   188
	m_HookWrapper = addr;
pred@2504
   189
}
pred@2504
   190
pred@2504
   191
HookWrapper::~HookWrapper()
pred@2504
   192
{
pred@2504
   193
	delete [] m_ProtoInfo.paramsPassInfo;
pred@2504
   194
	delete [] m_ParamOffs;
pred@2504
   195
}
pred@2504
   196
/*****************************
pred@2504
   197
 * SMDelegate Implementation *
pred@2504
   198
 *****************************/
pred@2504
   199
pred@2504
   200
SMDelegate::SMDelegate(void *data)
pred@2504
   201
{
pred@2504
   202
	m_Data = data;
pred@2504
   203
}
pred@2504
   204
pred@2504
   205
bool SMDelegate::IsEqual(ISHDelegate *pOtherDeleg)
pred@2504
   206
{
pred@2504
   207
	return false;
pred@2504
   208
}
pred@2504
   209
pred@2504
   210
void SMDelegate::DeleteThis()
pred@2504
   211
{
pred@2504
   212
	/* Remove our allocated vtable */
pred@2504
   213
	delete [] *reinterpret_cast<void ***>(this);
pred@2504
   214
pred@2504
   215
	delete this;
pred@2504
   216
}
pred@2504
   217
pred@2504
   218
void SMDelegate::Call()
pred@2504
   219
{
pred@2504
   220
}
pred@2504
   221
pred@2504
   222
void *SMDelegate::GetUserData()
pred@2504
   223
{
pred@2504
   224
	return m_Data;
pred@2504
   225
}
pred@2504
   226
pred@2504
   227
/** 
pred@2504
   228
 * This duplicates the vtable (so each instance has a unique table) and patches SMDelgate::Call to point to our allocated function 
pred@2504
   229
 */
pred@2504
   230
void SMDelegate::PatchVtable(void *addr)
pred@2504
   231
{
pred@2504
   232
	void **new_vtptr = new void *[4];
pred@2504
   233
	void **cur_vtptr = *reinterpret_cast<void ***>(this);
pred@2504
   234
	memcpy(new_vtptr, cur_vtptr, sizeof(void *)*4);
pred@2504
   235
pred@2504
   236
	*reinterpret_cast<void ***>(this) = new_vtptr;
pred@2504
   237
pred@2504
   238
	void *cur_vfnptr = reinterpret_cast<void *>(new_vtptr + VTABLE_PATCH_OFFS);
pred@2504
   239
pred@2504
   240
	*reinterpret_cast<void **>(cur_vfnptr) = addr;
pred@2504
   241
}
pred@2504
   242
pred@2504
   243
#endif