3 * =============================================================================
4 * SourceMod BinTools Extension
5 * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
6 * =============================================================================
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.
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
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/>.
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>.
29 * Version: $Id: CallMaker.h 1964 2008-03-27 04:54:56Z damagedsoul $
32 #if defined HOOKING_ENABLED
34 #include "jit_compile.h"
35 #include "sm_platform.h"
36 #include "extension.h"
42 inline void Write_Function_Prologue(JitWriter *jit, bool RetInMemory)
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
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));
59 inline void Write_Function_Epilogue(JitWriter *jit, unsigned short size)
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);
74 IA32_Return_Popstack(jit, size);
78 inline void Write_Stack_Alloc(JitWriter *jit, jit_uint32_t size)
81 if (size <= SCHAR_MAX)
83 IA32_Sub_Rm_Imm8(jit, REG_ESP, (jit_int8_t)size, MOD_REG);
87 IA32_Sub_Rm_Imm32(jit, REG_ESP, size, MOD_REG);
91 inline void Write_Copy_Params(JitWriter *jit, bool RetInMemory, jit_uint32_t retsize, jit_uint32_t paramsize)
93 //:TODO: inline this memcpy!! - For small numbers of params mov's (with clever reg allocation?) would be faster
98 //lea edi, [ebp-<retsize>-<paramsize>]
99 //lea esi, [ebp+12+(RetInMemory)?4:0]
110 jit_uint32_t dwords = paramsize >> 2;
111 jit_uint32_t bytes = paramsize & 0x3;
114 IA32_Push_Reg(jit, REG_EDI);
115 IA32_Push_Reg(jit, REG_ESI);
116 offs = -(jit_int32_t)retsize - paramsize;
118 if (offs > SCHAR_MIN)
120 IA32_Lea_DispRegImm8(jit, REG_EDI, REG_EBP, (jit_int8_t)offs);
124 IA32_Lea_DispRegImm32(jit, REG_EDI, REG_EBP, offs);
126 offs = 12 + ((RetInMemory) ? sizeof(void *) : 0);
128 #if defined PLATFORM_LINUX || defined PLATFORM_APPLE
132 if (offs < SCHAR_MAX)
134 IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBP, (jit_int8_t)offs);
138 IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBP, offs);
142 IA32_Mov_Reg_Imm32(jit, REG_ECX, dwords);
148 IA32_Mov_Reg_Imm32(jit, REG_ECX, bytes);
152 IA32_Pop_Reg(jit, REG_ESI);
153 IA32_Pop_Reg(jit, REG_EDI);
156 inline void Write_Push_Params(JitWriter *jit,
159 jit_uint32_t retsize,
160 jit_uint32_t paramsize,
161 HookWrapper *pWrapper)
163 //and esp, 0xFFFFFFF0
164 IA32_And_Rm_Imm8(jit, REG_ESP, MOD_REG, -16);
169 // lea reg, [ebp-<retsize>]
173 IA32_Push_Imm8(jit, 0);
177 jit_int32_t offs = -(jit_int32_t)retsize;
178 if (offs >= SCHAR_MIN)
180 IA32_Lea_DispRegImm8(jit, REG_EAX, REG_EBP, (jit_int8_t)offs);
184 IA32_Lea_DispRegImm32(jit, REG_EAX, REG_EBP, offs);
186 IA32_Push_Reg(jit, REG_EAX);
192 // lea reg, [ebp-<retsize>-<paramsize>]
196 IA32_Push_Imm8(jit, 0);
200 jit_int32_t offs = -(jit_int32_t)retsize - paramsize;
201 if (offs > SCHAR_MIN)
203 IA32_Lea_DispRegImm8(jit, REG_EDX, REG_EBP, (jit_int8_t)offs);
205 IA32_Lea_DispRegImm32(jit, REG_EDX, REG_EBP, offs);
207 IA32_Push_Reg(jit, REG_EDX);
211 //IA32_Push_Reg(jit, REG_ECX);
214 IA32_Push_Reg(jit, REG_EBX);
217 IA32_Push_Imm32(jit, (jit_int32_t)pWrapper);
220 inline void Write_Call_Handler(JitWriter *jit, void *addr)
223 jitoffs_t call = IA32_Call_Imm32(jit, 0);
224 IA32_Write_Jump32_Abs(jit, call, addr);
227 inline void Write_Copy_RetVal(JitWriter *jit, SourceHook::PassInfo *pRetInfo)
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.
232 size_t size = pRetInfo->size;
233 if (pRetInfo->flags & PASSFLAG_BYREF)
235 size = sizeof(void *);
238 if (pRetInfo->type == SourceHook::PassInfo::PassType_Float &&
239 pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal)
245 //fld DWORD PTR [ebp-4]
246 IA32_Fld_Mem32_Disp8(jit, REG_EBP, -4);
251 //fld QWORD PTR [ebp-8]
252 IA32_Fld_Mem64_Disp8(jit, REG_EBP, -8);
257 else if (pRetInfo->type == SourceHook::PassInfo::PassType_Object &&
258 pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal)
264 //lea esi, [ebp-<retsize>]
275 jit_uint32_t dwords = size >> 2;
276 jit_uint32_t bytes = size & 0x3;
279 IA32_Push_Reg(jit, REG_EDI);
280 IA32_Push_Reg(jit, REG_ESI);
282 IA32_Mov_Reg_Rm_Disp8(jit, REG_EDI, REG_EBP, 12);
284 offs = -(jit_int32_t)pRetInfo->size;
285 if (offs >= SCHAR_MIN)
287 IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBP, (jit_int8_t)offs);
291 IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBP, offs);
293 IA32_Mov_Reg_Rm(jit, REG_EAX, REG_EDI, MOD_REG);
297 IA32_Mov_Reg_Imm32(jit, REG_ECX, dwords);
303 IA32_Mov_Reg_Imm32(jit, REG_ECX, bytes);
307 IA32_Pop_Reg(jit, REG_ESI);
308 IA32_Pop_Reg(jit, REG_EDI);
316 //mov al, BYTE PTR [ebp-4]
317 IA32_Mov_Reg8_Rm8_Disp8(jit, REG_EAX, REG_EBP, -4);
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);
329 //mov eax, DWORD PTR [ebp-4]
330 IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, -4);
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);
346 /******************************
347 * Assembly Compiler Function *
348 ******************************/
350 void *JIT_HookCompile(HookWrapper *pWrapper)
353 JitWriter *jit = &writer;
355 jit_uint32_t CodeSize = 0;
356 jit_uint32_t ParamSize = pWrapper->GetParamSize();
357 jit_uint32_t RetSize = pWrapper->GetRetSize();
359 /* Local variable size allocated in the stack for the param and retval buffers */
360 jit_uint32_t LocalVarSize = ParamSize + RetSize;
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))
371 writer.outbase = NULL;
372 writer.outptr = NULL;
375 /* Write the function prologue */
376 Write_Function_Prologue(jit, RetInMemory);
378 /* Allocate the local variables into the stack */
381 Write_Stack_Alloc(jit, LocalVarSize);
384 /* Copy all the parameters into the buffer */
387 Write_Copy_Params(jit, RetInMemory, RetSize, ParamSize);
390 /* Push the parameters into the handler */
391 Write_Push_Params(jit, !RetSize, !ParamSize, RetSize, ParamSize, pWrapper);
393 /* Call the handler function */
394 Write_Call_Handler(jit, pWrapper->GetHandlerAddr());
396 /* Copy back the return value into eax or the hidden return buffer */
399 Write_Copy_RetVal(jit, pRetInfo);
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
411 if (writer.outbase == NULL)
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;
420 g_SPEngine->SetReadExecute(writer.outbase);
422 return writer.outbase;
425 void JIT_FreeHook(void *addr)
427 g_SPEngine->FreePageMemory(addr);