Mon Jan 19 17:44:50 CET 2004 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / mini / exceptions-x86.c
index 447090bcd10af8e66d176a11b0fec5bc92dc0963..4a35c4b75ca3cfc8af533e4f37c1d087171431a2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * exception.c: exception support
+ * exceptions-x86.c: exception support for x86
  *
  * Authors:
  *   Dietmar Maurer (dietmar@ximian.com)
@@ -71,17 +71,6 @@ static MonoW32ExceptionHandler segv_handler;
 
 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
 
-#define W32_SEH_COPY_CONTEXT \
-       sctx->eax = ctx->Eax;\
-       sctx->ebx = ctx->Ebx;\
-       sctx->ecx = ctx->Ecx;\
-       sctx->edx = ctx->Edx;\
-       sctx->ebp = ctx->Ebp;\
-       sctx->esp = ctx->Esp;\
-       sctx->esi = ctx->Esi;\
-       sctx->edi = ctx->Edi;\
-       sctx->eip = ctx->Eip;
-
 #define W32_SEH_HANDLE_EX(_ex) \
        if (_ex##_handler) _ex##_handler((int)sctx)
 
@@ -96,12 +85,22 @@ LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
        struct sigcontext* sctx;
        LONG res;
 
-       res = EXCEPTION_CONTINUE_SEARCH;
+       res = EXCEPTION_CONTINUE_EXECUTION;
 
        er = ep->ExceptionRecord;
        ctx = ep->ContextRecord;
        sctx = g_malloc(sizeof(struct sigcontext));
-       W32_SEH_COPY_CONTEXT
+
+       /* Copy Win32 context to UNIX style context */
+       sctx->eax = ctx->Eax;
+       sctx->ebx = ctx->Ebx;
+       sctx->ecx = ctx->Ecx;
+       sctx->edx = ctx->Edx;
+       sctx->ebp = ctx->Ebp;
+       sctx->esp = ctx->Esp;
+       sctx->esi = ctx->Esi;
+       sctx->edi = ctx->Edi;
+       sctx->eip = ctx->Eip;
 
        switch (er->ExceptionCode) {
        case EXCEPTION_ACCESS_VIOLATION:
@@ -122,6 +121,17 @@ LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
                break;
        }
 
+       /* Copy context back */
+       ctx->Eax = sctx->eax;
+       ctx->Ebx = sctx->ebx;
+       ctx->Ecx = sctx->ecx;
+       ctx->Edx = sctx->edx;
+       ctx->Ebp = sctx->ebp;
+       ctx->Esp = sctx->esp;
+       ctx->Esi = sctx->esi;
+       ctx->Edi = sctx->edi;
+       ctx->Eip = sctx->eip;
+
        return res;
 }
 
@@ -333,16 +343,14 @@ mono_arch_has_unwind_info (gconstpointer addr)
        /* offset 10 is just a guess, but it works for all methods tested */
        if ((res = frame_state_for ((char *)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;
+               if (res->saved [X86_EBX] == 1 &&
+                   res->saved [X86_EDI] == 1 &&
+                   res->saved [X86_EBP] == 1 &&
+                   res->saved [X86_ESI] == 1)
+                       return TRUE;
        }
+
+       return FALSE;
 }
 
 struct stack_frame
@@ -511,7 +519,7 @@ arch_get_call_filter (void)
                return start;
 
        inited = 1;
-       /* call_filter (struct sigcontext *ctx, unsigned long eip, gpointer exc) */
+       /* call_filter (struct sigcontext *ctx, unsigned long eip) */
        code = start;
 
        x86_push_reg (code, X86_EBP);
@@ -526,8 +534,7 @@ arch_get_call_filter (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) */
@@ -538,7 +545,6 @@ arch_get_call_filter (void)
        /* 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);
 
@@ -567,7 +573,8 @@ throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsign
        /* adjust eip so that it point into the call instruction */
        eip -= 1;
 
-       ctx.SC_ESP = esp;
+       /* Pop argument and return address */
+       ctx.SC_ESP = esp + (2 * sizeof (gpointer));
        ctx.SC_EIP = eip;
        ctx.SC_EBP = ebp;
        ctx.SC_EDI = edi;
@@ -719,11 +726,6 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
 
                *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)
@@ -753,22 +755,46 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
                        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--;
+
+               /*
+                * Some managed methods like pinvoke wrappers might have save_lmf set.
+                * In this case, register save/restore code is not generated by the 
+                * JIT, so we have to restore callee saved registers from the lmf.
+                */
+               if (ji->method->save_lmf) {
+                       /* 
+                        * We only need to do this if the exception was raised in managed
+                        * code, since otherwise the lmf was already popped of the stack.
+                        */
+                       if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
+                               new_ctx->SC_ESI = (*lmf)->esi;
+                               new_ctx->SC_EDI = (*lmf)->edi;
+                               new_ctx->SC_EBX = (*lmf)->ebx;
+                       }
                }
