2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / jit / exception.c
index 99a56d597a7c692a93a676174e12572cb6e49ae3..59b4c811a9ce0504d699148e13b69c429478a932 100644 (file)
 #include <mono/metadata/tabledefs.h>
 #include <mono/metadata/threads.h>
 #include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/mono-debug.h>
 
 #include "jit.h"
 #include "codegen.h"
-#include "debug.h"
 
 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
 # define SC_EAX sc_eax
@@ -52,6 +52,271 @@ typedef struct sigcontext MonoContext;
 #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:
  *
@@ -296,8 +561,9 @@ glist_to_array (GList *list)
  * start of the function or -1 if that info is not available.
  */
 static MonoJitInfo *
-mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContext *ctx, 
-                        MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset)
+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);
@@ -310,13 +576,16 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContex
        if (native_offset)
                *native_offset = -1;
 
-       *new_ctx = *ctx;
+       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;
@@ -327,12 +596,13 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContex
                if (native_offset)
                        *native_offset = address;
 
-               if (trace) {
-                       if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
-                               mono_debug_make_symbols ();
+               if (managed)
+                       if (!ji->method->wrapper_type)
+                               *managed = TRUE;
 
-                       source_location = mono_debug_source_location_from_address (ji->method, address, NULL);
-                       iloffset = mono_debug_il_offset_from_address (ji->method, address);
+               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);
@@ -370,15 +640,30 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContex
                new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1;
                new_ctx->SC_EBP = *((int *)ctx->SC_EBP);
 
-               return ji;
-
+               *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));
-               
-               ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip);
-               g_assert (ji != NULL);
+
+
+               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;
@@ -391,15 +676,11 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContex
 
                *lmf = (*lmf)->previous_lmf;
 
-               return ji;
+               return res;
                
-       } else {
-               /* no lmf available - usually a serious error? */
-               new_ctx->SC_EBP = (unsigned long)jit_tls->end_of_stack;
-               return NULL;
        }
 
-       g_assert_not_reached ();
+       return NULL;
 }
 
 MonoArray *
@@ -409,6 +690,11 @@ ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info
        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);
 
@@ -420,16 +706,22 @@ ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info
                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);
+               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);
+                       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;
@@ -448,8 +740,9 @@ 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;
+       MonoJitInfo *ji, rji;
        gint native_offset, il_offset;
+       gboolean managed;
 
        MonoContext ctx, new_ctx;
 
@@ -458,12 +751,15 @@ mono_jit_walk_stack (MonoStackWalk func, gpointer user_data) {
 
        while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) {
                
-               ji = mono_arch_find_jit_info (domain, jit_tls, &ctx, &new_ctx, NULL, &lmf, &native_offset);
+               ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
                g_assert (ji);
-               
-               il_offset = mono_debug_il_offset_from_address (ji->method, native_offset);
 
-               if (func (ji->method, native_offset, il_offset, user_data))
+               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;
@@ -479,30 +775,35 @@ ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
        MonoDomain *domain = mono_domain_get ();
        MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
        MonoLMF *lmf = jit_tls->lmf;
-       MonoJitInfo *ji;
+       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));
 
-       do {
-               ji = mono_arch_find_jit_info (domain, jit_tls, &ctx, &new_ctx, NULL, &lmf, native_offset);
-               g_assert (ji);
+       skip++;
 
+       do {
+               ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &ctx, &new_ctx, NULL, &lmf, native_offset, NULL);
                ctx = new_ctx;
                
-               if (MONO_CONTEXT_GET_BP (&ctx) >= jit_tls->end_of_stack)
+               if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_BP (&ctx) >= jit_tls->end_of_stack)
                        return FALSE;
 
-       } while (skip-- > 0);
+               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);
+       *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);
+               filename = mono_debug_source_location_from_address (ji->method, *native_offset, line, domain);
 
                *file = mono_string_new (domain, filename ? filename : "<unknown>");
                *column = 0;
@@ -525,7 +826,7 @@ gboolean
 arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
 {
        MonoDomain *domain = mono_domain_get ();
-       MonoJitInfo *ji;
+       MonoJitInfo *ji, rji;
        static int (*call_filter) (MonoContext *, gpointer, gpointer) = NULL;
        MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
        MonoLMF *lmf = jit_tls->lmf;            
@@ -550,11 +851,8 @@ arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
        if (!test_only) {
                MonoContext ctx_cp = *ctx;
                if (!arch_handle_exception (&ctx_cp, obj, TRUE)) {
-                       if (mono_break_on_exc) {
-                               if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
-                                       mono_debug_make_symbols ();
+                       if (mono_break_on_exc)
                                G_BREAKPOINT ();
-                       }
                        mono_unhandled_exception (obj);
                }
        }
@@ -563,13 +861,16 @@ arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
                MonoContext new_ctx;
                char *trace = NULL;
                
-               ji = mono_arch_find_jit_info (domain, jit_tls, ctx, &new_ctx, 
-                                             test_only ? &trace : NULL, &lmf, NULL);
-               g_assert (ji);
+               ji = mono_arch_find_jit_info (domain, jit_tls, &rji, ctx, &new_ctx, 
+                                             test_only ? &trace : NULL, &lmf, NULL, NULL);
 
-               if (ji->method != mono_start_method) {
-                       
-                       if (test_only) {
+               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));
@@ -578,7 +879,7 @@ arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
                                        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);
 
@@ -634,7 +935,7 @@ arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
                        
                *ctx = new_ctx;
 
-               if (ji->method == mono_start_method || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) {
+               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);
@@ -650,3 +951,4 @@ arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
        g_assert_not_reached ();
 }
 
+