/** * mono_arch_handle_exception: * * @ctx: saved processor state * @obj: the exception object */ gboolean mono_arch_handle_exception (void *sigctx, gpointer obj) { #if defined(MONO_ARCH_USE_SIGACTION) MonoContext mctx; /* * Handling the exception in the signal handler is problematic, since the original * signal is disabled, and we could run arbitrary code though the debugger. So * resume into the normal stack and do most work there if possible. */ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id); /* Pass the ctx parameter in TLS */ mono_sigctx_to_monoctx (sigctx, &jit_tls->ex_ctx); mctx = jit_tls->ex_ctx; mono_arch_setup_async_callback (&mctx, handle_signal_exception, obj); mono_monoctx_to_sigctx (&mctx, sigctx); return TRUE; #else MonoContext mctx; mono_sigctx_to_monoctx (sigctx, &mctx); mono_handle_exception (&mctx, obj); mono_monoctx_to_sigctx (&mctx, sigctx); return TRUE; #endif }
/* * This is the function called from the signal handler */ gboolean mono_arch_handle_exception (void *ctx, gpointer obj) { #if defined(MONO_CROSS_COMPILE) || !defined(MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX) g_assert_not_reached (); #elif defined(MONO_ARCH_USE_SIGACTION) arm_ucontext *sigctx = ctx; /* * Handling the exception in the signal handler is problematic, since the original * signal is disabled, and we could run arbitrary code though the debugger. So * resume into the normal stack and do most work there if possible. */ MonoJitTlsData *jit_tls = mono_tls_get_jit_tls (); guint64 sp = UCONTEXT_REG_SP (sigctx); /* Pass the ctx parameter in TLS */ mono_sigctx_to_monoctx (sigctx, &jit_tls->ex_ctx); /* The others in registers */ UCONTEXT_REG_R0 (sigctx) = (gsize)obj; /* Allocate a stack frame */ sp -= 16; UCONTEXT_REG_SP (sigctx) = sp; UCONTEXT_REG_PC (sigctx) = (gsize)get_handle_signal_exception_addr (); #ifdef UCONTEXT_REG_CPSR if ((gsize)UCONTEXT_REG_PC (sigctx) & 1) /* Transition to thumb */ UCONTEXT_REG_CPSR (sigctx) |= (1 << 5); else /* Transition to ARM */ UCONTEXT_REG_CPSR (sigctx) &= ~(1 << 5); #endif return TRUE; #else MonoContext mctx; gboolean result; mono_sigctx_to_monoctx (ctx, &mctx); result = mono_handle_exception (&mctx, obj); /* restore the context so that returning from the signal handler will invoke * the catch clause */ mono_monoctx_to_sigctx (&mctx, ctx); return result; #endif }
/* * This is the function called from the signal handler */ gboolean mono_arch_handle_exception (void *ctx, gpointer obj) { #if defined(MONO_CROSS_COMPILE) g_assert_not_reached (); #else MonoJitTlsData *jit_tls; void *sigctx = ctx; /* * Resume into the normal stack and handle the exception there. */ jit_tls = mono_tls_get_jit_tls (); /* Pass the ctx parameter in TLS */ mono_sigctx_to_monoctx (sigctx, &jit_tls->ex_ctx); /* The others in registers */ UCONTEXT_REG_R0 (sigctx) = (gsize)obj; UCONTEXT_REG_PC (sigctx) = (gsize)handle_signal_exception; UCONTEXT_REG_SP (sigctx) = UCONTEXT_REG_SP (sigctx) - MONO_ARCH_REDZONE_SIZE; #endif return TRUE; }
gboolean mono_setup_thread_context(DWORD thread_id, MonoContext *mono_context) { HANDLE handle; CONTEXT context; g_assert (thread_id != GetCurrentThreadId ()); handle = OpenThread (THREAD_ALL_ACCESS, FALSE, thread_id); g_assert (handle); context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; if (!GetThreadContext (handle, &context)) { CloseHandle (handle); return FALSE; } g_assert (context.ContextFlags & CONTEXT_INTEGER); g_assert (context.ContextFlags & CONTEXT_CONTROL); memset (mono_context, 0, sizeof (MonoContext)); mono_sigctx_to_monoctx (&context, mono_context); CloseHandle (handle); return TRUE; }
gboolean mono_arch_handle_exception (void *ctx, gpointer obj) { #if defined(MONO_ARCH_USE_SIGACTION) && defined(UCONTEXT_REG_Rn) /* * Handling the exception in the signal handler is problematic, since the original * signal is disabled, and we could run arbitrary code though the debugger. So * resume into the normal stack and do most work there if possible. */ MonoJitTlsData *jit_tls = mono_tls_get_jit_tls (); mgreg_t sp; void *sigctx = ctx; int frame_size; void *uc = sigctx; /* Pass the ctx parameter in TLS */ mono_sigctx_to_monoctx (sigctx, &jit_tls->ex_ctx); /* The others in registers */ UCONTEXT_REG_Rn (sigctx, PPC_FIRST_ARG_REG) = (gsize)obj; /* Allocate a stack frame below the red zone */ /* Similar to mono_arch_handle_altstack_exception () */ frame_size = 224; frame_size += 15; frame_size &= ~15; sp = (mgreg_t)(UCONTEXT_REG_Rn(uc, 1) & ~15); sp = (mgreg_t)(sp - frame_size); UCONTEXT_REG_Rn(uc, 1) = (mgreg_t)sp; setup_ucontext_return (uc, handle_signal_exception); return TRUE; #else MonoContext mctx; gboolean result; mono_sigctx_to_monoctx (ctx, &mctx); result = mono_handle_exception (&mctx, obj); /* restore the context so that returning from the signal handler will invoke * the catch clause */ mono_monoctx_to_sigctx (&mctx, ctx); return result; #endif }
static void altstack_handle_and_restore (void *sigctx, gpointer obj) { MonoContext mctx; mono_sigctx_to_monoctx (sigctx, &mctx); mono_handle_exception (&mctx, obj); mono_restore_context (&mctx); }
gboolean mono_thread_state_init_from_handle (MonoThreadUnwindState *tctx, MonoNativeThreadId thread_id, MonoNativeThreadHandle thread_handle) { kern_return_t ret; mach_msg_type_number_t num_state; thread_state_t state; ucontext_t ctx; mcontext_t mctx; guint32 domain_key, jit_key; MonoJitTlsData *jit_tls; void *domain; #if defined (MONO_ARCH_ENABLE_MONO_LMF_VAR) guint32 lmf_key; #endif /*Zero enough state to make sure the caller doesn't confuse itself*/ tctx->valid = FALSE; tctx->unwind_data [MONO_UNWIND_DATA_DOMAIN] = NULL; tctx->unwind_data [MONO_UNWIND_DATA_LMF] = NULL; tctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS] = NULL; state = (thread_state_t) alloca (mono_mach_arch_get_thread_state_size ()); mctx = (mcontext_t) alloca (mono_mach_arch_get_mcontext_size ()); ret = mono_mach_arch_get_thread_state (thread_handle, state, &num_state); if (ret != KERN_SUCCESS) return FALSE; mono_mach_arch_thread_state_to_mcontext (state, mctx); ctx.uc_mcontext = mctx; mono_sigctx_to_monoctx (&ctx, &tctx->ctx); domain_key = mono_domain_get_tls_offset (); jit_key = mono_get_jit_tls_key (); jit_tls = mono_mach_arch_get_tls_value_from_thread (thread_id, jit_key); domain = mono_mach_arch_get_tls_value_from_thread (thread_id, domain_key); /*Thread already started to cleanup, can no longer capture unwind state*/ if (!jit_tls) return FALSE; g_assert (domain); #if defined (MONO_ARCH_ENABLE_MONO_LMF_VAR) lmf_key = mono_get_lmf_tls_offset (); tctx->unwind_data [MONO_UNWIND_DATA_LMF] = mono_mach_arch_get_tls_value_from_thread (thread_id, lmf_key);; #else tctx->unwind_data [MONO_UNWIND_DATA_LMF] = jit_tls ? jit_tls->lmf : NULL; #endif tctx->unwind_data [MONO_UNWIND_DATA_DOMAIN] = domain; tctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS] = jit_tls; tctx->valid = TRUE; return TRUE; }
gboolean sgen_suspend_thread (SgenThreadInfo *info) { mach_msg_type_number_t num_state; thread_state_t state; kern_return_t ret; ucontext_t ctx; mcontext_t mctx; gpointer stack_start; state = (thread_state_t) alloca (mono_mach_arch_get_thread_state_size ()); mctx = (mcontext_t) alloca (mono_mach_arch_get_mcontext_size ()); do { ret = thread_suspend (info->client_info.info.native_handle); } while (ret == KERN_ABORTED); if (ret != KERN_SUCCESS) return FALSE; do { ret = mono_mach_arch_get_thread_state (info->client_info.info.native_handle, state, &num_state); } while (ret == KERN_ABORTED); if (ret != KERN_SUCCESS) return FALSE; mono_mach_arch_thread_state_to_mcontext (state, mctx); ctx.uc_mcontext = mctx; info->client_info.stopped_domain = mono_thread_info_tls_get (info, TLS_KEY_DOMAIN); info->client_info.stopped_ip = (gpointer) mono_mach_arch_get_ip (state); info->client_info.stack_start = NULL; stack_start = (char*) mono_mach_arch_get_sp (state) - REDZONE_SIZE; /* 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 mono_sigctx_to_monoctx (&ctx, &info->client_info.ctx); #else ARCH_COPY_SIGCTX_REGS (&info->client_info.regs, &ctx); #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, &ctx, NULL); SGEN_LOG (2, "thread %p stopped at %p stack_start=%p", (void*)(gsize)info->client_info.info.native_handle, info->client_info.stopped_ip, info->client_info.stack_start); binary_protocol_thread_suspend ((gpointer)mono_thread_info_get_tid (info), info->client_info.stopped_ip); return TRUE; }
/* * This is the function called from the signal handler */ gboolean mono_arch_handle_exception (void *ctx, gpointer obj) { #if defined(MONO_CROSS_COMPILE) g_assert_not_reached (); #elif defined(MONO_ARCH_USE_SIGACTION) void *sigctx = ctx; /* * Handling the exception in the signal handler is problematic, since the original * signal is disabled, and we could run arbitrary code though the debugger. So * resume into the normal stack and do most work there if possible. */ MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id); guint64 sp = UCONTEXT_GREGS (sigctx) [mips_sp]; /* Pass the ctx parameter in TLS */ mono_sigctx_to_monoctx (sigctx, &jit_tls->ex_ctx); /* The others in registers */ UCONTEXT_GREGS (sigctx)[mips_a0] = (gsize)obj; /* Allocate a stack frame */ sp -= 256; UCONTEXT_GREGS (sigctx)[mips_sp] = sp; UCONTEXT_REG_PC (sigctx) = (gsize)handle_signal_exception; return TRUE; #else MonoContext mctx; gboolean result; mono_sigctx_to_monoctx (ctx, &mctx); result = mono_handle_exception (&mctx, obj); /* restore the context so that returning from the signal handler will invoke * the catch clause */ mono_monoctx_to_sigctx (&mctx, ctx); return result; #endif }
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 }
void mono_arch_handle_altstack_exception (void *sigctx, MONO_SIG_HANDLER_INFO_TYPE *siginfo, gpointer fault_addr, gboolean stack_ovf) { #ifdef MONO_ARCH_USE_SIGACTION MonoException *exc = NULL; ucontext_t *ctx = (ucontext_t*)sigctx; MonoJitInfo *ji = mini_jit_info_table_find (mono_domain_get (), (gpointer)UCONTEXT_REG_EIP (ctx), NULL); gpointer *sp; int frame_size; /* if we didn't find a managed method for the ip address and it matches the fault * address, we assume we followed a broken pointer during an indirect call, so * we try the lookup again with the return address pushed on the stack */ if (!ji && fault_addr == (gpointer)UCONTEXT_REG_EIP (ctx)) { glong *sp = (gpointer)UCONTEXT_REG_ESP (ctx); ji = mini_jit_info_table_find (mono_domain_get (), (gpointer)sp [0], NULL); if (ji) UCONTEXT_REG_EIP (ctx) = sp [0]; } if (stack_ovf) exc = mono_domain_get ()->stack_overflow_ex; if (!ji) mono_handle_native_sigsegv (SIGSEGV, sigctx, siginfo); /* setup a call frame on the real stack so that control is returned there * and exception handling can continue. * If this was a stack overflow the caller already ensured the stack pages * needed have been unprotected. * The frame looks like: * ucontext struct * test_only arg * exception arg * ctx arg * return ip */ // FIXME: test_only is no more. frame_size = sizeof (MonoContext) + sizeof (gpointer) * 4; frame_size += 15; frame_size &= ~15; sp = (gpointer)(UCONTEXT_REG_ESP (ctx) & ~15); sp = (gpointer)((char*)sp - frame_size); /* the incoming arguments are aligned to 16 bytes boundaries, so the return address IP * goes at sp [-1] */ sp [-1] = (gpointer)UCONTEXT_REG_EIP (ctx); sp [0] = sp + 4; sp [1] = exc; sp [2] = (gpointer)stack_ovf; mono_sigctx_to_monoctx (sigctx, (MonoContext*)(sp + 4)); /* at the return form the signal handler execution starts in altstack_handle_and_restore() */ UCONTEXT_REG_EIP (ctx) = (unsigned long)altstack_handle_and_restore; UCONTEXT_REG_ESP (ctx) = (unsigned long)(sp - 1); #endif }
/* Special hack to workaround the fact that the * when the SEH handler is called the stack is * to small to recover. * * Stack walking part of this method is from mono_handle_exception * * The idea is simple; * - walk the stack to free some space (64k) * - set esp to new stack location * - call mono_arch_handle_exception with stack overflow exception * - set esp to SEH handlers stack * - done */ static void win32_handle_stack_overflow (EXCEPTION_POINTERS* ep, struct sigcontext *sctx) { SYSTEM_INFO si; DWORD page_size; MonoDomain *domain = mono_domain_get (); MonoJitInfo rji; MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id); MonoLMF *lmf = jit_tls->lmf; MonoContext initial_ctx; MonoContext ctx; guint32 free_stack = 0; StackFrameInfo frame; mono_sigctx_to_monoctx (sctx, &ctx); /* get our os page size */ GetSystemInfo(&si); page_size = si.dwPageSize; /* Let's walk the stack to recover * the needed stack space (if possible) */ memset (&rji, 0, sizeof (rji)); initial_ctx = ctx; free_stack = (guint8*)(MONO_CONTEXT_GET_BP (&ctx)) - (guint8*)(MONO_CONTEXT_GET_BP (&initial_ctx)); /* try to free 64kb from our stack */ do { MonoContext new_ctx; mono_arch_unwind_frame (domain, jit_tls, &rji, &ctx, &new_ctx, &lmf, NULL, &frame); if (!frame.ji) { g_warning ("Exception inside function without unwind info"); g_assert_not_reached (); } if (frame.ji != (gpointer)-1) { free_stack = (guint8*)(MONO_CONTEXT_GET_BP (&ctx)) - (guint8*)(MONO_CONTEXT_GET_BP (&initial_ctx)); } /* todo: we should call abort if ji is -1 */ ctx = new_ctx; } while (free_stack < 64 * 1024 && frame.ji != (gpointer) -1); mono_monoctx_to_sigctx (&ctx, sctx); /* todo: install new stack-guard page */ /* use the new stack and call mono_arch_handle_exception () */ restore_stack (sctx); }
gboolean mono_thread_state_init_from_handle (MonoThreadUnwindState *tctx, MonoThreadInfo *info, void *sigctx) { MonoJitTlsData *jit_tls; void *domain; MonoLMF *lmf = NULL; gpointer *addr; tctx->valid = FALSE; tctx->unwind_data [MONO_UNWIND_DATA_DOMAIN] = NULL; tctx->unwind_data [MONO_UNWIND_DATA_LMF] = NULL; tctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS] = NULL; if (sigctx == NULL) { DWORD id = mono_thread_info_get_tid (info); mono_setup_thread_context (id, &tctx->ctx); } else { g_assert (((CONTEXT *)sigctx)->ContextFlags & CONTEXT_INTEGER); g_assert (((CONTEXT *)sigctx)->ContextFlags & CONTEXT_CONTROL); mono_sigctx_to_monoctx (sigctx, &tctx->ctx); } /* mono_set_jit_tls () sets this */ jit_tls = mono_thread_info_tls_get (info, TLS_KEY_JIT_TLS); /* SET_APPDOMAIN () sets this */ domain = mono_thread_info_tls_get (info, TLS_KEY_DOMAIN); /*Thread already started to cleanup, can no longer capture unwind state*/ if (!jit_tls || !domain) return FALSE; /* * The current LMF address is kept in a separate TLS variable, and its hard to read its value without * arch-specific code. But the address of the TLS variable is stored in another TLS variable which * can be accessed through MonoThreadInfo. */ /* mono_set_lmf_addr () sets this */ addr = mono_thread_info_tls_get (info, TLS_KEY_LMF_ADDR); if (addr) lmf = *addr; tctx->unwind_data [MONO_UNWIND_DATA_DOMAIN] = domain; tctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS] = jit_tls; tctx->unwind_data [MONO_UNWIND_DATA_LMF] = lmf; tctx->valid = TRUE; return TRUE; }
gboolean mono_sgen_suspend_thread (SgenThreadInfo *info) { mach_msg_type_number_t num_state; thread_state_t state; kern_return_t ret; ucontext_t ctx; mcontext_t mctx; gpointer stack_start; state = (thread_state_t) alloca (mono_mach_arch_get_thread_state_size ()); mctx = (mcontext_t) alloca (mono_mach_arch_get_mcontext_size ()); ret = thread_suspend (info->mach_port); if (ret != KERN_SUCCESS) return FALSE; ret = mono_mach_arch_get_thread_state (info->mach_port, state, &num_state); if (ret != KERN_SUCCESS) return FALSE; mono_mach_arch_thread_state_to_mcontext (state, mctx); ctx.uc_mcontext = mctx; info->stopped_domain = mono_mach_arch_get_tls_value_from_thread ((pthread_t)info->id, mono_domain_get_tls_offset ()); info->stopped_ip = (gpointer) mono_mach_arch_get_ip (state); stack_start = (char*) mono_mach_arch_get_sp (state) - REDZONE_SIZE; /* If stack_start is not within the limits, then don't set it in info and we will be restarted. */ if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) { info->stack_start = stack_start; #ifdef USE_MONO_CTX mono_sigctx_to_monoctx (&ctx, &info->ctx); info->monoctx = &info->ctx; #else ARCH_COPY_SIGCTX_REGS (&info->regs, &ctx); info->stopped_regs = &info->regs; #endif } else { g_assert (!info->stack_start); } /* Notify the JIT */ if (mono_gc_get_gc_callbacks ()->thread_suspend_func) mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, &ctx); return TRUE; }
MONO_SIG_HANDLER_FUNC (static, sigterm_signal_handler) { MONO_SIG_HANDLER_GET_CONTEXT; // Note: this function only returns for a single thread // When it's invoked on other threads once the dump begins, // those threads perform their dumps and then sleep until we // die. The dump ends with the exit(1) below MonoContext mctx; gchar *output = NULL; mono_sigctx_to_monoctx (ctx, &mctx); if (!mono_threads_summarize (&mctx, &output, NULL)) g_assert_not_reached (); // Only the dumping-supervisor thread exits mono_thread_summarize MOSTLY_ASYNC_SAFE_PRINTF("Unhandled exception dump: \n######\n%s\n######\n", output); mono_chain_signal (MONO_SIG_HANDLER_PARAMS); exit (1); }
gboolean mono_thread_state_init_from_handle (MonoThreadUnwindState *tctx, MonoNativeThreadId thread_id, MonoNativeThreadHandle thread_handle) { kern_return_t ret; mach_msg_type_number_t num_state; thread_state_t state; ucontext_t ctx; mcontext_t mctx; guint32 domain_key, jit_key; MonoJitTlsData *jit_tls; void *domain; state = (thread_state_t) alloca (mono_mach_arch_get_thread_state_size ()); mctx = (mcontext_t) alloca (mono_mach_arch_get_mcontext_size ()); ret = mono_mach_arch_get_thread_state (thread_handle, state, &num_state); if (ret != KERN_SUCCESS) return FALSE; mono_mach_arch_thread_state_to_mcontext (state, mctx); ctx.uc_mcontext = mctx; mono_sigctx_to_monoctx (&ctx, &tctx->ctx); domain_key = mono_domain_get_tls_offset (); jit_key = mono_pthread_key_for_tls (mono_get_jit_tls_key ()); jit_tls = mono_mach_arch_get_tls_value_from_thread (thread_id, jit_key); domain = mono_mach_arch_get_tls_value_from_thread (thread_id, domain_key); /*Thread already started to cleanup, can no longer capture unwind state*/ if (!jit_tls) return FALSE; g_assert (domain); tctx->unwind_data [MONO_UNWIND_DATA_DOMAIN] = domain; tctx->unwind_data [MONO_UNWIND_DATA_LMF] = jit_tls ? jit_tls->lmf : NULL; tctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS] = jit_tls; tctx->valid = TRUE; return TRUE; }
void mono_arch_handle_altstack_exception (void *sigctx, MONO_SIG_HANDLER_INFO_TYPE *siginfo, gpointer fault_addr, gboolean stack_ovf) { #if defined(MONO_ARCH_USE_SIGACTION) MonoException *exc = NULL; MonoJitInfo *ji = mini_jit_info_table_find (mono_domain_get (), (char *)UCONTEXT_REG_RIP (sigctx), NULL); gpointer *sp; int frame_size; MonoContext *copied_ctx; if (stack_ovf) exc = mono_domain_get ()->stack_overflow_ex; if (!ji) mono_handle_native_sigsegv (SIGSEGV, sigctx, siginfo); /* setup a call frame on the real stack so that control is returned there * and exception handling can continue. * The frame looks like: * ucontext struct * ... * return ip * 128 is the size of the red zone */ frame_size = sizeof (MonoContext) + sizeof (gpointer) * 4 + 128; frame_size += 15; frame_size &= ~15; sp = (gpointer *)(UCONTEXT_REG_RSP (sigctx) & ~15); sp = (gpointer *)((char*)sp - frame_size); copied_ctx = (MonoContext*)(sp + 4); /* the arguments must be aligned */ sp [-1] = (gpointer)UCONTEXT_REG_RIP (sigctx); mono_sigctx_to_monoctx (sigctx, copied_ctx); /* at the return form the signal handler execution starts in altstack_handle_and_restore() */ UCONTEXT_REG_RIP (sigctx) = (unsigned long)altstack_handle_and_restore; UCONTEXT_REG_RSP (sigctx) = (unsigned long)(sp - 1); UCONTEXT_REG_RDI (sigctx) = (unsigned long)(copied_ctx); UCONTEXT_REG_RSI (sigctx) = (guint64)exc; UCONTEXT_REG_RDX (sigctx) = stack_ovf; #endif }
static void dump_native_stacktrace (const char *signal, void *ctx) { #ifdef HAVE_BACKTRACE_SYMBOLS void *array [256]; char **names; int i, size; mono_runtime_printf_err ("\nNative stacktrace:\n"); size = backtrace (array, 256); names = backtrace_symbols (array, size); for (i = 0; i < size; ++i) { mono_runtime_printf_err ("\t%s", names [i]); } g_free (names); /* Try to get more meaningful information using gdb */ char *debugger_log = mono_debugger_state_str (); if (debugger_log) { fprintf (stderr, "\n\tDebugger session state:\n%s\n", debugger_log); } #if !defined(HOST_WIN32) && defined(HAVE_SYS_SYSCALL_H) && (defined(SYS_fork) || HAVE_FORK) if (!mini_get_debug_options ()->no_gdb_backtrace) { /* From g_spawn_command_line_sync () in eglib */ pid_t pid; int status; pid_t crashed_pid = getpid (); #if defined(TARGET_OSX) MonoStackHash hashes; #endif gchar *output = NULL; MonoContext mctx; if (ctx) { gboolean leave = FALSE; gboolean dump_for_merp = FALSE; #if defined(TARGET_OSX) dump_for_merp = mono_merp_enabled (); #endif if (!dump_for_merp) { #ifdef DISABLE_STRUCTURED_CRASH leave = TRUE; #elif defined(TARGET_OSX) mini_register_sigterm_handler (); #endif } #ifdef TARGET_OSX if (!leave) { mono_sigctx_to_monoctx (ctx, &mctx); // Do before forking if (!mono_threads_summarize (&mctx, &output, &hashes)) g_assert_not_reached (); } // We want our crash, and don't have telemetry // So we dump to disk if (!leave && !dump_for_merp) mono_crash_dump (output); #endif } /* * glibc fork acquires some locks, so if the crash happened inside malloc/free, * it will deadlock. Call the syscall directly instead. */ #if defined(HOST_ANDROID) /* SYS_fork is defined to be __NR_fork which is not defined in some ndk versions */ g_assert_not_reached (); #elif !defined(HOST_DARWIN) && defined(SYS_fork) pid = (pid_t) syscall (SYS_fork); #elif HAVE_FORK pid = (pid_t) fork (); #else g_assert_not_reached (); #endif #if defined (HAVE_PRCTL) && defined(PR_SET_PTRACER) if (pid > 0) { // Allow gdb to attach to the process even if ptrace_scope sysctl variable is set to // a value other than 0 (the most permissive ptrace scope). Most modern Linux // distributions set the scope to 1 which allows attaching only to direct children of // the current process prctl (PR_SET_PTRACER, pid, 0, 0, 0); } #endif #if defined(TARGET_OSX) if (mono_merp_enabled ()) { if (pid == 0) { if (!ctx) { mono_runtime_printf_err ("\nMust always pass non-null context when using merp.\n"); exit (1); } char *full_version = mono_get_runtime_build_info (); mono_merp_invoke (crashed_pid, signal, output, &hashes, full_version); exit (1); } } #endif if (pid == 0) { dup2 (STDERR_FILENO, STDOUT_FILENO); mono_gdb_render_native_backtraces (crashed_pid); exit (1); } mono_runtime_printf_err ("\nDebug info from gdb:\n"); waitpid (pid, &status, 0); } #endif #else #ifdef HOST_ANDROID /* set DUMPABLE for this process so debuggerd can attach with ptrace(2), see: * https://android.googlesource.com/platform/bionic/+/151da681000c07da3c24cd30a3279b1ca017f452/linker/debugger.cpp#206 * this has changed on later versions of Android. Also, we don't want to * set this on start-up as DUMPABLE has security implications. */ prctl (PR_SET_DUMPABLE, 1); mono_runtime_printf_err ("\nNo native Android stacktrace (see debuggerd output).\n"); #endif #endif }
gboolean mono_thread_state_init_from_handle (MonoThreadUnwindState *tctx, MonoThreadInfo *info) { DWORD id = mono_thread_info_get_tid (info); HANDLE handle; CONTEXT context; DWORD result; MonoContext *ctx; MonoJitTlsData *jit_tls; void *domain; MonoLMF *lmf = NULL; gpointer *addr; tctx->valid = FALSE; tctx->unwind_data [MONO_UNWIND_DATA_DOMAIN] = NULL; tctx->unwind_data [MONO_UNWIND_DATA_LMF] = NULL; tctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS] = NULL; g_assert (id != GetCurrentThreadId ()); handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id); g_assert (handle); context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; if (!GetThreadContext (handle, &context)) { CloseHandle (handle); return FALSE; } g_assert (context.ContextFlags & CONTEXT_INTEGER); g_assert (context.ContextFlags & CONTEXT_CONTROL); ctx = &tctx->ctx; memset (ctx, 0, sizeof (MonoContext)); mono_sigctx_to_monoctx (&context, ctx); /* mono_set_jit_tls () sets this */ jit_tls = mono_thread_info_tls_get (info, TLS_KEY_JIT_TLS); /* SET_APPDOMAIN () sets this */ domain = mono_thread_info_tls_get (info, TLS_KEY_DOMAIN); /*Thread already started to cleanup, can no longer capture unwind state*/ if (!jit_tls || !domain) return FALSE; /* * The current LMF address is kept in a separate TLS variable, and its hard to read its value without * arch-specific code. But the address of the TLS variable is stored in another TLS variable which * can be accessed through MonoThreadInfo. */ /* mono_set_lmf_addr () sets this */ addr = mono_thread_info_tls_get (info, TLS_KEY_LMF_ADDR); if (addr) lmf = *addr; tctx->unwind_data [MONO_UNWIND_DATA_DOMAIN] = domain; tctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS] = jit_tls; tctx->unwind_data [MONO_UNWIND_DATA_LMF] = lmf; tctx->valid = TRUE; return TRUE; }
static void suspend_thread (SgenThreadInfo *info, void *context) { int stop_count; #ifdef USE_MONO_CTX MonoContext monoctx; #else gpointer regs [ARCH_NUM_REGS]; #endif gpointer stack_start; g_assert (info->doing_handshake); info->stopped_domain = mono_domain_get (); info->stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL; stop_count = sgen_global_stop_count; /* duplicate signal */ if (0 && info->stop_count == stop_count) return; sgen_fill_thread_info_for_suspend (info); stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL; /* If stack_start is not within the limits, then don't set it in info and we will be restarted. */ if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) { info->stack_start = stack_start; #ifdef USE_MONO_CTX if (context) { mono_sigctx_to_monoctx (context, &monoctx); info->monoctx = &monoctx; } else { info->monoctx = NULL; } #else if (context) { ARCH_COPY_SIGCTX_REGS (regs, context); info->stopped_regs = regs; } else { info->stopped_regs = NULL; } #endif } else { g_assert (!info->stack_start); } /* Notify the JIT */ if (mono_gc_get_gc_callbacks ()->thread_suspend_func) mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, context); DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)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 */ MONO_SEM_POST (suspend_ack_semaphore_ptr); info->stop_count = stop_count; /* wait until we receive the restart signal */ do { info->signal = 0; sigsuspend (&suspend_signal_mask); } while (info->signal != restart_signal_num && info->doing_handshake); /* Unblock the restart signal. */ pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL); DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)mono_native_thread_id_get ())); /* notify the waiting thread */ MONO_SEM_POST (suspend_ack_semaphore_ptr); }
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); }
void mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx) { mono_sigctx_to_monoctx (sigctx, mctx); }
int mono_sgen_thread_handshake (int signum) { SgenThreadInfo *cur_thread = mono_sgen_thread_info_current (); mach_msg_type_number_t num_state; thread_state_t state; kern_return_t ret; ucontext_t ctx; mcontext_t mctx; SgenThreadInfo *info; gpointer stack_start; int count = 0; state = (thread_state_t) alloca (mono_mach_arch_get_thread_state_size ()); mctx = (mcontext_t) alloca (mono_mach_arch_get_mcontext_size ()); FOREACH_THREAD (info) { if (info == cur_thread || mono_sgen_is_worker_thread (info->id)) continue; if (signum == suspend_signal_num) { ret = thread_suspend (info->mach_port); if (ret != KERN_SUCCESS) continue; ret = mono_mach_arch_get_thread_state (info->mach_port, state, &num_state); if (ret != KERN_SUCCESS) continue; mono_mach_arch_thread_state_to_mcontext (state, mctx); ctx.uc_mcontext = mctx; info->stopped_domain = mono_mach_arch_get_tls_value_from_thread ((pthread_t)info->id, mono_pthread_key_for_tls (mono_domain_get_tls_key ())); info->stopped_ip = (gpointer) mono_mach_arch_get_ip (state); stack_start = (char*) mono_mach_arch_get_sp (state) - REDZONE_SIZE; /* If stack_start is not within the limits, then don't set it in info and we will be restarted. */ if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) { info->stack_start = stack_start; #ifdef USE_MONO_CTX mono_sigctx_to_monoctx (&ctx, &info->ctx); info->monoctx = &info->ctx; #else ARCH_COPY_SIGCTX_REGS (&info->regs, &ctx); info->stopped_regs = &info->regs; #endif } else { g_assert (!info->stack_start); } /* Notify the JIT */ if (mono_gc_get_gc_callbacks ()->thread_suspend_func) mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, &ctx); } else { ret = thread_resume (info->mach_port); if (ret != KERN_SUCCESS) continue; } count ++; } END_FOREACH_THREAD return count; }