-               if (ji->used_regs & X86_EDI_MASK) {
-                       new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
-                       offset--;
+               else {
+                       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);
+                       }
                }
-               if (ji->used_regs & X86_ESI_MASK) {
-                       new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
+
+               if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
+                       /* remove any unused lmf */
+                       *lmf = (*lmf)->previous_lmf;
                }
 
-               new_ctx->SC_ESP = ctx->SC_EBP;
+               /* Pop EBP and the return address */
+               new_ctx->SC_ESP = ctx->SC_EBP + (2 * sizeof (gpointer));
                /* 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);
@@ -964,10 +990,11 @@ mono_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;
+       static int (*call_filter) (MonoContext *, gpointer) = NULL;
        MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
        MonoLMF *lmf = jit_tls->lmf;            
        GList *trace_ips = NULL;
+       MonoException *mono_ex;
 
        g_assert (ctx != NULL);
        if (!obj) {
@@ -977,7 +1004,12 @@ mono_arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
                obj = (MonoObject *)ex;
        } 
 
-       g_assert (mono_object_isinst (obj, mono_defaults.exception_class));
+       if (mono_object_isinst (obj, mono_defaults.exception_class)) {
+               mono_ex = (MonoException*)obj;
+               mono_ex->stack_trace = NULL;
+       } else {
+               mono_ex = NULL;
+       }
 
        if (!call_filter)
                call_filter = arch_get_call_filter ();
@@ -987,7 +1019,7 @@ mono_arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
 
        if (!test_only) {
                MonoContext ctx_cp = *ctx;
-               if (mono_jit_trace_calls)
+               if (mono_jit_trace_calls != NULL)
                        g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name);
                if (!mono_arch_handle_exception (&ctx_cp, obj, TRUE)) {
                        if (mono_break_on_exc)
@@ -1009,20 +1041,20 @@ mono_arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
 
                if (ji != (gpointer)-1) {
                        
-                       if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) {
+                       if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE && mono_ex) {
                                char *tmp, *strace;
 
                                trace_ips = g_list_append (trace_ips, MONO_CONTEXT_GET_IP (ctx));
 
-                               if (!((MonoException*)obj)->stack_trace)
+                               if (!mono_ex->stack_trace)
                                        strace = g_strdup ("");
                                else
-                                       strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
+                                       strace = mono_string_to_utf8 (mono_ex->stack_trace);
                        
                                tmp = g_strdup_printf ("%s%s\n", strace, trace);
                                g_free (strace);
 
-                               ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
+                               mono_ex->stack_trace = mono_string_new (domain, tmp);
 
                                g_free (tmp);
                        }
@@ -1038,20 +1070,27 @@ mono_arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
                                        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) || (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)) {
+                                                       /* store the exception object int cfg->excvar */
+                                                       g_assert (ji->exvar_offset);
+                                                       *((gpointer *)((char *)MONO_CONTEXT_GET_BP (ctx) + ji->exvar_offset)) = obj;
+                                               }
+
                                                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)))) {
+                                                     call_filter (ctx, ei->data.filter)))) {
                                                        if (test_only) {
-                                                               ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
+                                                               if (mono_ex)
+                                                                       mono_ex->trace_ips = glist_to_array (trace_ips);
                                                                g_list_free (trace_ips);
                                                                g_free (trace);
                                                                return TRUE;
                                                        }
-                                                       if (mono_jit_trace_calls)
+                                                       if (mono_jit_trace_calls != NULL && mono_trace_eval (ji->method))
                                                                g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
                                                        MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
-                                                       *((gpointer *)((char *)MONO_CONTEXT_GET_BP (ctx) + ji->exvar_offset)) = obj;
                                                        jit_tls->lmf = lmf;
                                                        g_free (trace);
                                                        return 0;
@@ -1059,9 +1098,9 @@ mono_arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
                                                if (!test_only && ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
                                                    MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
                                                    (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
-                                                       if (mono_jit_trace_calls)
+                                                       if (mono_jit_trace_calls != NULL && mono_trace_eval (ji->method))
                                                                g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
-                                                       call_filter (ctx, ei->handler_start, NULL);
+                                                       call_filter (ctx, ei->handler_start);
                                                }
                                                
                                        }
@@ -1079,7 +1118,8 @@ mono_arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
                                jit_tls->abort_func (obj);
                                g_assert_not_reached ();
                        } else {
-                               ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
+                               if (mono_ex)
+                                       mono_ex->trace_ips = glist_to_array (trace_ips);
                                g_list_free (trace_ips);
                                return FALSE;
                        }