From 15778979255df85735ad1aea87dd93523d4debb0 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 20 Sep 2006 08:44:21 +0000 Subject: [PATCH] I'm proud to present the first non-backpatched error checking routine! all of this is untested --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%4080 --- sourcepawn/vm/jit/jit_helpers.h | 2 + sourcepawn/vm/jit/x86/jit_x86.cpp | 29 +++++-- sourcepawn/vm/jit/x86/jit_x86.h | 10 ++- sourcepawn/vm/jit/x86/opcode_helpers.cpp | 105 ++++++++++++++++++----- sourcepawn/vm/jit/x86/opcode_helpers.h | 19 ++-- sourcepawn/vm/jit/x86/x86_macros.h | 36 ++++++++ 6 files changed, 166 insertions(+), 35 deletions(-) diff --git a/sourcepawn/vm/jit/jit_helpers.h b/sourcepawn/vm/jit/jit_helpers.h index bbfda1ec..5b74ce65 100644 --- a/sourcepawn/vm/jit/jit_helpers.h +++ b/sourcepawn/vm/jit/jit_helpers.h @@ -2,6 +2,7 @@ #define _INCLUDE_SOURCEPAWN_JIT_HELPERS_H_ #include +#include #if defined HAVE_STDINT_H && !defined WIN32 #include @@ -70,6 +71,7 @@ public: cell_t *inptr; /* input pointer */ jitcode_t outbase; /* output pointer */ jitcode_t outptr; /* output base */ + SourcePawn::ICompilation *data; /* compiler live info */ }; #endif //_INCLUDE_SOURCEPAWN_JIT_HELPERS_H_ diff --git a/sourcepawn/vm/jit/x86/jit_x86.cpp b/sourcepawn/vm/jit/x86/jit_x86.cpp index ca58068b..e7cc2885 100644 --- a/sourcepawn/vm/jit/x86/jit_x86.cpp +++ b/sourcepawn/vm/jit/x86/jit_x86.cpp @@ -1,7 +1,6 @@ #include #include #include "jit_x86.h" -#include "..\jit_helpers.h" #include "opcode_helpers.h" #include "x86_macros.h" @@ -1001,6 +1000,13 @@ inline void WriteOp_Const_S(JitWriter *jit) } } +inline void WriteOp_Load_I(JitWriter *jit) +{ + //mov eax, [edi+eax] + Write_Check_VerifyAddr(jit, REG_EAX, false); + IA32_Mov_Reg_Rm_Disp_Reg(jit, AMX_REG_PRI, AMX_REG_DAT, AMX_REG_PRI, NOSCALE); +} + /************************************************* ************************************************* @@ -1074,7 +1080,6 @@ IPluginContext *JITX86::CompileToContext(ICompilation *co, int *err) JitWriter writer; JitWriter *jit = &writer; cell_t *endptr = (cell_t *)(end_cip); - jitoffs_t jit_return; /* Initial code is written "blank," * so we can check the exact memory usage. @@ -1085,16 +1090,17 @@ IPluginContext *JITX86::CompileToContext(ICompilation *co, int *err) /* Get inlining level */ int inline_level = data->inline_level; - bool never_inline = false; - - if (inline_level == 0) - { - never_inline = true; - } //:TODO: Jump back here once finished! + /* Start writing the actual code */ - jit_return = Write_Execute_Function(jit, never_inline); + data->jit_return = Write_Execute_Function(jit); + + /* Write error checking routines in case they are needed */ + data->jit_verify_addr_eax = jit->jit_curpos(); + Write_Check_VerifyAddr(jit, REG_EAX, true); + data->jit_verify_addr_edx = jit->jit_curpos(); + Write_Check_VerifyAddr(jit, REG_EDX, true); for (; writer.inptr <= endptr;) { @@ -1656,6 +1662,11 @@ IPluginContext *JITX86::CompileToContext(ICompilation *co, int *err) WriteOp_Const_S(jit); break; } + case OP_LOAD_I: + { + WriteOp_Load_I(jit); + break; + } default: { AbortCompilation(co); diff --git a/sourcepawn/vm/jit/x86/jit_x86.h b/sourcepawn/vm/jit/x86/jit_x86.h index b3ba2e8d..0eb22f8f 100644 --- a/sourcepawn/vm/jit/x86/jit_x86.h +++ b/sourcepawn/vm/jit/x86/jit_x86.h @@ -3,17 +3,24 @@ #include #include +#include "..\jit_helpers.h" using namespace SourcePawn; +#define JIT_INLINE_ERRORCHECKS (1<<0) +#define JIT_INLINE_NATIVES (1<<1) + class CompData : public ICompilation { public: - CompData() : plugin(NULL), debug(false), inline_level(2) + CompData() : plugin(NULL), debug(false), inline_level(3) { }; public: sp_plugin_t *plugin; + jitoffs_t jit_return; + jitoffs_t jit_verify_addr_eax; + jitoffs_t jit_verify_addr_edx; int inline_level; bool debug; }; @@ -44,5 +51,6 @@ public: #define AMX_INFO_HEAP 4 #define AMX_INFO_RETVAL 8 #define AMX_INFO_CONTEXT 12 +#define AMX_INFO_STACKTOP 16 #endif //_INCLUDE_SOURCEPAWN_JIT_X86_H_ diff --git a/sourcepawn/vm/jit/x86/opcode_helpers.cpp b/sourcepawn/vm/jit/x86/opcode_helpers.cpp index 43e39e77..0b5180f9 100644 --- a/sourcepawn/vm/jit/x86/opcode_helpers.cpp +++ b/sourcepawn/vm/jit/x86/opcode_helpers.cpp @@ -9,19 +9,6 @@ int OpAdvTable[OP_NUM_OPCODES]; jitoffs_t Write_Execute_Function(JitWriter *jit, bool never_inline) { /** - * The state expected by our plugin is: - * #define AMX_REG_PRI REG_EAX (done) - #define AMX_REG_ALT REG_EDX (done) - #define AMX_REG_STK REG_EBP (done) - #define AMX_REG_DAT REG_EDI (done) - #define AMX_REG_TMP REG_ECX (nothing) - #define AMX_REG_INFO REG_ESI (done) - #define AMX_REG_FRM REG_EBX (done) - #define AMX_INFO_FRM AMX_REG_INFO (done) - #define AMX_INFO_HEAP 4 (done) - #define AMX_INFO_RETVAL 8 (done) - #define AMX_INFO_CONTEXT 12 (done) - * * The variables we're passed in: * sp_context_t *ctx, uint32_t code_idx, cell_t *result */ @@ -45,7 +32,7 @@ jitoffs_t Write_Execute_Function(JitWriter *jit, bool never_inline) IA32_Push_Reg(jit, REG_EDI); IA32_Push_Reg(jit, REG_EBX); - //sub esp, 4*4 - allocate info array + //sub esp, 4*5 - allocate info array //mov esi, esp - save info pointer IA32_Sub_Rm_Imm8(jit, REG_ESP, 4*4, MOD_REG); IA32_Mov_Reg_Rm(jit, AMX_REG_INFO, REG_ESP, MOD_REG); @@ -69,13 +56,21 @@ jitoffs_t Write_Execute_Function(JitWriter *jit, bool never_inline) //mov ebp, [eax+] - get stack pointer //add ebp, edi - relocate to data section //mov ebx, ebp - copy sp to frm + IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_STK, REG_EAX, offsetof(sp_context_t, sp)); + IA32_Add_Rm_Reg(jit, REG_EBP, AMX_REG_STK, AMX_REG_DAT); + IA32_Mov_Reg_Rm(jit, AMX_REG_FRM, AMX_REG_STK, MOD_REG); + + //mov ecx, edi - copy base of data to temp var + //add ecx, [eax+] - add memsize to get stack top + //mov [esi+16], ecx - store stack top into info pointer + IA32_Mov_Reg_Rm(jit, REG_ECX, AMX_REG_DAT, MOD_REG); + IA32_Add_Reg_Rm_Disp8(jit, REG_ECX, REG_EAX, offsetof(sp_context_t, memory)); + IA32_Mov_Rm_Reg_Disp8(jit, REG_ESI, REG_ECX, AMX_INFO_STACKTOP); + //mov ecx, [ebp+12] - get code index //add ecx, [eax+] - add code base to index //mov edx, [eax+] - get alt //mov eax, [eax+] - get pri - IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_STK, REG_EAX, offsetof(sp_context_t, sp)); - IA32_Add_Rm_Reg(jit, REG_EBP, AMX_REG_STK, AMX_REG_DAT); - IA32_Mov_Reg_Rm(jit, AMX_REG_FRM, AMX_REG_STK, MOD_REG); IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_EBP, 12); IA32_Add_Reg_Rm_Disp8(jit, REG_ECX, REG_EAX, offsetof(sp_context_t, base)); IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_ALT, REG_EAX, offsetof(sp_context_t, alt)); @@ -94,9 +89,10 @@ jitoffs_t Write_Execute_Function(JitWriter *jit, bool never_inline) IA32_Mov_Rm_Reg(jit, REG_EBP, AMX_REG_PRI, MOD_MEM_REG); IA32_Mov_Reg_Imm32(jit, REG_EAX, SP_ERR_NONE); - /* where error checking/return functions should go to */ + /* save where error checking/halting functions should go to */ jitoffs_t offs_return; - if (never_inline) + CompData *data = (CompData *)jit->data; + if (!(data->inline_level & JIT_INLINE_ERRORCHECKS)) { /* We have to write code assume we're breaking out of a call */ //jmp [past the next instruction] @@ -121,7 +117,7 @@ jitoffs_t Write_Execute_Function(JitWriter *jit, bool never_inline) //pop esi //pop ebp //ret - IA32_Add_Rm_Imm8(jit, REG_ESP, 4*4, MOD_REG); + IA32_Add_Rm_Imm8(jit, REG_ESP, 4*5, MOD_REG); IA32_Pop_Reg(jit, REG_EBX); IA32_Pop_Reg(jit, REG_EDI); IA32_Pop_Reg(jit, REG_ESI); @@ -131,6 +127,75 @@ jitoffs_t Write_Execute_Function(JitWriter *jit, bool never_inline) return offs_return; } +void Write_Error(JitWriter *jit, int error) +{ + CompData *data = (CompData *)jit->data; + + /* These are so small that we always inline them! */ + //mov eax, + //jmp [...jit_return] + IA32_Mov_Reg_Imm32(jit, REG_EAX, error); + jitoffs_t jmp = IA32_Jump_Imm32(jit, 0); + IA32_Write_Jump32(jit, jmp, data->jit_return); +} + +void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg, bool firstcall) +{ + CompData *data = (CompData *)jit->data; + + /* :TODO: Should this be checking for below heaplow? + * The old JIT did not. + */ + + bool call = false; + if (!(data->inline_level & JIT_INLINE_ERRORCHECKS)) + { + /* If we're not in the initial generation phase, + * Write a call to the actual routine instead. + */ + if (!firstcall) + { + jitoffs_t call = IA32_Call_Imm32(jit, 0); + if (reg == REG_EAX) + { + IA32_Write_Jump32(jit, call, data->jit_verify_addr_eax); + } else if (reg == REG_EDX) { + IA32_Write_Jump32(jit, call, data->jit_verify_addr_edx); + } + return; + } + call = true; + } else if (firstcall) { + /* Inline + initial gen == no code */ + return; + } + + //cmp reg, [stp] + //jae memaccess + //cmp reg, [hea] + //jb continue + //lea ecx, [reg+edi] + //cmp ecx, ebp + //jae continue + //memaccess: (write error) + //continue: + IA32_Cmp_Reg_Rm_Disp8(jit, reg, AMX_REG_INFO, AMX_INFO_STACKTOP); + jitoffs_t jmp1 = IA32_Jump_Cond_Imm8(jit, CC_AE, 0); + IA32_Cmp_Reg_Rm_Disp8(jit, reg, AMX_REG_INFO, AMX_INFO_HEAP); + jitoffs_t jmp2 = IA32_Jump_Cond_Imm8(jit, CC_B, 0); + IA32_Lea_DispRegReg(jit, REG_ECX, reg, REG_EDI); + IA32_Cmp_Rm_Reg(jit, REG_ECX, AMX_REG_STK, MOD_REG); + jitoffs_t jmp3 = IA32_Jump_Cond_Imm8(jit, CC_AE, 0); + IA32_Send_Jump8_Here(jit, jmp1); + Write_Error(jit, SP_ERR_MEMACCESS); + IA32_Send_Jump8_Here(jit, jmp2); + IA32_Send_Jump8_Here(jit, jmp3); + if (call) + { + IA32_Return(jit); + } +} + void Macro_PushN_Addr(JitWriter *jit, int i) { //push eax diff --git a/sourcepawn/vm/jit/x86/opcode_helpers.h b/sourcepawn/vm/jit/x86/opcode_helpers.h index 64a753c6..7cb8efd5 100644 --- a/sourcepawn/vm/jit/x86/opcode_helpers.h +++ b/sourcepawn/vm/jit/x86/opcode_helpers.h @@ -7,11 +7,20 @@ * This outputs the execution function for a plugin. * It also returns the 'return' offset, which is used for * breaking out of the JIT during runtime. - * - * If 'never_inline' is true, it outputs slightly different code used for - * inlining the error checking routines. */ -jitoffs_t Write_Execute_Function(JitWriter *jit, bool never_inline); +jitoffs_t Write_Execute_Function(JitWriter *jit); + +/** + * Generates code to set an error state in the VM and return. + * Note that this should only be called from inside an error + * checking function. + */ +void Write_Error(JitWriter *jit, int error); + +/** + * Verifies an address by register. + */ +void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg, bool firstcall); /** * These are for writing the PushN opcodes. @@ -32,7 +41,7 @@ typedef enum OP_LREF_ALT, //DONE OP_LREF_S_PRI, //DONE OP_LREF_S_ALT, //DONE - OP_LOAD_I, + OP_LOAD_I, //DONE OP_LODB_I, OP_CONST_PRI, //DONE OP_CONST_ALT, //DONE diff --git a/sourcepawn/vm/jit/x86/x86_macros.h b/sourcepawn/vm/jit/x86/x86_macros.h index 7d0d5a0a..067e6620 100644 --- a/sourcepawn/vm/jit/x86/x86_macros.h +++ b/sourcepawn/vm/jit/x86/x86_macros.h @@ -97,6 +97,7 @@ #define IA32_SHL_RM_CL 0xD3 // encoding is /4 #define IA32_SAR_RM_IMM8 0xC1 // encoding is /7 #define IA32_CMP_RM_REG 0x39 // encoding is /r +#define IA32_CMP_REG_RM 0x3B // encoding is /r #define IA32_SETCC_RM8_1 0x0F // opcode part 1 #define IA32_SETCC_RM8_2 0x90 // encoding is +cc /0 (8bits) #define IA32_XCHG_EAX_REG 0x90 // encoding is +r @@ -498,8 +499,16 @@ inline void IA32_Lea_Reg_DispRegMultImm8(JitWriter *jit, jit->write_byte(val); } +inline void IA32_Lea_DispRegReg(JitWriter *jit, jit_uint8_t dest, jit_uint8_t src_base, jit_uint8_t dispreg) +{ + jit->write_ubyte(IA32_LEA_REG_MEM); + jit->write_ubyte(ia32_modrm(MOD_MEM_REG, dest, REG_SIB)); + jit->write_ubyte(ia32_sib(NOSCALE, dispreg, src_base)); +} + inline void IA32_Lea_DispRegImm8(JitWriter *jit, jit_uint8_t dest, jit_uint8_t src_base, jit_int8_t val) { + /* :TODO: - why does this take in src_base? */ jit->write_ubyte(IA32_LEA_REG_MEM); jit->write_ubyte(ia32_modrm(MOD_DISP8, dest, MOD_MEM_REG)); jit->write_byte(val); @@ -507,6 +516,7 @@ inline void IA32_Lea_DispRegImm8(JitWriter *jit, jit_uint8_t dest, jit_uint8_t s inline void IA32_Lea_DispRegImm32(JitWriter *jit, jit_uint8_t dest, jit_uint8_t src_base, jit_int32_t val) { + /* :TODO: - why does this take in src_base? */ jit->write_ubyte(IA32_LEA_REG_MEM); jit->write_ubyte(ia32_modrm(MOD_DISP32, dest, MOD_MEM_REG)); jit->write_int32(val); @@ -709,12 +719,31 @@ inline void IA32_Write_Jump8(JitWriter *jit, jitoffs_t jmp, jitoffs_t target) jit->outptr = oldptr; } +inline void IA32_Write_Jump32(JitWriter *jit, jitoffs_t jmp, jitoffs_t target) +{ + //save old ptr + jitcode_t oldptr = jit->outptr; + //get relative difference + jit_int32_t diff = (target - (jmp + 1)); + //overwrite old value + jit->outptr = jit->outbase + jmp; + jit->write_int32(diff); + //restore old ptr + jit->outptr = oldptr; +} + inline void IA32_Send_Jump8_Here(JitWriter *jit, jitoffs_t jmp) { jitoffs_t curptr = jit->jit_curpos(); IA32_Write_Jump8(jit, jmp, curptr); } +inline void IA32_Send_Jump32_Here(JitWriter *jit, jitoffs_t jmp) +{ + jitoffs_t curptr = jit->jit_curpos(); + IA32_Write_Jump32(jit, jmp, curptr); +} + inline void IA32_Return(JitWriter *jit) { jit->write_ubyte(IA32_RET); @@ -732,6 +761,13 @@ inline void IA32_Cmp_Rm_Reg(JitWriter *jit, jit_uint8_t dest, jit_uint8_t src, j jit->write_ubyte(ia32_modrm(mode, src, dest)); } +inline void IA32_Cmp_Reg_Rm_Disp8(JitWriter *jit, jit_uint8_t reg1, jit_uint8_t reg2, jit_int8_t disp8) +{ + jit->write_ubyte(IA32_CMP_REG_RM); + jit->write_ubyte(ia32_modrm(MOD_DISP8, reg1, reg2)); + jit->write_byte(disp8); +} + inline void IA32_Cmp_Rm_Imm8(JitWriter *jit, jit_uint8_t mode, jit_uint8_t rm, jit_int8_t imm8) { jit->write_ubyte(IA32_CMP_RM_IMM8);