+
+static MonoArray *
+glist_to_array (GList *list)
+{
+ MonoDomain *domain = mono_domain_get ();
+ MonoArray *res;
+ int len, i;
+
+ if (!list)
+ return NULL;
+
+ len = g_list_length (list);
+ res = mono_array_new (domain, mono_defaults.int_class, len);
+
+ for (i = 0; list; list = list->next, i++)
+ mono_array_set (res, gpointer, i, list->data);
+
+ return res;
+}
+
+/* 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.
+ */
+static MonoJitInfo *
+mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoContext *ctx,
+ MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
+ gboolean *managed)
+{
+ MonoJitInfo *ji;
+ gpointer ip = MONO_CONTEXT_GET_IP (ctx);
+
+ ji = mono_jit_info_table_find (domain, ip);
+
+ if (trace)
+ *trace = NULL;
+
+ if (native_offset)
+ *native_offset = -1;
+
+ if (managed)
+ *managed = FALSE;
+
+ if (ji != NULL) {
+ char *source_location, *tmpaddr, *fname;
+ gint32 address, iloffset;
+ int offset;
+
+ *new_ctx = *ctx;
+
+ 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 (native_offset)
+ *native_offset = address;
+
+ if (managed)
+ if (!ji->method->wrapper_type)
+ *managed = TRUE;
+
+ if (trace) {
+ source_location = mono_debug_source_location_from_address (ji->method, address, NULL, domain);
+ iloffset = mono_debug_il_offset_from_address (ji->method, address, domain);
+
+ if (iloffset < 0)
+ tmpaddr = g_strdup_printf ("<0x%05x>", address);
+ else
+ tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
+
+ fname = mono_method_full_name (ji->method, TRUE);
+
+ if (source_location)
+ *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
+ else
+ *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
+
+ g_free (fname);
+ g_free (source_location);
+ g_free (tmpaddr);
+ }
+
+ offset = -1;
+ /* restore caller saved registers */
+ if (ji->used_regs & X86_EBX_MASK) {
+ new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
+ offset--;
+ }
+ if (ji->used_regs & X86_EDI_MASK) {
+ new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
+ offset--;
+ }
+ if (ji->used_regs & X86_ESI_MASK) {
+ new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
+ }
+
+ new_ctx->SC_ESP = ctx->SC_EBP;
+ /* we substract 1, so that the IP points into the call instruction */
+ new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1;
+ new_ctx->SC_EBP = *((int *)ctx->SC_EBP);
+
+ *res = *ji;
+ return res;
+#ifdef MONO_USE_EXC_TABLES
+ } else if ((ji = x86_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) {
+ *res = *ji;
+ return res;
+#endif
+ } else if (*lmf) {
+
+ *new_ctx = *ctx;
+
+ if (!(*lmf)->method)
+ return (gpointer)-1;
+
+ if (trace)
+ *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
+
+
+ if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
+ *res = *ji;
+ } else {
+ memset (res, 0, sizeof (MonoJitInfo));
+ res->method = (*lmf)->method;
+ }
+
+ new_ctx->SC_ESI = (*lmf)->esi;
+ new_ctx->SC_EDI = (*lmf)->edi;
+ new_ctx->SC_EBX = (*lmf)->ebx;
+ new_ctx->SC_EBP = (*lmf)->ebp;
+ new_ctx->SC_EIP = (*lmf)->eip;
+ /* 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->SC_ESP = (unsigned long)&((*lmf)->eip);
+
+ *lmf = (*lmf)->previous_lmf;
+
+ return res;
+
+ }
+
+ return NULL;
+}
+
+MonoArray *
+ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
+{
+ MonoDomain *domain = mono_domain_get ();
+ MonoArray *res;
+ MonoArray *ta = exc->trace_ips;
+ int i, len;
+
+ if (ta == NULL) {
+ /* Exception is not thrown yet */
+ return mono_array_new (domain, mono_defaults.stack_frame_class, 0);
+ }
+
+ len = mono_array_length (ta);
+
+ res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0);
+
+ for (i = skip; i < len; i++) {
+ MonoJitInfo *ji;
+ MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
+ gpointer ip = mono_array_get (ta, gpointer, i);
+
+ ji = mono_jit_info_table_find (domain, ip);
+ if (ji == NULL) {
+ /* Unmanaged frame */
+ mono_array_set (res, gpointer, i, sf);
+ continue;
+ }
+
+ g_assert (ji != NULL);
+
+ sf->method = mono_method_get_object (domain, ji->method, NULL);
+ sf->native_offset = (char *)ip - (char *)ji->code_start;
+ sf->il_offset = mono_debug_il_offset_from_address (ji->method, sf->native_offset, domain);
+
+ if (need_file_info) {
+ gchar *filename;
+
+ filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line, domain);
+
+ sf->filename = mono_string_new (domain, filename ? filename : "<unknown>");
+ sf->column = 0;
+
+ g_free (filename);
+ }
+
+ mono_array_set (res, gpointer, i, sf);
+ }
+
+ return res;
+}
+
+void
+mono_jit_walk_stack (MonoStackWalk func, gpointer user_data) {
+ MonoDomain *domain = mono_domain_get ();
+ MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+ MonoLMF *lmf = jit_tls->lmf;
+ MonoJitInfo *ji, rji;
+ gint native_offset, il_offset;
+ gboolean managed;
+
+ MonoContext ctx, new_ctx;
+
+ MONO_CONTEXT_SET_IP (&ctx, __builtin_return_address (0));
+ MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (1));
+
+ while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) {
+
+ ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
+ g_assert (ji);
+
+ if (ji == (gpointer)-1)
+ return;
+
+ il_offset = mono_debug_il_offset_from_address (ji->method, native_offset, domain);
+
+ if (func (ji->method, native_offset, il_offset, managed, user_data))
+ return;
+
+ ctx = new_ctx;
+ }
+}
+
+MonoBoolean
+ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
+ MonoReflectionMethod **method,
+ gint32 *iloffset, gint32 *native_offset,
+ MonoString **file, gint32 *line, gint32 *column)
+{
+ MonoDomain *domain = mono_domain_get ();
+ MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+ MonoLMF *lmf = jit_tls->lmf;
+ MonoJitInfo *ji, rji;
+ MonoContext ctx, new_ctx;
+
+ MONO_CONTEXT_SET_IP (&ctx, ves_icall_get_frame_info);
+ MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (0));
+
+ skip++;
+
+ do {
+ ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &ctx, &new_ctx, NULL, &lmf, native_offset, NULL);
+ ctx = new_ctx;
+
+ if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_BP (&ctx) >= jit_tls->end_of_stack)
+ return FALSE;
+
+ if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE)
+ continue;
+
+ skip--;
+
+ } while (skip >= 0);
+
+ *method = mono_method_get_object (domain, ji->method, NULL);
+ *iloffset = mono_debug_il_offset_from_address (ji->method, *native_offset, domain);
+
+ if (need_file_info) {
+ gchar *filename;
+
+ filename = mono_debug_source_location_from_address (ji->method, *native_offset, line, domain);
+
+ *file = mono_string_new (domain, filename ? filename : "<unknown>");
+ *column = 0;
+
+ g_free (filename);
+ }
+
+ return TRUE;
+}
+
+/**
+ * arch_handle_exception:
+ * @ctx: saved processor state
+ * @obj: the exception object
+ * @test_only: only test if the exception is caught, but dont call handlers
+ *
+ *
+ */
+gboolean
+arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
+{
+ MonoDomain *domain = mono_domain_get ();
+ MonoJitInfo *ji, rji;
+ static int (*call_filter) (MonoContext *, gpointer, gpointer) = NULL;
+ MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
+ MonoLMF *lmf = jit_tls->lmf;
+ GList *trace_ips = NULL;
+
+ g_assert (ctx != NULL);
+ if (!obj) {
+ MonoException *ex = mono_get_exception_null_reference ();
+ ex->message = mono_string_new (domain,
+ "Object reference not set to an instance of an object");
+ obj = (MonoObject *)ex;
+ }
+
+ g_assert (mono_object_isinst (obj, mono_defaults.exception_class));
+
+ if (!call_filter)
+ call_filter = arch_get_call_filter ();
+
+ g_assert (jit_tls->end_of_stack);
+ g_assert (jit_tls->abort_func);
+
+ if (!test_only) {
+ MonoContext ctx_cp = *ctx;
+ if (!arch_handle_exception (&ctx_cp, obj, TRUE)) {
+ if (mono_break_on_exc)
+ G_BREAKPOINT ();
+ mono_unhandled_exception (obj);
+ }
+ }
+
+ while (1) {
+ MonoContext new_ctx;
+ char *trace = NULL;
+
+ ji = mono_arch_find_jit_info (domain, jit_tls, &rji, ctx, &new_ctx,
+ test_only ? &trace : NULL, &lmf, NULL, NULL);
+
+ if (!ji) {
+ g_warning ("Exception inside function without unwind info");
+ g_assert_not_reached ();
+ }
+
+ if (ji != (gpointer)-1) {
+ if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) {
+ char *tmp, *strace;
+
+ trace_ips = g_list_append (trace_ips, MONO_CONTEXT_GET_IP (ctx));
+
+ if (!((MonoException*)obj)->stack_trace)
+ strace = g_strdup ("");
+ else
+ strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
+
+ tmp = g_strdup_printf ("%s%s\n", strace, trace);
+ g_free (strace);
+
+ ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
+
+ g_free (tmp);
+ }
+
+ if (ji->num_clauses) {
+ int i;
+
+ g_assert (ji->clauses);
+
+ for (i = 0; i < ji->num_clauses; i++) {
+ MonoJitExceptionInfo *ei = &ji->clauses [i];
+
+ if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
+ MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) {
+ /* catch block */
+ if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE &&
+ mono_object_isinst (obj, mono_class_get (ji->method->klass->image, ei->data.token))) ||
+ ((ei->flags == MONO_EXCEPTION_CLAUSE_FILTER &&
+ call_filter (ctx, ei->data.filter, obj)))) {
+ if (test_only) {
+ ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
+ g_list_free (trace_ips);
+ return TRUE;
+ }
+ MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
+ MONO_CONTEXT_SET_EXCREG (ctx, obj);
+ jit_tls->lmf = lmf;
+ return 0;
+ }
+ }
+ }
+
+ /* no handler found - we need to call all finally handlers */
+ if (!test_only) {
+ for (i = 0; i < ji->num_clauses; i++) {
+ MonoJitExceptionInfo *ei = &ji->clauses [i];
+
+ if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
+ MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
+ (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
+ call_filter (ctx, ei->handler_start, NULL);
+ }
+ }
+ }
+ }
+ }
+
+ g_free (trace);
+
+ *ctx = new_ctx;
+
+ if ((ji == (gpointer)-1) || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) {
+ if (!test_only) {
+ jit_tls->lmf = lmf;
+ jit_tls->abort_func (obj);
+ g_assert_not_reached ();
+ } else {
+ ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
+ g_list_free (trace_ips);
+ return FALSE;
+ }
+ }
+ }
+
+ g_assert_not_reached ();
+}
+
+