pred@2504: /**
pred@2504: * vim: set ts=4 :
pred@2504: * =============================================================================
pred@2504: * SourceMod BinTools Extension
pred@2504: * Copyright (C) 2004-2008 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 "jit_compile.h"
pred@2504: #include "sm_platform.h"
pred@2504: #include "extension.h"
pred@2504:
pred@2504: /********************
pred@2504: * Assembly Opcodes *
pred@2504: ********************/
pred@2504:
pred@2504: inline void Write_Function_Prologue(JitWriter *jit, bool RetInMemory)
pred@2504: {
pred@2504: //push ebp
pred@2504: //push ebx
pred@2504: //mov ebp, esp
pred@2504: IA32_Push_Reg(jit, REG_EBP);
pred@2504: IA32_Push_Reg(jit, REG_EBX);
pred@2504: IA32_Mov_Reg_Rm(jit, REG_EBP, REG_ESP, MOD_REG);
pred@2504: #if defined PLATFORM_WINDOWS
pred@2504: //mov ebx, ecx
pred@2504: IA32_Mov_Reg_Rm(jit, REG_EBX, REG_ECX, MOD_REG);
pred@2504: #elif defined PLATFORM_LINUX
pred@2504: //mov ebx, [ebp+12+(RetInMemory)?4:0]
pred@2504: IA32_Mov_Reg_Rm_Disp8(jit, REG_EBX, REG_EBP, 12+((RetInMemory)?4:0));
pred@2504: #endif
pred@2504: }
pred@2504:
pred@2504: inline void Write_Function_Epilogue(JitWriter *jit, unsigned short size)
pred@2504: {
pred@2504: //mov esp, ebp
pred@2504: //pop ebx
pred@2504: //pop ebp
pred@2504: //ret
pred@2504: IA32_Mov_Reg_Rm(jit, REG_ESP, REG_EBP, MOD_REG);
pred@2504: IA32_Pop_Reg(jit, REG_EBX);
pred@2504: IA32_Pop_Reg(jit, REG_EBP);
pred@2504: if (size == 0)
pred@2504: {
pred@2504: IA32_Return(jit);
pred@2504: }
pred@2504: else
pred@2504: {
pred@2504: IA32_Return_Popstack(jit, size);
pred@2504: }
pred@2504: }
pred@2504:
pred@2504: inline void Write_Stack_Alloc(JitWriter *jit, jit_uint32_t size)
pred@2504: {
pred@2504: //sub esp,
pred@2504: if (size <= SCHAR_MAX)
pred@2504: {
pred@2504: IA32_Sub_Rm_Imm8(jit, REG_ESP, (jit_int8_t)size, MOD_REG);
pred@2504: }
pred@2504: else
pred@2504: {
pred@2504: IA32_Sub_Rm_Imm32(jit, REG_ESP, size, MOD_REG);
pred@2504: }
pred@2504: }
pred@2504:
pred@2504: inline void Write_Copy_Params(JitWriter *jit, bool RetInMemory, jit_uint32_t retsize, jit_uint32_t paramsize)
pred@2504: {
pred@2504: //:TODO: inline this memcpy!! - For small numbers of params mov's (with clever reg allocation?) would be faster
pred@2504:
pred@2504: //cld
pred@2504: //push edi
pred@2504: //push esi
pred@2504: //lea edi, [ebp--]
pred@2504: //lea esi, [ebp+12+(RetInMemory)?4:0]
pred@2504: //if dwords
pred@2504: // mov ecx,
pred@2504: // rep movsd
pred@2504: //if bytes
pred@2504: // mov ecx,
pred@2504: // rep movsb
pred@2504: //pop esi
pred@2504: //pop edi
pred@2504:
pred@2504: jit_int32_t offs;
pred@2504: jit_uint32_t dwords = paramsize >> 2;
pred@2504: jit_uint32_t bytes = paramsize & 0x3;
pred@2504:
pred@2504: IA32_Cld(jit);
pred@2504: IA32_Push_Reg(jit, REG_EDI);
pred@2504: IA32_Push_Reg(jit, REG_ESI);
pred@2504: offs = -(jit_int32_t)retsize - paramsize;
pred@2504:
pred@2504: if (offs > SCHAR_MIN)
pred@2504: {
pred@2504: IA32_Lea_DispRegImm8(jit, REG_EDI, REG_EBP, (jit_int8_t)offs);
pred@2504: }
pred@2504: else
pred@2504: {
pred@2504: IA32_Lea_DispRegImm32(jit, REG_EDI, REG_EBP, offs);
pred@2504: }
pred@2504: offs = 12 + ((RetInMemory) ? sizeof(void *) : 0);
pred@2504:
pred@2504: #if defined PLATFORM_LINUX
pred@2504: offs += 4;
pred@2504: #endif
pred@2504:
pred@2504: if (offs < SCHAR_MAX)
pred@2504: {
pred@2504: IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBP, (jit_int8_t)offs);
pred@2504: }
pred@2504: else
pred@2504: {
pred@2504: IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBP, offs);
pred@2504: }
pred@2504: if (dwords)
pred@2504: {
pred@2504: IA32_Mov_Reg_Imm32(jit, REG_ECX, dwords);
pred@2504: IA32_Rep(jit);
pred@2504: IA32_Movsd(jit);
pred@2504: }
pred@2504: if (bytes)
pred@2504: {
pred@2504: IA32_Mov_Reg_Imm32(jit, REG_ECX, bytes);
pred@2504: IA32_Rep(jit);
pred@2504: IA32_Movsb(jit);
pred@2504: }
pred@2504: IA32_Pop_Reg(jit, REG_ESI);
pred@2504: IA32_Pop_Reg(jit, REG_EDI);
pred@2504: }
pred@2504:
pred@2504: inline void Write_Push_Params(JitWriter *jit,
pred@2504: bool isretvoid,
pred@2504: bool isvoid,
pred@2504: jit_uint32_t retsize,
pred@2504: jit_uint32_t paramsize,
pred@2504: HookWrapper *pWrapper)
pred@2504: {
pred@2504: //and esp, 0xFFFFFFF0
pred@2504: IA32_And_Rm_Imm8(jit, REG_ESP, MOD_REG, -16);
pred@2504:
pred@2504: //if retvoid
pred@2504: // push 0
pred@2504: //else
pred@2504: // lea reg, [ebp-]
pred@2504: // push reg
pred@2504: if (isretvoid)
pred@2504: {
pred@2504: IA32_Push_Imm8(jit, 0);
pred@2504: }
pred@2504: else
pred@2504: {
pred@2504: jit_int32_t offs = -(jit_int32_t)retsize;
pred@2504: if (offs >= SCHAR_MIN)
pred@2504: {
pred@2504: IA32_Lea_DispRegImm8(jit, REG_EAX, REG_EBP, (jit_int8_t)offs);
pred@2504: }
pred@2504: else
pred@2504: {
pred@2504: IA32_Lea_DispRegImm32(jit, REG_EAX, REG_EBP, offs);
pred@2504: }
pred@2504: IA32_Push_Reg(jit, REG_EAX);
pred@2504: }
pred@2504:
pred@2504: //if void
pred@2504: // push 0
pred@2504: //else
pred@2504: // lea reg, [ebp--]
pred@2504: // push reg
pred@2504: if (isvoid)
pred@2504: {
pred@2504: IA32_Push_Imm8(jit, 0);
pred@2504: }
pred@2504: else
pred@2504: {
pred@2504: jit_int32_t offs = -(jit_int32_t)retsize - paramsize;
pred@2504: if (offs > SCHAR_MIN)
pred@2504: {
pred@2504: IA32_Lea_DispRegImm8(jit, REG_EDX, REG_EBP, (jit_int8_t)offs);
pred@2504: } else {
pred@2504: IA32_Lea_DispRegImm32(jit, REG_EDX, REG_EBP, offs);
pred@2504: }
pred@2504: IA32_Push_Reg(jit, REG_EDX);
pred@2504: }
pred@2504:
pred@2504: //push eax (thisptr)
pred@2504: //IA32_Push_Reg(jit, REG_ECX);
pred@2504:
pred@2504: //push ebx
pred@2504: IA32_Push_Reg(jit, REG_EBX);
pred@2504:
pred@2504: //push
pred@2504: IA32_Push_Imm32(jit, (jit_int32_t)pWrapper);
pred@2504: }
pred@2504:
pred@2504: inline void Write_Call_Handler(JitWriter *jit, void *addr)
pred@2504: {
pred@2504: //call
pred@2504: jitoffs_t call = IA32_Call_Imm32(jit, 0);
pred@2504: IA32_Write_Jump32_Abs(jit, call, addr);
pred@2504: }
pred@2504:
pred@2504: inline void Write_Copy_RetVal(JitWriter *jit, SourceHook::PassInfo *pRetInfo)
pred@2504: {
pred@2504: /* If the return value is a reference the size will probably be >sizeof(void *)
pred@2504: * for objects, we need to fix that so we can actually return the reference.
pred@2504: */
pred@2504: size_t size = pRetInfo->size;
pred@2504: if (pRetInfo->flags & PASSFLAG_BYREF)
pred@2504: {
pred@2504: size = sizeof(void *);
pred@2504: }
pred@2504:
pred@2504: if (pRetInfo->type == SourceHook::PassInfo::PassType_Float &&
pred@2504: pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal)
pred@2504: {
pred@2504: switch (size)
pred@2504: {
pred@2504: case 4:
pred@2504: {
pred@2504: //fld DWORD PTR [ebp-4]
pred@2504: IA32_Fld_Mem32_Disp8(jit, REG_EBP, -4);
pred@2504: break;
pred@2504: }
pred@2504: case 8:
pred@2504: {
pred@2504: //fld QWORD PTR [ebp-8]
pred@2504: IA32_Fld_Mem64_Disp8(jit, REG_EBP, -8);
pred@2504: break;
pred@2504: }
pred@2504: }
pred@2504: }
pred@2504: else if (pRetInfo->type == SourceHook::PassInfo::PassType_Object &&
pred@2504: pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal)
pred@2504: {
pred@2504: //cld
pred@2504: //push edi
pred@2504: //push esi
pred@2504: //mov edi, [ebp+12]
pred@2504: //lea esi, [ebp-]
pred@2504: //if dwords
pred@2504: // mov ecx,
pred@2504: // rep movsd
pred@2504: //if bytes
pred@2504: // mov ecx,
pred@2504: // rep movsb
pred@2504: //pop esi
pred@2504: //pop edi
pred@2504:
pred@2504: jit_int32_t offs;
pred@2504: jit_uint32_t dwords = size >> 2;
pred@2504: jit_uint32_t bytes = size & 0x3;
pred@2504:
pred@2504: IA32_Cld(jit);
pred@2504: IA32_Push_Reg(jit, REG_EDI);
pred@2504: IA32_Push_Reg(jit, REG_ESI);
pred@2504:
pred@2504: IA32_Mov_Reg_Rm_Disp8(jit, REG_EDI, REG_EBP, 12);
pred@2504:
pred@2504: offs = -(jit_int32_t)pRetInfo->size;
pred@2504: if (offs >= SCHAR_MIN)
pred@2504: {
pred@2504: IA32_Lea_DispRegImm8(jit, REG_ESI, REG_EBP, (jit_int8_t)offs);
pred@2504: }
pred@2504: else
pred@2504: {
pred@2504: IA32_Lea_DispRegImm32(jit, REG_ESI, REG_EBP, offs);
pred@2504: }
pred@2504: IA32_Mov_Reg_Rm(jit, REG_EAX, REG_EDI, MOD_REG);
pred@2504:
pred@2504: if (dwords)
pred@2504: {
pred@2504: IA32_Mov_Reg_Imm32(jit, REG_ECX, dwords);
pred@2504: IA32_Rep(jit);
pred@2504: IA32_Movsd(jit);
pred@2504: }
pred@2504: if (bytes)
pred@2504: {
pred@2504: IA32_Mov_Reg_Imm32(jit, REG_ECX, bytes);
pred@2504: IA32_Rep(jit);
pred@2504: IA32_Movsb(jit);
pred@2504: }
pred@2504: IA32_Pop_Reg(jit, REG_ESI);
pred@2504: IA32_Pop_Reg(jit, REG_EDI);
pred@2504: }
pred@2504: else
pred@2504: {
pred@2504: switch (size)
pred@2504: {
pred@2504: case 1:
pred@2504: {
pred@2504: //mov al, BYTE PTR [ebp-4]
pred@2504: IA32_Mov_Reg8_Rm8_Disp8(jit, REG_EAX, REG_EBP, -4);
pred@2504: break;
pred@2504: }
pred@2504: case 2:
pred@2504: {
pred@2504: //mov ax, WORD PTR [ebp-4]
pred@2504: jit->write_ubyte(IA32_16BIT_PREFIX);
pred@2504: IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, -4);
pred@2504: break;
pred@2504: }
pred@2504: case 4:
pred@2504: {
pred@2504: //mov eax, DWORD PTR [ebp-4]
pred@2504: IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, -4);
pred@2504: break;
pred@2504: }
pred@2504: case 8:
pred@2504: {
pred@2504: //mov eax, DWORD PTR [ebp-8]
pred@2504: //mov edx, DWORD PTR [ebp-4]
pred@2504: //:TODO: this is broken due to SH
pred@2504: IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, -8);
pred@2504: IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_EBP, -4);
pred@2504: break;
pred@2504: }
pred@2504: }
pred@2504: }
pred@2504: }
pred@2504:
pred@2504: /******************************
pred@2504: * Assembly Compiler Function *
pred@2504: ******************************/
pred@2504:
pred@2504: void *JIT_HookCompile(HookWrapper *pWrapper)
pred@2504: {
pred@2504: JitWriter writer;
pred@2504: JitWriter *jit = &writer;
pred@2504:
pred@2504: jit_uint32_t CodeSize = 0;
pred@2504: jit_uint32_t ParamSize = pWrapper->GetParamSize();
pred@2504: jit_uint32_t RetSize = pWrapper->GetRetSize();
pred@2504:
pred@2504: /* Local variable size allocated in the stack for the param and retval buffers */
pred@2504: jit_uint32_t LocalVarSize = ParamSize + RetSize;
pred@2504:
pred@2504: /* Check if the return value is returned in memory */
pred@2504: bool RetInMemory = false;
pred@2504: SourceHook::PassInfo *pRetInfo = &pWrapper->GetProtoInfo()->retPassInfo;
pred@2504: if ((pRetInfo->type == SourceHook::PassInfo::PassType_Object) &&
pred@2504: (pRetInfo->flags & SourceHook::PassInfo::PassFlag_ByVal))
pred@2504: {
pred@2504: RetInMemory = true;
pred@2504: }
pred@2504:
pred@2504: writer.outbase = NULL;
pred@2504: writer.outptr = NULL;
pred@2504:
pred@2504: jit_rewind:
pred@2504: /* Write the function prologue */
pred@2504: Write_Function_Prologue(jit, RetInMemory);
pred@2504:
pred@2504: /* Allocate the local variables into the stack */
pred@2504: if (LocalVarSize)
pred@2504: {
pred@2504: Write_Stack_Alloc(jit, LocalVarSize);
pred@2504: }
pred@2504:
pred@2504: /* Copy all the parameters into the buffer */
pred@2504: if (ParamSize)
pred@2504: {
pred@2504: Write_Copy_Params(jit, RetInMemory, RetSize, ParamSize);
pred@2504: }
pred@2504:
pred@2504: /* Push the parameters into the handler */
pred@2504: Write_Push_Params(jit, !RetSize, !ParamSize, RetSize, ParamSize, pWrapper);
pred@2504:
pred@2504: /* Call the handler function */
pred@2504: Write_Call_Handler(jit, pWrapper->GetHandlerAddr());
pred@2504:
pred@2504: /* Copy back the return value into eax or the hidden return buffer */
pred@2504: if (RetSize)
pred@2504: {
pred@2504: Write_Copy_RetVal(jit, pRetInfo);
pred@2504: }
pred@2504:
pred@2504: /* Write the function epilogue */
pred@2504: Write_Function_Epilogue(jit,
pred@2504: #if defined PLATFORM_WINDOWS
pred@2504: ParamSize + ((RetInMemory) ? sizeof(void *) : 0)
pred@2504: #elif defined PLATFORM_LINUX
pred@2504: (RetInMemory) ? sizeof(void *) : 0
pred@2504: #endif
pred@2504: );
pred@2504:
pred@2504: if (writer.outbase == NULL)
pred@2504: {
pred@2504: CodeSize = writer.get_outputpos();
pred@2504: writer.outbase = (jitcode_t)g_SPEngine->AllocatePageMemory(CodeSize);
pred@2504: g_SPEngine->SetReadWrite(writer.outbase);
pred@2504: writer.outptr = writer.outbase;
pred@2504: g_RegDecoder = 0;
pred@2504: goto jit_rewind;
pred@2504: }
pred@2504: g_SPEngine->SetReadExecute(writer.outbase);
pred@2504:
pred@2504: return writer.outbase;
pred@2504: }
pred@2504:
pred@2504: void JIT_FreeHook(void *addr)
pred@2504: {
pred@2504: g_SPEngine->FreePageMemory(addr);
pred@2504: }
pred@2504:
pred@2504: #endif