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>.
32 #include <sm_platform.h>
33 #include "extension.h"
34 #include <jit_helpers.h>
35 #include <x86_macros.h>
36 #include "jit_compile.h"
38 jit_uint32_t g_StackUsage = 0;
39 jit_uint32_t g_StackAlign = 0;
40 jit_uint32_t g_RegDecoder = 0;
46 inline void Write_Execution_Prologue(JitWriter *jit, bool is_void, bool has_params)
59 //sub esp, <alignment>
60 IA32_Push_Reg(jit, kREG_EBP);
61 IA32_Mov_Reg_Rm(jit, kREG_EBP, kREG_ESP, MOD_REG);
64 IA32_Push_Reg(jit, kREG_EDI);
65 IA32_Mov_Reg_Rm_Disp8(jit, kREG_EDI, kREG_EBP, 12);
69 IA32_Push_Reg(jit, kREG_EBX);
70 IA32_Mov_Reg_Rm_Disp8(jit, kREG_EBX, kREG_EBP, 8);
72 IA32_Push_Reg(jit, kREG_ESI);
73 IA32_Mov_Reg_Rm(jit, kREG_ESI, kREG_ESP, MOD_REG);
74 IA32_And_Rm_Imm8(jit, kREG_ESP, MOD_REG, -16);
78 /* Alloc this instruction before knowing the real stack usage */
79 IA32_Sub_Rm_Imm32(jit, kREG_ESP, 1337, MOD_REG);
83 IA32_Sub_Rm_Imm32(jit, kREG_ESP, g_StackAlign, MOD_REG);
88 inline void Write_Function_Epilogue(JitWriter *jit, bool is_void, bool has_params)
99 IA32_Mov_Reg_Rm(jit, kREG_ESP, kREG_ESI, MOD_REG);
100 IA32_Pop_Reg(jit, kREG_ESI);
103 IA32_Pop_Reg(jit, kREG_EBX);
107 IA32_Pop_Reg(jit, kREG_EDI);
109 IA32_Mov_Reg_Rm(jit, kREG_ESP, kREG_EBP, MOD_REG);
110 IA32_Pop_Reg(jit, kREG_EBP);
114 inline void Write_PushPOD(JitWriter *jit, const SourceHook::PassInfo *info, unsigned int offset)
116 jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++);
118 if (info->flags & PASSFLAG_BYVAL)
124 //movzx reg, BYTE PTR [ebx+<offset>]
126 if (offset < SCHAR_MAX)
128 IA32_Movzx_Reg32_Rm8_Disp8(jit, reg, kREG_EBX, (jit_int8_t)offset);
129 } else if (!offset) {
130 IA32_Movzx_Reg32_Rm8(jit, reg, kREG_EBX, MOD_MEM_REG);
132 IA32_Movzx_Reg32_Rm8_Disp32(jit, reg, kREG_EBX, offset);
134 IA32_Push_Reg(jit, reg);
141 //movzx reg, WORD PTR [ebx+<offset>]
143 jit->write_ubyte(IA32_16BIT_PREFIX);
144 if (offset < SCHAR_MAX)
146 IA32_Movzx_Reg32_Rm16_Disp8(jit, reg, kREG_EBX, (jit_int8_t)offset);
147 } else if (!offset) {
148 IA32_Movzx_Reg32_Rm16(jit, reg, kREG_EBX, MOD_MEM_REG);
150 IA32_Movzx_Reg32_Rm16_Disp32(jit, reg, kREG_EBX, offset);
152 IA32_Push_Reg(jit, reg);
159 //mov reg, DWORD PTR [ebx+<offset>]
161 if (offset < SCHAR_MAX)
163 IA32_Mov_Reg_Rm_Disp8(jit, reg, kREG_EBX, (jit_int8_t)offset);
164 } else if (!offset) {
165 IA32_Mov_Reg_Rm(jit, reg, kREG_EBX, MOD_MEM_REG);
167 IA32_Mov_Reg_Rm_Disp32(jit, reg, kREG_EBX, offset);
169 IA32_Push_Reg(jit, reg);
176 //mov reg, DWORD PTR [ebx+<offset>+4]
177 //mov reg2, DWORD PTR [ebx+<offset>]
180 jit_uint8_t reg2 = _DecodeRegister3(g_RegDecoder++);
182 if (offset+4 < SCHAR_MAX)
184 IA32_Mov_Reg_Rm_Disp8(jit, reg, kREG_EBX, (jit_int8_t)(offset+4));
186 IA32_Mov_Reg_Rm_Disp32(jit, reg, kREG_EBX, offset+4);
188 if (offset < SCHAR_MAX)
190 IA32_Mov_Reg_Rm_Disp8(jit, reg2, kREG_EBX, (jit_int8_t)offset);
191 } else if (!offset) {
192 IA32_Mov_Reg_Rm(jit, reg2, kREG_EBX, MOD_MEM_REG);
194 IA32_Mov_Reg_Rm_Disp32(jit, reg2, kREG_EBX, offset);
196 IA32_Push_Reg(jit, reg);
197 IA32_Push_Reg(jit, reg2);
203 } else if (info->flags & PASSFLAG_BYREF) {
204 //lea reg, [ebx+<offset>]
208 IA32_Push_Reg(jit, kREG_EBX);
212 if (offset < SCHAR_MAX)
214 IA32_Lea_DispRegImm8(jit, reg, kREG_EBX, (jit_int8_t)offset);
216 IA32_Lea_DispRegImm32(jit, reg, kREG_EBX, offset);
218 IA32_Push_Reg(jit, reg);
224 inline void Write_PushFloat(JitWriter *jit, const SourceHook::PassInfo *info, unsigned int offset)
226 if (info->flags & PASSFLAG_BYVAL)
232 //fld DWORD PTR [ebx+<offset>]
234 //fstp DWORD PTR [esp]
235 if (offset < SCHAR_MAX)
237 IA32_Fld_Mem32_Disp8(jit, kREG_EBX, (jit_int8_t)offset);
238 } else if (!offset) {
239 IA32_Fld_Mem32(jit, kREG_EBX);
241 IA32_Fld_Mem32_Disp32(jit, kREG_EBX, offset);
243 IA32_Push_Reg(jit, _DecodeRegister3(g_RegDecoder++));
244 IA32_Fstp_Mem32_ESP(jit);
250 //fld QWORD PTR [ebx+<offset>]
252 //fstp QWORD PTR [esp]
253 if (offset < SCHAR_MAX)
255 IA32_Fld_Mem64_Disp8(jit, kREG_EBX, (jit_int8_t)offset);
256 } else if (!offset) {
257 IA32_Fld_Mem64(jit, kREG_EBX);
259 IA32_Fld_Mem64_Disp32(jit, kREG_EBX, offset);
261 IA32_Sub_Rm_Imm8(jit, kREG_ESP, 8, MOD_REG);
262 IA32_Fstp_Mem64_ESP(jit);
267 } else if (info->flags & PASSFLAG_BYREF) {
268 //lea reg, [ebx+<offset>]
272 IA32_Push_Reg(jit, kREG_EBX);
277 jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++);
278 if (offset < SCHAR_MAX)
280 IA32_Lea_DispRegImm8(jit, reg, kREG_EBX, (jit_int8_t)offset);
282 IA32_Lea_DispRegImm32(jit, reg, kREG_EBX, offset);
284 IA32_Push_Reg(jit, reg);
290 inline void Write_PushObject(JitWriter *jit, const SourceHook::PassInfo *info, unsigned int offset)
292 if (info->flags & PASSFLAG_BYVAL)
294 #ifdef PLATFORM_POSIX
295 if (info->flags & PASSFLAG_ODTOR)
300 jit_uint32_t dwords = info->size >> 2;
301 jit_uint32_t bytes = info->size & 0x3;
308 //lea esi, [ebx+<offs>]
317 if (info->size < SCHAR_MAX)
319 IA32_Sub_Rm_Imm8(jit, kREG_ESP, (jit_int8_t)info->size, MOD_REG);
321 IA32_Sub_Rm_Imm32(jit, kREG_ESP, info->size, MOD_REG);
324 IA32_Push_Reg(jit, kREG_EDI);
325 IA32_Push_Reg(jit, kREG_ESI);
326 IA32_Lea_Reg_DispRegMultImm8(jit, kREG_EDI, kREG_NOIDX, kREG_ESP, NOSCALE, 8);
327 if (offset < SCHAR_MAX)
329 IA32_Lea_DispRegImm8(jit, kREG_ESI, kREG_EBX, (jit_int8_t)offset);
330 } else if (!offset) {
331 IA32_Mov_Reg_Rm(jit, kREG_ESI, kREG_EBX, MOD_REG);
333 IA32_Lea_DispRegImm32(jit, kREG_ESI, kREG_EBX, offset);
337 IA32_Mov_Reg_Imm32(jit, kREG_ECX, dwords);
343 IA32_Mov_Reg_Imm32(jit, kREG_ECX, bytes);
347 IA32_Pop_Reg(jit, kREG_ESI);
348 IA32_Pop_Reg(jit, kREG_EDI);
350 g_StackUsage += info->size;
351 } else if (info->flags & PASSFLAG_BYREF) {
352 #ifdef PLATFORM_POSIX
357 IA32_Push_Reg(jit, kREG_EBX);
362 //lea reg, [ebx+<offset>]
364 jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++);
365 if (offset < SCHAR_MAX)
367 IA32_Lea_DispRegImm8(jit, reg, kREG_EBX, (jit_int8_t)offset);
369 IA32_Lea_DispRegImm32(jit, reg, kREG_EBX, offset);
371 IA32_Push_Reg(jit, reg);
377 inline void Write_PushThisPtr(JitWriter *jit)
379 #ifdef PLATFORM_POSIX
382 jit_uint8_t reg = _DecodeRegister3(g_RegDecoder++);
384 IA32_Mov_Reg_Rm(jit, reg, kREG_EBX, MOD_MEM_REG);
385 IA32_Push_Reg(jit, reg);
388 #elif defined PLATFORM_WINDOWS
390 IA32_Mov_Reg_Rm(jit, kREG_ECX, kREG_EBX, MOD_MEM_REG);
394 inline void Write_PushRetBuffer(JitWriter *jit)
397 IA32_Push_Reg(jit, kREG_EDI);
400 inline void Write_CallFunction(JitWriter *jit, FuncAddrMethod method, CallWrapper *pWrapper)
402 if (method == FuncAddr_Direct)
405 jitoffs_t call = IA32_Call_Imm32(jit, 0);
406 IA32_Write_Jump32_Abs(jit, call, pWrapper->GetCalleeAddr());
407 } else if (method == FuncAddr_VTable) {
408 //*(this + thisOffs + vtblOffs)[vtblIdx]
410 //mov eax, [edx+<thisOffs>+<vtblOffs>]
411 //mov edx, [eax+<vtblIdx>*4]
413 SourceHook::MemFuncInfo *funcInfo = pWrapper->GetMemFuncInfo();
414 jit_uint32_t total_offs = funcInfo->thisptroffs + funcInfo->vtbloffs;
415 jit_uint32_t vfunc_pos = funcInfo->vtblindex * 4;
417 IA32_Mov_Reg_Rm(jit, kREG_EDX, kREG_EBX, MOD_MEM_REG);
418 if (total_offs < SCHAR_MAX)
420 IA32_Mov_Reg_Rm_Disp8(jit, kREG_EAX, kREG_EDX, (jit_int8_t)total_offs);
421 } else if (!total_offs) {
422 IA32_Mov_Reg_Rm(jit, kREG_EAX, kREG_EDX, MOD_MEM_REG);
424 IA32_Mov_Reg_Rm_Disp32(jit, kREG_EAX, kREG_EDX, total_offs);
426 if (vfunc_pos < SCHAR_MAX)
428 IA32_Mov_Reg_Rm_Disp8(jit, kREG_EDX, kREG_EAX, (jit_int8_t)vfunc_pos);
429 } else if (!vfunc_pos) {
430 IA32_Mov_Reg_Rm(jit, kREG_EDX, kREG_EAX, MOD_MEM_REG);
432 IA32_Mov_Reg_Rm_Disp32(jit, kREG_EDX, kREG_EAX, vfunc_pos);
434 IA32_Call_Reg(jit, kREG_EDX);
438 inline void Write_RectifyStack(JitWriter *jit, jit_uint32_t value)
441 if (value < SCHAR_MAX)
443 IA32_Add_Rm_Imm8(jit, kREG_ESP, (jit_int8_t)value, MOD_REG);
445 IA32_Add_Rm_Imm32(jit, kREG_ESP, value, MOD_REG);
449 inline void Write_MovRet2Buf(JitWriter *jit, const PassInfo *pRet)
451 if (pRet->type == PassType_Float)
457 //fstp DWORD PTR [edi]
458 IA32_Fstp_Mem32(jit, kREG_EDI);
463 //fstp QWORD PTR [edi]
464 IA32_Fstp_Mem64(jit, kREG_EDI);
475 //mov BYTE PTR [edi], al
476 IA32_Mov_Rm8_Reg8(jit, kREG_EDI, kREG_EAX, MOD_MEM_REG);
481 //mov WORD PTR [edi], ax
482 jit->write_ubyte(IA32_16BIT_PREFIX);
483 IA32_Mov_Rm_Reg(jit, kREG_EDI, kREG_EAX, MOD_MEM_REG);
488 //mov DWORD PTR [edi], eax
489 IA32_Mov_Rm_Reg(jit, kREG_EDI, kREG_EAX, MOD_MEM_REG);
494 //mov DWORD PTR [edi], eax
495 //mov DWORD PTR [edi+4], edx
496 IA32_Mov_Rm_Reg(jit, kREG_EDI, kREG_EAX, MOD_MEM_REG);
497 IA32_Mov_Rm_Reg_Disp8(jit, kREG_EDI, kREG_EDX, 4);
503 /******************************
504 * Assembly Compiler Function *
505 ******************************/
507 void *JIT_CallCompile(CallWrapper *pWrapper, FuncAddrMethod method)
510 JitWriter *jit = &writer;
512 jit_uint32_t CodeSize = 0;
513 bool Needs_Retbuf = false;
514 CallConvention Convention = pWrapper->GetCallConvention();
515 jit_uint32_t ParamCount = pWrapper->GetParamCount();
516 const PassInfo *pRet = pWrapper->GetReturnInfo();
517 bool hasParams = (ParamCount || Convention == CallConv_ThisCall);
521 writer.outbase = NULL;
522 writer.outptr = NULL;
525 /* Write function prologue */
526 Write_Execution_Prologue(jit, (pRet) ? false : true, hasParams);
528 /* Write parameter push code */
529 for (jit_int32_t i=ParamCount-1; i>=0; i--)
531 unsigned int offset = pWrapper->GetParamOffset(i);
532 const SourceHook::PassInfo *info = pWrapper->GetSHParamInfo(i);
533 assert(info != NULL);
537 case SourceHook::PassInfo::PassType_Basic:
539 Write_PushPOD(jit, info, offset);
542 case SourceHook::PassInfo::PassType_Float:
544 Write_PushFloat(jit, info, offset);
547 case SourceHook::PassInfo::PassType_Object:
549 Write_PushObject(jit, info, offset);
555 /* Prepare the this ptr if applicable */
556 if (Convention == CallConv_ThisCall)
558 Write_PushThisPtr(jit);
561 /* Skip the return buffer stuff if this is a void function */
567 if ((pRet->type == PassType_Object) && (pRet->flags & PASSFLAG_BYVAL))
569 #ifdef PLATFORM_LINUX
571 #elif defined PLATFORM_APPLE
573 * On OS X, need retbuf if size > 8 or not power of 2.
575 * See Mac OS X ABI Function Call Guide:
576 * http://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW5
578 if (pRet->size > 8 || (pRet->size & (pRet->size - 1)) != 0)
582 #elif defined PLATFORM_WINDOWS
583 if ((Convention == CallConv_ThisCall) ||
584 ((Convention == CallConv_Cdecl) &&
585 ((pRet->size > 8) || (pRet->flags & PASSFLAG_ODTOR|PASSFLAG_OCTOR|PASSFLAG_OASSIGNOP))))
592 /* Prepare the return buffer in case we are returning objects by value. */
595 Write_PushRetBuffer(jit);
599 /* Write the calling code */
600 Write_CallFunction(jit, method, pWrapper);
602 /* Clean up the calling stack */
603 #ifdef PLATFORM_WINDOWS
604 if ((ParamCount || Needs_Retbuf) && (Convention == CallConv_Cdecl))
606 /* Pop all parameters from the stack + hidden return pointer */
607 jit_uint32_t total = (Needs_Retbuf) ? g_StackUsage + sizeof(void *) : g_StackUsage;
608 Write_RectifyStack(jit, total);
609 #elif defined PLATFORM_POSIX
612 /* Pop all parameters from the stack */
613 Write_RectifyStack(jit, g_StackUsage);
617 /* Copy the return type to the return buffer if the function is not void */
618 if (pRet && !Needs_Retbuf)
620 Write_MovRet2Buf(jit, pRet);
623 /* Write Function Epilogue */
624 Write_Function_Epilogue(jit, (pRet) ? false : true, hasParams);
626 if (writer.outbase == NULL)
628 CodeSize = writer.get_outputpos();
629 writer.outbase = (jitcode_t)g_SPEngine->AllocatePageMemory(CodeSize);
630 g_SPEngine->SetReadWrite(writer.outbase);
631 writer.outptr = writer.outbase;
632 pWrapper->SetCodeBaseAddr(writer.outbase);
633 g_StackAlign = (g_StackUsage) ? ((g_StackUsage & 0xFFFFFFF0) + 16) - g_StackUsage : 0;
636 Needs_Retbuf = false;
639 g_SPEngine->SetReadExecute(writer.outbase);
640 return writer.outbase;