extensions/bintools/jit_hook.cpp
author David Anderson <danderson@mozilla.com>
Thu Aug 08 20:26:36 2013 -0700 (2013-08-08)
changeset 3979 bdad6d9c8351
parent 2993 76da4d93e0d6
child 4136 08043605bc39
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
pred@2504
     5
 * Copyright (C) 2004-2008 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 "jit_compile.h"
pred@2504
    35
#include "sm_platform.h"
pred@2504
    36
#include "extension.h"
pred@2504
    37
pred@2504
    38
/********************
pred@2504
    39
 * Assembly Opcodes *
pred@2504
    40
 ********************/
pred@2504
    41
pred@2504
    42
inline void Write_Function_Prologue(JitWriter *jit, bool RetInMemory)
pred@2504
    43
{
pred@2504
    44
	//push ebp
pred@2504
    45
	//push ebx
pred@2504
    46
	//mov ebp, esp
pred@2504
    47
	IA32_Push_Reg(jit, REG_EBP);
pred@2504
    48
	IA32_Push_Reg(jit, REG_EBX);
pred@2504
    49
	IA32_Mov_Reg_Rm(jit, REG_EBP, REG_ESP, MOD_REG);
pred@2504
    50
#if defined PLATFORM_WINDOWS
pred@2504
    51
	//mov ebx, ecx
pred@2504
    52
	IA32_Mov_Reg_Rm(jit, REG_EBX, REG_ECX, MOD_REG);
ds@2993
    53
#elif defined PLATFORM_LINUX || defined PLATFORM_APPLE
pred@2504
    54
	//mov ebx, [ebp+12+(RetInMemory)?4:0]
pred@2504
    55
	IA32_Mov_Reg_Rm_Disp8(jit, REG_EBX, REG_EBP, 12+((RetInMemory)?4:0));
pred@2504
    56
#endif
pred@2504
    57
}
pred@2504
    58
pred@2504
    59
inline void Write_Function_Epilogue(JitWriter *jit, unsigned short size)
pred@2504
    60
{
pred@2504
    61
	//mov esp, ebp
pred@2504
    62
	//pop ebx
pred@2504
    63
	//pop ebp
pred@2504
    64
	//ret <value>
pred@2504
    65
	IA32_Mov_Reg_Rm(jit, REG_ESP, REG_EBP, MOD_REG);
pred@2504
    66
	IA32_Pop_Reg(jit, REG_EBX);
pred@2504
    67
	IA32_Pop_Reg(jit, REG_EBP);
pred@2504
    68
	if (size == 0)
pred@2504
    69
	{
pred@2504
    70
		IA32_Return(jit);
pred@2504
    71
	}
pred@2504
    72
	else
pred@2504
    73
	{
pred@2504
    74
		IA32_Return_Popstack(jit, size);
pred@2504
    75
	}
pred@2504
    76
}
pred@2504
    77
pred@2504
    78
inline void Write_Stack_Alloc(JitWriter *jit, jit_uint32_t size)
pred@2504
    79
{
pred@2504
    80
	//sub esp, <value>
pred@2504
    81
	if (size <= SCHAR_MAX)
pred@2504
    82
	{
pred@2504
    83
		IA32_Sub_Rm_Imm8(jit, REG_ESP, (jit_int8_t)size, MOD_REG);
pred@2504
    84
	}
pred@2504
    85
	else
pred@2504
    86
	{
pred@2504
    87
		IA32_Sub_Rm_Imm32(jit, REG_ESP, size, MOD_REG);
pred@2504
    88
	}
pred@2504
    89
}
pred@2504
    90
pred@2504
    91
