#include #include #include #include "jit_x86.h" #include "opcode_helpers.h" #include "x86_macros.h" #define NUM_INFO_PARAMS 5 jitoffs_t Write_Execute_Function(JitWriter *jit) { /** * The variables we're passed in: * sp_context_t *ctx, uint32_t code_idx, cell_t *result */ /** * !NOTE! * Currently, we do not accept ctx->frm as the new frame pointer. * Instead, we copy the frame from the stack pointer. * This is because we do not support resuming or sleeping! */ //push ebp //mov ebp, esp IA32_Push_Reg(jit, REG_EBP); IA32_Mov_Reg_Rm(jit, REG_EBP, REG_ESP, MOD_REG); //push esi //push edi //push ebx IA32_Push_Reg(jit, REG_ESI); IA32_Push_Reg(jit, REG_EDI); IA32_Push_Reg(jit, REG_EBX); //sub esp, 4*n - reserve info array //mov esi, esp - save info pointer IA32_Sub_Rm_Imm8(jit, REG_ESP, 4*NUM_INFO_PARAMS, MOD_REG); IA32_Mov_Reg_Rm(jit, AMX_REG_INFO, REG_ESP, MOD_REG); /* Initial memory setup */ //mov eax, [ebp+16] - get result pointer //mov [esi+8], eax - store into info pointer //mov eax, [ebp+8] - get context //mov [esi+12], eax - store context into info pointer //mov ecx, [eax+] - get heap pointer //mov [esi+4], ecx - store heap into info pointer //mov ebp, [eax+] - get data pointer IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, 16); IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_EAX, AMX_INFO_RETVAL); IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, 8); IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_EAX, AMX_INFO_CONTEXT); IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_EAX, offsetof(sp_context_t, hp)); IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_ECX, AMX_INFO_HEAP); IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_DAT, REG_EAX, offsetof(sp_context_t, memory)); /* Frame setup */ //mov edi, [eax+] - get stack pointer //add edi, ebp - relocate to data section //mov ebx, edi - 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, AMX_REG_STK, AMX_REG_DAT, MOD_REG); IA32_Mov_Reg_Rm(jit, AMX_REG_FRM, AMX_REG_STK, MOD_REG); /* Info memory setup */ //mov ecx, [eax+] - copy memsize to temp var //add ecx, ebp - relocate //mov [esi+x], ecx - store relocated IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_EAX, offsetof(sp_context_t, memory)); IA32_Add_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_DAT, MOD_REG); IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_ECX, AMX_INFO_STACKTOP); /* Remaining needed vars */ //mov ecx, [esp+(4*(NUM_INFO_PARAMS+3))+12] - get code index (normally esp+12, but we have another array on the stack) //add ecx, [eax+] - add code base to index IA32_Mov_Reg_Esp_Disp8(jit, REG_ECX, 12+(4*(NUM_INFO_PARAMS+3))); IA32_Add_Reg_Rm_Disp8(jit, REG_ECX, REG_EAX, offsetof(sp_context_t, codebase)); /* by now, everything is set up, so we can call into the plugin */ //call ecx IA32_Call_Reg(jit, REG_ECX); /* if the code flow gets to here, there was a normal return */ //mov ecx, [esi+8] - get retval pointer //mov [ecx], eax - store retval from PRI //mov eax, SP_ERROR_NONE - set no error IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, AMX_REG_INFO, AMX_INFO_RETVAL); IA32_Mov_Rm_Reg(jit, REG_ECX, AMX_REG_PRI, MOD_MEM_REG); IA32_Mov_Reg_Imm32(jit, REG_EAX, SP_ERROR_NONE); /* save where error checking/halting functions should go to */ jitoffs_t offs_return = jit->get_outputpos(); //mov esp, esi - restore stack pointer IA32_Mov_Reg_Rm(jit, REG_ESP, REG_ESI, MOD_REG); /* _FOR NOW_ ... * We are going to restore SP, HP, and FRM for now. This is for * debugging only, to check for alignment errors. As such: * :TODO: probably remove this. */ //mov ecx, [esi+context] //sub edi, ebp //mov edx, [esi+heap] //mov [ecx+sp], edi //mov [ecx+hp], edx IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_ESI, AMX_INFO_CONTEXT); IA32_Sub_Reg_Rm(jit, REG_EDI, REG_EBP, MOD_REG); IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_ESI, AMX_INFO_HEAP); IA32_Mov_Rm_Reg_Disp8(jit, REG_ECX, REG_EDI, offsetof(sp_context_t, sp)); IA32_Mov_Rm_Reg_Disp8(jit, REG_ECX, REG_EDX, offsetof(sp_context_t, hp)); //add esp, 4*NUM_INFO_PARAMS //pop ebx //pop edi //pop esi //pop ebp //ret IA32_Add_Rm_Imm8(jit, REG_ESP, 4*NUM_INFO_PARAMS, MOD_REG); IA32_Pop_Reg(jit, REG_EBX); IA32_Pop_Reg(jit, REG_EDI); IA32_Pop_Reg(jit, REG_ESI); IA32_Pop_Reg(jit, REG_EBP); IA32_Return(jit); return offs_return; } void Write_BreakDebug(JitWriter *jit) { //push ecx //mov ecx, [esi+ctx] //cmp [ecx+dbreak], 0 //jnz :nocall IA32_Push_Reg(jit, AMX_REG_TMP); IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT); IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, dbreak), 0); jitoffs_t jmp = IA32_Jump_Cond_Imm8(jit, CC_NZ, 0); //pushad IA32_Pushad(jit); //push [esi+frm] //push [ecx+context] //mov ecx, [ecx+dbreak] //call ecx //add esp, 8 //popad IA32_Push_Rm_Disp8(jit, AMX_REG_INFO, AMX_INFO_FRAME); //:TODO: move to regs and push? and dont disp for 0 IA32_Push_Rm_Disp8(jit, AMX_REG_TMP, offsetof(sp_context_t, context)); IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_TMP, offsetof(sp_context_t, dbreak)); IA32_Call_Reg(jit, AMX_REG_TMP); IA32_Add_Rm_Imm8(jit, REG_ESP, 4*2, MOD_REG); IA32_Popad(jit); //:nocall IA32_Send_Jump8_Here(jit, jmp); IA32_Add_Rm_Imm8(jit, REG_ESP, 4*1, MOD_REG); IA32_Return(jit); } void Write_GetError(JitWriter *jit) { CompData *data = (CompData *)jit->data; //mov eax, [esi+info.context] //mov eax, [eax+ctx.error] //jmp [jit_return] IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT); IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EAX, offsetof(sp_context_t, err)); IA32_Jump_Imm32_Abs(jit, data->jit_return); } void Write_SetError(JitWriter *jit, int error) { CompData *data = (CompData *)jit->data; //mov eax, //jmp [jit_return] IA32_Mov_Reg_Imm32(jit, REG_EAX, error); IA32_Jump_Imm32_Abs(jit, data->jit_return); } void Write_Check_DivZero(JitWriter *jit, jit_uint8_t reg) { //test reg, reg //jz :error IA32_Test_Rm_Reg(jit, reg, reg, MOD_REG); IA32_Jump_Cond_Imm32_Abs(jit, CC_Z, ((CompData *)jit->data)->jit_error_divzero); } void Write_CheckHeap_Min(JitWriter *jit) { /* Check if the stack went beyond the heap low. * This usually means there was a compiler error. * NOTE: Special optimization here. * The heap low is always known ahead of time! :) */ CompData *data = (CompData *)jit->data; //cmp [esi+info.heap], //jb :error IA32_Cmp_Rm_Imm32_Disp8(jit, AMX_REG_INFO, AMX_INFO_HEAP, data->plugin->data_size); IA32_Jump_Cond_Imm32_Abs(jit, CC_B, data->jit_error_heapmin); } void Write_CheckHeap_Low(JitWriter *jit) { /* Check if the heap is trying to grow beyond the stack. */ //mov ecx, [esi+info.heap] //lea ecx, [ebp+ecx+STACK_MARGIN] //cmp ecx, edi //ja :error ; I think this is right IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP); IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, AMX_REG_TMP, NOSCALE, STACK_MARGIN); IA32_Cmp_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_STK, MOD_REG); IA32_Jump_Cond_Imm32_Abs(jit, CC_A, ((CompData *)jit->data)->jit_error_heaplow); } void Write_CheckStack_Min(JitWriter *jit) { /* Check if the stack went beyond the stack top * This usually means there was a compiler error. */ //cmp edi, [esi+info.stacktop] //jae :error IA32_Cmp_Reg_Rm_Disp8(jit, AMX_REG_STK, AMX_REG_INFO, AMX_INFO_STACKTOP); IA32_Jump_Cond_Imm32_Abs(jit, CC_AE, ((CompData *)jit->data)->jit_error_stackmin); } void Write_CheckStack_Low(JitWriter *jit) { /* Check if the stack went beyond the heap boundary. * Unfortunately this one isn't as quick as the other check. * The stack margin check is important for sysreq.n having space. */ //mov ecx, [esi+info.heap] //lea ecx, [ebp+ecx+STACK_MARGIN] //cmp edi, ecx //jb :error ; I think this is right IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP); IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, AMX_REG_TMP, NOSCALE, STACK_MARGIN); IA32_Cmp_Reg_Rm(jit, AMX_REG_STK, AMX_REG_TMP, MOD_REG); IA32_Jump_Cond_Imm32_Abs(jit, CC_B, ((CompData *)jit->data)->jit_error_stacklow); } void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg) { 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 ((reg == REG_EAX) && data->jit_verify_addr_eax) { jitoffs_t call = IA32_Call_Imm32(jit, 0); IA32_Write_Jump32(jit, call, data->jit_verify_addr_eax); return; } else if ((reg == REG_EDX) && data->jit_verify_addr_edx) { jitoffs_t call = IA32_Call_Imm32(jit, 0); IA32_Write_Jump32(jit, call, data->jit_verify_addr_edx); return; } call = true; } /** * :TODO: If we can't find a nicer way of doing this, * then scrap it on high optimizations. The second portion is not needed at all! */ /* Part 1: Check if we're in the memory bounds */ //cmp , //jae :error IA32_Cmp_Rm_Imm32(jit, MOD_REG, reg, ((CompData *)jit->data)->plugin->memory); IA32_Jump_Cond_Imm32_Abs(jit, CC_AE, ((CompData *)jit->data)->jit_error_memaccess); /* Part 2: Check if we're in the invalid region between HP and SP */ jitoffs_t jmp; //cmp , [esi+info.heap] //jb :continue //lea ecx, [ebp+] //cmp edi, ecx //jb :error //:continue IA32_Cmp_Reg_Rm_Disp8(jit, reg, AMX_REG_INFO, AMX_INFO_HEAP); jmp = IA32_Jump_Cond_Imm8(jit, CC_B, 0); IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, reg, NOSCALE, 0); IA32_Cmp_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_STK, MOD_REG); IA32_Jump_Cond_Imm32_Abs(jit, CC_B, ((CompData *)jit->data)->jit_error_memaccess); IA32_Send_Jump8_Here(jit, jmp); if (call) { IA32_Return(jit); } } void Macro_PushN_Addr(JitWriter *jit, int i) { //push eax //mov eax, [esi+frm] //loop i times: // lea ecx, [eax+] // mov [edi-4*i], ecx //sub edi, 4*N //pop eax cell_t val; int n = 1; IA32_Push_Reg(jit, AMX_REG_PRI); IA32_Mov_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_INFO, MOD_MEM_REG); do { val = jit->read_cell(); if (val < SCHAR_MAX && val > SCHAR_MIN) IA32_Lea_DispRegImm8(jit, AMX_REG_TMP, AMX_REG_PRI, (jit_int8_t)val); else IA32_Lea_DispRegImm32(jit, AMX_REG_TMP, AMX_REG_PRI, val); IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n); } while (n++ < i); IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG); IA32_Pop_Reg(jit, AMX_REG_PRI); } void Macro_PushN_S(JitWriter *jit, int i) { //loop i times: // mov ecx, [ebx+] // mov [edi-4*i], ecx //sub edi, 4*N cell_t val; int n = 1; do { val = jit->read_cell(); if (val < SCHAR_MAX && val > SCHAR_MIN) IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_FRM, (jit_int8_t)val); else IA32_Mov_Reg_Rm_Disp32(jit, AMX_REG_TMP, AMX_REG_FRM, val); IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n); } while (n++ < i); IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG); } void Macro_PushN_C(JitWriter *jit, int i) { //loop i times: // mov [edi-4*i], //sub edi, 4*N int n = 1; do { IA32_Mov_Rm_Imm32_Disp8(jit, AMX_REG_STK, jit->read_cell(), -4*n); } while (n++ < i); IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG); } void Macro_PushN(JitWriter *jit, int i) { //loop i times: // mov ecx, [ebp+] // mov [edi-4*i], ecx //sub edi, 4*N cell_t val; int n = 1; do { val = jit->read_cell(); if (val < SCHAR_MAX && val > SCHAR_MIN) IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_DAT, (jit_int8_t)val); else IA32_Mov_Reg_Rm_Disp32(jit, AMX_REG_TMP, AMX_REG_DAT, val); IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n); } while (n++ < i); IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG); } void WriteOp_Sysreq_C_Function(JitWriter *jit) { /* The small daddy of the big daddy of opcodes. * ecx - native index */ CompData *data = (CompData *)jit->data; /* save registers we will need */ //push edx IA32_Push_Reg(jit, AMX_REG_ALT); /* push some callback stuff */ //push edi ; stack //push ecx ; native index IA32_Push_Reg(jit, AMX_REG_STK); IA32_Push_Reg(jit, REG_ECX); /* Relocate stack, heap, frm information, then store back */ //mov eax, [esi+context] //mov ecx, [esi+hea] //sub edi, ebp //mov [eax+hp], ecx //mov ecx, [esi] //mov [eax+sp], edi //mov [eax+frm], ecx IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT); IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP); IA32_Sub_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG); IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, hp)); IA32_Mov_Reg_Rm(jit, AMX_REG_TMP, AMX_INFO_FRM, MOD_MEM_REG); IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_STK, offsetof(sp_context_t, sp)); IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, frm)); /* finally, push the last parameter and make the call */ //push eax ; context //call NativeCallback IA32_Push_Reg(jit, REG_EAX); jitoffs_t call = IA32_Call_Imm32(jit, 0); if (!data->debug) { IA32_Write_Jump32_Abs(jit, call, NativeCallback); } else { IA32_Write_Jump32_Abs(jit, call, NativeCallback_Debug); } /* Test for error */ //mov ecx, [esi+context] //cmp [ecx+err], 0 //jnz :error IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT); IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, err), 0); IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error); /* restore what we damaged */ //add esp, 4*3 //add edi, ebp //pop edx IA32_Add_Rm_Imm8(jit, REG_ESP, 4*3, MOD_REG); IA32_Add_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG); IA32_Pop_Reg(jit, AMX_REG_ALT); //ret IA32_Return(jit); } void GenerateArrayIndirectionVectors(cell_t *arraybase, cell_t dims[], ucell_t _dimcount, bool autozero) { cell_t vectors = 1; /* we need one vector to start off with */ cell_t cur_offs = 0; cell_t cur_write = 0; cell_t dimcount = _dimcount; /* Initialize rotation */ cur_write = dims[dimcount-1]; while (--dimcount >= 1) { cell_t cur_dim = dims[dimcount]; cell_t sub_dim = dims[dimcount-1]; for (cell_t i=0; iget_outputpos(); IA32_Cmp_Reg_Rm_ESP(jit, REG_EAX); jitoffs_t done1 = IA32_Jump_Cond_Imm8(jit, CC_AE, 0); IA32_Mov_Reg_Rm_Disp_Reg(jit, REG_ECX, AMX_REG_STK, REG_EAX, SCALE4); IA32_IMul_Reg_Rm(jit, REG_EDX, REG_ECX, MOD_REG); IA32_Add_Rm_Imm8(jit, REG_EAX, 1, MOD_REG); IA32_Add_Reg_Rm(jit, REG_EDX, REG_ECX, MOD_REG); IA32_Write_Jump8(jit, IA32_Jump_Imm8(jit, loop1), loop1); IA32_Send_Jump8_Here(jit, done1); /* Test if we have heap space for this */ //mov eax, [esi+info.heap] ;get heap pointer //lea eax, [eax+edx*4] ;new heap pointer //cmp eax, ;compare to heap low //jbe :error ;die if we hit this (it should always be >) //add eax, ebp ;relocate to stack //cmp eax, edi ;die if above the stack pointer //jae :error IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_HEAP); IA32_Lea_Reg_DispRegMult(jit, REG_EAX, REG_EAX, REG_EDX, SCALE4); IA32_Cmp_Rm_Imm32(jit, MOD_REG, REG_EAX, ((CompData *)jit->data)->plugin->data_size); IA32_Jump_Cond_Imm32_Abs(jit, CC_BE, ((CompData *)jit->data)->jit_error_array_too_big); IA32_Add_Reg_Rm(jit, REG_EAX, AMX_REG_DAT, MOD_REG); IA32_Cmp_Reg_Rm(jit, REG_EAX, AMX_REG_STK, MOD_REG); IA32_Jump_Cond_Imm32_Abs(jit, CC_AE, ((CompData *)jit->data)->jit_error_array_too_big); /* Prepare for indirection iteration */ //mov eax, [esi+info.heap] ;get heap pointer //lea ebx, [eax+edx*4] ;new heap pointer //mov [esi+info.heap], ebx ;store back //push eax ;save heap pointer - we need it IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_HEAP); IA32_Lea_Reg_DispRegMult(jit, REG_EBX, REG_EAX, REG_EDX, SCALE4); IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_EBX, AMX_INFO_HEAP); IA32_Push_Reg(jit, REG_EAX); WriteOp_Tracker_Push_Reg(jit, REG_EDX); /* This part is too messy to do in straight assembly. * I'm letting the compiler handle it and thus it's in C. */ //lea ebx, [ebp+eax] ;get base pointer //push dword [esp-8] ;push autozero //push dword [esp-8] ;push dimension count //push edi ;push dim array //push ebx //call GenerateArrayIndirectionVectors //add esp, 4*4 IA32_Lea_Reg_DispRegMult(jit, REG_EBX, REG_EAX, REG_EBP, NOSCALE); IA32_Push_Rm_Disp8_ESP(jit, 8); IA32_Push_Rm_Disp8_ESP(jit, 8); IA32_Push_Reg(jit, REG_EDI); IA32_Push_Reg(jit, REG_EBX); IA32_Write_Jump32_Abs(jit, IA32_Call_Imm32(jit, 0), (void *)&GenerateArrayIndirectionVectors); IA32_Add_Rm_Imm8(jit, REG_ESP, 4*4, MOD_REG); /* Store the heap pointer back into the stack */ //pop eax ;restore heap pointer //pop ecx ;restore param count //lea edi, [edi+ecx*4-4] ;pop params-4 off the stack //mov [edi], eax ;store back the heap pointer IA32_Pop_Reg(jit, REG_EAX); IA32_Pop_Reg(jit, REG_ECX); IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_STK, AMX_REG_STK, REG_ECX, SCALE4, -4); IA32_Mov_Rm_Reg(jit, AMX_REG_STK, REG_EAX, MOD_MEM_REG); /* Return to caller */ //pop edx //pop eax //pop ebx //ret IA32_Pop_Reg(jit, REG_ECX); IA32_Pop_Reg(jit, REG_EAX); IA32_Pop_Reg(jit, REG_EBX); IA32_Return(jit); } void WriteOp_Sysreq_N_Function(JitWriter *jit) { /* The big daddy of opcodes. * eax - num_params * ecx - native index */ CompData *data = (CompData *)jit->data; /* store the number of parameters on the stack */ //mov [edi-4], eax //sub edi, 4 IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, REG_EAX, -4); IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4, MOD_REG); /* save registers we will need */ //push eax ; num_params for stack popping //push edx IA32_Push_Reg(jit, REG_EAX); IA32_Push_Reg(jit, AMX_REG_ALT); /* push some callback stuff */ //push edi ; stack //push ecx ; native index IA32_Push_Reg(jit, AMX_REG_STK); IA32_Push_Reg(jit, REG_ECX); /* Relocate stack, heap, frm information, then store back */ //mov eax, [esi+context] //mov ecx, [esi+hea] //sub edi, ebp //mov [eax+hp], ecx //mov ecx, [esi] //mov [eax+sp], edi //mov [eax+frm], ecx IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT); IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP); IA32_Sub_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG); IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, hp)); IA32_Mov_Reg_Rm(jit, AMX_REG_TMP, AMX_INFO_FRM, MOD_MEM_REG); IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_STK, offsetof(sp_context_t, sp)); IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, frm)); /* finally, push the last parameter and make the call */ //push eax ; context //call NativeCallback IA32_Push_Reg(jit, REG_EAX); jitoffs_t call = IA32_Call_Imm32(jit, 0); if (!data->debug) { IA32_Write_Jump32_Abs(jit, call, NativeCallback); } else { IA32_Write_Jump32_Abs(jit, call, NativeCallback_Debug); } /* Test for error */ //mov ecx, [esi+context] //cmp [ecx+err], 0 //jnz :error IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT); IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, err), 0); IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error); /* restore what we damaged */ //add esp, 4*3 //add edi, ebp //pop edx //pop ecx ; num_params IA32_Add_Rm_Imm8(jit, REG_ESP, 4*3, MOD_REG); IA32_Add_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG); IA32_Pop_Reg(jit, AMX_REG_ALT); IA32_Pop_Reg(jit, REG_ECX); /* pop the AMX stack. do not check the margins. * Note that this is not a true macro - we don't bother to * set ALT here because nothing will be using it. */ //lea edi, [edi+ecx*4+4] IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_STK, AMX_REG_STK, REG_ECX, SCALE4, 4); //ret IA32_Return(jit); } void WriteOp_Tracker_Push_Reg(JitWriter *jit, uint8_t reg) { CompData *data = (CompData *)jit->data; /* Save registers that may be damaged by the call */ //push eax //push ecx //push edi //lea edi, [*4] ; we want the count in bytes not in cells IA32_Push_Reg(jit, AMX_REG_PRI); if (reg == REG_ECX) { IA32_Push_Reg(jit, AMX_REG_TMP); } IA32_Push_Reg(jit, AMX_REG_STK); IA32_Lea_Reg_RegMultImm32(jit, REG_EDI, reg, SCALE4, 0); /* Get the context ptr, push it and call the check */ //mov eax, [esi+context] //push eax //call JIT_VerifyOrAllocateTracker IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT); IA32_Push_Reg(jit, REG_EAX); jitoffs_t call = IA32_Call_Imm32(jit, 0); IA32_Write_Jump32_Abs(jit, call, JIT_VerifyOrAllocateTracker); /* Check for errors */ //pop eax //cmp [eax+err], 0 //jnz :error IA32_Pop_Reg(jit, REG_EAX); IA32_Cmp_Rm_Disp8_Imm8(jit, REG_EAX, offsetof(sp_context_t, err), 0); IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_error_tracker_bounds); /* Push the register into the stack and increment pCur */ //mov edx, [eax+vm[]] //mov eax, [edx+pcur] //add [edx+pcur], 4 //mov [eax], edi IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_EAX, offsetof(sp_context_t, vm[JITVARS_TRACKER])); IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EDX, offsetof(tracker_t, pCur)); IA32_Add_Rm_Imm8_Disp8(jit, REG_EDX, 4, offsetof(tracker_t, pCur)); IA32_Mov_Rm_Reg(jit, REG_EAX, REG_EDI, MOD_MEM_REG); /* Restore PRI, ALT and STK */ //pop edi //pop ecx //pop eax IA32_Pop_Reg(jit, AMX_REG_STK); if (reg == REG_ECX) { IA32_Pop_Reg(jit, AMX_REG_TMP); } IA32_Pop_Reg(jit, AMX_REG_PRI); } void JIT_VerifyOrAllocateTracker(sp_context_t *ctx) { tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]); if ((size_t)(trk->pCur - trk->pBase) >= trk->size) { ctx->err = SP_ERROR_TRACKER_BOUNDS; return; } if (trk->pCur+1 - (trk->pBase + trk->size) == 0) { size_t disp = trk->size - 1; trk->size *= 2; trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t)); if (!trk->pBase) { ctx->err = SP_ERROR_TRACKER_BOUNDS; return; } trk->pCur = trk->pBase + disp; } } void JIT_VerifyLowBoundTracker(sp_context_t *ctx) { tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]); if (trk->pCur <= trk->pBase) { ctx->err = SP_ERROR_TRACKER_BOUNDS; } }