static void mono_native_state_add_ctx (MonoStateWriter *writer, MonoContext *ctx) { // Context mono_state_writer_indent (writer); mono_state_writer_object_key (writer, "ctx"); mono_state_writer_printf(writer, "{\n"); writer->indent++; assert_has_space (writer); mono_state_writer_indent (writer); mono_state_writer_object_key (writer, "IP"); mono_state_writer_printf(writer, "\"%p\",\n", (gpointer) MONO_CONTEXT_GET_IP (ctx)); assert_has_space (writer); mono_state_writer_indent (writer); mono_state_writer_object_key (writer, "SP"); mono_state_writer_printf(writer, "\"%p\",\n", (gpointer) MONO_CONTEXT_GET_SP (ctx)); assert_has_space (writer); mono_state_writer_indent (writer); mono_state_writer_object_key (writer, "BP"); mono_state_writer_printf(writer, "\"%p\"\n", (gpointer) MONO_CONTEXT_GET_BP (ctx)); writer->indent--; mono_state_writer_indent (writer); mono_state_writer_printf(writer, "}"); }
/* * mono_arch_setup_resume_sighandler_ctx: * * Setup CTX so execution continues at FUNC. */ void mono_arch_setup_resume_sighandler_ctx (MonoContext *ctx, gpointer func) { MONO_CONTEXT_SET_IP (ctx,func); if ((mgreg_t)MONO_CONTEXT_GET_IP (ctx) & 1) /* Transition to thumb */ ctx->cpsr |= (1 << 5); else /* Transition to ARM */ ctx->cpsr &= ~(1 << 5); }
static void dump_memory_around_ip (void *ctx) { #ifdef MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX MonoContext mctx; mono_sigctx_to_monoctx (ctx, &mctx); gpointer native_ip = MONO_CONTEXT_GET_IP (&mctx); g_printerr ("Memory around native instruction pointer (%p):\n", native_ip); xxd_mem (((guint8 *) native_ip) - 0x10, 0x40); #endif }
/* FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard. */ static gboolean is_thread_in_critical_region (MonoThreadInfo *info) { MonoMethod *method; MonoJitInfo *ji = mono_jit_info_table_find ( info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], MONO_CONTEXT_GET_IP (&info->suspend_state.ctx)); if (!ji) return FALSE; method = ji->method; return mono_gc_is_critical_method (method); }
/* * handle_exception: * * Called by resuming from a signal handler. */ static void handle_signal_exception (gpointer obj, gboolean test_only) { MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); MonoContext ctx; static void (*restore_context) (MonoContext *); if (!restore_context) restore_context = mono_get_restore_context (); memcpy (&ctx, &jit_tls->ex_ctx, sizeof (MonoContext)); mono_handle_exception (&ctx, obj, MONO_CONTEXT_GET_IP (&ctx), test_only); restore_context (&ctx); }
static void altstack_handle_and_restore (MonoContext *ctx, MonoObject *obj, gboolean stack_ovf) { MonoContext mctx; MonoJitInfo *ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (ctx), NULL); if (!ji) mono_handle_native_sigsegv (SIGSEGV, NULL, NULL); mctx = *ctx; mono_handle_exception (&mctx, obj); if (stack_ovf) prepare_for_guard_pages (&mctx); mono_restore_context (&mctx); }
/* FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard. */ static gboolean is_thread_in_critical_region (MonoThreadInfo *info) { MonoMethod *method; MonoJitInfo *ji; gpointer stack_start; MonoThreadUnwindState *state; if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info))) return TRUE; /* Are we inside a system critical region? */ if (info->inside_critical_region) return TRUE; /* Are we inside a GC critical region? */ if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) { return TRUE; } /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */ state = mono_thread_info_get_suspend_state (info); if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN]) return FALSE; stack_start = MONO_CONTEXT_GET_SP (&state->ctx); /* altstack signal handler, sgen can't handle them, so we treat them as critical */ if (stack_start < info->stack_start_limit || stack_start >= info->stack_end) return TRUE; if (threads_callbacks.ip_in_critical_region) return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx)); ji = mono_jit_info_table_find ( (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx)); if (!ji) return FALSE; method = mono_jit_info_get_method (ji); return threads_callbacks.mono_method_is_critical (method); }
/* * handle_exception: * * Called by resuming from a signal handler. */ static void handle_signal_exception (gpointer obj) { MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id); MonoContext ctx; static void (*restore_context) (MonoContext *); if (!restore_context) restore_context = mono_get_restore_context (); memcpy (&ctx, &jit_tls->ex_ctx, sizeof (MonoContext)); if (mono_debugger_handle_exception (&ctx, (MonoObject *)obj)) return; mono_handle_exception (&ctx, obj, MONO_CONTEXT_GET_IP (&ctx), FALSE); restore_context (&ctx); }
/* FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard. */ static gboolean is_thread_in_critical_region (MonoThreadInfo *info) { MonoMethod *method; MonoJitInfo *ji; if (info->inside_critical_region) return TRUE; ji = mono_jit_info_table_find ( info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], MONO_CONTEXT_GET_IP (&info->suspend_state.ctx)); if (!ji) return FALSE; method = mono_jit_info_get_method (ji); return threads_callbacks.mono_method_is_critical (method); }
/* FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard. */ static gboolean is_thread_in_critical_region (MonoThreadInfo *info) { MonoMethod *method; MonoJitInfo *ji; if (info->inside_critical_region) return TRUE; /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */ if (!info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN]) return FALSE; ji = mono_jit_info_table_find ( info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], MONO_CONTEXT_GET_IP (&info->suspend_state.ctx)); if (!ji) return FALSE; method = mono_jit_info_get_method (ji); return threads_callbacks.mono_method_is_critical (method); }
/* * mono_arch_unwind_frame: * * See exceptions-amd64.c for docs. */ gboolean mono_arch_unwind_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *ji, MonoContext *ctx, MonoContext *new_ctx, MonoLMF **lmf, mgreg_t **save_locations, StackFrameInfo *frame) { gpointer ip = MONO_CONTEXT_GET_IP (ctx); memset (frame, 0, sizeof (StackFrameInfo)); frame->ji = ji; *new_ctx = *ctx; if (ji != NULL) { gssize regs [MONO_MAX_IREGS + 1]; guint8 *cfa; guint32 unwind_info_len; guint8 *unwind_info; if (ji->is_trampoline) frame->type = FRAME_TYPE_TRAMPOLINE; else frame->type = FRAME_TYPE_MANAGED; unwind_info = mono_jinfo_get_unwind_info (ji, &unwind_info_len); regs [X86_EAX] = new_ctx->eax; regs [X86_EBX] = new_ctx->ebx; regs [X86_ECX] = new_ctx->ecx; regs [X86_EDX] = new_ctx->edx; regs [X86_ESP] = new_ctx->esp; regs [X86_EBP] = new_ctx->ebp; regs [X86_ESI] = new_ctx->esi; regs [X86_EDI] = new_ctx->edi; regs [X86_NREG] = new_ctx->eip; mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, (guint8*)ji->code_start + ji->code_size, ip, NULL, regs, MONO_MAX_IREGS + 1, save_locations, MONO_MAX_IREGS, &cfa); new_ctx->eax = regs [X86_EAX]; new_ctx->ebx = regs [X86_EBX]; new_ctx->ecx = regs [X86_ECX]; new_ctx->edx = regs [X86_EDX]; new_ctx->esp = regs [X86_ESP]; new_ctx->ebp = regs [X86_EBP]; new_ctx->esi = regs [X86_ESI]; new_ctx->edi = regs [X86_EDI]; new_ctx->eip = regs [X86_NREG]; /* The CFA becomes the new SP value */ new_ctx->esp = (gssize)cfa; /* Adjust IP */ new_ctx->eip --; return TRUE; } else if (*lmf) { if (((guint64)(*lmf)->previous_lmf) & 2) { /* * This LMF entry is created by the soft debug code to mark transitions to * managed code done during invokes. */ MonoLMFExt *ext = (MonoLMFExt*)(*lmf); g_assert (ext->debugger_invoke); memcpy (new_ctx, &ext->ctx, sizeof (MonoContext)); *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); frame->type = FRAME_TYPE_DEBUGGER_INVOKE; return TRUE; } if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL))) { frame->ji = ji; } else { if (!(*lmf)->method) return FALSE; frame->method = (*lmf)->method; } new_ctx->esi = (*lmf)->esi; new_ctx->edi = (*lmf)->edi; new_ctx->ebx = (*lmf)->ebx; new_ctx->ebp = (*lmf)->ebp; new_ctx->eip = (*lmf)->eip; /* Adjust IP */ new_ctx->eip --; frame->type = FRAME_TYPE_MANAGED_TO_NATIVE; /* Check if we are in a trampoline LMF frame */ if ((guint32)((*lmf)->previous_lmf) & 1) { /* lmf->esp is set by the trampoline code */ new_ctx->esp = (*lmf)->esp; } else /* the lmf is always stored on the stack, so the following * expression points to a stack location which can be used as ESP */ new_ctx->esp = (unsigned long)&((*lmf)->eip); *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); return TRUE; } return FALSE; }
/* * mono_arch_unwind_frame: * * This function is used to gather information from @ctx, and store it in @frame_info. * It unwinds one stack frame, and stores the resulting context into @new_ctx. @lmf * is modified if needed. * Returns TRUE on success, FALSE otherwise. */ gboolean mono_arch_unwind_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *ji, MonoContext *ctx, MonoContext *new_ctx, MonoLMF **lmf, mgreg_t **save_locations, StackFrameInfo *frame) { gpointer ip = MONO_CONTEXT_GET_IP (ctx); int i; memset (frame, 0, sizeof (StackFrameInfo)); frame->ji = ji; *new_ctx = *ctx; if (ji != NULL) { mgreg_t regs [MONO_MAX_IREGS + 1]; guint8 *cfa; guint32 unwind_info_len; guint8 *unwind_info; guint8 *epilog = NULL; if (ji->is_trampoline) frame->type = FRAME_TYPE_TRAMPOLINE; else frame->type = FRAME_TYPE_MANAGED; unwind_info = mono_jinfo_get_unwind_info (ji, &unwind_info_len); frame->unwind_info = unwind_info; frame->unwind_info_len = unwind_info_len; /* printf ("%s %p %p\n", ji->d.method->name, ji->code_start, ip); mono_print_unwind_info (unwind_info, unwind_info_len); */ /* LLVM compiled code doesn't have this info */ if (ji->has_arch_eh_info) epilog = (guint8*)ji->code_start + ji->code_size - mono_jinfo_get_epilog_size (ji); for (i = 0; i < AMD64_NREG; ++i) regs [i] = new_ctx->gregs [i]; mono_unwind_frame (unwind_info, unwind_info_len, (guint8 *)ji->code_start, (guint8*)ji->code_start + ji->code_size, (guint8 *)ip, epilog ? &epilog : NULL, regs, MONO_MAX_IREGS + 1, save_locations, MONO_MAX_IREGS, &cfa); for (i = 0; i < AMD64_NREG; ++i) new_ctx->gregs [i] = regs [i]; /* The CFA becomes the new SP value */ new_ctx->gregs [AMD64_RSP] = (mgreg_t)cfa; /* Adjust IP */ new_ctx->gregs [AMD64_RIP] --; return TRUE; } else if (*lmf) { guint64 rip; if (((guint64)(*lmf)->previous_lmf) & 2) { /* * This LMF entry is created by the soft debug code to mark transitions to * managed code done during invokes. */ MonoLMFExt *ext = (MonoLMFExt*)(*lmf); g_assert (ext->debugger_invoke); memcpy (new_ctx, &ext->ctx, sizeof (MonoContext)); *lmf = (MonoLMF *)(((guint64)(*lmf)->previous_lmf) & ~7); frame->type = FRAME_TYPE_DEBUGGER_INVOKE; return TRUE; } if (((guint64)(*lmf)->previous_lmf) & 4) { MonoLMFTramp *ext = (MonoLMFTramp*)(*lmf); rip = (guint64)MONO_CONTEXT_GET_IP (ext->ctx); } else if (((guint64)(*lmf)->previous_lmf) & 1) { /* This LMF has the rip field set */ rip = (*lmf)->rip; } else if ((*lmf)->rsp == 0) { /* Top LMF entry */ return FALSE; } else { /* * The rsp field is set just before the call which transitioned to native * code. Obtain the rip from the stack. */ rip = *(guint64*)((*lmf)->rsp - sizeof(mgreg_t)); } ji = mini_jit_info_table_find (domain, (char *)rip, NULL); /* * FIXME: ji == NULL can happen when a managed-to-native wrapper is interrupted * in the soft debugger suspend code, since (*lmf)->rsp no longer points to the * return address. */ //g_assert (ji); if (!ji) return FALSE; frame->ji = ji; frame->type = FRAME_TYPE_MANAGED_TO_NATIVE; if (((guint64)(*lmf)->previous_lmf) & 4) { MonoLMFTramp *ext = (MonoLMFTramp*)(*lmf); /* Trampoline frame */ for (i = 0; i < AMD64_NREG; ++i) new_ctx->gregs [i] = ext->ctx->gregs [i]; /* Adjust IP */ new_ctx->gregs [AMD64_RIP] --; } else { /* * The registers saved in the LMF will be restored using the normal unwind info, * when the wrapper frame is processed. */ /* Adjust IP */ rip --; new_ctx->gregs [AMD64_RIP] = rip; new_ctx->gregs [AMD64_RSP] = (*lmf)->rsp; new_ctx->gregs [AMD64_RBP] = (*lmf)->rbp; for (i = 0; i < AMD64_NREG; ++i) { if (AMD64_IS_CALLEE_SAVED_REG (i) && i != AMD64_RBP) new_ctx->gregs [i] = 0; } } *lmf = (MonoLMF *)(((guint64)(*lmf)->previous_lmf) & ~7); return TRUE; } return FALSE; }
/* * mono_arch_find_jit_info: * * See exceptions-amd64.c for docs. */ gboolean mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *ji, MonoContext *ctx, MonoContext *new_ctx, MonoLMF **lmf, mgreg_t **save_locations, StackFrameInfo *frame) { gpointer ip = MONO_CONTEXT_GET_IP (ctx); memset (frame, 0, sizeof (StackFrameInfo)); frame->ji = ji; *new_ctx = *ctx; if (ji != NULL) { gssize regs [MONO_MAX_IREGS + 1]; guint8 *cfa; guint32 unwind_info_len; guint8 *unwind_info; frame->type = FRAME_TYPE_MANAGED; if (ji->from_aot) unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len); else unwind_info = mono_get_cached_unwind_info (ji->used_regs, &unwind_info_len); regs [X86_EAX] = new_ctx->eax; regs [X86_EBX] = new_ctx->ebx; regs [X86_ECX] = new_ctx->ecx; regs [X86_EDX] = new_ctx->edx; regs [X86_ESP] = new_ctx->esp; regs [X86_EBP] = new_ctx->ebp; regs [X86_ESI] = new_ctx->esi; regs [X86_EDI] = new_ctx->edi; regs [X86_NREG] = new_ctx->eip; mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, (guint8*)ji->code_start + ji->code_size, ip, regs, MONO_MAX_IREGS + 1, save_locations, MONO_MAX_IREGS, &cfa); new_ctx->eax = regs [X86_EAX]; new_ctx->ebx = regs [X86_EBX]; new_ctx->ecx = regs [X86_ECX]; new_ctx->edx = regs [X86_EDX]; new_ctx->esp = regs [X86_ESP]; new_ctx->ebp = regs [X86_EBP]; new_ctx->esi = regs [X86_ESI]; new_ctx->edi = regs [X86_EDI]; new_ctx->eip = regs [X86_NREG]; /* The CFA becomes the new SP value */ new_ctx->esp = (gssize)cfa; /* Adjust IP */ new_ctx->eip --; if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) { /* remove any unused lmf */ *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); } /* Pop arguments off the stack */ /* * FIXME: LLVM doesn't push these, we can't use ji->from_llvm as it describes * the callee. */ #ifndef ENABLE_LLVM if (ji->has_arch_eh_info) new_ctx->esp += mono_jit_info_get_arch_eh_info (ji)->stack_size; #endif return TRUE; } else if (*lmf) { if (((guint64)(*lmf)->previous_lmf) & 2) { /* * This LMF entry is created by the soft debug code to mark transitions to * managed code done during invokes. */ MonoLMFExt *ext = (MonoLMFExt*)(*lmf); g_assert (ext->debugger_invoke); memcpy (new_ctx, &ext->ctx, sizeof (MonoContext)); *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); frame->type = FRAME_TYPE_DEBUGGER_INVOKE; return TRUE; } if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL))) { } else { if (!((guint32)((*lmf)->previous_lmf) & 1)) /* Top LMF entry */ return FALSE; g_assert_not_reached (); /* Trampoline lmf frame */ frame->method = (*lmf)->method; } new_ctx->esi = (*lmf)->esi; new_ctx->edi = (*lmf)->edi; new_ctx->ebx = (*lmf)->ebx; new_ctx->ebp = (*lmf)->ebp; new_ctx->eip = (*lmf)->eip; /* Adjust IP */ new_ctx->eip --; frame->ji = ji; frame->type = FRAME_TYPE_MANAGED_TO_NATIVE; /* Check if we are in a trampoline LMF frame */ if ((guint32)((*lmf)->previous_lmf) & 1) { /* lmf->esp is set by the trampoline code */ new_ctx->esp = (*lmf)->esp; /* Pop arguments off the stack */ /* FIXME: Handle the delegate case too ((*lmf)->method == NULL) */ /* FIXME: Handle the IMT/vtable case too */ #if 0 #ifndef ENABLE_LLVM if ((*lmf)->method) { MonoMethod *method = (*lmf)->method; MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (method)->param_count + 1); guint32 stack_to_pop = mono_arch_get_argument_info (NULL, mono_method_signature (method), mono_method_signature (method)->param_count, arg_info); new_ctx->esp += stack_to_pop; } #endif #endif } else /* the lmf is always stored on the stack, so the following * expression points to a stack location which can be used as ESP */ new_ctx->esp = (unsigned long)&((*lmf)->eip); *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); return TRUE; } return FALSE; }
/* * mono_arch_unwind_frame: * * See exceptions-amd64.c for docs. */ gboolean mono_arch_unwind_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *ji, MonoContext *ctx, MonoContext *new_ctx, MonoLMF **lmf, mgreg_t **save_locations, StackFrameInfo *frame) { gpointer ip = MONO_CONTEXT_GET_IP (ctx); MonoPPCStackFrame *sframe; memset (frame, 0, sizeof (StackFrameInfo)); frame->ji = ji; *new_ctx = *ctx; setup_context (new_ctx); if (ji != NULL) { int i; mgreg_t regs [ppc_lr + 1]; guint8 *cfa; guint32 unwind_info_len; guint8 *unwind_info; if (ji->is_trampoline) frame->type = FRAME_TYPE_TRAMPOLINE; else frame->type = FRAME_TYPE_MANAGED; unwind_info = mono_jinfo_get_unwind_info (ji, &unwind_info_len); sframe = (MonoPPCStackFrame*)MONO_CONTEXT_GET_SP (ctx); MONO_CONTEXT_SET_BP (new_ctx, sframe->sp); if (!ji->is_trampoline && jinfo_get_method (ji)->save_lmf) { /* sframe->sp points just past the end of the LMF */ guint8 *lmf_addr = (guint8*)sframe->sp - sizeof (MonoLMF); memcpy (&new_ctx->fregs [MONO_PPC_FIRST_SAVED_FREG], lmf_addr + G_STRUCT_OFFSET (MonoLMF, fregs), sizeof (double) * MONO_SAVED_FREGS); memcpy (&new_ctx->regs [MONO_PPC_FIRST_SAVED_GREG], lmf_addr + G_STRUCT_OFFSET (MonoLMF, iregs), sizeof (mgreg_t) * MONO_SAVED_GREGS); /* the calling IP is in the parent frame */ sframe = (MonoPPCStackFrame*)sframe->sp; /* we substract 4, so that the IP points into the call instruction */ MONO_CONTEXT_SET_IP (new_ctx, sframe->lr - 4); } else { regs [ppc_lr] = ctx->sc_ir; regs [ppc_sp] = ctx->sc_sp; for (i = MONO_PPC_FIRST_SAVED_GREG; i < MONO_MAX_IREGS; ++i) regs [i] = ctx->regs [i]; mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, (guint8*)ji->code_start + ji->code_size, ip, NULL, regs, ppc_lr + 1, save_locations, MONO_MAX_IREGS, &cfa); /* we substract 4, so that the IP points into the call instruction */ MONO_CONTEXT_SET_IP (new_ctx, regs [ppc_lr] - 4); MONO_CONTEXT_SET_BP (new_ctx, cfa); for (i = MONO_PPC_FIRST_SAVED_GREG; i < MONO_MAX_IREGS; ++i) new_ctx->regs [i] = regs [i]; } return TRUE; } else if (*lmf) { if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL))) { } else { if (!(*lmf)->method) return FALSE; /* Trampoline lmf frame */ frame->method = (*lmf)->method; } /*sframe = (MonoPPCStackFrame*)MONO_CONTEXT_GET_SP (ctx); MONO_CONTEXT_SET_BP (new_ctx, sframe->sp); MONO_CONTEXT_SET_IP (new_ctx, sframe->lr);*/ MONO_CONTEXT_SET_BP (new_ctx, (*lmf)->ebp); MONO_CONTEXT_SET_IP (new_ctx, (*lmf)->eip); memcpy (&new_ctx->regs [MONO_PPC_FIRST_SAVED_GREG], (*lmf)->iregs, sizeof (mgreg_t) * MONO_SAVED_GREGS); memcpy (&new_ctx->fregs [MONO_PPC_FIRST_SAVED_FREG], (*lmf)->fregs, sizeof (double) * MONO_SAVED_FREGS); frame->ji = ji; frame->type = FRAME_TYPE_MANAGED_TO_NATIVE; /* FIXME: what about trampoline LMF frames? see exceptions-x86.c */ *lmf = (*lmf)->previous_lmf; return TRUE; } return FALSE; }
/* mono_arch_find_jit_info: * * This function is used to gather information from @ctx. It returns the * MonoJitInfo of the corresponding function, unwinds one stack frame and * stores the resulting context into @new_ctx. It also stores a string * describing the stack location into @trace (if not NULL), and modifies * the @lmf if necessary. @native_offset return the IP offset from the * start of the function or -1 if that info is not available. */ MonoJitInfo * mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed) { MonoJitInfo *ji; gpointer ip = MONO_CONTEXT_GET_IP (ctx); gpointer fp = MONO_CONTEXT_GET_BP (ctx); guint32 sp; /* Avoid costly table lookup during stack overflow */ if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size))) ji = prev_ji; else ji = mini_jit_info_table_find (domain, ip, NULL); if (managed) *managed = FALSE; memcpy (new_ctx, ctx, sizeof (MonoContext)); if (ji != NULL) { int i; gint32 address; int offset = 0; if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) { /* remove any unused lmf */ *lmf = (*lmf)->previous_lmf; } address = (char *)ip - (char *)ji->code_start; if (managed) if (!ji->method->wrapper_type) *managed = TRUE; /* My stack frame */ fp = MONO_CONTEXT_GET_BP (ctx); /* Compute the previous stack frame */ sp = (guint32)(fp) - (short)(*(guint32 *)(ji->code_start)); /* Sanity check the frame */ if (!sp || (sp == 0xffffffff) || (sp & 0x07) || (sp < 64*1024)) { #ifdef DEBUG_EXCEPTIONS g_print ("mono_arch_find_jit_info: bad stack sp=%p\n", (void *) sp); #endif return (gpointer)-1; } if (ji->method->save_lmf && 0) { /* only enable this when prologue stops emitting * normal save of s-regs when save_lmf is true. * Will have to sync with prologue code at that point. */ memcpy (&new_ctx->sc_fpregs, (char*)sp - sizeof (float) * MONO_SAVED_FREGS, sizeof (float) * MONO_SAVED_FREGS); memcpy (&new_ctx->sc_regs, (char*)sp - sizeof (float) * MONO_SAVED_FREGS - sizeof (gulong) * MONO_SAVED_GREGS, sizeof (gulong) * MONO_SAVED_GREGS); } else if (ji->used_regs) { guint32 *insn; guint32 mask = ji->used_regs; /* these all happen before adjustment of fp */ /* Look for sw ??, ????(sp) */ insn = ((guint32 *)ji->code_start) + 1; while (!*insn || ((*insn & 0xffe00000) == 0xafa00000) || ((*insn & 0xffe00000) == 0xffa00000)) { int reg = (*insn >> 16) & 0x1f; guint32 addr = (((guint32)fp) + (short)(*insn & 0x0000ffff)); mask &= ~(1 << reg); if ((*insn & 0xffe00000) == 0xafa00000) new_ctx->sc_regs [reg] = *(guint32 *)addr; else new_ctx->sc_regs [reg] = *(guint64 *)addr; insn++; } MONO_CONTEXT_SET_SP (new_ctx, sp); MONO_CONTEXT_SET_BP (new_ctx, sp); /* assert that we found all registers we were supposed to */ g_assert (!mask); }
/* * mono_arch_find_jit_info: * * See exceptions-amd64.c for docs; */ gboolean mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *ji, MonoContext *ctx, MonoContext *new_ctx, MonoLMF **lmf, mgreg_t **save_locations, StackFrameInfo *frame) { gpointer ip = MONO_CONTEXT_GET_IP (ctx); memset (frame, 0, sizeof (StackFrameInfo)); frame->ji = ji; *new_ctx = *ctx; if (ji != NULL) { int i; gssize regs [MONO_MAX_IREGS + 1]; guint8 *cfa; guint32 unwind_info_len; guint8 *unwind_info; frame->type = FRAME_TYPE_MANAGED; if (ji->from_aot) unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len); else unwind_info = mono_get_cached_unwind_info (ji->used_regs, &unwind_info_len); for (i = 0; i < 16; ++i) regs [i] = new_ctx->regs [i]; mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, (guint8*)ji->code_start + ji->code_size, ip, regs, MONO_MAX_IREGS, save_locations, MONO_MAX_IREGS, &cfa); for (i = 0; i < 16; ++i) new_ctx->regs [i] = regs [i]; new_ctx->pc = regs [ARMREG_LR]; new_ctx->regs [ARMREG_SP] = (gsize)cfa; if (*lmf && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->sp)) { /* remove any unused lmf */ *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); } /* Clear thumb bit */ new_ctx->pc &= ~1; /* we substract 1, so that the IP points into the call instruction */ new_ctx->pc--; return TRUE; } else if (*lmf) { if (((gsize)(*lmf)->previous_lmf) & 2) { /* * This LMF entry is created by the soft debug code to mark transitions to * managed code done during invokes. */ MonoLMFExt *ext = (MonoLMFExt*)(*lmf); g_assert (ext->debugger_invoke); memcpy (new_ctx, &ext->ctx, sizeof (MonoContext)); *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); frame->type = FRAME_TYPE_DEBUGGER_INVOKE; return TRUE; } frame->type = FRAME_TYPE_MANAGED_TO_NATIVE; if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->ip, NULL))) { frame->ji = ji; } else { if (!(*lmf)->method) return FALSE; frame->method = (*lmf)->method; } /* * The LMF is saved at the start of the method using: * ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP) * ARM_PUSH (code, 0x5ff0); * So it stores the register state as it existed at the caller. We need to * produce the register state which existed at the time of the call which * transitioned to native call, so we save the sp/fp/ip in the LMF. */ memcpy (&new_ctx->regs [0], &(*lmf)->iregs [0], sizeof (mgreg_t) * 13); new_ctx->pc = (*lmf)->ip; new_ctx->regs [ARMREG_SP] = (*lmf)->sp; new_ctx->regs [ARMREG_FP] = (*lmf)->fp; /* Clear thumb bit */ new_ctx->pc &= ~1; /* we substract 1, so that the IP points into the call instruction */ new_ctx->pc--; *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); return TRUE; } return FALSE; }
/* * mono_arch_find_jit_info: * * This function is used to gather information from @ctx, and store it in @frame_info. * It unwinds one stack frame, and stores the resulting context into @new_ctx. @lmf * is modified if needed. * Returns TRUE on success, FALSE otherwise. */ gboolean mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *ji, MonoContext *ctx, MonoContext *new_ctx, MonoLMF **lmf, mgreg_t **save_locations, StackFrameInfo *frame) { gpointer ip = MONO_CONTEXT_GET_IP (ctx); memset (frame, 0, sizeof (StackFrameInfo)); frame->ji = ji; *new_ctx = *ctx; if (ji != NULL) { mgreg_t regs [MONO_MAX_IREGS + 1]; guint8 *cfa; guint32 unwind_info_len; guint8 *unwind_info; frame->type = FRAME_TYPE_MANAGED; if (ji->from_aot) unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len); else unwind_info = mono_get_cached_unwind_info (ji->used_regs, &unwind_info_len); frame->unwind_info = unwind_info; frame->unwind_info_len = unwind_info_len; regs [AMD64_RAX] = new_ctx->rax; regs [AMD64_RBX] = new_ctx->rbx; regs [AMD64_RCX] = new_ctx->rcx; regs [AMD64_RDX] = new_ctx->rdx; regs [AMD64_RBP] = new_ctx->rbp; regs [AMD64_RSP] = new_ctx->rsp; regs [AMD64_RSI] = new_ctx->rsi; regs [AMD64_RDI] = new_ctx->rdi; regs [AMD64_RIP] = new_ctx->rip; regs [AMD64_R12] = new_ctx->r12; regs [AMD64_R13] = new_ctx->r13; regs [AMD64_R14] = new_ctx->r14; regs [AMD64_R15] = new_ctx->r15; mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, (guint8*)ji->code_start + ji->code_size, ip, regs, MONO_MAX_IREGS + 1, save_locations, MONO_MAX_IREGS, &cfa); new_ctx->rax = regs [AMD64_RAX]; new_ctx->rbx = regs [AMD64_RBX]; new_ctx->rcx = regs [AMD64_RCX]; new_ctx->rdx = regs [AMD64_RDX]; new_ctx->rbp = regs [AMD64_RBP]; new_ctx->rsp = regs [AMD64_RSP]; new_ctx->rsi = regs [AMD64_RSI]; new_ctx->rdi = regs [AMD64_RDI]; new_ctx->rip = regs [AMD64_RIP]; new_ctx->r12 = regs [AMD64_R12]; new_ctx->r13 = regs [AMD64_R13]; new_ctx->r14 = regs [AMD64_R14]; new_ctx->r15 = regs [AMD64_R15]; /* The CFA becomes the new SP value */ new_ctx->rsp = (mgreg_t)cfa; /* Adjust IP */ new_ctx->rip --; if (*lmf && ((*lmf) != jit_tls->first_lmf) && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->rsp)) { /* remove any unused lmf */ *lmf = (gpointer)(((guint64)(*lmf)->previous_lmf) & ~3); } #ifndef MONO_AMD64_NO_PUSHES /* Pop arguments off the stack */ if (ji->has_arch_eh_info) new_ctx->rsp += mono_jit_info_get_arch_eh_info (ji)->stack_size; #endif return TRUE; } else if (*lmf) { guint64 rip; if (((guint64)(*lmf)->previous_lmf) & 2) { /* * This LMF entry is created by the soft debug code to mark transitions to * managed code done during invokes. */ MonoLMFExt *ext = (MonoLMFExt*)(*lmf); g_assert (ext->debugger_invoke); memcpy (new_ctx, &ext->ctx, sizeof (MonoContext)); *lmf = (gpointer)(((guint64)(*lmf)->previous_lmf) & ~3); frame->type = FRAME_TYPE_DEBUGGER_INVOKE; return TRUE; } if (((guint64)(*lmf)->previous_lmf) & 1) { /* This LMF has the rip field set */ rip = (*lmf)->rip; } else if ((*lmf)->rsp == 0) { /* Top LMF entry */ return FALSE; } else { /* * The rsp field is set just before the call which transitioned to native * code. Obtain the rip from the stack. */ rip = *(guint64*)((*lmf)->rsp - sizeof(mgreg_t)); } ji = mini_jit_info_table_find (domain, (gpointer)rip, NULL); /* * FIXME: ji == NULL can happen when a managed-to-native wrapper is interrupted * in the soft debugger suspend code, since (*lmf)->rsp no longer points to the * return address. */ //g_assert (ji); if (!ji) return FALSE; /* Adjust IP */ rip --; frame->ji = ji; frame->type = FRAME_TYPE_MANAGED_TO_NATIVE; new_ctx->rip = rip; new_ctx->rbp = (*lmf)->rbp; new_ctx->rsp = (*lmf)->rsp; new_ctx->rbx = (*lmf)->rbx; new_ctx->r12 = (*lmf)->r12; new_ctx->r13 = (*lmf)->r13; new_ctx->r14 = (*lmf)->r14; new_ctx->r15 = (*lmf)->r15; #ifdef TARGET_WIN32 new_ctx->rdi = (*lmf)->rdi; new_ctx->rsi = (*lmf)->rsi; #endif *lmf = (gpointer)(((guint64)(*lmf)->previous_lmf) & ~3); return TRUE; } return FALSE; }
static void suspend_thread (SgenThreadInfo *info, void *context) { int stop_count; #ifndef USE_MONO_CTX gpointer regs [ARCH_NUM_REGS]; #endif MonoContext ctx; gpointer stack_start; info->client_info.stopped_domain = mono_domain_get (); info->client_info.signal = 0; stop_count = sgen_global_stop_count; /* duplicate signal */ if (0 && info->client_info.stop_count == stop_count) return; #ifdef USE_MONO_CTX if (context) { mono_sigctx_to_monoctx (context, &ctx); info->client_info.stopped_ip = MONO_CONTEXT_GET_IP (&ctx); stack_start = (((guint8 *) MONO_CONTEXT_GET_SP (&ctx)) - REDZONE_SIZE); } else { info->client_info.stopped_ip = NULL; stack_start = NULL; } #else info->client_info.stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL; stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL; #endif /* If stack_start is not within the limits, then don't set it in info and we will be restarted. */ if (stack_start >= info->client_info.stack_start_limit && stack_start <= info->client_info.stack_end) { info->client_info.stack_start = stack_start; #ifdef USE_MONO_CTX if (context) { memcpy (&info->client_info.ctx, &ctx, sizeof (MonoContext)); } else { memset (&info->client_info.ctx, 0, sizeof (MonoContext)); } #else if (context) { ARCH_COPY_SIGCTX_REGS (regs, context); memcpy (&info->client_info.regs, regs, sizeof (info->client_info.regs)); } else { memset (&info->client_info.regs, 0, sizeof (info->client_info.regs)); } #endif } else { g_assert (!info->client_info.stack_start); } /* Notify the JIT */ if (mono_gc_get_gc_callbacks ()->thread_suspend_func) mono_gc_get_gc_callbacks ()->thread_suspend_func (info->client_info.runtime_data, context, NULL); SGEN_LOG (4, "Posting suspend_ack_semaphore for suspend from %p %p", info, (gpointer) (gsize) mono_native_thread_id_get ()); /* Block the restart signal. We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend, which might miss the signal and get stuck. */ pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL); /* notify the waiting thread */ SGEN_SEMAPHORE_POST (suspend_ack_semaphore_ptr); info->client_info.stop_count = stop_count; /* wait until we receive the restart signal */ do { info->client_info.signal = 0; sigsuspend (&suspend_signal_mask); } while (info->client_info.signal != restart_signal_num); /* Unblock the restart signal. */ pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL); SGEN_LOG (4, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer) (gsize) mono_native_thread_id_get ()); /* notify the waiting thread */ SGEN_SEMAPHORE_POST (suspend_ack_semaphore_ptr); }
static void SIG_HANDLER_SIGNATURE (sigprof_signal_handler) { int call_chain_depth = mono_profiler_stat_get_call_chain_depth (); MonoProfilerCallChainStrategy call_chain_strategy = mono_profiler_stat_get_call_chain_strategy (); GET_CONTEXT; if (call_chain_depth == 0) { mono_profiler_stat_hit (mono_arch_ip_from_context (ctx), ctx); } else { MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id); int current_frame_index = 1; MonoContext mono_context; guchar *ips [call_chain_depth + 1]; mono_arch_sigctx_to_monoctx (ctx, &mono_context); ips [0] = MONO_CONTEXT_GET_IP (&mono_context); if (jit_tls != NULL) { if (call_chain_strategy == MONO_PROFILER_CALL_CHAIN_NATIVE) { #if FULL_STAT_PROFILER_BACKTRACE guchar *current_frame; guchar *stack_bottom; guchar *stack_top; stack_bottom = jit_tls->end_of_stack; stack_top = MONO_CONTEXT_GET_SP (&mono_context); current_frame = MONO_CONTEXT_GET_BP (&mono_context); while ((current_frame_index <= call_chain_depth) && (stack_bottom IS_BEFORE_ON_STACK (guchar*) current_frame) && ((guchar*) current_frame IS_BEFORE_ON_STACK stack_top)) { ips [current_frame_index] = CURRENT_FRAME_GET_RETURN_ADDRESS (current_frame); current_frame_index ++; stack_top = current_frame; current_frame = CURRENT_FRAME_GET_BASE_POINTER (current_frame); } #else call_chain_strategy = MONO_PROFILER_CALL_CHAIN_GLIBC; #endif } if (call_chain_strategy == MONO_PROFILER_CALL_CHAIN_GLIBC) { #if GLIBC_PROFILER_BACKTRACE current_frame_index = backtrace ((void**) & ips [1], call_chain_depth); #else call_chain_strategy = MONO_PROFILER_CALL_CHAIN_MANAGED; #endif } if (call_chain_strategy == MONO_PROFILER_CALL_CHAIN_MANAGED) { MonoDomain *domain = mono_domain_get (); if (domain != NULL) { MonoLMF *lmf = NULL; MonoJitInfo *ji; MonoJitInfo res; MonoContext new_mono_context; int native_offset; ji = mono_find_jit_info (domain, jit_tls, &res, NULL, &mono_context, &new_mono_context, NULL, &lmf, &native_offset, NULL); while ((ji != NULL) && (current_frame_index <= call_chain_depth)) { ips [current_frame_index] = MONO_CONTEXT_GET_IP (&new_mono_context); current_frame_index ++; mono_context = new_mono_context; ji = mono_find_jit_info (domain, jit_tls, &res, NULL, &mono_context, &new_mono_context, NULL, &lmf, &native_offset, NULL); } } } } mono_profiler_stat_call_chain (current_frame_index, & ips [0], ctx); } mono_chain_signal (SIG_HANDLER_PARAMS); }
/* * mono_arch_find_jit_info: * * This function is used to gather information from @ctx, and store it in @frame_info. * It unwinds one stack frame, and stores the resulting context into @new_ctx. @lmf * is modified if needed. * Returns TRUE on success, FALSE otherwise. */ gboolean mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *ji, MonoContext *ctx, MonoContext *new_ctx, MonoLMF **lmf, mgreg_t **save_locations, StackFrameInfo *frame) { memset (frame, 0, sizeof (StackFrameInfo)); frame->ji = ji; *new_ctx = *ctx; if (ji != NULL) { int i; gpointer ip = MONO_CONTEXT_GET_IP (ctx); mgreg_t regs [MONO_MAX_IREGS + 1]; guint8 *cfa; guint32 unwind_info_len; guint8 *unwind_info; frame->type = FRAME_TYPE_MANAGED; unwind_info = mono_jinfo_get_unwind_info (ji, &unwind_info_len); for (i = 0; i < MONO_MAX_IREGS; ++i) regs [i] = new_ctx->sc_regs [i]; mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, (guint8*)ji->code_start + ji->code_size, ip, NULL, regs, MONO_MAX_IREGS, save_locations, MONO_MAX_IREGS, &cfa); for (i = 0; i < MONO_MAX_IREGS; ++i) new_ctx->sc_regs [i] = regs [i]; new_ctx->sc_pc = regs [mips_ra]; new_ctx->sc_regs [mips_sp] = (mgreg_t)cfa; /* we substract 8, so that the IP points into the call instruction */ MONO_CONTEXT_SET_IP (new_ctx, new_ctx->sc_pc - 8); /* Sanity check -- we should have made progress here */ g_assert (MONO_CONTEXT_GET_SP (new_ctx) != MONO_CONTEXT_GET_SP (ctx)); return TRUE; } else if (*lmf) { if (((mgreg_t)(*lmf)->previous_lmf) & 2) { /* * This LMF entry is created by the soft debug code to mark transitions to * managed code done during invokes. */ MonoLMFExt *ext = (MonoLMFExt*)(*lmf); g_assert (ext->debugger_invoke); memcpy (new_ctx, &ext->ctx, sizeof (MonoContext)); *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); frame->type = FRAME_TYPE_DEBUGGER_INVOKE; return TRUE; } if (!(*lmf)->method) { #ifdef DEBUG_EXCEPTIONS g_print ("mono_arch_find_jit_info: bad lmf @ %p\n", (void *) *lmf); #endif return FALSE; } g_assert (((*lmf)->magic == MIPS_LMF_MAGIC1) || ((*lmf)->magic == MIPS_LMF_MAGIC2)); ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL); if (!ji) { // FIXME: This can happen with multiple appdomains (bug #444383) return FALSE; } frame->ji = ji; frame->type = FRAME_TYPE_MANAGED_TO_NATIVE; memcpy (&new_ctx->sc_regs, (*lmf)->iregs, sizeof (gulong) * MONO_SAVED_GREGS); memcpy (&new_ctx->sc_fpregs, (*lmf)->fregs, sizeof (float) * MONO_SAVED_FREGS); MONO_CONTEXT_SET_IP (new_ctx, (*lmf)->eip); /* ensure that we've made progress */ g_assert (new_ctx->sc_pc != ctx->sc_pc); *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); return TRUE; } return FALSE; }
/* * mono_arch_unwind_frame: * * See exceptions-amd64.c for docs; */ gboolean mono_arch_unwind_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *ji, MonoContext *ctx, MonoContext *new_ctx, MonoLMF **lmf, mgreg_t **save_locations, StackFrameInfo *frame) { gpointer ip = MONO_CONTEXT_GET_IP (ctx); memset (frame, 0, sizeof (StackFrameInfo)); frame->ji = ji; *new_ctx = *ctx; if (ji != NULL) { int i; mono_unwind_reg_t regs [MONO_MAX_IREGS + 1 + 8]; guint8 *cfa; guint32 unwind_info_len; guint8 *unwind_info; if (ji->is_trampoline) frame->type = FRAME_TYPE_TRAMPOLINE; else frame->type = FRAME_TYPE_MANAGED; unwind_info = mono_jinfo_get_unwind_info (ji, &unwind_info_len); /* printf ("%s %p %p\n", ji->d.method->name, ji->code_start, ip); mono_print_unwind_info (unwind_info, unwind_info_len); */ for (i = 0; i < 16; ++i) regs [i] = new_ctx->regs [i]; #ifdef TARGET_IOS /* On IOS, d8..d15 are callee saved. They are mapped to 8..15 in unwind.c */ for (i = 0; i < 8; ++i) regs [MONO_MAX_IREGS + i] = *(guint64*)&(new_ctx->fregs [8 + i]); #endif mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, (guint8*)ji->code_start + ji->code_size, ip, NULL, regs, MONO_MAX_IREGS + 8, save_locations, MONO_MAX_IREGS, &cfa); for (i = 0; i < 16; ++i) new_ctx->regs [i] = regs [i]; new_ctx->pc = regs [ARMREG_LR]; new_ctx->regs [ARMREG_SP] = (gsize)cfa; #ifdef TARGET_IOS for (i = 0; i < 8; ++i) new_ctx->fregs [8 + i] = *(double*)&(regs [MONO_MAX_IREGS + i]); #endif /* Clear thumb bit */ new_ctx->pc &= ~1; /* we substract 1, so that the IP points into the call instruction */ new_ctx->pc--; return TRUE; } else if (*lmf) { if (((gsize)(*lmf)->previous_lmf) & 2) { /* * This LMF entry is created by the soft debug code to mark transitions to * managed code done during invokes. */ MonoLMFExt *ext = (MonoLMFExt*)(*lmf); g_assert (ext->debugger_invoke); memcpy (new_ctx, &ext->ctx, sizeof (MonoContext)); *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); frame->type = FRAME_TYPE_DEBUGGER_INVOKE; return TRUE; } frame->type = FRAME_TYPE_MANAGED_TO_NATIVE; if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->ip, NULL))) { frame->ji = ji; } else { if (!(*lmf)->method) return FALSE; frame->method = (*lmf)->method; } /* * The LMF is saved at the start of the method using: * ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP) * ARM_PUSH (code, 0x5ff0); * So it stores the register state as it existed at the caller. We need to * produce the register state which existed at the time of the call which * transitioned to native call, so we save the sp/fp/ip in the LMF. */ memcpy (&new_ctx->regs [0], &(*lmf)->iregs [0], sizeof (mgreg_t) * 13); new_ctx->pc = (*lmf)->ip; new_ctx->regs [ARMREG_SP] = (*lmf)->sp; new_ctx->regs [ARMREG_FP] = (*lmf)->fp; /* Clear thumb bit */ new_ctx->pc &= ~1; /* we substract 1, so that the IP points into the call instruction */ new_ctx->pc--; *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); return TRUE; } return FALSE; }
/* * mono_arch_unwind_frame: * * See exceptions-amd64.c for docs; */ gboolean mono_arch_unwind_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *ji, MonoContext *ctx, MonoContext *new_ctx, MonoLMF **lmf, mgreg_t **save_locations, StackFrameInfo *frame) { gpointer ip = MONO_CONTEXT_GET_IP (ctx); memset (frame, 0, sizeof (StackFrameInfo)); frame->ji = ji; *new_ctx = *ctx; if (ji != NULL) { mgreg_t regs [MONO_MAX_IREGS + 8 + 1]; guint8 *cfa; guint32 unwind_info_len; guint8 *unwind_info; frame->type = FRAME_TYPE_MANAGED; unwind_info = mono_jinfo_get_unwind_info (ji, &unwind_info_len); memcpy (regs, &new_ctx->regs, sizeof (mgreg_t) * 32); /* v8..v15 are callee saved */ memcpy (regs + MONO_MAX_IREGS, &(new_ctx->fregs [8]), sizeof (mgreg_t) * 8); mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, (guint8*)ji->code_start + ji->code_size, ip, NULL, regs, MONO_MAX_IREGS + 8, save_locations, MONO_MAX_IREGS, &cfa); memcpy (&new_ctx->regs, regs, sizeof (mgreg_t) * 32); memcpy (&(new_ctx->fregs [8]), regs + MONO_MAX_IREGS, sizeof (mgreg_t) * 8); new_ctx->pc = regs [ARMREG_LR]; new_ctx->regs [ARMREG_SP] = (mgreg_t)cfa; if (*lmf && (*lmf)->gregs [MONO_ARCH_LMF_REG_SP] && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->gregs [MONO_ARCH_LMF_REG_SP])) { /* remove any unused lmf */ *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); } /* we substract 1, so that the IP points into the call instruction */ new_ctx->pc--; return TRUE; } else if (*lmf) { if (((gsize)(*lmf)->previous_lmf) & 2) { /* * This LMF entry is created by the soft debug code to mark transitions to * managed code done during invokes. */ MonoLMFExt *ext = (MonoLMFExt*)(*lmf); g_assert (ext->debugger_invoke); memcpy (new_ctx, &ext->ctx, sizeof (MonoContext)); *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); frame->type = FRAME_TYPE_DEBUGGER_INVOKE; return TRUE; } frame->type = FRAME_TYPE_MANAGED_TO_NATIVE; ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->pc, NULL); if (!ji) return FALSE; g_assert (MONO_ARCH_LMF_REGS == ((0x3ff << 19) | (1 << ARMREG_FP) | (1 << ARMREG_SP))); memcpy (&new_ctx->regs [ARMREG_R19], &(*lmf)->gregs [0], sizeof (mgreg_t) * 10); new_ctx->regs [ARMREG_FP] = (*lmf)->gregs [MONO_ARCH_LMF_REG_FP]; new_ctx->regs [ARMREG_SP] = (*lmf)->gregs [MONO_ARCH_LMF_REG_SP]; new_ctx->pc = (*lmf)->pc; /* we substract 1, so that the IP points into the call instruction */ new_ctx->pc--; *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3); return TRUE; } return FALSE; }
static void sgen_unified_suspend_stop_world (void) { int sleep_duration = -1; mono_threads_begin_global_suspend (); THREADS_STW_DEBUG ("[GC-STW-BEGIN][%p] *** BEGIN SUSPEND *** \n", mono_thread_info_get_tid (mono_thread_info_current ())); FOREACH_THREAD (info) { info->client_info.skip = FALSE; info->client_info.suspend_done = FALSE; int reason; if (!sgen_is_thread_in_current_stw (info, &reason)) { THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] IGNORE thread %p skip %s reason %d\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false", reason); continue; } info->client_info.skip = !mono_thread_info_begin_suspend (info); THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] SUSPEND thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false"); } FOREACH_THREAD_END mono_thread_info_current ()->client_info.suspend_done = TRUE; mono_threads_wait_pending_operations (); for (;;) { gint restart_counter = 0; FOREACH_THREAD (info) { gint suspend_count; int reason = 0; if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) { THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE RESUME thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason); continue; } /* All threads that reach here are pristine suspended. This means the following: - We haven't accepted the previous suspend as good. - We haven't gave up on it for this STW (it's either bad or asked not to) */ if (!mono_thread_info_in_critical_location (info)) { info->client_info.suspend_done = TRUE; THREADS_STW_DEBUG ("[GC-STW-RESTART] DONE thread %p deemed fully suspended\n", mono_thread_info_get_tid (info)); continue; } suspend_count = mono_thread_info_suspend_count (info); if (!(suspend_count == 1)) g_error ("[%p] suspend_count = %d, but should be 1", mono_thread_info_get_tid (info), suspend_count); info->client_info.skip = !mono_thread_info_begin_resume (info); if (!info->client_info.skip) restart_counter += 1; THREADS_STW_DEBUG ("[GC-STW-RESTART] RESTART thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false"); } FOREACH_THREAD_END mono_threads_wait_pending_operations (); if (restart_counter == 0) break; if (sleep_duration < 0) { mono_thread_info_yield (); sleep_duration = 0; } else { g_usleep (sleep_duration); sleep_duration += 10; } FOREACH_THREAD (info) { int reason = 0; if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) { THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE SUSPEND thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason); continue; } if (!mono_thread_info_is_running (info)) { THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE SUSPEND thread %p not running\n", mono_thread_info_get_tid (info)); continue; } info->client_info.skip = !mono_thread_info_begin_suspend (info); THREADS_STW_DEBUG ("[GC-STW-RESTART] SUSPEND thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false"); } FOREACH_THREAD_END mono_threads_wait_pending_operations (); } FOREACH_THREAD (info) { gpointer stopped_ip; int reason = 0; if (!sgen_is_thread_in_current_stw (info, &reason)) { g_assert (!info->client_info.suspend_done || info == mono_thread_info_current ()); THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is NOT suspended, reason %d\n", mono_thread_info_get_tid (info), reason); continue; } g_assert (info->client_info.suspend_done); info->client_info.ctx = mono_thread_info_get_suspend_state (info)->ctx; /* Once we remove the old suspend code, we should move sgen to directly access the state in MonoThread */ info->client_info.stack_start = (gpointer) ((char*)MONO_CONTEXT_GET_SP (&info->client_info.ctx) - REDZONE_SIZE); /* altstack signal handler, sgen can't handle them, mono-threads should have handled this. */ if (!info->client_info.stack_start || info->client_info.stack_start < info->client_info.stack_start_limit || info->client_info.stack_start >= info->client_info.stack_end) { g_error ("BAD STACK: stack_start = %p, stack_start_limit = %p, stack_end = %p", info->client_info.stack_start, info->client_info.stack_start_limit, info->client_info.stack_end); } stopped_ip = (gpointer) (MONO_CONTEXT_GET_IP (&info->client_info.ctx)); binary_protocol_thread_suspend ((gpointer) mono_thread_info_get_tid (info), stopped_ip); THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is suspended, stopped_ip = %p, stack = %p -> %p\n", mono_thread_info_get_tid (info), stopped_ip, info->client_info.stack_start, info->client_info.stack_start ? info->client_info.stack_end : NULL); } FOREACH_THREAD_END }
static void sgen_unified_suspend_stop_world (void) { int restart_counter; int sleep_duration = -1; mono_threads_begin_global_suspend (); THREADS_STW_DEBUG ("[GC-STW-BEGIN] *** BEGIN SUSPEND *** \n"); FOREACH_THREAD (info) { int reason; info->client_info.skip = FALSE; info->client_info.suspend_done = FALSE; if (sgen_is_thread_in_current_stw (info, &reason)) { info->client_info.skip = !mono_thread_info_begin_suspend (info); THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] SUSPEND thread %p skip %d\n", mono_thread_info_get_tid (info), info->client_info.skip); } else { THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] IGNORE thread %p skip %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.skip, reason); } } FOREACH_THREAD_END mono_thread_info_current ()->client_info.suspend_done = TRUE; mono_threads_wait_pending_operations (); for (;;) { restart_counter = 0; FOREACH_THREAD (info) { int reason = 0; if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) { THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE RESUME thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason); continue; } /* All threads that reach here are pristine suspended. This means the following: - We haven't accepted the previous suspend as good. - We haven't gave up on it for this STW (it's either bad or asked not to) */ if (mono_thread_info_in_critical_location (info)) { gboolean res; gint suspend_count = mono_thread_info_suspend_count (info); if (!(suspend_count == 1)) g_error ("[%p] suspend_count = %d, but should be 1", mono_thread_info_get_tid (info), suspend_count); res = mono_thread_info_begin_resume (info); THREADS_STW_DEBUG ("[GC-STW-RESTART] RESTART thread %p skip %d\n", mono_thread_info_get_tid (info), res); if (res) ++restart_counter; else info->client_info.skip = TRUE; } else { THREADS_STW_DEBUG ("[GC-STW-RESTART] DONE thread %p deemed fully suspended\n", mono_thread_info_get_tid (info)); g_assert (!info->client_info.in_critical_region); info->client_info.suspend_done = TRUE; } } FOREACH_THREAD_END if (restart_counter == 0) break; mono_threads_wait_pending_operations (); if (sleep_duration < 0) { mono_thread_info_yield (); sleep_duration = 0; } else { g_usleep (sleep_duration); sleep_duration += 10; } FOREACH_THREAD (info) { int reason = 0; if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) { THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE SUSPEND thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason); continue; } if (mono_thread_info_is_running (info)) { gboolean res = mono_thread_info_begin_suspend (info); THREADS_STW_DEBUG ("[GC-STW-RESTART] SUSPEND thread %p skip %d\n", mono_thread_info_get_tid (info), res); if (!res) info->client_info.skip = TRUE; } } FOREACH_THREAD_END mono_threads_wait_pending_operations (); } FOREACH_THREAD (info) { int reason = 0; if (sgen_is_thread_in_current_stw (info, &reason)) { MonoThreadUnwindState *state; THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is suspended\n", mono_thread_info_get_tid (info)); g_assert (info->client_info.suspend_done); state = mono_thread_info_get_suspend_state (info); info->client_info.ctx = state->ctx; if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN] || !state->unwind_data [MONO_UNWIND_DATA_LMF]) { /* thread is starting or detaching, nothing to scan here */ info->client_info.stopped_domain = NULL; info->client_info.stopped_ip = NULL; info->client_info.stack_start = NULL; } else { /* Once we remove the old suspend code, we should move sgen to directly access the state in MonoThread */ info->client_info.stopped_domain = (MonoDomain*) mono_thread_info_tls_get (info, TLS_KEY_DOMAIN); info->client_info.stopped_ip = (gpointer) (MONO_CONTEXT_GET_IP (&info->client_info.ctx)); info->client_info.stack_start = (gpointer) ((char*)MONO_CONTEXT_GET_SP (&info->client_info.ctx) - REDZONE_SIZE); /* altstack signal handler, sgen can't handle them, mono-threads should have handled this. */ if (!info->client_info.stack_start || info->client_info.stack_start < info->client_info.stack_start_limit || info->client_info.stack_start >= info->client_info.stack_end) { g_error ("BAD STACK: stack_start = %p, stack_start_limit = %p, stack_end = %p", info->client_info.stack_start, info->client_info.stack_start_limit, info->client_info.stack_end); } } binary_protocol_thread_suspend ((gpointer) mono_thread_info_get_tid (info), info->client_info.stopped_ip); } else { THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is NOT suspended, reason %d\n", mono_thread_info_get_tid (info), reason); g_assert (!info->client_info.suspend_done || info == mono_thread_info_current ()); } } FOREACH_THREAD_END }
/* mono_arch_find_jit_info: * * This function is used to gather information from @ctx. It return the * MonoJitInfo of the corresponding function, unwinds one stack frame and * stores the resulting context into @new_ctx. It also stores a string * describing the stack location into @trace (if not NULL), and modifies * the @lmf if necessary. @native_offset return the IP offset from the * start of the function or -1 if that info is not available. */ MonoJitInfo * mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed) { MonoJitInfo *ji; gpointer ip = MONO_CONTEXT_GET_IP (ctx); gpointer *window; /* Avoid costly table lookup during stack overflow */ if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size))) ji = prev_ji; else ji = mini_jit_info_table_find (domain, ip, NULL); if (managed) *managed = FALSE; if (ji != NULL) { *new_ctx = *ctx; if (managed) if (!ji->method->wrapper_type) *managed = TRUE; if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) { /* remove any unused lmf */ *lmf = (*lmf)->previous_lmf; } /* Restore ip and sp from the saved register window */ window = MONO_SPARC_WINDOW_ADDR (ctx->sp); new_ctx->ip = window [sparc_i7 - 16]; new_ctx->sp = (gpointer*)(window [sparc_i6 - 16]); new_ctx->fp = (gpointer*)(MONO_SPARC_WINDOW_ADDR (new_ctx->sp) [sparc_i6 - 16]); return ji; } else { if (!(*lmf)) return NULL; *new_ctx = *ctx; if (!(*lmf)->method) return (gpointer)-1; if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->ip, NULL))) { } else { memset (res, 0, MONO_SIZEOF_JIT_INFO); res->method = (*lmf)->method; } new_ctx->ip = (*lmf)->ip; new_ctx->sp = (*lmf)->sp; new_ctx->fp = (*lmf)->ebp; *lmf = (*lmf)->previous_lmf; return ji ? ji : res; } }