inline void Write_Copy_Params(JitWriter *jit, bool RetInMemory, jit_uint32_t retsize, jit_uint32_t paramsize)
pred@2504
    92
{
pred@2504
    93
	//:TODO: inline this memcpy!! - For small numbers of params mov's (with clever reg allocation?) would be faster
pred@2504
    94
pred@2504
    95
	//cld
pred@2504
    96
	//push edi
pred@2504
    97
	//push esi
pred@2504
    98
	//lea edi, [ebp-<retsize>-<paramsize>]
pred@2504
    99
	//lea esi, [ebp+12+(RetInMemory)?4:0]
pred@2504
   100
	//if dwords
pred@2504
   101
	// mov ecx, <dwords>
pred@2504
   102
	// rep movsd
pred@2504
   103
	//if bytes
pred@2504
   104
	// mov ecx, <bytes>
pred@2504
   105
	// rep movsb
pred@2504
   106
	//pop esi
pred@2504
   107
	//pop edi
pred@2504
   108
pred@2504
   109
	jit_int32_t offs;
pred@2504
   110
	jit_uint32_t dwords = paramsize >> 2;
pred@2504
   111
	jit_uint32_t bytes = paramsize & 0x3;
pred@2504
   112
pred@2504
   113
	IA32_Cld(jit);
pred@2504
   114
	IA32_Push_Reg(jit, REG_EDI);
pred@2504
   115
	IA32_Push_Reg(jit, REG_ESI);
pred@2504
   116
	offs = -(jit_int32_t)retsize - paramsize;
pred@2504
   117
pred@2504
   118
	if (offs > SCHAR_MIN)
pred@2504
   119
	{
pred@2504
   120
		IA32_Lea_DispRegImm8(jit, REG_EDI, REG_EBP, (jit_int8_t)offs);
pred@2504
   121
	}
pred@2504
   122
	else
pred@2504
   123
	{
pred@2504
   124
		IA32_Lea_DispRegImm32(jit, REG_EDI, REG_EBP, offs);
pred@2504
   125
	}
pred@2504
   126
	offs = 12 + ((RetInMemory) ? sizeof(void *) : 0);
pred@2504
   127
ds@2993
   128
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
pred@2504
   129
	offs += 4;
pred@2504
   130
#endif
pred@2504
   131
pred@2504
   132
	if (offs < SCHAR_MAX)
pred@2504
   133
	{
pred@2504
   134
		IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBP, (jit_int8_t)offs);
pred@2504
   135
	}
pred@2504
   136
	else
pred@2504
   137
	{
pred@2504
   138
		IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBP, offs);
pred@2504
   139
	}
pred@2504
   140
	if (dwords)
pred@2504
   141
	{
pred@2504
   142
		IA32_Mov_Reg_Imm32(jit, REG_ECX, dwords);
pred@2504
   143
		IA32_Rep(jit);
pred@2504
   144
		IA32_Movsd(jit);
pred@2504
   145
	}
pred@2504
   146
	if (bytes)
pred@2504
   147
	{
pred@2504
   148
		IA32_Mov_Reg_Imm32(jit, REG_ECX, bytes);
pred@2504
   149
		IA32_Rep(jit);
pred@2504
   150
		IA32_Movsb(jit);
pred@2504
   151
	}
pred@2504
   152
	IA32_Pop_Reg(jit, REG_ESI);
pred@2504
   153
	IA32_Pop_Reg(jit, REG_EDI);
pred@2504
   154
}
pred@2504
   155
pred@2504
   156
inline void Write_Push_Params(JitWriter *jit, 
pred@2504
   157
							  bool isretvoid, 
pred@2504
   158
							  bool isvoid, 
pred@2504
   159
							  jit_uint32_t retsize, 
pred@2504
   160
							  jit_uint32_t paramsize, 
pred@2504
   161
							  HookWrapper *pWrapper)
