gpointer mono_arch_get_unbox_trampoline (MonoMethod *method, gpointer addr) { guint8 *code, *start; int this_pos = s390_r2; MonoDomain *domain = mono_domain_get (); char trampName[128]; start = code = mono_domain_code_reserve (domain, 28); S390_SET (code, s390_r1, addr); s390_aghi (code, this_pos, sizeof(MonoObject)); s390_br (code, s390_r1); g_assert ((code - start) <= 28); mono_arch_flush_icache (start, code - start); if (mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE, method); snprintf(trampName, sizeof(trampName), "%s_unbox_trampoline", method->name); mono_tramp_info_register (mono_tramp_info_create (trampName, start, code - start, NULL, NULL), domain); return start; }
/* * arch_get_restore_context: * * Returns a pointer to a method which restores a previously saved sigcontext. * The first argument in r3 is the pointer to the context. */ gpointer mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot) { guint8 *start, *code; int size = MONO_PPC_32_64_CASE (128, 172) + PPC_FTNPTR_SIZE; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; code = start = mono_global_codeman_reserve (size); if (!aot) code = mono_ppc_create_pre_code_ftnptr (code); restore_regs_from_context (ppc_r3, ppc_r4, ppc_r5); /* restore also the stack pointer */ ppc_ldptr (code, ppc_sp, G_STRUCT_OFFSET (MonoContext, sc_sp), ppc_r3); //ppc_break (code); /* jump to the saved IP */ ppc_mtctr (code, ppc_r4); ppc_bcctr (code, PPC_BR_ALWAYS, 0); /* never reached */ ppc_break (code); g_assert ((code - start) <= size); mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); if (info) *info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops); return start; }
gpointer mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr) { guint8 *code, *start; gint32 displace; int buf_len; char trampName[128]; MonoDomain *domain = mono_domain_get (); buf_len = 32; start = code = mono_domain_code_reserve (domain, buf_len); S390_SET (code, MONO_ARCH_RGCTX_REG, mrgctx); displace = ((uintptr_t) addr - (uintptr_t) code) / 2; s390_jg (code, displace); g_assert ((code - start) < buf_len); mono_arch_flush_icache (start, code - start); if (mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_HELPER, NULL); snprintf(trampName, sizeof(trampName), "%s_rgctx_trampoline", m->name); mono_tramp_info_register (mono_tramp_info_create (trampName, start, code - start, NULL, NULL), domain); return(start); }
gpointer mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len) { guint8 *code, *buf, *tramp; gint32 displace; tramp = mono_get_trampoline_code (tramp_type); /*----------------------------------------------------------*/ /* This is the method-specific part of the trampoline. Its */ /* purpose is to provide the generic part with the */ /* MonoMethod *method pointer. We'll use r1 to keep it. */ /*----------------------------------------------------------*/ code = buf = mono_domain_code_reserve (domain, SPECIFIC_TRAMPOLINE_SIZE); S390_SET (buf, s390_r1, arg1); displace = (tramp - buf) / 2; s390_jg (buf, displace); /* Flush instruction cache, since we've generated code */ mono_arch_flush_icache (code, buf - code); if (mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE, (void *) mono_get_generic_trampoline_simple_name (tramp_type)); /* Sanity check */ g_assert ((buf - code) <= SPECIFIC_TRAMPOLINE_SIZE); if (code_len) *code_len = buf - code; return code; }
gpointer mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr) { guint8 *code, *start; gint32 displace; int buf_len; MonoDomain *domain = mono_domain_get (); buf_len = 32; start = code = mono_domain_code_reserve (domain, buf_len); S390_SET (code, MONO_ARCH_RGCTX_REG, mrgctx); displace = ((uintptr_t) addr - (uintptr_t) code) / 2; s390_jg (code, displace); g_assert ((code - start) < buf_len); mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_HELPER, 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, *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 = 31; i >= 14; --i) { pos -= sizeof (gdouble); ppc_lfd (code, i, pos, ppc_sp); } pos -= sizeof (gpointer) * MONO_SAVED_GREGS; ppc_load_multiple_regs (code, ppc_r13, 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_code_buffer_new (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; }
/* * 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 (256); amd64_mov_reg_reg (code, AMD64_R11, AMD64_ARG_REG1, 8); /* Restore all registers except %rip and %r11 */ amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, rax), 8); amd64_mov_reg_membase (code, AMD64_RCX, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, rcx), 8); amd64_mov_reg_membase (code, AMD64_RDX, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, rdx), 8); amd64_mov_reg_membase (code, AMD64_RBX, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, rbx), 8); amd64_mov_reg_membase (code, AMD64_RBP, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, rbp), 8); amd64_mov_reg_membase (code, AMD64_RSI, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, rsi), 8); amd64_mov_reg_membase (code, AMD64_RDI, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, rdi), 8); //amd64_mov_reg_membase (code, AMD64_R8, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, r8), 8); //amd64_mov_reg_membase (code, AMD64_R9, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, r9), 8); //amd64_mov_reg_membase (code, AMD64_R10, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, r10), 8); amd64_mov_reg_membase (code, AMD64_R12, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, r12), 8); amd64_mov_reg_membase (code, AMD64_R13, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, r13), 8); amd64_mov_reg_membase (code, AMD64_R14, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, r14), 8); #if !defined(__native_client_codegen__) amd64_mov_reg_membase (code, AMD64_R15, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, r15), 8); #endif /* * 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. */ amd64_mov_reg_membase (code, AMD64_R8, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, rsp), 8); amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, MONO_STRUCT_OFFSET (MonoContext, rip), 8); amd64_mov_reg_reg (code, AMD64_RSP, AMD64_R8, 8); /* jump to the saved IP */ amd64_jump_reg (code, AMD64_R11); nacl_global_codeman_validate(&start, 256, &code); mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); if (info) *info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops); 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; int i, gregs_offset; /* restore_contect (MonoContext *ctx) */ start = code = (guint8 *)mono_global_codeman_reserve (256); amd64_mov_reg_reg (code, AMD64_R11, AMD64_ARG_REG1, 8); /* Restore all registers except %rip and %r11 */ gregs_offset = MONO_STRUCT_OFFSET (MonoContext, gregs); for (i = 0; i < AMD64_NREG; ++i) { #if defined(__native_client_codegen__) if (i == AMD64_R15) continue; #endif if (i != AMD64_RIP && i != AMD64_RSP && i != AMD64_R8 && i != AMD64_R9 && i != AMD64_R10 && i != AMD64_R11) amd64_mov_reg_membase (code, i, AMD64_R11, gregs_offset + (i * 8), 8); } /* * 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. */ amd64_mov_reg_membase (code, AMD64_R8, AMD64_R11, gregs_offset + (AMD64_RSP * 8), 8); amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, gregs_offset + (AMD64_RIP * 8), 8); amd64_mov_reg_reg (code, AMD64_RSP, AMD64_R8, 8); /* jump to the saved IP */ amd64_jump_reg (code, AMD64_R11); nacl_global_codeman_validate (&start, 256, &code); mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); if (info) *info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops); return start; }
MonoContinuationRestore mono_tasklets_arch_restore (void) { static guint8* saved = NULL; guint8 *code, *start; int cont_reg = AMD64_R9; /* register usable on both call conventions */ const guint kMaxCodeSize = 64; if (saved) return (MonoContinuationRestore)saved; code = start = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize); /* the signature is: restore (MonoContinuation *cont, int state, MonoLMF **lmf_addr) */ /* cont is in AMD64_ARG_REG1 ($rcx or $rdi) * state is in AMD64_ARG_REG2 ($rdx or $rsi) * lmf_addr is in AMD64_ARG_REG3 ($r8 or $rdx) * We move cont to cont_reg since we need both rcx and rdi for the copy * state is moved to $rax so it's setup as the return value and we can overwrite $rsi */ amd64_mov_reg_reg (code, cont_reg, MONO_AMD64_ARG_REG1, 8); amd64_mov_reg_reg (code, AMD64_RAX, MONO_AMD64_ARG_REG2, 8); /* setup the copy of the stack */ amd64_mov_reg_membase (code, AMD64_RCX, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, stack_used_size), sizeof (int)); amd64_shift_reg_imm (code, X86_SHR, AMD64_RCX, 3); x86_cld (code); amd64_mov_reg_membase (code, AMD64_RSI, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, saved_stack), sizeof (gpointer)); amd64_mov_reg_membase (code, AMD64_RDI, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, return_sp), sizeof (gpointer)); amd64_prefix (code, X86_REP_PREFIX); amd64_movsl (code); /* now restore the registers from the LMF */ amd64_mov_reg_membase (code, AMD64_RCX, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, lmf), 8); amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RCX, MONO_STRUCT_OFFSET (MonoLMF, rbp), 8); amd64_mov_reg_membase (code, AMD64_RSP, AMD64_RCX, MONO_STRUCT_OFFSET (MonoLMF, rsp), 8); #ifdef WIN32 amd64_mov_reg_reg (code, AMD64_R14, AMD64_ARG_REG3, 8); #else amd64_mov_reg_reg (code, AMD64_R12, AMD64_ARG_REG3, 8); #endif /* state is already in rax */ amd64_jump_membase (code, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, return_ip)); g_assert ((code - start) <= kMaxCodeSize); mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); saved = start; return (MonoContinuationRestore)saved; }
gpointer mono_arch_create_handler_block_trampoline (MonoTrampInfo **info, gboolean aot) { guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD); guint8 *code, *buf; int tramp_size = 64; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; g_assert (!aot); code = buf = mono_global_codeman_reserve (tramp_size); /* * This trampoline restore the call chain of the handler block * then jumps into the code that deals with it. */ if (mono_get_jit_tls_offset () != -1) { s390_ear (code, s390_r1, 0); s390_sllg (code, s390_r1, s390_r1, 0, 32); s390_ear (code, s390_r1, 1); S390_SET (code, s390_r14, mono_get_jit_tls_offset()); s390_lg (code, s390_r14, s390_r1, 0, G_STRUCT_OFFSET(MonoJitTlsData, handler_block_return_address)); /* * Simulate a call */ S390_SET (code, s390_r1, tramp); s390_br (code, s390_r1); } else { /* * Slow path uses a C helper */ S390_SET (code, s390_r2, tramp); S390_SET (code, s390_r1, handler_block_trampoline_helper); s390_br (code, s390_r1); } mono_arch_flush_icache (buf, code - buf); if (mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL); g_assert (code - buf <= tramp_size); *info = mono_tramp_info_create ("handler_block_trampoline", buf, code - buf, ji, unwind_ops); return buf; }
gpointer mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len) { guint8 *code, *buf, *tramp; gint32 displace; tramp = mono_get_trampoline_code (tramp_type); /*----------------------------------------------------------*/ /* This is the method-specific part of the trampoline. Its */ /* purpose is to provide the generic part with the */ /* MonoMethod *method pointer. We'll use r1 to keep it. */ /*----------------------------------------------------------*/ code = buf = mono_domain_code_reserve (domain, SPECIFIC_TRAMPOLINE_SIZE); switch (tramp_type) { /* * Monitor tramps have the object in r2 */ case MONO_TRAMPOLINE_MONITOR_ENTER: case MONO_TRAMPOLINE_MONITOR_ENTER_V4: case MONO_TRAMPOLINE_MONITOR_EXIT: s390_lgr (buf, s390_r1, s390_r2); break; /* * Generic class trampoline arg is in r2 */ case MONO_TRAMPOLINE_GENERIC_CLASS_INIT: s390_lgr (buf, s390_r1, s390_r2); break; default : S390_SET (buf, s390_r1, arg1); } displace = (tramp - buf) / 2; s390_jg (buf, displace); /* Flush instruction cache, since we've generated code */ mono_arch_flush_icache (code, buf - code); mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE, (void *) mono_get_generic_trampoline_simple_name (tramp_type)); /* Sanity check */ g_assert ((buf - code) <= SPECIFIC_TRAMPOLINE_SIZE); if (code_len) *code_len = buf - code; return code; }
gpointer mono_arch_get_throw_exception_by_name (void) { guint8 *start, *code; int size = 64; /* Not used on MIPS */ start = code = mono_global_codeman_reserve (size); mips_break (code, 0xfd); mono_arch_flush_icache (start, code - start); if (mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); return start; }
gpointer mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot) { guint8 *start, *code; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; int i, ctx_reg, size; guint8 *labels [16]; size = 256; code = start = mono_global_codeman_reserve (size); arm_movx (code, ARMREG_IP0, ARMREG_R0); ctx_reg = ARMREG_IP0; /* Restore fregs */ arm_ldrx (code, ARMREG_IP1, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, has_fregs)); labels [0] = code; arm_cbzx (code, ARMREG_IP1, 0); for (i = 0; i < 32; ++i) arm_ldrfpx (code, i, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, fregs) + (i * 8)); mono_arm_patch (labels [0], code, MONO_R_ARM64_CBZ); /* Restore gregs */ // FIXME: Restore less registers // FIXME: fp should be restored later code = mono_arm_emit_load_regarray (code, 0xffffffff & ~(1 << ctx_reg) & ~(1 << ARMREG_SP), ctx_reg, MONO_STRUCT_OFFSET (MonoContext, regs)); /* ip0/ip1 doesn't need to be restored */ /* ip1 = pc */ arm_ldrx (code, ARMREG_IP1, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, pc)); /* ip0 = sp */ arm_ldrx (code, ARMREG_IP0, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_SP * 8)); /* Restore sp, ctx is no longer valid */ arm_movspx (code, ARMREG_SP, ARMREG_IP0); /* Branch to pc */ arm_brx (code, ARMREG_IP1); /* Not reached */ arm_brk (code, 0); g_assert ((code - start) < size); mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); if (info) *info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops); return start; }
/* * mono_x86_get_signal_exception_trampoline: * * This x86 specific trampoline is used to call handle_signal_exception. */ gpointer mono_x86_get_signal_exception_trampoline (MonoTrampInfo **info, gboolean aot) { guint8 *start, *code; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; int stack_size; start = code = mono_global_codeman_reserve (128); /* FIXME no unwind before we push ip */ /* Caller ip */ x86_push_reg (code, X86_ECX); mono_add_unwind_op_def_cfa (unwind_ops, code, start, X86_ESP, 4); mono_add_unwind_op_offset (unwind_ops, code, start, X86_NREG, -4); /* Fix the alignment to be what apple expects */ stack_size = 12; 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); /* Arg1 */ x86_mov_membase_reg (code, X86_ESP, 0, X86_EAX, 4); /* Branch to target */ x86_call_reg (code, X86_EDX); g_assert ((code - start) < 128); if (info) *info = mono_tramp_info_create ("x86_signal_exception_trampoline", 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); if (mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); return start; }
/* * mono_arch_get_restore_context: * * Returns a pointer to a method which restores a previously saved MonoContext. * The first argument in a0 is the pointer to the MonoContext. */ gpointer mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot) { int i; guint8 *code; static guint8 start [512]; static int inited = 0; guint32 iregs_to_restore; g_assert (!aot); if (info) *info = NULL; if (inited) return start; inited = 1; code = start; mips_move (code, mips_at, mips_a0); iregs_to_restore = (MONO_ARCH_CALLEE_SAVED_REGS \ | (1 << mips_sp) | (1 << mips_ra)); for (i = 0; i < MONO_SAVED_GREGS; ++i) { //if (iregs_to_restore & (1 << i)) { if (i != mips_zero && i != mips_at) { MIPS_LW (code, i, mips_at, G_STRUCT_OFFSET (MonoContext, sc_regs[i])); } } /* Get the address to return to */ mips_lw (code, mips_t9, mips_at, G_STRUCT_OFFSET (MonoContext, sc_pc)); /* jump to the saved IP */ mips_jr (code, mips_t9); mips_nop (code); /* never reached */ mips_break (code, 0xff); g_assert ((code - start) < sizeof(start)); mono_arch_flush_icache (start, code - start); if (mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); return start; }
/* * mono_win32_get_handle_stackoverflow (void): * * Returns a pointer to a method which restores the current context stack * and calls handle_exceptions, when done restores the original stack. */ static gpointer mono_win32_get_handle_stackoverflow (void) { static guint8 *start = NULL; guint8 *code; if (start) return start; /* restore_contect (void *sigctx) */ start = code = mono_global_codeman_reserve (128); /* load context into ebx */ x86_mov_reg_membase (code, X86_EBX, X86_ESP, 4, 4); /* move current stack into edi for later restore */ x86_mov_reg_reg (code, X86_EDI, X86_ESP, 4); /* use the new freed stack from sigcontext */ /* XXX replace usage of struct sigcontext with MonoContext so we can use MONO_STRUCT_OFFSET */ x86_mov_reg_membase (code, X86_ESP, X86_EBX, G_STRUCT_OFFSET (struct sigcontext, esp), 4); /* get the current domain */ x86_call_code (code, mono_domain_get); /* get stack overflow exception from domain object */ x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoDomain, stack_overflow_ex), 4); /* call mono_arch_handle_exception (sctx, stack_overflow_exception_obj) */ x86_push_reg (code, X86_EAX); x86_push_reg (code, X86_EBX); x86_call_code (code, mono_arch_handle_exception); /* restore the SEH handler stack */ x86_mov_reg_reg (code, X86_ESP, X86_EDI, 4); /* return */ x86_ret (code); mono_arch_flush_icache (start, code - start); if (mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); return start; }
gpointer mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info) { guint8 *buf, *code; code = buf = mono_global_codeman_reserve (16); s390_br (code, s390_r14); mono_arch_flush_icache (buf, code - buf); mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL); *info = mono_tramp_info_create ("nullified_class_init_trampoline", buf, code - buf, NULL, NULL); return (buf); }
gpointer mono_arch_get_unbox_trampoline (MonoMethod *method, gpointer addr) { guint8 *code, *start; int this_pos = s390_r2; MonoDomain *domain = mono_domain_get (); start = code = mono_domain_code_reserve (domain, 28); S390_SET (code, s390_r1, addr); s390_aghi (code, this_pos, sizeof(MonoObject)); s390_br (code, s390_r1); g_assert ((code - start) <= 28); mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE, method); return start; }
/* * mono_arch_get_gsharedvt_arg_trampoline: * * See tramp-x86.c for documentation. */ gpointer mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr) { guint8 *code, *start; int buf_len; buf_len = 32; start = code = mono_domain_code_reserve (domain, buf_len); amd64_mov_reg_imm (code, AMD64_RAX, arg); amd64_jump_code (code, addr); g_assert ((code - start) < buf_len); mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL); mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), domain); return start; }
gpointer mono_arch_create_monitor_exit_trampoline (MonoTrampInfo **info, gboolean aot) { guint8 *tramp, *code, *buf; gint16 *jump_obj_null, *jump_have_waiters, *jump_sync_null, *jump_not_owned, *jump_cs_failed, *jump_next, *jump_sync_thin_hash = NULL; int tramp_size, status_offset, nest_offset; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; int obj_reg = s390_r2, sync_reg = s390_r3, status_reg = s390_r4; g_assert (obj_reg == MONO_ARCH_MONITOR_OBJECT_REG); mono_monitor_threads_sync_members_offset (&status_offset, &nest_offset); g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (status_offset) == sizeof (guint32)); g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32)); status_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (status_offset); nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset); tramp_size = 160; code = buf = mono_global_codeman_reserve (tramp_size); unwind_ops = mono_arch_get_cie_program (); if (mono_thread_get_tls_offset () != -1) { /* MonoObject* obj is in obj_reg */ /* is obj null? */ s390_ltgr (code, obj_reg, obj_reg); /* if yes, jump to actual trampoline */ s390_jz (code, 0); CODEPTR(code, jump_obj_null); /* load obj->synchronization to RCX */ s390_lg (code, sync_reg, 0, obj_reg, MONO_STRUCT_OFFSET (MonoObject, synchronisation)); if (mono_gc_is_moving ()) { /*if bit zero is set it's a thin hash*/ s390_tmll (code, sync_reg, 1); s390_jo (code, 0); CODEPTR(code, jump_sync_thin_hash); /* Clear bits used by the gc */ s390_nill (code, sync_reg, ~0x3); } /* is synchronization null? */ s390_ltgr (code, sync_reg, sync_reg); /* if yes, jump to actual trampoline */ s390_jz (code, 0); CODEPTR(code, jump_sync_null); /* next case: synchronization is not null */ /* load MonoInternalThread* into r5 */ s390_ear (code, s390_r5, 0); s390_sllg(code, s390_r5, s390_r5, 0, 32); s390_ear (code, s390_r5, 1); /* load TID into r1 */ s390_lg (code, s390_r1, 0, s390_r5, mono_thread_get_tls_offset ()); s390_lgf (code, s390_r1, 0, s390_r1, MONO_STRUCT_OFFSET (MonoInternalThread, small_id)); /* is synchronization->owner == TID */ s390_lgf (code, status_reg, 0, sync_reg, status_offset); s390_xr (code, s390_r1, status_reg); s390_tmlh (code, s390_r1, OWNER_MASK); /* if not, jump to actual trampoline */ s390_jno (code, 0); CODEPTR(code, jump_not_owned); /* next case: synchronization->owner == TID */ /* is synchronization->nest == 1 */ s390_lgf (code, s390_r0, 0, sync_reg, nest_offset); s390_chi (code, s390_r0, 1); /* if not, jump to next case */ s390_jne (code, 0); CODEPTR(code, jump_next); /* if yes, is synchronization->entry_count greater than zero */ s390_cfi (code, status_reg, ENTRY_COUNT_WAITERS); /* if not, jump to actual trampoline */ s390_jnz (code, 0); CODEPTR(code, jump_have_waiters); /* if yes, try to set synchronization->owner to null and return */ /* old status in s390_r0 */ s390_lgfr (code, s390_r0, status_reg); /* form new status */ s390_nilf (code, status_reg, ENTRY_COUNT_MASK); /* compare and exchange */ s390_cs (code, s390_r0, status_reg, sync_reg, status_offset); /* if not successful, jump to actual trampoline */ s390_jnz (code, 0); CODEPTR(code, jump_cs_failed); s390_br (code, s390_r14); /* next case: synchronization->nest is not 1 */ PTRSLOT (code, jump_next); /* decrease synchronization->nest and return */ s390_lgf (code, s390_r0, 0, sync_reg, nest_offset); s390_ahi (code, s390_r0, -1); s390_st (code, s390_r0, 0, sync_reg, nest_offset); s390_br (code, s390_r14); PTRSLOT (code, jump_obj_null); if (jump_sync_thin_hash) PTRSLOT (code, jump_sync_thin_hash); PTRSLOT (code, jump_have_waiters); PTRSLOT (code, jump_not_owned); PTRSLOT (code, jump_cs_failed); PTRSLOT (code, jump_sync_null); } /* jump to the actual trampoline */ tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_MONITOR_EXIT, mono_get_root_domain (), NULL); S390_SET (code, s390_r1, tramp); s390_br (code, s390_r1); mono_arch_flush_icache (code, code - buf); mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_MONITOR, NULL); g_assert (code - buf <= tramp_size); if (info) *info = mono_tramp_info_create ("monitor_exit_trampoline", buf, code - buf, ji, unwind_ops); return buf; }
gpointer mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot) { guint8 *code; guint8* start; int i, size, offset, gregs_offset, fregs_offset, ctx_offset, num_fregs, frame_size; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; guint8 *labels [16]; size = 512; start = code = mono_global_codeman_reserve (size); /* Compute stack frame size and offsets */ offset = 0; /* frame block */ offset += 2 * 8; /* gregs */ gregs_offset = offset; offset += 32 * 8; /* fregs */ num_fregs = 8; fregs_offset = offset; offset += num_fregs * 8; ctx_offset = offset; ctx_offset += 8; frame_size = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT); /* * We are being called from C code, ctx is in r0, the address to call is in r1. * We need to save state, restore ctx, make the call, then restore the previous state, * returning the value returned by the call. */ /* Setup a frame */ arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -frame_size); arm_movspx (code, ARMREG_FP, ARMREG_SP); /* Save ctx */ arm_strx (code, ARMREG_R0, ARMREG_FP, ctx_offset); /* Save gregs */ code = mono_arm_emit_store_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS | (1 << ARMREG_FP), ARMREG_FP, gregs_offset); /* Save fregs */ for (i = 0; i < num_fregs; ++i) arm_strfpx (code, ARMREG_D8 + i, ARMREG_FP, fregs_offset + (i * 8)); /* Load regs from ctx */ code = mono_arm_emit_load_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, regs)); /* Load fregs */ arm_ldrx (code, ARMREG_IP0, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, has_fregs)); labels [0] = code; arm_cbzx (code, ARMREG_IP0, 0); for (i = 0; i < num_fregs; ++i) arm_ldrfpx (code, ARMREG_D8 + i, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, fregs) + (i * 8)); mono_arm_patch (labels [0], code, MONO_R_ARM64_CBZ); /* Load fp */ arm_ldrx (code, ARMREG_FP, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_FP * 8)); /* Make the call */ arm_blrx (code, ARMREG_R1); /* For filters, the result is in R0 */ /* Restore fp */ arm_ldrx (code, ARMREG_FP, ARMREG_SP, gregs_offset + (ARMREG_FP * 8)); /* Load ctx */ arm_ldrx (code, ARMREG_IP0, ARMREG_FP, ctx_offset); /* Save registers back to ctx */ /* This isn't strictly neccessary since we don't allocate variables used in eh clauses to registers */ code = mono_arm_emit_store_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoContext, regs)); /* Restore regs */ code = mono_arm_emit_load_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_FP, gregs_offset); /* Restore fregs */ for (i = 0; i < num_fregs; ++i) arm_ldrfpx (code, ARMREG_D8 + i, ARMREG_FP, fregs_offset + (i * 8)); /* Destroy frame */ code = mono_arm_emit_destroy_frame (code, frame_size, (1 << ARMREG_IP0)); arm_retx (code, ARMREG_LR); g_assert ((code - start) < size); mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (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; }
/* * 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_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); return start; }
static gpointer get_throw_trampoline (int size, gboolean corlib, gboolean rethrow, gboolean llvm, gboolean resume_unwind, const char *tramp_name, MonoTrampInfo **info, gboolean aot) { guint8 *start, *code; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; int i, offset, gregs_offset, fregs_offset, frame_size, num_fregs; code = start = mono_global_codeman_reserve (size); /* We are being called by JITted code, the exception object/type token is in R0 */ /* Compute stack frame size and offsets */ offset = 0; /* frame block */ offset += 2 * 8; /* gregs */ gregs_offset = offset; offset += 32 * 8; /* fregs */ num_fregs = 8; fregs_offset = offset; offset += num_fregs * 8; frame_size = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT); /* Setup a frame */ arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -frame_size); arm_movspx (code, ARMREG_FP, ARMREG_SP); /* Save gregs */ code = mono_arm_emit_store_regarray (code, 0xffffffff, ARMREG_FP, gregs_offset); if (corlib && !llvm) /* The real LR is in R1 */ arm_strx (code, ARMREG_R1, ARMREG_FP, gregs_offset + (ARMREG_LR * 8)); /* Save fp/sp */ arm_ldrx (code, ARMREG_IP0, ARMREG_FP, 0); arm_strx (code, ARMREG_IP0, ARMREG_FP, gregs_offset + (ARMREG_FP * 8)); arm_addx_imm (code, ARMREG_IP0, ARMREG_FP, frame_size); arm_strx (code, ARMREG_IP0, ARMREG_FP, gregs_offset + (ARMREG_SP * 8)); /* Save fregs */ for (i = 0; i < num_fregs; ++i) arm_strfpx (code, ARMREG_D8 + i, ARMREG_FP, fregs_offset + (i * 8)); /* Call the C trampoline function */ /* Arg1 = exception object/type token */ arm_movx (code, ARMREG_R0, ARMREG_R0); /* Arg2 = caller ip */ if (corlib) { if (llvm) arm_ldrx (code, ARMREG_R1, ARMREG_FP, gregs_offset + (ARMREG_LR * 8)); else arm_movx (code, ARMREG_R1, ARMREG_R1); } else { arm_ldrx (code, ARMREG_R1, ARMREG_FP, 8); } /* Arg 3 = gregs */ arm_addx_imm (code, ARMREG_R2, ARMREG_FP, gregs_offset); /* Arg 4 = fregs */ arm_addx_imm (code, ARMREG_R3, ARMREG_FP, fregs_offset); /* Arg 5 = corlib */ arm_movzx (code, ARMREG_R4, corlib ? 1 : 0, 0); /* Arg 6 = rethrow */ arm_movzx (code, ARMREG_R5, rethrow ? 1 : 0, 0); /* Call the function */ if (aot) { const char *icall_name; if (resume_unwind) icall_name = "mono_arm_resume_unwind"; else icall_name = "mono_arm_throw_exception"; code = mono_arm_emit_aotconst (&ji, code, start, ARMREG_LR, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name); } else { gpointer icall_func; if (resume_unwind) icall_func = mono_arm_resume_unwind; else icall_func = mono_arm_throw_exception; code = mono_arm_emit_imm64 (code, ARMREG_LR, (guint64)icall_func); } arm_blrx (code, ARMREG_LR); /* This shouldn't return */ arm_brk (code, 0x0); g_assert ((code - start) < size); mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); if (info) *info = mono_tramp_info_create (tramp_name, start, code - start, ji, unwind_ops); 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_code_buffer_new (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, 4); 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, 4); 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_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); g_assert ((code - start) < kMaxCodeSize); return start; }
/* * get_throw_trampoline: * * Generate a call to mono_amd64_throw_exception/ * mono_amd64_throw_corlib_exception. */ static gpointer get_throw_trampoline (MonoTrampInfo **info, gboolean rethrow, gboolean corlib, gboolean llvm_abs, gboolean resume_unwind, const char *tramp_name, gboolean aot) { guint8* start; guint8 *code; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; int i, stack_size, arg_offsets [16], ctx_offset, regs_offset, dummy_stack_space; const guint kMaxCodeSize = NACL_SIZE (256, 512); #ifdef TARGET_WIN32 dummy_stack_space = 6 * sizeof(mgreg_t); /* Windows expects stack space allocated for all 6 dummy args. */ #else dummy_stack_space = 0; #endif start = code = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize); /* The stack is unaligned on entry */ stack_size = ALIGN_TO (sizeof (MonoContext) + 64 + dummy_stack_space, MONO_ARCH_FRAME_ALIGNMENT) + 8; code = start; if (info) unwind_ops = mono_arch_get_cie_program (); /* Alloc frame */ amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, stack_size); if (info) mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, stack_size + 8); /* * To hide linux/windows calling convention differences, we pass all arguments on * the stack by passing 6 dummy values in registers. */ arg_offsets [0] = dummy_stack_space + 0; arg_offsets [1] = dummy_stack_space + sizeof(mgreg_t); arg_offsets [2] = dummy_stack_space + sizeof(mgreg_t) * 2; ctx_offset = dummy_stack_space + sizeof(mgreg_t) * 4; regs_offset = ctx_offset + MONO_STRUCT_OFFSET (MonoContext, gregs); /* Save registers */ for (i = 0; i < AMD64_NREG; ++i) if (i != AMD64_RSP) amd64_mov_membase_reg (code, AMD64_RSP, regs_offset + (i * sizeof(mgreg_t)), i, sizeof(mgreg_t)); /* Save RSP */ amd64_lea_membase (code, AMD64_RAX, AMD64_RSP, stack_size + sizeof(mgreg_t)); amd64_mov_membase_reg (code, AMD64_RSP, regs_offset + (AMD64_RSP * sizeof(mgreg_t)), X86_EAX, sizeof(mgreg_t)); /* Save IP */ if (llvm_abs) amd64_alu_reg_reg (code, X86_XOR, AMD64_RAX, AMD64_RAX); else amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RSP, stack_size, sizeof(mgreg_t)); amd64_mov_membase_reg (code, AMD64_RSP, regs_offset + (AMD64_RIP * sizeof(mgreg_t)), AMD64_RAX, sizeof(mgreg_t)); /* Set arg1 == ctx */ amd64_lea_membase (code, AMD64_RAX, AMD64_RSP, ctx_offset); amd64_mov_membase_reg (code, AMD64_RSP, arg_offsets [0], AMD64_RAX, sizeof(mgreg_t)); /* Set arg2 == exc/ex_token_index */ if (resume_unwind) amd64_mov_membase_imm (code, AMD64_RSP, arg_offsets [1], 0, sizeof(mgreg_t)); else amd64_mov_membase_reg (code, AMD64_RSP, arg_offsets [1], AMD64_ARG_REG1, sizeof(mgreg_t)); /* Set arg3 == rethrow/pc offset */ if (resume_unwind) { amd64_mov_membase_imm (code, AMD64_RSP, arg_offsets [2], 0, sizeof(mgreg_t)); } else if (corlib) { amd64_mov_membase_reg (code, AMD64_RSP, arg_offsets [2], AMD64_ARG_REG2, sizeof(mgreg_t)); if (llvm_abs) /* * The caller is LLVM code which passes the absolute address not a pc offset, * so compensate by passing 0 as 'rip' and passing the negated abs address as * the pc offset. */ amd64_neg_membase (code, AMD64_RSP, arg_offsets [2]); } else { amd64_mov_membase_imm (code, AMD64_RSP, arg_offsets [2], rethrow, sizeof(mgreg_t)); } if (aot) { const char *icall_name; if (resume_unwind) icall_name = "mono_amd64_resume_unwind"; else if (corlib) icall_name = "mono_amd64_throw_corlib_exception"; else icall_name = "mono_amd64_throw_exception"; ji = mono_patch_info_list_prepend (ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name); amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8); } else { amd64_mov_reg_imm (code, AMD64_R11, resume_unwind ? ((gpointer)mono_amd64_resume_unwind) : (corlib ? (gpointer)mono_amd64_throw_corlib_exception : (gpointer)mono_amd64_throw_exception)); } amd64_call_reg (code, AMD64_R11); amd64_breakpoint (code); mono_arch_flush_icache (start, code - start); g_assert ((code - start) < kMaxCodeSize); nacl_global_codeman_validate(&start, kMaxCodeSize, &code); mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); if (info) *info = mono_tramp_info_create (tramp_name, 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; int i, gregs_offset; guint8 *code; guint32 pos; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; const guint kMaxCodeSize = NACL_SIZE (128, 256); start = code = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize); /* call_filter (MonoContext *ctx, unsigned long eip) */ code = start; /* Alloc new frame */ amd64_push_reg (code, AMD64_RBP); amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8); /* Save callee saved regs */ pos = 0; for (i = 0; i < AMD64_NREG; ++i) if (AMD64_IS_CALLEE_SAVED_REG (i)) { amd64_push_reg (code, i); pos += 8; } /* Save EBP */ pos += 8; amd64_push_reg (code, AMD64_RBP); /* Make stack misaligned, the call will make it aligned again */ if (! (pos & 8)) amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8); gregs_offset = MONO_STRUCT_OFFSET (MonoContext, gregs); /* set new EBP */ amd64_mov_reg_membase (code, AMD64_RBP, AMD64_ARG_REG1, gregs_offset + (AMD64_RBP * 8), 8); /* load callee saved regs */ for (i = 0; i < AMD64_NREG; ++i) { #if defined(__native_client_codegen__) if (i == AMD64_R15) continue; #endif if (AMD64_IS_CALLEE_SAVED_REG (i) && i != AMD64_RBP) amd64_mov_reg_membase (code, i, AMD64_ARG_REG1, gregs_offset + (i * 8), 8); } /* load exc register */ amd64_mov_reg_membase (code, AMD64_RAX, AMD64_ARG_REG1, gregs_offset + (AMD64_RAX * 8), 8); /* call the handler */ amd64_call_reg (code, AMD64_ARG_REG2); if (! (pos & 8)) amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8); /* restore RBP */ amd64_pop_reg (code, AMD64_RBP); /* Restore callee saved regs */ for (i = AMD64_NREG; i >= 0; --i) if (AMD64_IS_CALLEE_SAVED_REG (i)) amd64_pop_reg (code, i); amd64_leave (code); amd64_ret (code); g_assert ((code - start) < kMaxCodeSize); nacl_global_codeman_validate(&start, kMaxCodeSize, &code); mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (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; }
/** * 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) { 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); if (mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) mono_profiler_code_buffer_new (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). * * 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); if (mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) mono_profiler_code_buffer_new (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) (guint32 ex_token, gpointer ip) * */ static gpointer mono_arch_get_throw_exception_generic (int size, MonoTrampInfo **info, int corlib, gboolean rethrow, gboolean aot) { 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 (gpointer), 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_SAVED_FREGS; ppc_addi (code, ppc_r7, ppc_sp, pos); /* pointer to the saved int regs */ pos -= sizeof (gpointer) * MONO_SAVED_GREGS; ppc_addi (code, ppc_r6, ppc_sp, pos); ppc_li (code, ppc_r8, rethrow); 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 (gpointer), 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_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); if (info) *info = mono_tramp_info_create (corlib ? "throw_corlib_exception" : (rethrow ? "rethrow_exception" : "throw_exception"), start, code - start, ji, unwind_ops); return start; }