static char * mono_extension_handle_native_sigsegv_libunwind (void *ctx, MONO_SIG_HANDLER_INFO_TYPE *info) { char *dl_err; int unw_err; unw_init_local_t unw_init_local_fn; unw_get_reg_t unw_get_reg_fn; unw_get_proc_name_t unw_get_proc_name_fn; unw_step_t unw_step_fn; unw_cursor_t cursor; size_t frames = 0; MonoDl *dl = mono_dl_open ("libunwind.so", MONO_DL_LAZY, &dl_err); if (!dl) return dl_err; LOAD_SYM (dl, dl_err, UNW_OBJ (init_local), unw_init_local_fn); LOAD_SYM (dl, dl_err, UNW_OBJ (get_reg), unw_get_reg_fn); LOAD_SYM (dl, dl_err, UNW_OBJ (get_proc_name), unw_get_proc_name_fn); LOAD_SYM (dl, dl_err, UNW_OBJ (step), unw_step_fn); if ((unw_err = unw_init_local_fn (&cursor, ctx))) { mono_dl_close (dl); return g_strdup_printf ("unw_init_local () returned %d", unw_err); } do { int reg_err; unw_word_t ip, off; char name [FUNC_NAME_LENGTH]; if ((reg_err = unw_get_reg_fn (&cursor, UNW_REG_IP, &ip))) { mono_runtime_printf_err ("unw_get_reg (UNW_REG_IP) returned %d", reg_err); break; } reg_err = unw_get_proc_name_fn (&cursor, name, FUNC_NAME_LENGTH, &off); if (reg_err == -UNW_ENOINFO) strcpy (name, "???"); mono_runtime_printf_err (" at %s+%zu [0x%zx]", name, off, ip); unw_err = unw_step_fn (&cursor); frames++; } while (unw_err > 0 && frames < FRAMES_TO_UNWIND); if (unw_err < 0) mono_runtime_printf_err ("unw_step () returned %d", unw_err); mono_dl_close (dl); return NULL; }
static void print_process_map (void) { #ifdef __linux__ FILE *fp = fopen ("/proc/self/maps", "r"); char line [256]; if (fp == NULL) { mono_runtime_printf_err ("no /proc/self/maps, not on linux?\n"); return; } mono_runtime_printf_err ("/proc/self/maps:"); const int max_lines = 25; int i = 0; while (fgets (line, sizeof (line), fp) && i++ < max_lines) { // strip newline size_t len = strlen (line); if (len > 0 && line [len - 1] == '\n') line [len - 1] = '\0'; mono_runtime_printf_err ("%s", line); } fclose (fp); #else /* do nothing */ #endif }
void mono_exception_native_unwind (void *ctx, MONO_SIG_HANDLER_INFO_TYPE *info) { char *unwind_err, *corkscrew_err; mono_runtime_printf_err ("\nAttempting native Android stacktrace:\n"); unwind_err = mono_extension_handle_native_sigsegv_libunwind (ctx, info); if (unwind_err) { corkscrew_err = mono_extension_handle_native_sigsegv_libcorkscrew (ctx, info); if (corkscrew_err) { mono_runtime_printf_err ("\tCould not unwind with `libunwind.so`: %s", unwind_err); mono_runtime_printf_err ("\tCould not unwind with `libcorkscrew.so`: %s", corkscrew_err); mono_runtime_printf_err ("\n\tNo options left to get a native stacktrace :-("); g_free (corkscrew_err); } g_free (unwind_err); } }
void mono_invoke_unhandled_exception_hook (MonoObject *exc) { if (unhandled_exception_hook) { unhandled_exception_hook (exc, unhandled_exception_hook_data); } else { MonoError inner_error; MonoObject *other = NULL; MonoString *str = mono_object_try_to_string (exc, &other, &inner_error); char *msg = NULL; if (str && is_ok (&inner_error)) { msg = mono_string_to_utf8_checked (str, &inner_error); if (!is_ok (&inner_error)) { msg = g_strdup_printf ("Nested exception while formatting original exception"); mono_error_cleanup (&inner_error); } } else if (other) { char *original_backtrace = mono_exception_get_managed_backtrace ((MonoException*)exc); char *nested_backtrace = mono_exception_get_managed_backtrace ((MonoException*)other); msg = g_strdup_printf ("Nested exception detected.\nOriginal Exception: %s\nNested exception:%s\n", original_backtrace, nested_backtrace); g_free (original_backtrace); g_free (nested_backtrace); } else { msg = g_strdup ("Nested exception trying to figure out what went wrong"); } mono_runtime_printf_err ("[ERROR] FATAL UNHANDLED EXCEPTION: %s", msg); g_free (msg); #if defined(HOST_IOS) g_assertion_message ("Terminating runtime due to unhandled exception"); #else exit (mono_environment_exitcode_get ()); #endif } g_assert_not_reached (); }
static char * mono_extension_handle_native_sigsegv_libcorkscrew (void *ctx, MONO_SIG_HANDLER_INFO_TYPE *info) { #if defined (__arm__) || defined (__i386__) char *dl_err; get_backtrace_symbols_t get_backtrace_symbols; free_backtrace_symbols_t free_backtrace_symbols; unwind_backtrace_signal_arch_t unwind_backtrace_signal_arch; acquire_my_map_info_list_t acquire_my_map_info_list; release_my_map_info_list_t release_my_map_info_list; backtrace_frame_t frames [FRAMES_TO_UNWIND]; backtrace_symbol_t symbols [FRAMES_TO_UNWIND]; map_info_t *map_info; ssize_t frames_unwound; size_t i; MonoDl *dl = mono_dl_open ("libcorkscrew.so", MONO_DL_LAZY, &dl_err); if (!dl) return dl_err; LOAD_SYM (dl, dl_err, get_backtrace_symbols, get_backtrace_symbols); LOAD_SYM (dl, dl_err, free_backtrace_symbols, free_backtrace_symbols); LOAD_SYM (dl, dl_err, unwind_backtrace_signal_arch, unwind_backtrace_signal_arch); LOAD_SYM (dl, dl_err, acquire_my_map_info_list, acquire_my_map_info_list); LOAD_SYM (dl, dl_err, release_my_map_info_list, release_my_map_info_list); map_info = acquire_my_map_info_list (); frames_unwound = unwind_backtrace_signal_arch (info, ctx, map_info, frames, 0, FRAMES_TO_UNWIND); release_my_map_info_list (map_info); if (frames_unwound == -1) { mono_dl_close (dl); return g_strdup ("unwind_backtrace_signal_arch () returned -1"); } get_backtrace_symbols (frames, frames_unwound, symbols); for (i = 0; i < frames_unwound; i++) { backtrace_frame_t *frame = frames + i; backtrace_symbol_t *symbol = symbols + i; const char *name = symbol->demangled_name ? symbol->demangled_name : (symbol->symbol_name ? symbol->symbol_name : "???"); uintptr_t off = symbol->relative_pc - symbol->relative_symbol_addr; uintptr_t ip = frame->absolute_pc; mono_runtime_printf_err (" at %s+%zu [0x%zx]", name, off, ip); } free_backtrace_symbols (symbols, frames_unwound); mono_dl_close (dl); return NULL; #else return g_strdup ("libcorkscrew is only supported on 32-bit ARM/x86"); #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 }