pred@2504
   162
{
pred@2504
   163
	//and esp, 0xFFFFFFF0
pred@2504
   164
	IA32_And_Rm_Imm8(jit, REG_ESP, MOD_REG, -16);
pred@2504
   165
pred@2504
   166
	//if retvoid
pred@2504
   167
	// push 0
pred@2504
   168
	//else
pred@2504
   169
	// lea reg, [ebp-<retsize>]
pred@2504
   170
	// push reg
pred@2504
   171
	if (isretvoid)
pred@2504
   172
	{
pred@2504
   173
		IA32_Push_Imm8(jit, 0);
pred@2504
   174
	}
pred@2504
   175
	else
pred@2504
   176
	{
pred@2504
   177
		jit_int32_t offs = -(jit_int32_t)retsize;
pred@2504
   178
		if (offs >= SCHAR_MIN)
pred@2504
   179
		{
pred@2504
   180
			IA32_Lea_DispRegImm8(jit, REG_EAX, REG_EBP, (jit_int8_t)offs);
pred@2504
   181
		}
pred@2504
   182
		else
pred@2504
   183
		{
pred@2504
   184
			IA32_Lea_DispRegImm32(jit, REG_EAX, REG_EBP, offs);
pred@2504
   185
		}
pred@2504
   186
		IA32_Push_Reg(jit, REG_EAX);
pred@2504
   187
	}
pred@2504
   188
pred@2504
   189
	//if void
pred@2504
   190
	// push 0
pred@2504
   191
	//else
pred@2504
   192
	// lea reg, [ebp-<retsize>-<paramsize>]
pred@2504
   193
	// push reg
pred@2504
   194
	if (isvoid)
pred@2504
   195
	{
pred@2504
   196
		IA32_Push_Imm8(jit, 0);
pred@2504
   197
	}
pred@2504
   198
	else
pred@2504
   199
	{
pred@2504
   200
		jit_int32_t offs = -(jit_int32_t)retsize - paramsize;
pred@2504
   201
		if (offs > SCHAR_MIN)
pred@2504
   202
		{
pred@2504
   203
			IA32_Lea_DispRegImm8(jit, REG_EDX, REG_EBP, (jit_int8_t)offs);
pred@2504
   204
		} else {
pred@2504
   205
			IA32_Lea_DispRegImm32(jit, REG_EDX, REG_EBP, offs);
pred@2504
   206
		}
pred@2504
   207
		IA32_Push_Reg(jit, REG_EDX);
pred@2504
   208
	}
pred@2504
   209
pred@2504
   210
	//push eax (thisptr)
pred@2504
   211
	//IA32_Push_Reg(jit, REG_ECX);
pred@2504
   212
pred@2504
   213
	//push ebx
pred@2504
   214
	IA32_Push_Reg(jit, REG_EBX);
pred@2504
   215
pred@2504
   216
	//push <pWrapper>
pred@2504
   217
	IA32_Push_Imm32(jit, (jit_int32_t)pWrapper);
pred@2504
   218
}
pred@2504
   219
pred@2504
   220
inline void Write_Call_Handler(JitWriter *jit, void *addr)
pred@2504
   221
{
pred@2504
   222
	//call <addr>
pred@2504
   223
	jitoffs_t call = IA32_Call_Imm32(jit, 0);
pred@2504
   224
	IA32_Write_Jump32_Abs(jit, call, addr);
pred@2504
   225
}
pred@2504
   226
pred@2504
   227
inline void Write_Copy_RetVal(JitWriter *jit, SourceHook::PassInfo *pRetInfo)
pred@2504
   228
{
pred@2504
   229
	/* If the return value is a reference the size will probably be >sizeof(void *)
pred@2504
   230
	* for objects, we need to fix that so we can actually return the reference.
pred@2504
   231
	*/
pred@2504
   232
	size_t size = pRetInfo->size;
pred@2504
   233
	if (pRetInfo->flags & PASSFLAG_BYREF)
pred@2504
   234
	{
pred@2504
   235
		size = sizeof(void *);
pred@2504
   236
	}
pred@2504
   237
pred@2504
   238
	if (pRetInfo->type == SourceHook::PassInfo::PassType_Float &&
pred@2504
   239
		pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal)
pred@2504
   240
	{
pred@2504
   241
		switch (size)
pred@2504
   242
		{
pred@2504
   243
		case 4:
pred@2504
   244
			{
pred@2504
   245
				//fld DWORD PTR [ebp-4]
pred@2504
   246
				IA32_Fld_Mem32_Disp8(jit, REG_EBP, -4);
pred@2504
   247
				break;
pred@2504
   248
			}
pred@2504
   249
		case 8:
pred@2504
   250
			{
pred@2504
   251
				//fld QWORD PTR [ebp-8]
pred@2504
   252
				IA32_Fld_Mem64_Disp8(jit, REG_EBP, -8);
pred@2504
   253
				break;
pred@2504
   254
			}
pred@2504
   255
		}
pred@2504
   256
	}
pred@2504
   257
	else if (pRetInfo->type == SourceHook::PassInfo::PassType_Object &&
pred@2504
   258
		pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal)
pred@2504
   259
	{
pred@2504
   260
		//cld
pred@2504
   261
		//push edi
pred@2504
   262
		//push esi
pred@2504
   263
		//mov edi, [ebp+12]
pred@2504
   264
		//lea esi, [ebp-<retsize>]
pred@2504
   265
		//if dwords
pred@2504
   266
		// mov ecx, <dwords>
pred@2504
   267
		// rep movsd
pred@2504
   268
		//if bytes
pred@2504
   269
		// mov ecx, <bytes>
pred@2504
   270
		// rep movsb
pred@2504
   271
		//pop esi
pred@2504
   272
		//pop edi
pred@2504
   273
pred@2504
   274
		jit_int32_t offs;
pred@2504
   275
		jit_uint32_t dwords = size >> 2;
pred@2504
   276
		jit_uint32_t bytes = size & 0x3;
pred@2504
   277
pred@2504
   278
		IA32_Cld(jit);
pred@2504
   279
		IA32_Push_Reg(jit, REG_EDI);
pred@2504
   280
		IA32_Push_Reg(jit, REG_ESI);
pred@2504
   281
pred@2504
   282
		IA32_Mov_Reg_Rm_Disp8(jit, REG_EDI, REG_EBP, 12);
pred@2504
   283
pred@2504
   284
		offs = -(jit_int32_t)pRetInfo->size;
pred@2504
   285
		if (offs >= SCHAR_MIN)
pred@2504
   286
		{
pred@2504
   287
			IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBP, (jit_int8_t)offs);
pred@2504
   288
		}
pred@2504
   289
		else
pred@2504
   290
		{
pred@2504
   291
			IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBP, offs);
pred@2504
   292
		}
