/* * Called when a thread receives an async exception while executing unmanaged code. * Instead of checking for this exception in the managed-to-native wrapper, we hijack * the return address on the stack to point to a helper routine which throws the * exception. */ void mono_arch_notify_pending_exc (MonoThreadInfo *info) { MonoLMF *lmf = mono_get_lmf (); if (!info) { lmf = mono_get_lmf (); } else { g_assert (info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].valid); lmf = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].unwind_data [MONO_UNWIND_DATA_LMF]; } if (!lmf) /* Not yet started */ return; if (lmf->rsp == 0) /* Initial LMF */ return; if ((guint64)lmf->previous_lmf & 1) /* Already hijacked or trampoline LMF entry */ return; /* lmf->rsp is set just before making the call which transitions to unmanaged code */ lmf->rip = *(guint64*)(lmf->rsp - 8); /* Signal that lmf->rip is set */ lmf->previous_lmf = (gpointer)((guint64)lmf->previous_lmf | 1); *(gpointer*)(lmf->rsp - 8) = throw_pending_exception; }
guint64 mono_amd64_get_original_ip (void) { MonoLMF *lmf = mono_get_lmf (); g_assert (lmf); /* Reset the change to previous_lmf */ lmf->previous_lmf = (gpointer)((guint64)lmf->previous_lmf & ~1); return lmf->rip; }
static int continuation_store (MonoContinuation *cont, int state, MonoException **e) { MonoLMF *lmf = mono_get_lmf (); gsize num_bytes; if (!cont->domain) { *e = mono_get_exception_argument ("cont", "Continuation not initialized"); return 0; } if (cont->domain != mono_domain_get () || cont->thread_id != GetCurrentThreadId ()) { *e = mono_get_exception_argument ("cont", "Continuation from another thread or domain"); return 0; } cont->lmf = lmf; cont->return_ip = __builtin_return_address (0); cont->return_sp = __builtin_frame_address (0); num_bytes = (char*)cont->top_sp - (char*)cont->return_sp; /*g_print ("store: %d bytes, sp: %p, ip: %p, lmf: %p\n", num_bytes, cont->return_sp, cont->return_ip, lmf);*/ if (cont->saved_stack && num_bytes <= cont->stack_alloc_size) { /* clear to avoid GC retention */ if (num_bytes < cont->stack_used_size) { memset ((char*)cont->saved_stack + num_bytes, 0, cont->stack_used_size - num_bytes); } cont->stack_used_size = num_bytes; } else { tasklets_lock (); internal_init (); if (cont->saved_stack) { mono_g_hash_table_remove (keepalive_stacks, cont->saved_stack); mono_gc_free_fixed (cont->saved_stack); } cont->stack_used_size = num_bytes; cont->stack_alloc_size = num_bytes * 1.1; cont->saved_stack = mono_gc_alloc_fixed (cont->stack_alloc_size, NULL); mono_g_hash_table_insert (keepalive_stacks, cont->saved_stack, cont->saved_stack); tasklets_unlock (); } memcpy (cont->saved_stack, cont->return_sp, num_bytes); return state; }
static int continuation_store (MonoContinuation *cont, int state, MonoException **e) { MonoLMF *lmf = mono_get_lmf (); gsize num_bytes; if (!cont->domain) { *e = mono_get_exception_argument ("cont", "Continuation not initialized"); return 0; } if (cont->domain != mono_domain_get () || !mono_native_thread_id_equals (cont->thread_id, mono_native_thread_id_get ())) { *e = mono_get_exception_argument ("cont", "Continuation from another thread or domain"); return 0; } cont->lmf = lmf; cont->return_ip = __builtin_extract_return_addr (__builtin_return_address (0)); cont->return_sp = __builtin_frame_address (0); num_bytes = (char*)cont->top_sp - (char*)cont->return_sp; /*g_print ("store: %d bytes, sp: %p, ip: %p, lmf: %p\n", num_bytes, cont->return_sp, cont->return_ip, lmf);*/ if (cont->saved_stack && num_bytes <= cont->stack_alloc_size) { /* clear to avoid GC retention */ if (num_bytes < cont->stack_used_size) { memset ((char*)cont->saved_stack + num_bytes, 0, cont->stack_used_size - num_bytes); } cont->stack_used_size = num_bytes; } else { tasklets_lock (); internal_init (); if (cont->saved_stack) mono_gc_free_fixed (cont->saved_stack); cont->stack_used_size = num_bytes; cont->stack_alloc_size = num_bytes * 1.1; cont->saved_stack = mono_gc_alloc_fixed (cont->stack_alloc_size, NULL, MONO_ROOT_SOURCE_THREADING, "saved tasklet stack"); tasklets_unlock (); } memcpy (cont->saved_stack, cont->return_sp, num_bytes); return state; }
static MonoException* continuation_mark_frame (MonoContinuation *cont) { MonoJitTlsData *jit_tls; MonoLMF *lmf; MonoContext ctx, new_ctx; MonoJitInfo *ji, rji; int endloop = FALSE; if (cont->domain) return mono_get_exception_argument ("cont", "Already marked"); jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id); lmf = mono_get_lmf(); cont->domain = mono_domain_get (); cont->thread_id = mono_native_thread_id_get (); /* get to the frame that called Mark () */ memset (&rji, 0, sizeof (rji)); memset (&ctx, 0, sizeof (ctx)); do { ji = mono_find_jit_info (cont->domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, NULL, NULL); if (!ji || ji == (gpointer)-1) { return mono_get_exception_not_supported ("Invalid stack frame"); } ctx = new_ctx; if (endloop) break; if (!ji->is_trampoline && strcmp (jinfo_get_method (ji)->name, "Mark") == 0) endloop = TRUE; } while (1); cont->top_sp = MONO_CONTEXT_GET_SP (&ctx); /*g_print ("method: %s, sp: %p\n", jinfo_get_method (ji)->name, cont->top_sp);*/ return NULL; }
/* * Called when a thread receives an async exception while executing unmanaged code. * Instead of checking for this exception in the managed-to-native wrapper, we hijack * the return address on the stack to point to a helper routine which throws the * exception. */ void mono_arch_notify_pending_exc (void) { MonoLMF *lmf = mono_get_lmf (); if (!lmf) /* Not yet started */ return; if (lmf->rsp == 0) /* Initial LMF */ return; if ((guint64)lmf->previous_lmf & 1) /* Already hijacked or trampoline LMF entry */ return; /* lmf->rsp is set just before making the call which transitions to unmanaged code */ lmf->rip = *(guint64*)(lmf->rsp - 8); /* Signal that lmf->rip is set */ lmf->previous_lmf = (gpointer)((guint64)lmf->previous_lmf | 1); *(gpointer*)(lmf->rsp - 8) = throw_pending_exception; }