/** * arch_get_throw_exception_generic: * * Returns a function pointer which can be used to raise * exceptions. The returned function has the following * signature: void (*func) (MonoException *exc); or * void (*func) (guint32 ex_token, gpointer ip) * */ static gpointer mono_arch_get_throw_exception_generic (int size, MonoTrampInfo **info, int corlib, gboolean rethrow, gboolean aot, gboolean preserve_ips) { guint8 *start, *code; int alloc_size, pos; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; code = start = mono_global_codeman_reserve (size); if (!aot) code = mono_ppc_create_pre_code_ftnptr (code); /* store ret addr */ if (corlib) ppc_mr (code, ppc_r0, ppc_r4); else ppc_mflr (code, ppc_r0); ppc_stptr (code, ppc_r0, PPC_RET_ADDR_OFFSET, ppc_sp); alloc_size = REG_SAVE_STACK_FRAME_SIZE; g_assert ((alloc_size & (MONO_ARCH_FRAME_ALIGNMENT-1)) == 0); ppc_stptr_update (code, ppc_sp, -alloc_size, ppc_sp); code = emit_save_saved_regs (code, alloc_size); //ppc_break (code); if (corlib) { ppc_mr (code, ppc_r4, ppc_r3); if (aot) { code = mono_arch_emit_load_aotconst (start, code, &ji, MONO_PATCH_INFO_IMAGE, mono_defaults.corlib); ppc_mr (code, ppc_r3, ppc_r12); code = mono_arch_emit_load_aotconst (start, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_exception_from_token"); #ifdef PPC_USES_FUNCTION_DESCRIPTOR ppc_ldptr (code, ppc_r2, sizeof (target_mgreg_t), ppc_r12); ppc_ldptr (code, ppc_r12, 0, ppc_r12); #endif ppc_mtctr (code, ppc_r12); ppc_bcctrl (code, PPC_BR_ALWAYS, 0); } else { ppc_load (code, ppc_r3, (gulong)mono_defaults.corlib); ppc_load_func (code, PPC_CALL_REG, mono_exception_from_token); ppc_mtctr (code, PPC_CALL_REG); ppc_bcctrl (code, PPC_BR_ALWAYS, 0); } } /* call throw_exception (exc, ip, sp, int_regs, fp_regs) */ /* caller sp */ ppc_ldptr (code, ppc_r5, 0, ppc_sp); /* exc is already in place in r3 */ if (corlib) ppc_ldptr (code, ppc_r4, PPC_RET_ADDR_OFFSET, ppc_r5); else ppc_mr (code, ppc_r4, ppc_r0); /* caller ip */ /* pointer to the saved fp regs */ pos = alloc_size - sizeof (gdouble) * MONO_MAX_FREGS; ppc_addi (code, ppc_r7, ppc_sp, pos); /* pointer to the saved int regs */ pos -= sizeof (target_mgreg_t) * MONO_MAX_IREGS; ppc_addi (code, ppc_r6, ppc_sp, pos); ppc_li (code, ppc_r8, rethrow); ppc_li (code, ppc_r9, preserve_ips); if (aot) { // This can be called from runtime code, which can't guarantee that // r30 contains the got address. // So emit the got address loading code too code = mono_arch_emit_load_got_addr (start, code, NULL, &ji); code = mono_arch_emit_load_aotconst (start, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_ppc_throw_exception"); #ifdef PPC_USES_FUNCTION_DESCRIPTOR ppc_ldptr (code, ppc_r2, sizeof (target_mgreg_t), ppc_r12); ppc_ldptr (code, ppc_r12, 0, ppc_r12); #endif ppc_mtctr (code, ppc_r12); ppc_bcctrl (code, PPC_BR_ALWAYS, 0); } else { ppc_load_func (code, PPC_CALL_REG, mono_ppc_throw_exception); ppc_mtctr (code, PPC_CALL_REG); ppc_bcctrl (code, PPC_BR_ALWAYS, 0); } /* we should never reach this breakpoint */ ppc_break (code); g_assert ((code - start) <= size); mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL)); if (info) *info = mono_tramp_info_create (corlib ? "throw_corlib_exception" : (preserve_ips ? "rethrow_preserve_exception" : (rethrow ? "rethrow_exception" : "throw_exception")), start, code - start, ji, unwind_ops); return start; }
/* * mono_arch_get_call_filter: * * Returns a pointer to a method which calls an exception filter. We * also use this function to call finally handlers (we pass NULL as * @exc object in this case). */ gpointer mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot) { guint8 *start, *code; int alloc_size, pos, i; int size = MONO_PPC_32_64_CASE (320, 500) + PPC_FTNPTR_SIZE; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */ code = start = mono_global_codeman_reserve (size); if (!aot) code = mono_ppc_create_pre_code_ftnptr (code); /* store ret addr */ ppc_mflr (code, ppc_r0); ppc_stptr (code, ppc_r0, PPC_RET_ADDR_OFFSET, ppc_sp); alloc_size = REG_SAVE_STACK_FRAME_SIZE; /* allocate stack frame and set link from sp in ctx */ g_assert ((alloc_size & (MONO_ARCH_FRAME_ALIGNMENT-1)) == 0); ppc_ldptr (code, ppc_r0, G_STRUCT_OFFSET (MonoContext, sc_sp), ppc_r3); ppc_ldptr_indexed (code, ppc_r0, ppc_r0, ppc_r0); ppc_stptr_update (code, ppc_r0, -alloc_size, ppc_sp); code = emit_save_saved_regs (code, alloc_size); /* restore all the regs from ctx (in r3), but not r1, the stack pointer */ restore_regs_from_context (ppc_r3, ppc_r6, ppc_r7); /* call handler at eip (r4) and set the first arg with the exception (r5) */ ppc_mtctr (code, ppc_r4); ppc_mr (code, ppc_r3, ppc_r5); ppc_bcctrl (code, PPC_BR_ALWAYS, 0); /* epilog */ ppc_ldptr (code, ppc_r0, alloc_size + PPC_RET_ADDR_OFFSET, ppc_sp); ppc_mtlr (code, ppc_r0); /* restore all the regs from the stack */ pos = alloc_size; for (i = MONO_MAX_FREGS - 1; i >= MONO_PPC_FIRST_SAVED_FREG; --i) { pos -= sizeof (gdouble); ppc_lfd (code, i, pos, ppc_sp); } pos -= (MONO_MAX_FREGS - MONO_SAVED_FREGS) * sizeof (gdouble); pos -= sizeof (gpointer) * MONO_SAVED_GREGS; ppc_load_multiple_regs (code, MONO_PPC_FIRST_SAVED_GREG, pos, ppc_sp); ppc_addic (code, ppc_sp, ppc_sp, alloc_size); ppc_blr (code); g_assert ((code - start) < size); mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL)); if (info) *info = mono_tramp_info_create ("call_filter", start, code - start, ji, unwind_ops); return start; }
gpointer mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot) { guint8 *tramp; guint8 *code, *buf; guint8 **rgctx_null_jumps; gint32 displace; int tramp_size, depth, index, iPatch = 0, i; gboolean mrgctx; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot); index = MONO_RGCTX_SLOT_INDEX (slot); if (mrgctx) index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (target_mgreg_t); for (depth = 0; ; ++depth) { int size = mono_class_rgctx_get_array_size (depth, mrgctx); if (index < size - 1) break; index -= size - 1; } tramp_size = 48 + 16 * depth; if (mrgctx) tramp_size += 4; else tramp_size += 12; code = buf = mono_global_codeman_reserve (tramp_size); unwind_ops = mono_arch_get_cie_program (); rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2)); if (mrgctx) { /* get mrgctx ptr */ s390_lgr (code, s390_r1, s390_r2); } else { /* load rgctx ptr from vtable */ s390_lg (code, s390_r1, 0, s390_r2, MONO_STRUCT_OFFSET(MonoVTable, runtime_generic_context)); /* is the rgctx ptr null? */ s390_ltgr (code, s390_r1, s390_r1); /* if yes, jump to actual trampoline */ rgctx_null_jumps [iPatch++] = code; s390_jge (code, 0); } for (i = 0; i < depth; ++i) { /* load ptr to next array */ if (mrgctx && i == 0) s390_lg (code, s390_r1, 0, s390_r1, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT); else s390_lg (code, s390_r1, 0, s390_r1, 0); s390_ltgr (code, s390_r1, s390_r1); /* if the ptr is null then jump to actual trampoline */ rgctx_null_jumps [iPatch++] = code; s390_jge (code, 0); } /* fetch slot */ s390_lg (code, s390_r1, 0, s390_r1, (sizeof (target_mgreg_t) * (index + 1))); /* is the slot null? */ s390_ltgr (code, s390_r1, s390_r1); /* if yes, jump to actual trampoline */ rgctx_null_jumps [iPatch++] = code; s390_jge (code, 0); /* otherwise return r1 */ s390_lgr (code, s390_r2, s390_r1); s390_br (code, s390_r14); for (i = 0; i < iPatch; i++) { displace = ((uintptr_t) code - (uintptr_t) rgctx_null_jumps[i]) / 2; s390_patch_rel ((rgctx_null_jumps [i] + 2), displace); } g_free (rgctx_null_jumps); /* move the rgctx pointer to the VTABLE register */ #if MONO_ARCH_VTABLE_REG != s390_r2 s390_lgr (code, MONO_ARCH_VTABLE_REG, s390_r2); #endif tramp = (guint8*)mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), NULL); /* jump to the actual trampoline */ displace = (tramp - code) / 2; s390_jg (code, displace); mono_arch_flush_icache (buf, code - buf); MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL)); g_assert (code - buf <= tramp_size); char *name = mono_get_rgctx_fetch_trampoline_name (slot); *info = mono_tramp_info_create (name, buf, code - buf, ji, unwind_ops); g_free (name); return(buf); }
MonoDynamicImage* mono_dynamic_image_create (MonoDynamicAssembly *assembly, char *assembly_name, char *module_name) { static const guchar entrycode [16] = {0xff, 0x25, 0}; MonoDynamicImage *image; int i; const char *version; if (!strcmp (mono_get_runtime_info ()->framework_version, "2.1")) version = "v2.0.50727"; /* HACK: SL 2 enforces the .net 2 metadata version */ else version = mono_get_runtime_info ()->runtime_version; image = g_new0 (MonoDynamicImage, 1); MONO_PROFILER_RAISE (image_loading, (&image->image)); /*g_print ("created image %p\n", image);*/ /* keep in sync with image.c */ image->image.name = assembly_name; image->image.assembly_name = image->image.name; /* they may be different */ image->image.module_name = module_name; image->image.version = g_strdup (version); image->image.md_version_major = 1; image->image.md_version_minor = 1; image->image.dynamic = TRUE; image->image.references = g_new0 (MonoAssembly*, 1); image->image.references [0] = NULL; mono_image_init (&image->image); image->token_fixups = mono_g_hash_table_new_type ((GHashFunc)mono_object_hash, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_REFLECTION, NULL, "Reflection Dynamic Image Token Fixup Table"); image->method_to_table_idx = g_hash_table_new (NULL, NULL); image->field_to_table_idx = g_hash_table_new (NULL, NULL); image->method_aux_hash = g_hash_table_new (NULL, NULL); image->vararg_aux_hash = g_hash_table_new (NULL, NULL); image->handleref = g_hash_table_new (NULL, NULL); image->tokens = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_REFLECTION, NULL, "Reflection Dynamic Image Token Table"); image->generic_def_objects = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_REFLECTION, NULL, "Reflection Dynamic Image Generic Definition Table"); image->typespec = g_hash_table_new ((GHashFunc)mono_metadata_type_hash, (GCompareFunc)mono_metadata_type_equal); image->typeref = g_hash_table_new ((GHashFunc)mono_metadata_type_hash, (GCompareFunc)mono_metadata_type_equal); image->blob_cache = g_hash_table_new ((GHashFunc)mono_blob_entry_hash, (GCompareFunc)mono_blob_entry_equal); image->gen_params = g_ptr_array_new (); image->remapped_tokens = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_REFLECTION, NULL, "Reflection Dynamic Image Remapped Token Table"); /*g_print ("string heap create for image %p (%s)\n", image, module_name);*/ string_heap_init (&image->sheap); mono_dynstream_add_data (&image->us, "", 1); mono_dynamic_image_add_to_blob_cached (image, (char*) "", 1, NULL, 0); /* import tables... */ mono_dynstream_add_data (&image->code, (char*)entrycode, sizeof (entrycode)); image->iat_offset = mono_dynstream_add_zero (&image->code, 8); /* two IAT entries */ image->idt_offset = mono_dynstream_add_zero (&image->code, 2 * sizeof (MonoIDT)); /* two IDT entries */ image->imp_names_offset = mono_dynstream_add_zero (&image->code, 2); /* flags for name entry */ mono_dynstream_add_data (&image->code, "_CorExeMain", 12); mono_dynstream_add_data (&image->code, "mscoree.dll", 12); image->ilt_offset = mono_dynstream_add_zero (&image->code, 8); /* two ILT entries */ mono_dynstream_data_align (&image->code); image->cli_header_offset = mono_dynstream_add_zero (&image->code, sizeof (MonoCLIHeader)); for (i=0; i < MONO_TABLE_NUM; ++i) { image->tables [i].next_idx = 1; image->tables [i].columns = table_sizes [i]; } image->image.assembly = (MonoAssembly*)assembly; image->run = assembly->run; image->save = assembly->save; image->pe_kind = 0x1; /* ILOnly */ image->machine = 0x14c; /* I386 */ MONO_PROFILER_RAISE (image_loaded, (&image->image)); dynamic_images_lock (); if (!dynamic_images) dynamic_images = g_ptr_array_new (); g_ptr_array_add (dynamic_images, image); dynamic_images_unlock (); return image; }
/* * mono_arch_get_call_filter: * * Returns a pointer to a method which calls an exception filter. We * also use this function to call finally handlers (we pass NULL as * @exc object in this case). * * call_filter (MonoContext *ctx, gpointer ip) */ gpointer mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot) { static guint32 *start; static int inited = 0; guint32 *code; int i; g_assert (!aot); if (info) *info = NULL; if (inited) return start; code = start = mono_global_codeman_reserve (64 * sizeof (guint32)); /* * There are two frames here: * - the first frame is used by call_filter * - the second frame is used to run the filter code */ /* Create first frame */ sparc_save_imm (code, sparc_sp, -256, sparc_sp); sparc_mov_reg_reg (code, sparc_i1, sparc_o0); sparc_ldi_imm (code, sparc_i0, G_STRUCT_OFFSET (MonoContext, sp), sparc_o1); /* Create second frame */ sparc_save_imm (code, sparc_sp, -256, sparc_sp); sparc_mov_reg_reg (code, sparc_i0, sparc_o0); sparc_mov_reg_reg (code, sparc_i1, sparc_o1); /* * We need to change %fp to point to the stack frame of the method * containing the filter. But changing %fp also changes the %sp of * the parent frame (the first frame), so if the OS saves the first frame, * it saves it to the stack frame of the method, which is not good. * So flush all register windows to memory before changing %fp. */ sparc_flushw (code); sparc_mov_reg_reg (code, sparc_fp, sparc_o7); /* * Modify the second frame so it is identical to the one used in the * method containing the filter. */ for (i = 0; i < 16; ++i) sparc_ldi_imm (code, sparc_o1, MONO_SPARC_STACK_BIAS + i * sizeof (gpointer), sparc_l0 + i); /* Save %fp to a location reserved in mono_arch_allocate_vars */ sparc_sti_imm (code, sparc_o7, sparc_fp, MONO_SPARC_STACK_BIAS - sizeof (gpointer)); /* Call the filter code, after this returns, %o0 will hold the result */ sparc_call_imm (code, sparc_o0, 0); sparc_nop (code); /* Restore original %fp */ sparc_ldi_imm (code, sparc_fp, MONO_SPARC_STACK_BIAS - sizeof (gpointer), sparc_fp); sparc_mov_reg_reg (code, sparc_o0, sparc_i0); /* Return to first frame */ sparc_restore (code, sparc_g0, sparc_g0, sparc_g0); /* FIXME: Save locals to the stack */ /* Return to caller */ sparc_ret (code); /* Return result in delay slot */ sparc_restore (code, sparc_o0, sparc_g0, sparc_o0); g_assert ((code - start) < 64); mono_arch_flush_icache ((guint8*)start, (guint8*)code - (guint8*)start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL)); inited = 1; return start; }
guchar* mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot) { char *tramp_name; guint8 *buf, *tramp, *code; int i, offset, has_caller; short *o[1]; GSList *unwind_ops = NULL; MonoJumpInfo *ji = NULL; g_assert (!aot); /* Now we'll create in 'buf' the S/390 trampoline code. This is the trampoline code common to all methods */ code = buf = mono_global_codeman_reserve(512); if (tramp_type == MONO_TRAMPOLINE_JUMP) has_caller = 0; else has_caller = 1; /*----------------------------------------------------------- STEP 0: First create a non-standard function prologue with a stack size big enough to save our registers. -----------------------------------------------------------*/ s390_stmg (buf, s390_r6, s390_r15, STK_BASE, S390_REG_SAVE_OFFSET); s390_lgr (buf, s390_r11, s390_r15); s390_aghi (buf, STK_BASE, -sizeof(trampStack_t)); s390_stg (buf, s390_r11, 0, STK_BASE, 0); /*---------------------------------------------------------------*/ /* we build the MonoLMF structure on the stack - see mini-s390.h */ /* Keep in sync with the code in mono_arch_emit_prolog */ /*---------------------------------------------------------------*/ s390_lgr (buf, LMFReg, STK_BASE); s390_aghi (buf, LMFReg, G_STRUCT_OFFSET(trampStack_t, LMF)); /*---------------------------------------------------------------*/ /* Save general and floating point registers in LMF */ /*---------------------------------------------------------------*/ s390_stmg (buf, s390_r0, s390_r1, LMFReg, G_STRUCT_OFFSET(MonoLMF, gregs[0])); s390_stmg (buf, s390_r2, s390_r5, LMFReg, G_STRUCT_OFFSET(MonoLMF, gregs[2])); s390_mvc (buf, 10*sizeof(gulong), LMFReg, G_STRUCT_OFFSET(MonoLMF, gregs[6]), s390_r11, S390_REG_SAVE_OFFSET); offset = G_STRUCT_OFFSET(MonoLMF, fregs[0]); for (i = s390_f0; i <= s390_f15; ++i) { s390_std (buf, i, 0, LMFReg, offset); offset += sizeof(gdouble); } /*---------------------------------------------------------- STEP 1: call 'mono_get_lmf_addr()' to get the address of our LMF. We'll need to restore it after the call to 's390_magic_trampoline' and before the call to the native method. ----------------------------------------------------------*/ S390_SET (buf, s390_r1, mono_get_lmf_addr); s390_basr (buf, s390_r14, s390_r1); /*---------------------------------------------------------------*/ /* Set lmf.lmf_addr = jit_tls->lmf */ /*---------------------------------------------------------------*/ s390_stg (buf, s390_r2, 0, LMFReg, G_STRUCT_OFFSET(MonoLMF, lmf_addr)); /*---------------------------------------------------------------*/ /* Get current lmf */ /*---------------------------------------------------------------*/ s390_lg (buf, s390_r0, 0, s390_r2, 0); /*---------------------------------------------------------------*/ /* Set our lmf as the current lmf */ /*---------------------------------------------------------------*/ s390_stg (buf, LMFReg, 0, s390_r2, 0); /*---------------------------------------------------------------*/ /* Have our lmf.previous_lmf point to the last lmf */ /*---------------------------------------------------------------*/ s390_stg (buf, s390_r0, 0, LMFReg, G_STRUCT_OFFSET(MonoLMF, previous_lmf)); /*---------------------------------------------------------------*/ /* save method info */ /*---------------------------------------------------------------*/ s390_lg (buf, s390_r1, 0, LMFReg, G_STRUCT_OFFSET(MonoLMF, gregs[1])); s390_stg (buf, s390_r1, 0, LMFReg, G_STRUCT_OFFSET(MonoLMF, method)); /*---------------------------------------------------------------*/ /* save the current SP */ /*---------------------------------------------------------------*/ s390_lg (buf, s390_r1, 0, STK_BASE, 0); s390_stg (buf, s390_r1, 0, LMFReg, G_STRUCT_OFFSET(MonoLMF, ebp)); /*---------------------------------------------------------------*/ /* save the current IP */ /*---------------------------------------------------------------*/ if (has_caller) { s390_lg (buf, s390_r1, 0, s390_r1, S390_RET_ADDR_OFFSET); } else { s390_lghi (buf, s390_r1, 0); } s390_stg (buf, s390_r1, 0, LMFReg, G_STRUCT_OFFSET(MonoLMF, eip)); /*---------------------------------------------------------------*/ /* STEP 2: call the C trampoline function */ /*---------------------------------------------------------------*/ /* Set arguments */ /* Arg 1: host_mgreg_t *regs */ s390_la (buf, s390_r2, 0, LMFReg, G_STRUCT_OFFSET(MonoLMF, gregs[0])); /* Arg 2: code (next address to the instruction that called us) */ if (has_caller) { s390_lg (buf, s390_r3, 0, s390_r11, S390_RET_ADDR_OFFSET); } else { s390_lghi (buf, s390_r3, 0); } /* Arg 3: Trampoline argument */ s390_lg (buf, s390_r4, 0, LMFReg, G_STRUCT_OFFSET(MonoLMF, gregs[1])); /* Arg 4: trampoline address. */ S390_SET (buf, s390_r5, buf); /* Calculate call address and call the C trampoline. Return value will be in r2 */ tramp = (guint8*)mono_get_trampoline_func (tramp_type); S390_SET (buf, s390_r1, tramp); s390_basr (buf, s390_r14, s390_r1); /* OK, code address is now on r2. Save it, so that we can restore r2 and use it later */ s390_stg (buf, s390_r2, 0, STK_BASE, G_STRUCT_OFFSET(trampStack_t, saveFn)); /*---------------------------------------------------------- STEP 3: Restore the LMF ----------------------------------------------------------*/ restoreLMF(buf, STK_BASE, sizeof(trampStack_t)); /* Check for thread interruption */ S390_SET (buf, s390_r1, (guint8 *)mono_thread_force_interruption_checkpoint_noraise); s390_basr (buf, s390_r14, s390_r1); s390_ltgr (buf, s390_r2, s390_r2); s390_jz (buf, 0); CODEPTR (buf, o[0]); /* * Exception case: * We have an exception we want to throw in the caller's frame, so pop * the trampoline frame and throw from the caller. */ S390_SET (buf, s390_r1, (guint *)mono_get_rethrow_preserve_exception_addr ()); s390_aghi (buf, STK_BASE, sizeof(trampStack_t)); s390_lg (buf, s390_r1, 0, s390_r1, 0); s390_lmg (buf, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET); s390_br (buf, s390_r1); PTRSLOT (buf, o[0]); /* Reload result */ s390_lg (buf, s390_r1, 0, STK_BASE, G_STRUCT_OFFSET(trampStack_t, saveFn)); /*---------------------------------------------------------- STEP 4: call the compiled method ----------------------------------------------------------*/ /* Restore parameter registers */ s390_lmg (buf, s390_r2, s390_r5, LMFReg, G_STRUCT_OFFSET(MonoLMF, gregs[2])); /* Restore the FP registers */ offset = G_STRUCT_OFFSET(MonoLMF, fregs[0]); for (i = s390_f0; i <= s390_f15; ++i) { s390_ld (buf, i, 0, LMFReg, offset); offset += sizeof(gdouble); } /* Restore stack pointer and jump to the code - * R14 contains the return address to our caller */ s390_lgr (buf, STK_BASE, s390_r11); s390_lmg (buf, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET); if (MONO_TRAMPOLINE_TYPE_MUST_RETURN(tramp_type)) { s390_lgr (buf, s390_r2, s390_r1); s390_br (buf, s390_r14); } else { s390_br (buf, s390_r1); } /* Flush instruction cache, since we've generated code */ mono_arch_flush_icache (code, buf - code); MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL)); g_assert (info); tramp_name = mono_get_generic_trampoline_name (tramp_type); *info = mono_tramp_info_create (tramp_name, buf, buf - code, ji, unwind_ops); g_free (tramp_name); /* Sanity check */ g_assert ((buf - code) <= 512); return code; }
/* * mono_arch_create_sdb_trampoline: * * Return a trampoline which captures the current context, passes it to * mini_get_dbg_callbacks ()->single_step_from_context ()/mini_get_dbg_callbacks ()->breakpoint_from_context (), * then restores the (potentially changed) context. */ guint8* mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot) { int tramp_size = 256; int framesize, ctx_offset, cfa_offset; guint8 *code, *buf; GSList *unwind_ops = NULL; MonoJumpInfo *ji = NULL; code = buf = mono_global_codeman_reserve (tramp_size); framesize = 0; /* Argument area */ framesize += sizeof (target_mgreg_t); framesize = ALIGN_TO (framesize, 8); ctx_offset = framesize; framesize += sizeof (MonoContext); framesize = ALIGN_TO (framesize, MONO_ARCH_FRAME_ALIGNMENT); // CFA = sp + 4 cfa_offset = 4; mono_add_unwind_op_def_cfa (unwind_ops, code, buf, X86_ESP, 4); // IP saved at CFA - 4 mono_add_unwind_op_offset (unwind_ops, code, buf, X86_NREG, -cfa_offset); x86_push_reg (code, X86_EBP); cfa_offset += sizeof (target_mgreg_t); mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset); mono_add_unwind_op_offset (unwind_ops, code, buf, X86_EBP, - cfa_offset); x86_mov_reg_reg (code, X86_EBP, X86_ESP); mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, X86_EBP); /* The + 8 makes the stack aligned */ x86_alu_reg_imm (code, X86_SUB, X86_ESP, framesize + 8); /* Initialize a MonoContext structure on the stack */ x86_mov_membase_reg (code, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, eax), X86_EAX, sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, ebx), X86_EBX, sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, ecx), X86_ECX, sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, edx), X86_EDX, sizeof (target_mgreg_t)); x86_mov_reg_membase (code, X86_EAX, X86_EBP, 0, sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, ebp), X86_EAX, sizeof (target_mgreg_t)); x86_mov_reg_reg (code, X86_EAX, X86_EBP); x86_alu_reg_imm (code, X86_ADD, X86_EAX, cfa_offset); x86_mov_membase_reg (code, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, esp), X86_ESP, sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, esi), X86_ESI, sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, edi), X86_EDI, sizeof (target_mgreg_t)); x86_mov_reg_membase (code, X86_EAX, X86_EBP, 4, sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, eip), X86_EAX, sizeof (target_mgreg_t)); /* Call the single step/breakpoint function in sdb */ x86_lea_membase (code, X86_EAX, X86_ESP, ctx_offset); x86_mov_membase_reg (code, X86_ESP, 0, X86_EAX, sizeof (target_mgreg_t)); if (aot) { x86_breakpoint (code); } else { if (single_step) x86_call_code (code, mini_get_dbg_callbacks ()->single_step_from_context); else x86_call_code (code, mini_get_dbg_callbacks ()->breakpoint_from_context); } /* Restore registers from ctx */ /* Overwrite the saved ebp */ x86_mov_reg_membase (code, X86_EAX, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, ebp), sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_EBP, 0, X86_EAX, sizeof (target_mgreg_t)); /* Overwrite saved eip */ x86_mov_reg_membase (code, X86_EAX, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, eip), sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_EBP, 4, X86_EAX, sizeof (target_mgreg_t)); x86_mov_reg_membase (code, X86_EAX, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, eax), sizeof (target_mgreg_t)); x86_mov_reg_membase (code, X86_EBX, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, ebx), sizeof (target_mgreg_t)); x86_mov_reg_membase (code, X86_ECX, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, ecx), sizeof (target_mgreg_t)); x86_mov_reg_membase (code, X86_EDX, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, edx), sizeof (target_mgreg_t)); x86_mov_reg_membase (code, X86_ESI, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, esi), sizeof (target_mgreg_t)); x86_mov_reg_membase (code, X86_EDI, X86_ESP, ctx_offset + G_STRUCT_OFFSET (MonoContext, edi), sizeof (target_mgreg_t)); x86_leave (code); cfa_offset -= sizeof (target_mgreg_t); mono_add_unwind_op_def_cfa (unwind_ops, code, buf, X86_ESP, cfa_offset); x86_ret (code); mono_arch_flush_icache (code, code - buf); MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL)); g_assert (code - buf <= tramp_size); const char *tramp_name = single_step ? "sdb_single_step_trampoline" : "sdb_breakpoint_trampoline"; *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops); return buf; }
/* * actually, we might want to queue the finalize requests in a separate thread, * but we need to be careful about the execution domain of the thread... */ void mono_gc_run_finalize (void *obj, void *data) { ERROR_DECL (error); MonoObject *exc = NULL; MonoObject *o; #ifndef HAVE_SGEN_GC MonoObject *o2; #endif MonoMethod* finalizer = NULL; MonoDomain *caller_domain = mono_domain_get (); MonoDomain *domain; // This function is called from the innards of the GC, so our best alternative for now is to do polling here mono_threads_safepoint (); o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data)); const char *o_ns = m_class_get_name_space (mono_object_class (o)); const char *o_name = m_class_get_name (mono_object_class (o)); if (mono_do_not_finalize) { if (!mono_do_not_finalize_class_names) return; size_t namespace_len = strlen (o_ns); for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) { const char *name = mono_do_not_finalize_class_names [i]; if (strncmp (name, o_ns, namespace_len)) break; if (name [namespace_len] != '.') break; if (strcmp (name + namespace_len + 1, o_name)) break; return; } } if (mono_log_finalizers) g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o_name, o); if (suspend_finalizers) return; domain = o->vtable->domain; #ifndef HAVE_SGEN_GC mono_domain_finalizers_lock (domain); o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o); mono_domain_finalizers_unlock (domain); if (!o2) /* Already finalized somehow */ return; #endif /* make sure the finalizer is not called again if the object is resurrected */ object_register_finalizer ((MonoObject *)obj, NULL); if (mono_log_finalizers) g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o_name, o); if (o->vtable->klass == mono_defaults.internal_thread_class) { MonoInternalThread *t = (MonoInternalThread*)o; if (mono_gc_is_finalizer_internal_thread (t)) /* Avoid finalizing ourselves */ return; } if (m_class_get_image (mono_object_class (o)) == mono_defaults.corlib && !strcmp (o_name, "DynamicMethod") && finalizing_root_domain) { /* * These can't be finalized during unloading/shutdown, since that would * free the native code which can still be referenced by other * finalizers. * FIXME: This is not perfect, objects dying at the same time as * dynamic methods can still reference them even when !shutdown. */ return; } if (mono_runtime_get_no_exec ()) return; /* speedup later... and use a timeout */ /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */ /* Use _internal here, since this thread can enter a doomed appdomain */ mono_domain_set_internal (mono_object_domain (o)); /* delegates that have a native function pointer allocated are * registered for finalization, but they don't have a Finalize * method, because in most cases it's not needed and it's just a waste. */ if (m_class_is_delegate (mono_object_class (o))) { MonoDelegate* del = (MonoDelegate*)o; if (del->delegate_trampoline) mono_delegate_free_ftnptr ((MonoDelegate*)o); mono_domain_set_internal (caller_domain); return; } finalizer = mono_class_get_finalizer (o->vtable->klass); /* If object has a CCW but has no finalizer, it was only * registered for finalization in order to free the CCW. * Else it needs the regular finalizer run. * FIXME: what to do about ressurection and suppression * of finalizer on object with CCW. */ if (mono_marshal_free_ccw (o) && !finalizer) { mono_domain_set_internal (caller_domain); return; } /* * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (), * create and precompile a wrapper which calls the finalize method using * a CALLVIRT. */ if (mono_log_finalizers) g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o_name, o); #ifndef HOST_WASM if (!domain->finalize_runtime_invoke) { MonoMethod *finalize_method = mono_class_get_method_from_name_checked (mono_defaults.object_class, "Finalize", 0, 0, error); mono_error_assert_ok (error); MonoMethod *invoke = mono_marshal_get_runtime_invoke (finalize_method, TRUE); domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, error); mono_error_assert_ok (error); /* expect this not to fail */ } RuntimeInvokeFunction runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke; #endif mono_runtime_class_init_full (o->vtable, error); goto_if_nok (error, unhandled_error); if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) { MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size_internal (o), o_ns, o_name); } if (mono_log_finalizers) g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o_name, o); MONO_PROFILER_RAISE (gc_finalizing_object, (o)); #ifdef HOST_WASM if (finalizer) { // null finalizers work fine when using the vcall invoke as Object has an empty one gpointer params [1]; params [0] = NULL; mono_runtime_try_invoke (finalizer, o, params, &exc, error); } #else runtime_invoke (o, NULL, &exc, NULL); #endif MONO_PROFILER_RAISE (gc_finalized_object, (o)); if (mono_log_finalizers) g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o_name, o); unhandled_error: if (!is_ok (error)) exc = (MonoObject*)mono_error_convert_to_exception (error); if (exc) mono_thread_internal_unhandled_exception (exc); mono_domain_set_internal (caller_domain); }
guchar* mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot) { const char *tramp_name; guint8 *buf, *code, *tramp, *br_ex_check; GSList *unwind_ops = NULL; MonoJumpInfo *ji = NULL; int i, offset, frame_size, regarray_offset, lmf_offset, caller_ip_offset, arg_offset; int cfa_offset; /* cfa = cfa_reg + cfa_offset */ code = buf = mono_global_codeman_reserve (256); /* Note that there is a single argument to the trampoline * and it is stored at: esp + pushed_args * sizeof (target_mgreg_t) * the ret address is at: esp + (pushed_args + 1) * sizeof (target_mgreg_t) */ /* Compute frame offsets relative to the frame pointer %ebp */ arg_offset = sizeof (target_mgreg_t); caller_ip_offset = 2 * sizeof (target_mgreg_t); offset = 0; offset += sizeof (MonoLMF); lmf_offset = -offset; offset += X86_NREG * sizeof (target_mgreg_t); regarray_offset = -offset; /* Argument area */ offset += 4 * sizeof (target_mgreg_t); frame_size = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT); /* ret addr and arg are on the stack */ cfa_offset = 2 * sizeof (target_mgreg_t); mono_add_unwind_op_def_cfa (unwind_ops, code, buf, X86_ESP, cfa_offset); // IP saved at CFA - 4 mono_add_unwind_op_offset (unwind_ops, code, buf, X86_NREG, -4); /* Allocate frame */ x86_push_reg (code, X86_EBP); cfa_offset += sizeof (target_mgreg_t); mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset); mono_add_unwind_op_offset (unwind_ops, code, buf, X86_EBP, -cfa_offset); x86_mov_reg_reg (code, X86_EBP, X86_ESP); mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, X86_EBP); /* There are three words on the stack, adding + 4 aligns the stack to 16, which is needed on osx */ x86_alu_reg_imm (code, X86_SUB, X86_ESP, frame_size + sizeof (target_mgreg_t)); /* Save all registers */ for (i = X86_EAX; i <= X86_EDI; ++i) { int reg = i; if (i == X86_EBP) { /* Save original ebp */ /* EAX is already saved */ x86_mov_reg_membase (code, X86_EAX, X86_EBP, 0, sizeof (target_mgreg_t)); reg = X86_EAX; } else if (i == X86_ESP) { /* Save original esp */ /* EAX is already saved */ x86_mov_reg_reg (code, X86_EAX, X86_EBP); /* Saved ebp + trampoline arg + return addr */ x86_alu_reg_imm (code, X86_ADD, X86_EAX, 3 * sizeof (target_mgreg_t)); reg = X86_EAX; } x86_mov_membase_reg (code, X86_EBP, regarray_offset + (i * sizeof (target_mgreg_t)), reg, sizeof (target_mgreg_t)); } /* Setup LMF */ /* eip */ if (tramp_type == MONO_TRAMPOLINE_JUMP) { x86_mov_membase_imm (code, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, eip), 0, sizeof (target_mgreg_t)); } else { x86_mov_reg_membase (code, X86_EAX, X86_EBP, caller_ip_offset, sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, eip), X86_EAX, sizeof (target_mgreg_t)); } /* method */ if ((tramp_type == MONO_TRAMPOLINE_JIT) || (tramp_type == MONO_TRAMPOLINE_JUMP)) { x86_mov_reg_membase (code, X86_EAX, X86_EBP, arg_offset, sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, method), X86_EAX, sizeof (target_mgreg_t)); } else { x86_mov_membase_imm (code, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, method), 0, sizeof (target_mgreg_t)); } /* esp */ x86_mov_reg_membase (code, X86_EAX, X86_EBP, regarray_offset + (X86_ESP * sizeof (target_mgreg_t)), sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, esp), X86_EAX, sizeof (target_mgreg_t)); /* callee save registers */ x86_mov_reg_membase (code, X86_EAX, X86_EBP, regarray_offset + (X86_EBX * sizeof (target_mgreg_t)), sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, ebx), X86_EAX, sizeof (target_mgreg_t)); x86_mov_reg_membase (code, X86_EAX, X86_EBP, regarray_offset + (X86_EDI * sizeof (target_mgreg_t)), sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, edi), X86_EAX, sizeof (target_mgreg_t)); x86_mov_reg_membase (code, X86_EAX, X86_EBP, regarray_offset + (X86_ESI * sizeof (target_mgreg_t)), sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, esi), X86_EAX, sizeof (target_mgreg_t)); x86_mov_reg_membase (code, X86_EAX, X86_EBP, regarray_offset + (X86_EBP * sizeof (target_mgreg_t)), sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, ebp), X86_EAX, sizeof (target_mgreg_t)); /* Push LMF */ /* get the address of lmf for the current thread */ if (aot) { code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_get_lmf_addr"); x86_call_reg (code, X86_EAX); } else { x86_call_code (code, mono_get_lmf_addr); } /* lmf->lmf_addr = lmf_addr (%eax) */ x86_mov_membase_reg (code, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr), X86_EAX, sizeof (target_mgreg_t)); /* lmf->previous_lmf = *(lmf_addr) */ x86_mov_reg_membase (code, X86_ECX, X86_EAX, 0, sizeof (target_mgreg_t)); /* Signal to mono_arch_unwind_frame () that this is a trampoline frame */ x86_alu_reg_imm (code, X86_ADD, X86_ECX, 1); x86_mov_membase_reg (code, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, previous_lmf), X86_ECX, sizeof (target_mgreg_t)); /* *lmf_addr = lmf */ x86_lea_membase (code, X86_ECX, X86_EBP, lmf_offset); x86_mov_membase_reg (code, X86_EAX, 0, X86_ECX, sizeof (target_mgreg_t)); /* Call trampoline function */ /* Arg 1 - registers */ x86_lea_membase (code, X86_EAX, X86_EBP, regarray_offset); x86_mov_membase_reg (code, X86_ESP, (0 * sizeof (target_mgreg_t)), X86_EAX, sizeof (target_mgreg_t)); /* Arg2 - calling code */ if (tramp_type == MONO_TRAMPOLINE_JUMP) { x86_mov_membase_imm (code, X86_ESP, (1 * sizeof (target_mgreg_t)), 0, sizeof (target_mgreg_t)); } else { x86_mov_reg_membase (code, X86_EAX, X86_EBP, caller_ip_offset, sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_ESP, (1 * sizeof (target_mgreg_t)), X86_EAX, sizeof (target_mgreg_t)); } /* Arg3 - trampoline argument */ x86_mov_reg_membase (code, X86_EAX, X86_EBP, arg_offset, sizeof (target_mgreg_t)); x86_mov_membase_reg (code, X86_ESP, (2 * sizeof (target_mgreg_t)), X86_EAX, sizeof (target_mgreg_t)); /* Arg4 - trampoline address */ // FIXME: x86_mov_membase_imm (code, X86_ESP, (3 * sizeof (target_mgreg_t)), 0, sizeof (target_mgreg_t)); #ifdef __APPLE__ /* check the stack is aligned after the ret ip is pushed */ /* x86_mov_reg_reg (code, X86_EDX, X86_ESP); x86_alu_reg_imm (code, X86_AND, X86_EDX, 15); x86_alu_reg_imm (code, X86_CMP, X86_EDX, 0); x86_branch_disp (code, X86_CC_Z, 3, FALSE); x86_breakpoint (code); */ #endif if (aot) { code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_TRAMPOLINE_FUNC_ADDR, GINT_TO_POINTER (tramp_type)); x86_call_reg (code, X86_EAX); } else { tramp = (guint8*)mono_get_trampoline_func (tramp_type); x86_call_code (code, tramp); } /* * Overwrite the trampoline argument with the address we need to jump to, * to free %eax. */ x86_mov_membase_reg (code, X86_EBP, arg_offset, X86_EAX, 4); /* Restore LMF */ x86_mov_reg_membase (code, X86_EAX, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr), sizeof (target_mgreg_t)); x86_mov_reg_membase (code, X86_ECX, X86_EBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, previous_lmf), sizeof (target_mgreg_t)); x86_alu_reg_imm (code, X86_SUB, X86_ECX, 1); x86_mov_membase_reg (code, X86_EAX, 0, X86_ECX, sizeof (target_mgreg_t)); /* Check for interruptions */ if (aot) { code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_thread_force_interruption_checkpoint_noraise"); x86_call_reg (code, X86_EAX); } else { x86_call_code (code, (guint8*)mono_thread_force_interruption_checkpoint_noraise); } x86_test_reg_reg (code, X86_EAX, X86_EAX); br_ex_check = code; x86_branch8 (code, X86_CC_Z, -1, 1); /* * Exception case: * We have an exception we want to throw in the caller's frame, so pop * the trampoline frame and throw from the caller. */ x86_leave (code); /* * The exception is in eax. * We are calling the throw trampoline used by OP_THROW, so we have to setup the * stack to look the same. * The stack contains the ret addr, and the trampoline argument, the throw trampoline * expects it to contain the ret addr and the exception. It also needs to be aligned * after the exception is pushed. */ /* Align stack */ x86_push_reg (code, X86_EAX); /* Push the exception */ x86_push_reg (code, X86_EAX); //x86_breakpoint (code); /* Push the original return value */ x86_push_membase (code, X86_ESP, 3 * 4); /* * EH is initialized after trampolines, so get the address of the variable * which contains throw_exception, and load it from there. */ if (aot) { /* Not really a jit icall */ code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "rethrow_preserve_exception_addr"); } else { x86_mov_reg_imm (code, X86_ECX, (guint8*)mono_get_rethrow_preserve_exception_addr ()); } x86_mov_reg_membase (code, X86_ECX, X86_ECX, 0, sizeof (target_mgreg_t)); x86_jump_reg (code, X86_ECX); /* Normal case */ mono_x86_patch (br_ex_check, code); /* Restore registers */ for (i = X86_EAX; i <= X86_EDI; ++i) { if (i == X86_ESP || i == X86_EBP) continue; if (i == X86_EAX && tramp_type != MONO_TRAMPOLINE_AOT_PLT) continue; x86_mov_reg_membase (code, i, X86_EBP, regarray_offset + (i * 4), 4); } /* Restore frame */ x86_leave (code); cfa_offset -= sizeof (target_mgreg_t); mono_add_unwind_op_def_cfa (unwind_ops, code, buf, X86_ESP, cfa_offset); mono_add_unwind_op_same_value (unwind_ops, code, buf, X86_EBP); if (MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type)) { /* Load the value returned by the trampoline */ x86_mov_reg_membase (code, X86_EAX, X86_ESP, 0, 4); /* The trampoline returns normally, pop the trampoline argument */ x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4); cfa_offset -= sizeof (target_mgreg_t); mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset); x86_ret (code); } else { x86_ret (code); } g_assert ((code - buf) <= 256); MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL)); tramp_name = mono_get_generic_trampoline_name (tramp_type); *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops); return buf; }
gpointer mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot) { guint8 *tramp; guint8 *code, *buf; guint8 **rgctx_null_jumps; int tramp_size; int depth, index; int i; gboolean mrgctx; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; unwind_ops = mono_arch_get_cie_program (); mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot); index = MONO_RGCTX_SLOT_INDEX (slot); if (mrgctx) index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (target_mgreg_t); for (depth = 0; ; ++depth) { int size = mono_class_rgctx_get_array_size (depth, mrgctx); if (index < size - 1) break; index -= size - 1; } tramp_size = (aot ? 64 : 36) + 6 * depth; code = buf = mono_global_codeman_reserve (tramp_size); rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2)); /* load vtable/mrgctx ptr */ x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4); if (!mrgctx) { /* load rgctx ptr from vtable */ x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context), 4); /* is the rgctx ptr null? */ x86_test_reg_reg (code, X86_EAX, X86_EAX); /* if yes, jump to actual trampoline */ rgctx_null_jumps [0] = code; x86_branch8 (code, X86_CC_Z, -1, 1); } for (i = 0; i < depth; ++i) { /* load ptr to next array */ if (mrgctx && i == 0) x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT, 4); else x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, 4); /* is the ptr null? */ x86_test_reg_reg (code, X86_EAX, X86_EAX); /* if yes, jump to actual trampoline */ rgctx_null_jumps [i + 1] = code; x86_branch8 (code, X86_CC_Z, -1, 1); } /* fetch slot */ x86_mov_reg_membase (code, X86_EAX, X86_EAX, sizeof (target_mgreg_t) * (index + 1), 4); /* is the slot null? */ x86_test_reg_reg (code, X86_EAX, X86_EAX); /* if yes, jump to actual trampoline */ rgctx_null_jumps [depth + 1] = code; x86_branch8 (code, X86_CC_Z, -1, 1); /* otherwise return */ x86_ret (code); for (i = mrgctx ? 1 : 0; i <= depth + 1; ++i) x86_patch (rgctx_null_jumps [i], code); g_free (rgctx_null_jumps); x86_mov_reg_membase (code, MONO_ARCH_VTABLE_REG, X86_ESP, 4, 4); if (aot) { code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_SPECIFIC_TRAMPOLINE_LAZY_FETCH_ADDR, GUINT_TO_POINTER (slot)); x86_jump_reg (code, X86_EAX); } else { tramp = (guint8*)mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), NULL); /* jump to the actual trampoline */ x86_jump_code (code, tramp); } mono_arch_flush_icache (buf, code - buf); MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL)); g_assert (code - buf <= tramp_size); char *name = mono_get_rgctx_fetch_trampoline_name (slot); *info = mono_tramp_info_create (name, buf, code - buf, ji, unwind_ops); g_free (name); return buf; }
/* * mono_arch_get_call_filter: * * Returns a pointer to a method which calls an exception filter. We * also use this function to call finally handlers (we pass NULL as * @exc object in this case). * * This function is invoked as * call_handler (MonoContext *ctx, handler) * * Where 'handler' is a function to be invoked as: * handler (void) */ gpointer mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot) { static guint8 start [320]; static int inited = 0; guint8 *code; int alloc_size; int offset; g_assert (!aot); if (info) *info = NULL; if (inited) return start; inited = 1; code = start; alloc_size = 64; g_assert ((alloc_size & (MIPS_STACK_ALIGNMENT-1)) == 0); mips_addiu (code, mips_sp, mips_sp, -alloc_size); mips_sw (code, mips_ra, mips_sp, alloc_size + MIPS_RET_ADDR_OFFSET); /* Save global registers on stack (s0 - s7) */ offset = 16; MIPS_SW (code, mips_s0, mips_sp, offset); offset += IREG_SIZE; MIPS_SW (code, mips_s1, mips_sp, offset); offset += IREG_SIZE; MIPS_SW (code, mips_s2, mips_sp, offset); offset += IREG_SIZE; MIPS_SW (code, mips_s3, mips_sp, offset); offset += IREG_SIZE; MIPS_SW (code, mips_s4, mips_sp, offset); offset += IREG_SIZE; MIPS_SW (code, mips_s5, mips_sp, offset); offset += IREG_SIZE; MIPS_SW (code, mips_s6, mips_sp, offset); offset += IREG_SIZE; MIPS_SW (code, mips_s7, mips_sp, offset); offset += IREG_SIZE; MIPS_SW (code, mips_fp, mips_sp, offset); offset += IREG_SIZE; /* Restore global registers from MonoContext, including the frame pointer */ MIPS_LW (code, mips_s0, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s0])); MIPS_LW (code, mips_s1, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s1])); MIPS_LW (code, mips_s2, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s2])); MIPS_LW (code, mips_s3, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s3])); MIPS_LW (code, mips_s4, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s4])); MIPS_LW (code, mips_s5, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s5])); MIPS_LW (code, mips_s6, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s6])); MIPS_LW (code, mips_s7, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s7])); MIPS_LW (code, mips_fp, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_fp])); /* a1 is the handler to call */ mips_move (code, mips_t9, mips_a1); /* jump to the saved IP */ mips_jalr (code, mips_t9, mips_ra); mips_nop (code); /* restore all regs from the stack */ offset = 16; MIPS_LW (code, mips_s0, mips_sp, offset); offset += IREG_SIZE; MIPS_LW (code, mips_s1, mips_sp, offset); offset += IREG_SIZE; MIPS_LW (code, mips_s2, mips_sp, offset); offset += IREG_SIZE; MIPS_LW (code, mips_s3, mips_sp, offset); offset += IREG_SIZE; MIPS_LW (code, mips_s4, mips_sp, offset); offset += IREG_SIZE; MIPS_LW (code, mips_s5, mips_sp, offset); offset += IREG_SIZE; MIPS_LW (code, mips_s6, mips_sp, offset); offset += IREG_SIZE; MIPS_LW (code, mips_s7, mips_sp, offset); offset += IREG_SIZE; MIPS_LW (code, mips_fp, mips_sp, offset); offset += IREG_SIZE; /* epilog */ mips_lw (code, mips_ra, mips_sp, alloc_size + MIPS_RET_ADDR_OFFSET); mips_addiu (code, mips_sp, mips_sp, alloc_size); mips_jr (code, mips_ra); mips_nop (code); g_assert ((code - start) < sizeof(start)); mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL)); return start; }
/** * arch_get_throw_exception_generic: * * Returns a function pointer which can be used to raise * exceptions. The returned function has the following * signature: void (*func) (MonoException *exc); or * void (*func) (char *exc_name); * */ static gpointer mono_arch_get_throw_exception_generic (guint8 *start, int size, int corlib, gboolean rethrow, gboolean preserve_ips) { guint8 *code; int alloc_size, pos, i; code = start; //g_print ("mono_arch_get_throw_exception_generic: code=%p\n", code); pos = 0; /* XXX - save all the FP regs on the stack ? */ pos += MONO_MAX_IREGS * sizeof(guint32); alloc_size = MIPS_MINIMAL_STACK_SIZE + pos + 64; // align to MIPS_STACK_ALIGNMENT bytes alloc_size += MIPS_STACK_ALIGNMENT - 1; alloc_size &= ~(MIPS_STACK_ALIGNMENT - 1); g_assert ((alloc_size & (MIPS_STACK_ALIGNMENT-1)) == 0); mips_addiu (code, mips_sp, mips_sp, -alloc_size); mips_sw (code, mips_ra, mips_sp, alloc_size + MIPS_RET_ADDR_OFFSET); /* Save all the regs on the stack */ for (i = 0; i < MONO_MAX_IREGS; i++) { if (i != mips_sp) MIPS_SW (code, i, mips_sp, i*IREG_SIZE + MIPS_STACK_PARAM_OFFSET); else { mips_addiu (code, mips_at, mips_sp, alloc_size); MIPS_SW (code, mips_at, mips_sp, i*IREG_SIZE + MIPS_STACK_PARAM_OFFSET); } } if (corlib) { mips_move (code, mips_a1, mips_a0); mips_load (code, mips_a0, mono_defaults.corlib); mips_load (code, mips_t9, mono_exception_from_token); mips_jalr (code, mips_t9, mips_ra); mips_nop (code); mips_move (code, mips_a0, mips_v0); } /* call throw_exception (exc, ip, sp, rethrow) */ /* exc is already in place in a0 */ /* pointer to ip */ if (corlib) mips_lw (code, mips_a1, mips_sp, alloc_size + MIPS_RET_ADDR_OFFSET); else mips_move (code, mips_a1, mips_ra); /* current sp & rethrow */ mips_move (code, mips_a2, mips_sp); mips_addiu (code, mips_a3, mips_zero, rethrow); mips_load (code, mips_t9, throw_exception); mips_jr (code, mips_t9); mips_nop (code); /* we should never reach this breakpoint */ mips_break (code, 0xfe); g_assert ((code - start) < size); mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL)); return start; }
/* * get_throw_trampoline: * * Generate a call to mono_x86_throw_exception/ * mono_x86_throw_corlib_exception. * If LLVM is true, generate code which assumes the caller is LLVM generated code, * which doesn't push the arguments. */ static guint8* get_throw_trampoline (const char *name, gboolean rethrow, gboolean llvm, gboolean corlib, gboolean llvm_abs, gboolean resume_unwind, MonoTrampInfo **info, gboolean aot) { guint8 *start, *code, *labels [16]; int i, stack_size, stack_offset, arg_offsets [5], regs_offset; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; guint kMaxCodeSize = 192; start = code = mono_global_codeman_reserve (kMaxCodeSize); stack_size = 128; /* * On apple, the stack is misaligned by the pushing of the return address. */ if (!llvm && corlib) /* On OSX, we don't generate alignment code to save space */ stack_size += 4; else stack_size += MONO_ARCH_FRAME_ALIGNMENT - 4; /* * The stack looks like this: * <pc offset> (only if corlib is TRUE) * <exception object>/<type token> * <return addr> <- esp (unaligned on apple) */ unwind_ops = mono_arch_get_cie_program (); /* Alloc frame */ x86_alu_reg_imm (code, X86_SUB, X86_ESP, stack_size); mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, stack_size + 4); arg_offsets [0] = 0; arg_offsets [1] = 4; arg_offsets [2] = 8; arg_offsets [3] = 12; regs_offset = 16; /* Save registers */ for (i = 0; i < X86_NREG; ++i) if (i != X86_ESP) x86_mov_membase_reg (code, X86_ESP, regs_offset + (i * 4), i, 4); /* Calculate the offset between the current sp and the sp of the caller */ if (llvm) { /* LLVM doesn't push the arguments */ stack_offset = stack_size + 4; } else { if (corlib) { /* Two arguments */ stack_offset = stack_size + 4 + 8; #ifdef __APPLE__ /* We don't generate stack alignment code on osx to save space */ #endif } else { /* One argument + stack alignment */ stack_offset = stack_size + 4 + 4; #ifdef __APPLE__ /* Pop the alignment added by OP_THROW too */ stack_offset += MONO_ARCH_FRAME_ALIGNMENT - 4; #else if (mono_do_x86_stack_align) stack_offset += MONO_ARCH_FRAME_ALIGNMENT - 4; #endif } } /* Save ESP */ x86_lea_membase (code, X86_EAX, X86_ESP, stack_offset); x86_mov_membase_reg (code, X86_ESP, regs_offset + (X86_ESP * 4), X86_EAX, 4); /* Clear fp stack */ labels [0] = code; x86_fnstsw (code); x86_shift_reg_imm (code, X86_SHR, X86_EAX, 11); x86_alu_reg_imm (code, X86_AND, X86_EAX, 7); x86_alu_reg_imm (code, X86_CMP, X86_EAX, 0); labels [1] = code; x86_branch8 (code, X86_CC_EQ, 0, FALSE); x86_fstp (code, 0); x86_jump_code (code, labels [0]); mono_x86_patch (labels [1], code); /* Set arg1 == regs */ x86_lea_membase (code, X86_EAX, X86_ESP, regs_offset); x86_mov_membase_reg (code, X86_ESP, arg_offsets [0], X86_EAX, 4); /* Set arg2 == exc/ex_token_index */ if (resume_unwind) x86_mov_reg_imm (code, X86_EAX, 0); else x86_mov_reg_membase (code, X86_EAX, X86_ESP, stack_size + 4, 4); x86_mov_membase_reg (code, X86_ESP, arg_offsets [1], X86_EAX, 4); /* Set arg3 == eip */ if (llvm_abs) x86_alu_reg_reg (code, X86_XOR, X86_EAX, X86_EAX); else x86_mov_reg_membase (code, X86_EAX, X86_ESP, stack_size, 4); x86_mov_membase_reg (code, X86_ESP, arg_offsets [2], X86_EAX, 4); /* Set arg4 == rethrow/pc_offset */ if (resume_unwind) { x86_mov_membase_imm (code, X86_ESP, arg_offsets [3], 0, 4); } else if (corlib) { x86_mov_reg_membase (code, X86_EAX, X86_ESP, stack_size + 8, 4); if (llvm_abs) { /* * The caller is LLVM code which passes the absolute address not a pc offset, * so compensate by passing 0 as 'ip' and passing the negated abs address as * the pc offset. */ x86_neg_reg (code, X86_EAX); } x86_mov_membase_reg (code, X86_ESP, arg_offsets [3], X86_EAX, 4); } else { x86_mov_membase_imm (code, X86_ESP, arg_offsets [3], rethrow, 4); } /* Make the call */ if (aot) { // This can be called from runtime code, which can't guarantee that // ebx contains the got address. // So emit the got address loading code too code = mono_arch_emit_load_got_addr (start, code, NULL, &ji); code = mono_arch_emit_load_aotconst (start, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, corlib ? "mono_x86_throw_corlib_exception" : "mono_x86_throw_exception"); x86_call_reg (code, X86_EAX); } else { x86_call_code (code, resume_unwind ? (gpointer)(mono_x86_resume_unwind) : (corlib ? (gpointer)mono_x86_throw_corlib_exception : (gpointer)mono_x86_throw_exception)); } x86_breakpoint (code); g_assert ((code - start) < kMaxCodeSize); if (info) *info = mono_tramp_info_create (name, start, code - start, ji, unwind_ops); else { GSList *l; for (l = unwind_ops; l; l = l->next) g_free (l->data); g_slist_free (unwind_ops); } mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL)); return start; }
/* * mono_arch_get_call_filter: * * Returns a pointer to a method which calls an exception filter. We * also use this function to call finally handlers (we pass NULL as * @exc object in this case). */ gpointer mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot) { guint8* start; guint8 *code; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; guint kMaxCodeSize = 64; /* call_filter (MonoContext *ctx, unsigned long eip) */ start = code = mono_global_codeman_reserve (kMaxCodeSize); x86_push_reg (code, X86_EBP); x86_mov_reg_reg (code, X86_EBP, X86_ESP); x86_push_reg (code, X86_EBX); x86_push_reg (code, X86_EDI); x86_push_reg (code, X86_ESI); /* load ctx */ x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4); /* load eip */ x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4); /* save EBP */ x86_push_reg (code, X86_EBP); /* set new EBP */ x86_mov_reg_membase (code, X86_EBP, X86_EAX, MONO_STRUCT_OFFSET (MonoContext, ebp), 4); /* restore registers used by global register allocation (EBX & ESI) */ x86_mov_reg_membase (code, X86_EBX, X86_EAX, MONO_STRUCT_OFFSET (MonoContext, ebx), 4); x86_mov_reg_membase (code, X86_ESI, X86_EAX, MONO_STRUCT_OFFSET (MonoContext, esi), 4); x86_mov_reg_membase (code, X86_EDI, X86_EAX, MONO_STRUCT_OFFSET (MonoContext, edi), 4); /* align stack and save ESP */ x86_mov_reg_reg (code, X86_EDX, X86_ESP); x86_alu_reg_imm (code, X86_AND, X86_ESP, -MONO_ARCH_FRAME_ALIGNMENT); g_assert (MONO_ARCH_FRAME_ALIGNMENT >= 8); x86_alu_reg_imm (code, X86_SUB, X86_ESP, MONO_ARCH_FRAME_ALIGNMENT - 8); x86_push_reg (code, X86_EDX); /* call the handler */ x86_call_reg (code, X86_ECX); /* restore ESP */ x86_pop_reg (code, X86_ESP); /* restore EBP */ x86_pop_reg (code, X86_EBP); /* restore saved regs */ x86_pop_reg (code, X86_ESI); x86_pop_reg (code, X86_EDI); x86_pop_reg (code, X86_EBX); x86_leave (code); x86_ret (code); if (info) *info = mono_tramp_info_create ("call_filter", start, code - start, ji, unwind_ops); else { GSList *l; for (l = unwind_ops; l; l = l->next) g_free (l->data); g_slist_free (unwind_ops); } mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL)); g_assert ((code - start) < kMaxCodeSize); return start; }
/* * mono_arch_get_restore_context: * * Returns a pointer to a method which restores a previously saved sigcontext. */ gpointer mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot) { guint8 *start = NULL; guint8 *code; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; /* restore_contect (MonoContext *ctx) */ start = code = mono_global_codeman_reserve (128); /* load ctx */ x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4); /* restore EBX */ x86_mov_reg_membase (code, X86_EBX, X86_EAX, MONO_STRUCT_OFFSET (MonoContext, ebx), 4); /* restore EDI */ x86_mov_reg_membase (code, X86_EDI, X86_EAX, MONO_STRUCT_OFFSET (MonoContext, edi), 4); /* restore ESI */ x86_mov_reg_membase (code, X86_ESI, X86_EAX, MONO_STRUCT_OFFSET (MonoContext, esi), 4); /* restore EDX */ x86_mov_reg_membase (code, X86_EDX, X86_EAX, MONO_STRUCT_OFFSET (MonoContext, edx), 4); /* * The context resides on the stack, in the stack frame of the * caller of this function. The stack pointer that we need to * restore is potentially many stack frames higher up, so the * distance between them can easily be more than the red zone * size. Hence the stack pointer can be restored only after * we have finished loading everything from the context. */ /* load ESP into EBP */ x86_mov_reg_membase (code, X86_EBP, X86_EAX, MONO_STRUCT_OFFSET (MonoContext, esp), 4); /* load return address into ECX */ x86_mov_reg_membase (code, X86_ECX, X86_EAX, MONO_STRUCT_OFFSET (MonoContext, eip), 4); /* save the return addr to the restored stack - 4 */ x86_mov_membase_reg (code, X86_EBP, -4, X86_ECX, 4); /* load EBP into ECX */ x86_mov_reg_membase (code, X86_ECX, X86_EAX, MONO_STRUCT_OFFSET (MonoContext, ebp), 4); /* save EBP to the restored stack - 8 */ x86_mov_membase_reg (code, X86_EBP, -8, X86_ECX, 4); /* load EAX into ECX */ x86_mov_reg_membase (code, X86_ECX, X86_EAX, MONO_STRUCT_OFFSET (MonoContext, eax), 4); /* save EAX to the restored stack - 12 */ x86_mov_membase_reg (code, X86_EBP, -12, X86_ECX, 4); /* restore ECX */ x86_mov_reg_membase (code, X86_ECX, X86_EAX, MONO_STRUCT_OFFSET (MonoContext, ecx), 4); /* restore ESP - 12 */ x86_lea_membase (code, X86_ESP, X86_EBP, -12); /* restore EAX */ x86_pop_reg (code, X86_EAX); /* restore EBP */ x86_pop_reg (code, X86_EBP); /* jump to the saved IP */ x86_ret (code); if (info) *info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops); else { GSList *l; for (l = unwind_ops; l; l = l->next) g_free (l->data); g_slist_free (unwind_ops); } mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL)); return start; }