pred@2504
   293
		IA32_Mov_Reg_Rm(jit, REG_EAX, REG_EDI, MOD_REG);
pred@2504
   294
pred@2504
   295
		if (dwords)
pred@2504
   296
		{
pred@2504
   297
			IA32_Mov_Reg_Imm32(jit, REG_ECX, dwords);
pred@2504
   298
			IA32_Rep(jit);
pred@2504
   299
			IA32_Movsd(jit);
pred@2504
   300
		}
pred@2504
   301
		if (bytes)
pred@2504
   302
		{
pred@2504
   303
			IA32_Mov_Reg_Imm32(jit, REG_ECX, bytes);
pred@2504
   304
			IA32_Rep(jit);
pred@2504
   305
			IA32_Movsb(jit);
pred@2504
   306
		}
pred@2504
   307
		IA32_Pop_Reg(jit, REG_ESI);
pred@2504
   308
		IA32_Pop_Reg(jit, REG_EDI);
pred@2504
   309
	}
pred@2504
   310
	else
pred@2504
   311
	{
pred@2504
   312
		switch (size)
pred@2504
   313
		{
pred@2504
   314
		case 1:
pred@2504
   315
			{
pred@2504
   316
				//mov al, BYTE PTR [ebp-4]
pred@2504
   317
				IA32_Mov_Reg8_Rm8_Disp8(jit, REG_EAX, REG_EBP, -4);
pred@2504
   318
				break;
pred@2504
   319
			}
pred@2504
   320
		case 2:
pred@2504
   321
			{
pred@2504
   322
				//mov ax, WORD PTR [ebp-4]
pred@2504
   323
				jit->write_ubyte(IA32_16BIT_PREFIX);
pred@2504
   324
				IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, -4);
pred@2504
   325
				break;
pred@2504
   326
			}
pred@2504
   327
		case 4:
pred@2504
   328
			{
pred@2504
   329
				//mov eax, DWORD PTR [ebp-4]
pred@2504
   330
				IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, -4);
pred@2504
   331
				break;
pred@2504
   332
			}
pred@2504
   333
		case 8:
pred@2504
   334
			{
pred@2504
   335
				//mov eax, DWORD PTR [ebp-8]
pred@2504
   336
				//mov edx, DWORD PTR [ebp-4]
pred@2504
   337
				//:TODO: this is broken due to SH
pred@2504
   338
				IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, -8);
pred@2504
   339
				IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_EBP, -4);
pred@2504
   340
				break;
pred@2504
   341
			}
pred@2504
   342
		}
pred@2504
   343
	}
pred@2504
   344
}
pred@2504
   345
pred@2504
   346
/******************************
pred@2504
   347
 * Assembly Compiler Function *
pred@2504
   348
 ******************************/
pred@2504
   349
pred@2504
   350
