2 * mini-exceptions-native-unwinder.c: libcorkscrew-based native unwinder
5 * Alex Rønne Petersen (alexrp@xamarin.com)
7 * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include <mono/utils/mono-logger-internals.h>
15 * Attempt to handle native SIGSEGVs with libunwind or libcorkscrew.
22 #include <mono/utils/mono-signal-handler.h>
25 #if defined (PLATFORM_ANDROID)
28 #include <sys/types.h>
29 #include <mono/utils/mono-dl.h>
31 #define UNW_LOCAL_ONLY
32 #undef _U /* ctype.h apparently defines this and it screws up the libunwind headers. */
33 #include "../../external/android-libunwind/include/libunwind.h"
36 #define FUNC_NAME_LENGTH 512
37 #define FRAMES_TO_UNWIND 256
39 /* Expand the SYM argument. */
40 #define LOAD_SYM(DL, ERR, SYM, VAR) _LOAD_SYM(DL, ERR, SYM, VAR)
41 #define _LOAD_SYM(DL, ERR, SYM, VAR) \
43 if ((ERR = mono_dl_symbol (DL, #SYM, (void **) &VAR))) { \
49 typedef int (*unw_init_local_t
) (unw_cursor_t
*, unw_context_t
*);
50 typedef int (*unw_get_reg_t
) (unw_cursor_t
*, int, unw_word_t
*);
51 typedef int (*unw_get_proc_name_t
) (unw_cursor_t
*, char *, size_t, unw_word_t
*);
52 typedef int (*unw_step_t
) (unw_cursor_t
*);
55 mono_extension_handle_native_sigsegv_libunwind (void *ctx
, MONO_SIG_HANDLER_INFO_TYPE
*info
)
60 unw_init_local_t unw_init_local_fn
;
61 unw_get_reg_t unw_get_reg_fn
;
62 unw_get_proc_name_t unw_get_proc_name_fn
;
63 unw_step_t unw_step_fn
;
69 MonoDl
*dl
= mono_dl_open ("libunwind.so", MONO_DL_LAZY
, &dl_err
);
74 LOAD_SYM (dl
, dl_err
, UNW_OBJ (init_local
), unw_init_local_fn
);
75 LOAD_SYM (dl
, dl_err
, UNW_OBJ (get_reg
), unw_get_reg_fn
);
76 LOAD_SYM (dl
, dl_err
, UNW_OBJ (get_proc_name
), unw_get_proc_name_fn
);
77 LOAD_SYM (dl
, dl_err
, UNW_OBJ (step
), unw_step_fn
);
79 if ((unw_err
= unw_init_local_fn (&cursor
, ctx
))) {
82 return g_strdup_printf ("unw_init_local () returned %d", unw_err
);
89 char name
[FUNC_NAME_LENGTH
];
91 if ((reg_err
= unw_get_reg_fn (&cursor
, UNW_REG_IP
, &ip
))) {
92 mono_runtime_printf_err ("unw_get_reg (UNW_REG_IP) returned %d", reg_err
);
96 reg_err
= unw_get_proc_name_fn (&cursor
, name
, FUNC_NAME_LENGTH
, &off
);
98 if (reg_err
== -UNW_ENOINFO
)
101 mono_runtime_printf_err (" at %s+%zu [0x%zx]", name
, off
, ip
);
103 unw_err
= unw_step_fn (&cursor
);
105 } while (unw_err
> 0 && frames
< FRAMES_TO_UNWIND
);
108 mono_runtime_printf_err ("unw_step () returned %d", unw_err
);
116 * This code is based on the AOSP header system/core/include/corkscrew/backtrace.h.
118 * This is copied here because libcorkscrew is not a stable library and the header (and
119 * other headers that it depends on) will eventually go away.
121 * We can probably remove this one day when libunwind becomes the norm.
125 uintptr_t absolute_pc
;
131 uintptr_t relative_pc
;
132 uintptr_t relative_symbol_addr
;
135 char *demangled_name
;
136 } backtrace_symbol_t
;
138 typedef void (*get_backtrace_symbols_t
) (const backtrace_frame_t
*backtrace
, size_t frames
, backtrace_symbol_t
*backtrace_symbols
);
139 typedef void (*free_backtrace_symbols_t
) (backtrace_symbol_t
*backtrace_symbols
, size_t frames
);
142 MAX_BACKTRACE_LINE_LENGTH
= 800,
145 /* Internals that we're exploiting to work in a signal handler. Only works on ARM/x86. */
147 typedef struct map_info_t map_info_t
;
149 typedef ssize_t (*unwind_backtrace_signal_arch_t
) (siginfo_t
*si
, void *sc
, const map_info_t
*lst
, backtrace_frame_t
*bt
, size_t ignore_depth
, size_t max_depth
);
150 typedef map_info_t
*(*acquire_my_map_info_list_t
) (void);
151 typedef void (*release_my_map_info_list_t
) (map_info_t
*milist
);
154 mono_extension_handle_native_sigsegv_libcorkscrew (void *ctx
, MONO_SIG_HANDLER_INFO_TYPE
*info
)
156 #if defined (__arm__) || defined (__i386__)
159 get_backtrace_symbols_t get_backtrace_symbols
;
160 free_backtrace_symbols_t free_backtrace_symbols
;
161 unwind_backtrace_signal_arch_t unwind_backtrace_signal_arch
;
162 acquire_my_map_info_list_t acquire_my_map_info_list
;
163 release_my_map_info_list_t release_my_map_info_list
;
165 backtrace_frame_t frames
[FRAMES_TO_UNWIND
];
166 backtrace_symbol_t symbols
[FRAMES_TO_UNWIND
];
168 map_info_t
*map_info
;
169 ssize_t frames_unwound
;
172 MonoDl
*dl
= mono_dl_open ("libcorkscrew.so", MONO_DL_LAZY
, &dl_err
);
177 LOAD_SYM (dl
, dl_err
, get_backtrace_symbols
, get_backtrace_symbols
);
178 LOAD_SYM (dl
, dl_err
, free_backtrace_symbols
, free_backtrace_symbols
);
179 LOAD_SYM (dl
, dl_err
, unwind_backtrace_signal_arch
, unwind_backtrace_signal_arch
);
180 LOAD_SYM (dl
, dl_err
, acquire_my_map_info_list
, acquire_my_map_info_list
);
181 LOAD_SYM (dl
, dl_err
, release_my_map_info_list
, release_my_map_info_list
);
183 map_info
= acquire_my_map_info_list ();
184 frames_unwound
= unwind_backtrace_signal_arch (info
, ctx
, map_info
, frames
, 0, FRAMES_TO_UNWIND
);
185 release_my_map_info_list (map_info
);
187 if (frames_unwound
== -1) {
190 return g_strdup ("unwind_backtrace_signal_arch () returned -1");
193 get_backtrace_symbols (frames
, frames_unwound
, symbols
);
195 for (i
= 0; i
< frames_unwound
; i
++) {
196 backtrace_frame_t
*frame
= frames
+ i
;
197 backtrace_symbol_t
*symbol
= symbols
+ i
;
199 const char *name
= symbol
->demangled_name
? symbol
->demangled_name
: (symbol
->symbol_name
? symbol
->symbol_name
: "???");
200 uintptr_t off
= symbol
->relative_pc
- symbol
->relative_symbol_addr
;
201 uintptr_t ip
= frame
->absolute_pc
;
203 mono_runtime_printf_err (" at %s+%zu [0x%zx]", name
, off
, ip
);
206 free_backtrace_symbols (symbols
, frames_unwound
);
212 return g_strdup ("libcorkscrew is only supported on 32-bit ARM/x86");
217 mono_exception_native_unwind (void *ctx
, MONO_SIG_HANDLER_INFO_TYPE
*info
)
219 char *unwind_err
, *corkscrew_err
;
221 mono_runtime_printf_err ("\nAttempting native Android stacktrace:\n");
223 unwind_err
= mono_extension_handle_native_sigsegv_libunwind (ctx
, info
);
226 corkscrew_err
= mono_extension_handle_native_sigsegv_libcorkscrew (ctx
, info
);
229 mono_runtime_printf_err ("\tCould not unwind with `libunwind.so`: %s", unwind_err
);
230 mono_runtime_printf_err ("\tCould not unwind with `libcorkscrew.so`: %s", corkscrew_err
);
231 mono_runtime_printf_err ("\n\tNo options left to get a native stacktrace :-(");
233 g_free (corkscrew_err
);
243 mono_exception_native_unwind (void *ctx
, MONO_SIG_HANDLER_INFO_TYPE
*info
)