extensions/bintools/jit_hook.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Wed Aug 14 11:56:34 2013 -0400 (2013-08-14)
changeset 4005 617def4b1770
parent 2993 76da4d93e0d6
child 4136 08043605bc39
permissions -rw-r--r--
Added gamedata and extension loading compat shims for Nuclear Dawn (bug 5813, r=asherkin).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod BinTools Extension
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id: CallMaker.h 1964 2008-03-27 04:54:56Z damagedsoul $
    30  */
    31 
    32 #if defined HOOKING_ENABLED
    33 
    34 #include "jit_compile.h"
    35 #include "sm_platform.h"
    36 #include "extension.h"
    37 
    38 /********************
    39  * Assembly Opcodes *
    40  ********************/
    41 
    42 inline void Write_Function_Prologue(JitWriter *jit, bool RetInMemory)
    43 {
    44 	//push ebp
    45 	//push ebx
    46 	//mov ebp, esp
    47 	IA32_Push_Reg(jit, REG_EBP);
    48 	IA32_Push_Reg(jit, REG_EBX);
    49 	IA32_Mov_Reg_Rm(jit, REG_EBP, REG_ESP, MOD_REG);
    50 #if defined PLATFORM_WINDOWS
    51 	//mov ebx, ecx
    52 	IA32_Mov_Reg_Rm(jit, REG_EBX, REG_ECX, MOD_REG);
    53 #elif defined PLATFORM_LINUX || defined PLATFORM_APPLE
    54 	//mov ebx, [ebp+12+(RetInMemory)?4:0]
    55 	IA32_Mov_Reg_Rm_Disp8(jit, REG_EBX, REG_EBP, 12+((RetInMemory)?4:0));
    56 #endif
    57 }
    58 
    59 inline void Write_Function_Epilogue(JitWriter *jit, unsigned short size)
    60 {
    61 	//mov esp, ebp
    62 	//pop ebx
    63 	//pop ebp
    64 	//ret <value>
    65 	IA32_Mov_Reg_Rm(jit, REG_ESP, REG_EBP, MOD_REG);
    66 	IA32_Pop_Reg(jit, REG_EBX);
    67 	IA32_Pop_Reg(jit, REG_EBP);
    68 	if (size == 0)
    69 	{
    70 		IA32_Return(jit);
    71 	}
    72 	else
    73 	{
    74 		IA32_Return_Popstack(jit, size);
    75 	}
    76 }
    77 
    78 inline void Write_Stack_Alloc(JitWriter *jit, jit_uint32_t size)
    79 {
    80 	//sub esp, <value>
    81 	if (size <= SCHAR_MAX)
    82 	{
    83 		IA32_Sub_Rm_Imm8(jit, REG_ESP, (jit_int8_t)size, MOD_REG);
    84 	}
    85 	else
    86 	{
    87 		IA32_Sub_Rm_Imm32(jit, REG_ESP, size, MOD_REG);
    88 	}
    89 }
    90 
    91 inline void Write_Copy_Params(JitWriter *jit, bool RetInMemory, jit_uint32_t retsize, jit_uint32_t paramsize)
    92 {
    93 	//:TODO: inline this memcpy!! - For small numbers of params mov's (with clever reg allocation?) would be faster
    94 
    95 	//cld
    96 	//push edi
    97 	//push esi
    98 	//lea edi, [ebp-<retsize>-<paramsize>]
    99 	//lea esi, [ebp+12+(RetInMemory)?4:0]
   100 	//if dwords
   101 	// mov ecx, <dwords>
   102 	// rep movsd
   103 	//if bytes
   104 	// mov ecx, <bytes>
   105 	// rep movsb
   106 	//pop esi
   107 	//pop edi
   108 
   109 	jit_int32_t offs;
   110 	jit_uint32_t dwords = paramsize >> 2;
   111 	jit_uint32_t bytes = paramsize & 0x3;
   112 
   113 	IA32_Cld(jit);
   114 	IA32_Push_Reg(jit, REG_EDI);
   115 	IA32_Push_Reg(jit, REG_ESI);
   116 	offs = -(jit_int32_t)retsize - paramsize;
   117 
   118 	if (offs > SCHAR_MIN)
   119 	{
   120 		IA32_Lea_DispRegImm8(jit, REG_EDI, REG_EBP, (jit_int8_t)offs);
   121 	}
   122 	else
   123 	{
   124 		IA32_Lea_DispRegImm32(jit, REG_EDI, REG_EBP, offs);
   125 	}
   126 	offs = 12 + ((RetInMemory) ? sizeof(void *) : 0);
   127 
   128 #if defined PLATFORM_LINUX || defined PLATFORM_APPLE
   129 	offs += 4;
   130 #endif
   131 
   132 	if (offs < SCHAR_MAX)
   133 	{
   134 		IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBP, (jit_int8_t)offs);
   135 	}
   136 	else
   137 	{
   138 		IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBP, offs);
   139 	}
   140 	if (dwords)
   141 	{
   142 		IA32_Mov_Reg_Imm32(jit, REG_ECX, dwords);
   143 		IA32_Rep(jit);
   144 		IA32_Movsd(jit);
   145 	}
   146 	if (bytes)
   147 	{
   148 		IA32_Mov_Reg_Imm32(jit, REG_ECX, bytes);
   149 		IA32_Rep(jit);
   150 		IA32_Movsb(jit);
   151 	}
   152 	IA32_Pop_Reg(jit, REG_ESI);
   153 	IA32_Pop_Reg(jit, REG_EDI);
   154 }
   155 
   156 inline void Write_Push_Params(JitWriter *jit, 
   157 							  bool isretvoid, 
   158 							  bool isvoid, 
   159 							  jit_uint32_t retsize, 
   160 							  jit_uint32_t paramsize, 
   161 							  HookWrapper *pWrapper)
   162 {
   163 	//and esp, 0xFFFFFFF0
   164 	IA32_And_Rm_Imm8(jit, REG_ESP, MOD_REG, -16);
   165 
   166 	//if retvoid
   167 	// push 0
   168 	//else
   169 	// lea reg, [ebp-<retsize>]
   170 	// push reg
   171 	if (isretvoid)
   172 	{
   173 		IA32_Push_Imm8(jit, 0);
   174 	}
   175 	else
   176 	{
   177 		jit_int32_t offs = -(jit_int32_t)retsize;
   178 		if (offs >= SCHAR_MIN)
   179 		{
   180 			IA32_Lea_DispRegImm8(jit, REG_EAX, REG_EBP, (jit_int8_t)offs);
   181 		}
   182 		else
   183 		{
   184 			IA32_Lea_DispRegImm32(jit, REG_EAX, REG_EBP, offs);
   185 		}
   186 		IA32_Push_Reg(jit, REG_EAX);
   187 	}
   188 
   189 	//if void
   190 	// push 0
   191 	//else
   192 	// lea reg, [ebp-<retsize>-<paramsize>]
   193 	// push reg
   194 	if (isvoid)
   195 	{
   196 		IA32_Push_Imm8(jit, 0);
   197 	}
   198 	else
   199 	{
   200 		jit_int32_t offs = -(jit_int32_t)retsize - paramsize;
   201 		if (offs > SCHAR_MIN)
   202 		{
   203 			IA32_Lea_DispRegImm8(jit, REG_EDX, REG_EBP, (jit_int8_t)offs);
   204 		} else {
   205 			IA32_Lea_DispRegImm32(jit, REG_EDX, REG_EBP, offs);
   206 		}
   207 		IA32_Push_Reg(jit, REG_EDX);
   208 	}
   209 
   210 	//push eax (thisptr)
   211 	//IA32_Push_Reg(jit, REG_ECX);
   212 
   213 	//push ebx
   214 	IA32_Push_Reg(jit, REG_EBX);
   215 
   216 	//push <pWrapper>
   217 	IA32_Push_Imm32(jit, (jit_int32_t)pWrapper);
   218 }
   219 
   220 inline void Write_Call_Handler(JitWriter *jit, void *addr)
   221 {
   222 	//call <addr>
   223 	jitoffs_t call = IA32_Call_Imm32(jit, 0);
   224 	IA32_Write_Jump32_Abs(jit, call, addr);
   225 }
   226 
   227 inline void Write_Copy_RetVal(JitWriter *jit, SourceHook::PassInfo *pRetInfo)
   228 {
   229 	/* If the return value is a reference the size will probably be >sizeof(void *)
   230 	* for objects, we need to fix that so we can actually return the reference.
   231 	*/
   232 	size_t size = pRetInfo->size;
   233 	if (pRetInfo->flags & PASSFLAG_BYREF)
   234 	{
   235 		size = sizeof(void *);
   236 	}
   237 
   238 	if (pRetInfo->type == SourceHook::PassInfo::PassType_Float &&
   239 		pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal)
   240 	{
   241 		switch (size)
   242 		{
   243 		case 4:
   244 			{
   245 				//fld DWORD PTR [ebp-4]
   246 				IA32_Fld_Mem32_Disp8(jit, REG_EBP, -4);
   247 				break;
   248 			}
   249 		case 8:
   250 			{
   251 				//fld QWORD PTR [ebp-8]
   252 				IA32_Fld_Mem64_Disp8(jit, REG_EBP, -8);
   253 				break;
   254 			}
   255 		}
   256 	}
   257 	else if (pRetInfo->type == SourceHook::PassInfo::PassType_Object &&
   258 		pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal)
   259 	{
   260 		//cld
   261 		//push edi
   262 		//push esi
   263 		//mov edi, [ebp+12]
   264 		//lea esi, [ebp-<retsize>]
   265 		//if dwords
   266 		// mov ecx, <dwords>
   267 		// rep movsd
   268 		//if bytes
   269 		// mov ecx, <bytes>
   270 		// rep movsb
   271 		//pop esi
   272 		//pop edi
   273 
   274 		jit_int32_t offs;
   275 		jit_uint32_t dwords = size >> 2;
   276 		jit_uint32_t bytes = size & 0x3;
   277 
   278 		IA32_Cld(jit);
   279 		IA32_Push_Reg(jit, REG_EDI);
   280 		IA32_Push_Reg(jit, REG_ESI);
   281 
   282 		IA32_Mov_Reg_Rm_Disp8(jit, REG_EDI, REG_EBP, 12);
   283 
   284 		offs = -(jit_int32_t)pRetInfo->size;
   285 		if (offs >= SCHAR_MIN)
   286 		{
   287 			IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBP, (jit_int8_t)offs);
   288 		}
   289 		else
   290 		{
   291 			IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBP, offs);
   292 		}
   293 		IA32_Mov_Reg_Rm(jit, REG_EAX, REG_EDI, MOD_REG);
   294 
   295 		if (dwords)
   296 		{
   297 			IA32_Mov_Reg_Imm32(jit, REG_ECX, dwords);
   298 			IA32_Rep(jit);
   299 			IA32_Movsd(jit);
   300 		}
   301 		if (bytes)
   302 		{
   303 			IA32_Mov_Reg_Imm32(jit, REG_ECX, bytes);
   304 			IA32_Rep(jit);
   305 			IA32_Movsb(jit);
   306 		}
   307 		IA32_Pop_Reg(jit, REG_ESI);
   308 		IA32_Pop_Reg(jit, REG_EDI);
   309 	}
   310 	else
   311 	{
   312 		switch (size)
   313 		{
   314 		case 1:
   315 			{
   316 				//mov al, BYTE PTR [ebp-4]
   317 				IA32_Mov_Reg8_Rm8_Disp8(jit, REG_EAX, REG_EBP, -4);
   318 				break;
   319 			}
   320 		case 2:
   321 			{
   322 				//mov ax, WORD PTR [ebp-4]
   323 				jit->write_ubyte(IA32_16BIT_PREFIX);
   324 				IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, -4);
   325 				break;
   326 			}
   327 		case 4:
   328 			{
   329 				//mov eax, DWORD PTR [ebp-4]
   330 				IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, -4);
   331 				break;
   332 			}
   333 		case 8:
   334 			{
   335 				//mov eax, DWORD PTR [ebp-8]
   336 				//mov edx, DWORD PTR [ebp-4]
   337 				//:TODO: this is broken due to SH
   338 				IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, -8);
   339 				IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_EBP, -4);
   340 				break;
   341 			}
   342 		}
   343 	}
   344 }
   345 
   346 /******************************
   347  * Assembly Compiler Function *
   348  ******************************/
   349 
   350 void *JIT_HookCompile(HookWrapper *pWrapper)
   351 {
   352 	JitWriter writer;
   353 	JitWriter *jit = &writer;
   354 
   355 	jit_uint32_t CodeSize = 0;
   356 	jit_uint32_t ParamSize = pWrapper->GetParamSize();
   357 	jit_uint32_t RetSize = pWrapper->GetRetSize();
   358 
   359 	/* Local variable size allocated in the stack for the param and retval buffers */
   360 	jit_uint32_t LocalVarSize = ParamSize + RetSize;
   361 
   362 	/* Check if the return value is returned in memory */
   363 	bool RetInMemory = false;
   364 	SourceHook::PassInfo *pRetInfo = &pWrapper->GetProtoInfo()->retPassInfo;
   365 	if ((pRetInfo->type == SourceHook::PassInfo::PassType_Object) && 
   366 		(pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal))
   367 	{
   368 		RetInMemory = true;
   369 	}
   370 
   371 	writer.outbase = NULL;
   372 	writer.outptr = NULL;
   373 
   374 jit_rewind:
   375 	/* Write the function prologue */
   376 	Write_Function_Prologue(jit, RetInMemory);
   377 
   378 	/* Allocate the local variables into the stack */
   379 	if (LocalVarSize)
   380 	{
   381 		Write_Stack_Alloc(jit, LocalVarSize);
   382 	}
   383 
   384 	/* Copy all the parameters into the buffer */
   385 	if (ParamSize)
   386 	{
   387 		Write_Copy_Params(jit, RetInMemory, RetSize, ParamSize);
   388 	}
   389 
   390 	/* Push the parameters into the handler */
   391 	Write_Push_Params(jit, !RetSize, !ParamSize, RetSize, ParamSize, pWrapper);
   392 
   393 	/* Call the handler function */
   394 	Write_Call_Handler(jit, pWrapper->GetHandlerAddr());
   395 
   396 	/* Copy back the return value into eax or the hidden return buffer */
   397 	if (RetSize)
   398 	{
   399 		Write_Copy_RetVal(jit, pRetInfo);
   400 	}
   401 
   402 	/* Write the function epilogue */
   403 	Write_Function_Epilogue(jit, 
   404 #if defined PLATFORM_WINDOWS
   405 		ParamSize + ((RetInMemory) ? sizeof(void *) : 0)
   406 #elif defined PLATFORM_LINUX || defined PLATFORM_APPLE
   407 		(RetInMemory) ? sizeof(void *) : 0
   408 #endif
   409 	);
   410 
   411 	if (writer.outbase == NULL)
   412 	{
   413 		CodeSize = writer.get_outputpos();
   414 		writer.outbase = (jitcode_t)g_SPEngine->AllocatePageMemory(CodeSize);
   415 		g_SPEngine->SetReadWrite(writer.outbase);
   416 		writer.outptr = writer.outbase;
   417 		g_RegDecoder = 0;
   418 		goto jit_rewind;
   419 	}
   420 	g_SPEngine->SetReadExecute(writer.outbase);
   421 
   422 	return writer.outbase;
   423 }
   424 
   425 void JIT_FreeHook(void *addr)
   426 {
   427 	g_SPEngine->FreePageMemory(addr);
   428 }
   429 
   430 #endif