void *JIT_HookCompile(HookWrapper *pWrapper)
pred@2504
   351
{
pred@2504
   352
	JitWriter writer;
pred@2504
   353
	JitWriter *jit = &writer;
pred@2504
   354
pred@2504
   355
	jit_uint32_t CodeSize = 0;
pred@2504
   356
	jit_uint32_t ParamSize = pWrapper->GetParamSize();
pred@2504
   357
	jit_uint32_t RetSize = pWrapper->GetRetSize();
pred@2504
   358
pred@2504
   359
	/* Local variable size allocated in the stack for the param and retval buffers */
pred@2504
   360
	jit_uint32_t LocalVarSize = ParamSize + RetSize;
pred@2504
   361
pred@2504
   362
	/* Check if the return value is returned in memory */
pred@2504
   363
	bool RetInMemory = false;
pred@2504
   364
	SourceHook::PassInfo *pRetInfo = &pWrapper->GetProtoInfo()->retPassInfo;
pred@2504
   365
	if ((pRetInfo->type == SourceHook::PassInfo::PassType_Object) && 
pred@2504
   366
		(pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal))
pred@2504
   367
	{
pred@2504
   368
		RetInMemory = true;
pred@2504
   369
	}
pred@2504
   370
pred@2504
   371
	writer.outbase = NULL;
pred@2504
   372
	writer.outptr = NULL;
pred@2504
   373
pred@2504
   374
jit_rewind:
pred@2504
   375
	/* Write the function prologue */
pred@2504
   376
	Write_Function_Prologue(jit, RetInMemory);
pred@2504
   377
pred@2504
   378
	/* Allocate the local variables into the stack */
pred@2504
   379
	if (LocalVarSize)
pred@2504
   380
	{
pred@2504
   381
		Write_Stack_Alloc(jit, LocalVarSize);
pred@2504
   382
	}
pred@2504
   383
pred@2504
   384
	/* Copy all the parameters into the buffer */
pred@2504
   385
	if (ParamSize)
pred@2504
   386
	{
pred@2504
   387
		Write_Copy_Params(jit, RetInMemory, RetSize, ParamSize);
pred@2504
   388
	}
pred@2504
   389
pred@2504
   390
	/* Push the parameters into the handler */
pred@2504
   391
	Write_Push_Params(jit, !RetSize, !ParamSize, RetSize, ParamSize, pWrapper);
pred@2504
   392
pred@2504
   393
	/* Call the handler function */
pred@2504
   394
	Write_Call_Handler(jit, pWrapper->GetHandlerAddr());
pred@2504
   395
pred@2504
   396
	/* Copy back the return value into eax or the hidden return buffer */
pred@2504
   397
	if (RetSize)
pred@2504
   398
	{
pred@2504
   399
		Write_Copy_RetVal(jit, pRetInfo);
pred@2504
   400
	}
pred@2504
   401
pred@2504
   402
	/* Write the function epilogue */
pred@2504
   403
	Write_Function_Epilogue(jit, 
pred@2504
   404
#if defined PLATFORM_WINDOWS
pred@2504
   405
		ParamSize + ((RetInMemory) ? sizeof(void *) : 0)
ds@2993
   406
#elif defined PLATFORM_LINUX || defined PLATFORM_APPLE
pred@2504
   407
		(RetInMemory) ? sizeof(void *) : 0
pred@2504
   408
#endif
pred@2504
   409
	);
pred@2504
   410
pred@2504
   411
	if (writer.outbase == NULL)
pred@2504
   412
	{
pred@2504
   413
		CodeSize = writer.get_outputpos();
pred@2504
   414
		writer.outbase = (jitcode_t)g_SPEngine->AllocatePageMemory(CodeSize);
pred@2504
   415
		g_SPEngine->SetReadWrite(writer.outbase);
pred@2504
   416
		writer.outptr = writer.outbase;
pred@2504
   417
		g_RegDecoder = 0;
pred@2504
   418
		goto jit_rewind;
pred@2504
   419
	}
pred@2504
   420
	g_SPEngine->SetReadExecute(writer.outbase);
pred@2504
   421
pred@2504
   422
	return writer.outbase;
pred@2504
   423
}
pred@2504
   424
pred@2504
   425
void JIT_FreeHook(void *addr)
pred@2504
   426
{
pred@2504
   427
	g_SPEngine->FreePageMemory(addr);
pred@2504
   428
}
pred@2504
   429
pred@2504
   430
#endif