X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fjit%2Fexception.c;h=59b4c811a9ce0504d699148e13b69c429478a932;hb=c39145af2464b19374fac41b252e07480ae1a197;hp=45699a9199a20751db031fc89532a2e578cdcc02;hpb=ed93954e7696781b5917c8bfae39d3b96ccdbf7a;p=mono.git diff --git a/mono/jit/exception.c b/mono/jit/exception.c index 45699a9199a..59b4c811a9c 100644 --- a/mono/jit/exception.c +++ b/mono/jit/exception.c @@ -9,17 +9,19 @@ #include #include +#include #include #include #include #include #include +#include #include "jit.h" #include "codegen.h" -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) # define SC_EAX sc_eax # define SC_EBX sc_ebx # define SC_ECX sc_ecx @@ -41,6 +43,280 @@ # define SC_ESI esi #endif +typedef struct sigcontext MonoContext; + +#define MONO_CONTEXT_SET_IP(ctx,ip) do { (ctx)->SC_EIP = (long)ip; } while (0); +#define MONO_CONTEXT_SET_BP(ctx,bp) do { (ctx)->SC_EBP = (long)bp; } while (0); +#define MONO_CONTEXT_SET_EXCREG(ctx,exc) do { (ctx)->SC_ECX = (long)exc; } while (0); + +#define MONO_CONTEXT_GET_IP(ctx) ((gpointer)((ctx)->SC_EIP)) +#define MONO_CONTEXT_GET_BP(ctx) ((gpointer)((ctx)->SC_EBP)) + +#ifdef MONO_USE_EXC_TABLES + +/*************************************/ +/* STACK UNWINDING STUFF */ +/*************************************/ + +/* These definitions are from unwind-dw2.c in glibc 2.2.5 */ + +/* For x86 */ +#define DWARF_FRAME_REGISTERS 17 + +typedef struct frame_state +{ + void *cfa; + void *eh_ptr; + long cfa_offset; + long args_size; + long reg_or_offset[DWARF_FRAME_REGISTERS+1]; + unsigned short cfa_reg; + unsigned short retaddr_column; + char saved[DWARF_FRAME_REGISTERS+1]; +} frame_state; + +static long +get_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum) +{ + switch (dwarf_regnum) { + case X86_EAX: + return ctx->eax; + case X86_EBX: + return ctx->ebx; + case X86_ECX: + return ctx->ecx; + case X86_EDX: + return ctx->edx; + case X86_ESI: + return ctx->esi; + case X86_EDI: + return ctx->edi; + case X86_EBP: + return ctx->ebp; + case X86_ESP: + return ctx->esp; + default: + g_assert_not_reached (); + } + + return 0; +} + +static void +set_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum, long value) +{ + switch (dwarf_regnum) { + case X86_EAX: + ctx->eax = value; + break; + case X86_EBX: + ctx->ebx = value; + break; + case X86_ECX: + ctx->ecx = value; + break; + case X86_EDX: + ctx->edx = value; + break; + case X86_ESI: + ctx->esi = value; + break; + case X86_EDI: + ctx->edi = value; + break; + case X86_EBP: + ctx->ebp = value; + break; + case X86_ESP: + ctx->esp = value; + break; + case 8: + ctx->eip = value; + break; + default: + g_assert_not_reached (); + } +} + +typedef struct frame_state * (*framesf) (void *, struct frame_state *); + +static framesf frame_state_for = NULL; + +static gboolean inited = FALSE; + +typedef char ** (*get_backtrace_symbols_type) (void *__const *__array, int __size); + +static get_backtrace_symbols_type get_backtrace_symbols = NULL; + +static void +init_frame_state_for (void) +{ + GModule *module; + + /* + * There are two versions of __frame_state_for: one in libgcc.a and the + * other in glibc.so. We need the version from glibc. + * For more info, see this: + * http://gcc.gnu.org/ml/gcc/2002-08/msg00192.html + */ + if ((module = g_module_open ("libc.so.6", G_MODULE_BIND_LAZY))) { + + if (!g_module_symbol (module, "__frame_state_for", (gpointer*)&frame_state_for)) + frame_state_for = NULL; + + if (!g_module_symbol (module, "backtrace_symbols", (gpointer*)&get_backtrace_symbols)) { + get_backtrace_symbols = NULL; + frame_state_for = NULL; + } + + g_module_close (module); + } + + inited = TRUE; +} + +/* mono_has_unwind_info: + * + * Tests if a function has an DWARF exception table able to restore + * all caller saved registers. + */ +gboolean +mono_has_unwind_info (MonoMethod *method) +{ + struct frame_state state_in; + struct frame_state *res; + + if (!inited) + init_frame_state_for (); + + if (!frame_state_for) + return FALSE; + + g_assert (method->addr); + + memset (&state_in, 0, sizeof (state_in)); + + /* offset 10 is just a guess, but it works for all methods tested */ + if ((res = frame_state_for ((char *)method->addr + 10, &state_in))) { + + if (res->saved [X86_EBX] != 1 || + res->saved [X86_EDI] != 1 || + res->saved [X86_EBP] != 1 || + res->saved [X86_ESI] != 1) { + return FALSE; + } + return TRUE; + } else + return FALSE; +} + +struct stack_frame +{ + void *next; + void *return_address; +}; + +static MonoJitInfo * +x86_unwind_native_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, struct sigcontext *ctx, + struct sigcontext *new_ctx, MonoLMF *lmf, char **trace) +{ + struct stack_frame *frame; + gpointer max_stack; + MonoJitInfo *ji; + struct frame_state state_in; + struct frame_state *res; + + if (trace) + *trace = NULL; + + if (!inited) + init_frame_state_for (); + + if (!frame_state_for) + return FALSE; + + frame = MONO_CONTEXT_GET_BP (ctx); + + max_stack = lmf && lmf->method ? lmf : jit_tls->end_of_stack; + + *new_ctx = *ctx; + + memset (&state_in, 0, sizeof (state_in)); + + while ((gpointer)frame->next < (gpointer)max_stack) { + gpointer ip, addr = frame->return_address; + void *cfa; + char *tmp, **symbols; + + if (trace) { + ip = MONO_CONTEXT_GET_IP (new_ctx); + symbols = get_backtrace_symbols (&ip, 1); + if (*trace) + tmp = g_strdup_printf ("%s\nin (unmanaged) %s", *trace, symbols [0]); + else + tmp = g_strdup_printf ("in (unmanaged) %s", symbols [0]); + + free (symbols); + g_free (*trace); + *trace = tmp; + } + + if ((res = frame_state_for (addr, &state_in))) { + int i; + + cfa = (gint8*) (get_sigcontext_reg (new_ctx, res->cfa_reg) + res->cfa_offset); + frame = (struct stack_frame *)((gint8*)cfa - 8); + for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++) { + int how = res->saved[i]; + long val; + g_assert ((how == 0) || (how == 1)); + + if (how == 1) { + val = * (long*) ((gint8*)cfa + res->reg_or_offset[i]); + set_sigcontext_reg (new_ctx, i, val); + } + } + new_ctx->esp = (long)cfa; + + if (res->saved [X86_EBX] == 1 && + res->saved [X86_EDI] == 1 && + res->saved [X86_EBP] == 1 && + res->saved [X86_ESI] == 1 && + (ji = mono_jit_info_table_find (domain, frame->return_address))) { + //printf ("FRAME CFA %s\n", mono_method_full_name (ji->method, TRUE)); + return ji; + } + + } else { + //printf ("FRAME %p %p %p\n", frame, MONO_CONTEXT_GET_IP (new_ctx), mono_jit_info_table_find (domain, MONO_CONTEXT_GET_IP (new_ctx))); + + MONO_CONTEXT_SET_IP (new_ctx, frame->return_address); + frame = frame->next; + MONO_CONTEXT_SET_BP (new_ctx, frame); + + /* stop if !frame or when we detect an unexpected managed frame */ + if (!frame || mono_jit_info_table_find (domain, frame->return_address)) { + if (trace) { + g_free (*trace); + *trace = NULL; + } + return NULL; + } + } + } + + //if (!lmf) + //g_assert_not_reached (); + + if (trace) { + g_free (*trace); + *trace = NULL; + } + return NULL; +} + +#endif + /* * arch_get_restore_context: * @@ -85,14 +361,16 @@ arch_get_restore_context (void) } /* - * arch_get_call_finally: + * arch_get_call_filter: * - * Returns a pointer to a method which calls a finally handler. + * Returns a pointer to a method which calls an exception filter. We + * also use this function to call finally handlers (we pass NULL as + * @exc object in this case). */ static gpointer -arch_get_call_finally (void) +arch_get_call_filter (void) { - static guint8 start [28]; + static guint8 start [64]; static int inited = 0; guint8 *code; @@ -100,7 +378,7 @@ arch_get_call_finally (void) return start; inited = 1; - /* call_finally (struct sigcontext *ctx, unsigned long eip) */ + /* call_filter (struct sigcontext *ctx, unsigned long eip, gpointer exc) */ code = start; x86_push_reg (code, X86_EBP); @@ -115,10 +393,18 @@ arch_get_call_finally (void) x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4); /* save EBP */ x86_push_reg (code, X86_EBP); + /* push exc */ + x86_push_membase (code, X86_EBP, 16); /* set new EBP */ x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4); + /* restore registers used by global register allocation (EBX & ESI) */ + x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4); + x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4); + /* save the ESP - this is used by endfinally */ + x86_mov_membase_reg (code, X86_EBP, mono_exc_esp_offset, X86_ESP, 4); /* call the handler */ x86_call_reg (code, X86_ECX); + x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4); /* restore EBP */ x86_pop_reg (code, X86_EBP); /* restore saved regs */ @@ -128,180 +414,23 @@ arch_get_call_finally (void) x86_leave (code); x86_ret (code); - g_assert ((code - start) < 28); + g_assert ((code - start) < 64); return start; } - -/** - * arch_handle_exception: - * @ctx: saved processor state - * @obj: - */ -void -arch_handle_exception (struct sigcontext *ctx, gpointer obj) -{ - MonoDomain *domain = mono_domain_get (); - MonoJitInfo *ji; - gpointer ip = (gpointer)ctx->SC_EIP; - static void (*restore_context) (struct sigcontext *); - static void (*call_finally) (struct sigcontext *, unsigned long); - void (*cleanup) (MonoObject *exc); - MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); - gpointer end_of_stack; - - g_assert (ctx != NULL); - g_assert (obj != NULL); - - ji = mono_jit_info_table_find (domain, ip); - - end_of_stack = jit_tls->end_of_stack; - - if (!restore_context) - restore_context = arch_get_restore_context (); - - if (!call_finally) - call_finally = arch_get_call_finally (); - - cleanup = jit_tls->abort_func; - - if (ji) { /* we are inside managed code */ - MonoMethod *m = ji->method; - int offset = 2; - - 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 <= ip && ip <= (ei->try_end)) { - /* catch block */ - if (ei->flags == 0 && mono_object_isinst (obj, - mono_class_get (m->klass->image, ei->token_or_filter))) { - - ctx->SC_EIP = (unsigned long)ei->handler_start; - ctx->SC_ECX = (unsigned long)obj; - restore_context (ctx); - g_assert_not_reached (); - } - } - } - - /* no handler found - we need to call all finally handlers */ - for (i = 0; i < ji->num_clauses; i++) { - MonoJitExceptionInfo *ei = &ji->clauses [i]; - - if (ei->try_start <= ip && ip < (ei->try_end) && - (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) { - call_finally (ctx, (unsigned long)ei->handler_start); - } - } - } - - if (mono_object_isinst (obj, mono_defaults.exception_class)) { - char *strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace); - char *tmp, *tmpsig; - - if (!strcmp (strace, "TODO: implement stack traces")){ - g_free (strace); - strace = g_strdup (""); - } - - tmpsig = mono_signature_get_desc(m->signature, TRUE); - tmp = g_strdup_printf ("%sin %04x %s.%s:%s (%s)\n", strace, - (char *)ip - (char *)ji->code_start, - m->klass->name_space, m->klass->name, m->name, tmpsig); - g_free (tmpsig); - g_free (strace); - - ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp); - g_free (tmp); - } - - /* continue unwinding */ - - /* restore caller saved registers */ - if (ji->used_regs & X86_ESI_MASK) { - ctx->SC_ESI = *((int *)ctx->SC_EBP + offset); - offset++; - } - if (ji->used_regs & X86_EDI_MASK) { - ctx->SC_EDI = *((int *)ctx->SC_EBP + offset); - offset++; - } - if (ji->used_regs & X86_EBX_MASK) { - ctx->SC_EBX = *((int *)ctx->SC_EBP + offset); - } - - ctx->SC_ESP = ctx->SC_EBP; - ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 5; - ctx->SC_EBP = *((int *)ctx->SC_EBP); - - if (ctx->SC_EBP < (unsigned)end_of_stack) - arch_handle_exception (ctx, obj); - else { - g_assert (cleanup); - cleanup (obj); - } - - } else { - MonoLMF *lmf = jit_tls->lmf; - MonoMethod *m; - - if (!lmf) { - g_assert (cleanup); - cleanup (obj); - } - m = lmf->method; - - jit_tls->lmf = lmf->previous_lmf; - - ctx->SC_ESI = lmf->esi; - ctx->SC_EDI = lmf->edi; - ctx->SC_EBX = lmf->ebx; - ctx->SC_EBP = lmf->ebp; - ctx->SC_EIP = lmf->eip; - ctx->SC_ESP = (unsigned long)&lmf->eip; - - if (mono_object_isinst (obj, mono_defaults.exception_class)) { - char *strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace); - char *tmp; - - if (!strcmp (strace, "TODO: implement stack traces")) - strace = g_strdup (""); - - tmp = g_strdup_printf ("%sin (unmanaged) %s.%s:%s ()\n", strace, m->klass->name_space, - m->klass->name, m->name); - - g_free (strace); - - ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp); - g_free (tmp); - } - - if (ctx->SC_EBP < (unsigned)end_of_stack) - arch_handle_exception (ctx, obj); - else { - g_assert (cleanup); - cleanup (obj); - } - } - - g_assert_not_reached (); -} - static void throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx, unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc, unsigned long eip, unsigned long esp) { + static void (*restore_context) (struct sigcontext *); struct sigcontext ctx; - /* adjust eip so that it point to the call instruction */ - eip -= 5; + if (!restore_context) + restore_context = arch_get_restore_context (); + + /* adjust eip so that it point into the call instruction */ + eip -= 1; ctx.SC_ESP = esp; ctx.SC_EIP = eip; @@ -313,7 +442,8 @@ throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsign ctx.SC_ECX = ecx; ctx.SC_EAX = eax; - arch_handle_exception (&ctx, exc); + arch_handle_exception (&ctx, exc, FALSE); + restore_context (&ctx); g_assert_not_reached (); } @@ -401,3 +531,424 @@ arch_get_throw_exception_by_name () return start; } + +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 : ""); + 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 : ""); + *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 (); +} + +