Thu Aug 7 15:59:35 CEST 2003 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / mini / exceptions-x86.c
1 /*
2  * exception.c: exception support
3  *
4  * Authors:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <signal.h>
13 #include <string.h>
14
15 #include <mono/arch/x86/x86-codegen.h>
16 #include <mono/metadata/appdomain.h>
17 #include <mono/metadata/tabledefs.h>
18 #include <mono/metadata/threads.h>
19 #include <mono/metadata/debug-helpers.h>
20 #include <mono/metadata/exception.h>
21 #include <mono/metadata/mono-debug.h>
22
23 #include "mini.h"
24 #include "mini-x86.h"
25
26 #ifdef PLATFORM_WIN32
27
28 #include <windows.h>
29
30 /* use SIG* defines if possible */
31 #ifdef HAVE_SIGNAL_H
32 #include <signal.h>
33 #endif
34
35 /* sigcontext surrogate */
36 struct sigcontext {
37         unsigned int eax;
38         unsigned int ebx;
39         unsigned int ecx;
40         unsigned int edx;
41         unsigned int ebp;
42         unsigned int esp;
43         unsigned int esi;
44         unsigned int edi;
45         unsigned int eip;
46 };
47
48
49 typedef void (* MonoW32ExceptionHandler) (int);
50 void win32_seh_init(void);
51 void win32_seh_cleanup(void);
52 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler);
53
54 #ifndef SIGFPE
55 #define SIGFPE 4
56 #endif
57
58 #ifndef SIGILL
59 #define SIGILL 8
60 #endif
61
62 #ifndef SIGSEGV
63 #define SIGSEGV 11
64 #endif
65
66 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep);
67
68 static MonoW32ExceptionHandler fpe_handler;
69 static MonoW32ExceptionHandler ill_handler;
70 static MonoW32ExceptionHandler segv_handler;
71
72 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
73
74 #define W32_SEH_COPY_CONTEXT \
75         sctx->eax = ctx->Eax;\
76         sctx->ebx = ctx->Ebx;\
77         sctx->ecx = ctx->Ecx;\
78         sctx->edx = ctx->Edx;\
79         sctx->ebp = ctx->Ebp;\
80         sctx->esp = ctx->Esp;\
81         sctx->esi = ctx->Esi;\
82         sctx->edi = ctx->Edi;\
83         sctx->eip = ctx->Eip;
84
85 #define W32_SEH_HANDLE_EX(_ex) \
86         if (_ex##_handler) _ex##_handler((int)sctx)
87
88 /*
89  * Unhandled Exception Filter
90  * Top-level per-process exception handler.
91  */
92 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
93 {
94         EXCEPTION_RECORD* er;
95         CONTEXT* ctx;
96         struct sigcontext* sctx;
97         LONG res;
98
99         res = EXCEPTION_CONTINUE_SEARCH;
100
101         er = ep->ExceptionRecord;
102         ctx = ep->ContextRecord;
103         sctx = g_malloc(sizeof(struct sigcontext));
104         W32_SEH_COPY_CONTEXT
105
106         switch (er->ExceptionCode) {
107         case EXCEPTION_ACCESS_VIOLATION:
108                 W32_SEH_HANDLE_EX(segv);
109                 break;
110         case EXCEPTION_ILLEGAL_INSTRUCTION:
111                 W32_SEH_HANDLE_EX(ill);
112                 break;
113         case EXCEPTION_INT_DIVIDE_BY_ZERO:
114         case EXCEPTION_INT_OVERFLOW:
115         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
116         case EXCEPTION_FLT_OVERFLOW:
117         case EXCEPTION_FLT_UNDERFLOW:
118         case EXCEPTION_FLT_INEXACT_RESULT:
119                 W32_SEH_HANDLE_EX(fpe);
120                 break;
121         default:
122                 break;
123         }
124
125         return res;
126 }
127
128 void win32_seh_init()
129 {
130         old_handler = SetUnhandledExceptionFilter(seh_handler);
131 }
132
133 void win32_seh_cleanup()
134 {
135         if (old_handler) SetUnhandledExceptionFilter(old_handler);
136 }
137
138 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
139 {
140         switch (type) {
141         case SIGFPE:
142                 fpe_handler = handler;
143                 break;
144         case SIGILL:
145                 ill_handler = handler;
146                 break;
147         case SIGSEGV:
148                 segv_handler = handler;
149                 break;
150         default:
151                 break;
152         }
153 }
154
155 #endif /* PLATFORM_WIN32 */
156
157 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
158 # define SC_EAX sc_eax
159 # define SC_EBX sc_ebx
160 # define SC_ECX sc_ecx
161 # define SC_EDX sc_edx
162 # define SC_EBP sc_ebp
163 # define SC_EIP sc_eip
164 # define SC_ESP sc_esp
165 # define SC_EDI sc_edi
166 # define SC_ESI sc_esi
167 #else
168 # define SC_EAX eax
169 # define SC_EBX ebx
170 # define SC_ECX ecx
171 # define SC_EDX edx
172 # define SC_EBP ebp
173 # define SC_EIP eip
174 # define SC_ESP esp
175 # define SC_EDI edi
176 # define SC_ESI esi
177 #endif
178
179 gboolean  mono_arch_handle_exception (struct sigcontext *ctx, gpointer obj, gboolean test_only);
180
181 typedef struct sigcontext MonoContext;
182
183 #define MONO_CONTEXT_SET_IP(ctx,ip) do { (ctx)->SC_EIP = (long)ip; } while (0); 
184 #define MONO_CONTEXT_SET_BP(ctx,bp) do { (ctx)->SC_EBP = (long)bp; } while (0); 
185
186 #define MONO_CONTEXT_GET_IP(ctx) ((gpointer)((ctx)->SC_EIP))
187 #define MONO_CONTEXT_GET_BP(ctx) ((gpointer)((ctx)->SC_EBP))
188
189 #ifdef MONO_USE_EXC_TABLES
190
191 /*************************************/
192 /*    STACK UNWINDING STUFF          */
193 /*************************************/
194
195 /* These definitions are from unwind-dw2.c in glibc 2.2.5 */
196
197 /* For x86 */
198 #define DWARF_FRAME_REGISTERS 17
199
200 typedef struct frame_state
201 {
202   void *cfa;
203   void *eh_ptr;
204   long cfa_offset;
205   long args_size;
206   long reg_or_offset[DWARF_FRAME_REGISTERS+1];
207   unsigned short cfa_reg;
208   unsigned short retaddr_column;
209   char saved[DWARF_FRAME_REGISTERS+1];
210 } frame_state;
211
212 static long
213 get_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum)
214 {
215         switch (dwarf_regnum) {
216         case X86_EAX:
217                 return ctx->SC_EAX;
218         case X86_EBX:
219                 return ctx->SC_EBX;
220         case X86_ECX:
221                 return ctx->SC_ECX;
222         case X86_EDX:
223                 return ctx->SC_EDX;
224         case X86_ESI:
225                 return ctx->SC_ESI;
226         case X86_EDI:
227                 return ctx->SC_EDI;
228         case X86_EBP:
229                 return ctx->SC_EBP;
230         case X86_ESP:
231                 return ctx->SC_ESP;
232         default:
233                 g_assert_not_reached ();
234         }
235
236         return 0;
237 }
238
239 static void
240 set_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum, long value)
241 {
242         switch (dwarf_regnum) {
243         case X86_EAX:
244                 ctx->SC_EAX = value;
245                 break;
246         case X86_EBX:
247                 ctx->SC_EBX = value;
248                 break;
249         case X86_ECX:
250                 ctx->SC_ECX = value;
251                 break;
252         case X86_EDX:
253                 ctx->SC_EDX = value;
254                 break;
255         case X86_ESI:
256                 ctx->SC_ESI = value;
257                 break;
258         case X86_EDI:
259                 ctx->SC_EDI = value;
260                 break;
261         case X86_EBP:
262                 ctx->SC_EBP = value;
263                 break;
264         case X86_ESP:
265                 ctx->SC_ESP = value;
266                 break;
267         case 8:
268                 ctx->SC_EIP = value;
269                 break;
270         default:
271                 g_assert_not_reached ();
272         }
273 }
274
275 typedef struct frame_state * (*framesf) (void *, struct frame_state *);
276
277 static framesf frame_state_for = NULL;
278
279 static gboolean inited = FALSE;
280
281 typedef char ** (*get_backtrace_symbols_type) (void *__const *__array, int __size);
282
283 static get_backtrace_symbols_type get_backtrace_symbols = NULL;
284
285 static void
286 init_frame_state_for (void)
287 {
288         GModule *module;
289
290         /*
291          * There are two versions of __frame_state_for: one in libgcc.a and the
292          * other in glibc.so. We need the version from glibc.
293          * For more info, see this:
294          * http://gcc.gnu.org/ml/gcc/2002-08/msg00192.html
295          */
296         if ((module = g_module_open ("libc.so.6", G_MODULE_BIND_LAZY))) {
297         
298                 if (!g_module_symbol (module, "__frame_state_for", (gpointer*)&frame_state_for))
299                         frame_state_for = NULL;
300
301                 if (!g_module_symbol (module, "backtrace_symbols", (gpointer*)&get_backtrace_symbols)) {
302                         get_backtrace_symbols = NULL;
303                         frame_state_for = NULL;
304                 }
305
306                 g_module_close (module);
307         }
308
309         inited = TRUE;
310 }
311
312 /* mono_arch_has_unwind_info:
313  *
314  * Tests if a function has an DWARF exception table able to restore
315  * all caller saved registers. 
316  */
317 gboolean
318 mono_arch_has_unwind_info (gconstpointer addr)
319 {
320         struct frame_state state_in;
321         struct frame_state *res;
322
323         if (!inited) 
324                 init_frame_state_for ();
325         
326         if (!frame_state_for)
327                 return FALSE;
328
329         g_assert (addr);
330
331         memset (&state_in, 0, sizeof (state_in));
332
333         /* offset 10 is just a guess, but it works for all methods tested */
334         if ((res = frame_state_for ((char *)addr + 10, &state_in))) {
335
336                 if (res->saved [X86_EBX] != 1 ||
337                     res->saved [X86_EDI] != 1 ||
338                     res->saved [X86_EBP] != 1 ||
339                     res->saved [X86_ESI] != 1) {
340                         return FALSE;
341                 }
342                 return TRUE;
343         } else {
344                 return FALSE;
345         }
346 }
347
348 struct stack_frame
349 {
350   void *next;
351   void *return_address;
352 };
353
354 static MonoJitInfo *
355 x86_unwind_native_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, struct sigcontext *ctx, 
356                          struct sigcontext *new_ctx, MonoLMF *lmf, char **trace)
357 {
358         struct stack_frame *frame;
359         gpointer max_stack;
360         MonoJitInfo *ji;
361         struct frame_state state_in;
362         struct frame_state *res;
363
364         if (trace)
365                 *trace = NULL;
366
367         if (!inited) 
368                 init_frame_state_for ();
369
370         if (!frame_state_for)
371                 return FALSE;
372
373         frame = MONO_CONTEXT_GET_BP (ctx);
374
375         max_stack = lmf && lmf->method ? lmf : jit_tls->end_of_stack;
376
377         *new_ctx = *ctx;
378
379         memset (&state_in, 0, sizeof (state_in));
380
381         while ((gpointer)frame->next < (gpointer)max_stack) {
382                 gpointer ip, addr = frame->return_address;
383                 void *cfa;
384                 char *tmp, **symbols;
385
386                 if (trace) {
387                         ip = MONO_CONTEXT_GET_IP (new_ctx);
388                         symbols = get_backtrace_symbols (&ip, 1);
389                         if (*trace)
390                                 tmp = g_strdup_printf ("%s\nin (unmanaged) %s", *trace, symbols [0]);
391                         else
392                                 tmp = g_strdup_printf ("in (unmanaged) %s", symbols [0]);
393
394                         free (symbols);
395                         g_free (*trace);
396                         *trace = tmp;
397                 }
398
399                 if ((res = frame_state_for (addr, &state_in))) {        
400                         int i;
401
402                         cfa = (gint8*) (get_sigcontext_reg (new_ctx, res->cfa_reg) + res->cfa_offset);
403                         frame = (struct stack_frame *)((gint8*)cfa - 8);
404                         for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++) {
405                                 int how = res->saved[i];
406                                 long val;
407                                 g_assert ((how == 0) || (how == 1));
408                         
409                                 if (how == 1) {
410                                         val = * (long*) ((gint8*)cfa + res->reg_or_offset[i]);
411                                         set_sigcontext_reg (new_ctx, i, val);
412                                 }
413                         }
414                         new_ctx->SC_ESP = (long)cfa;
415
416                         if (res->saved [X86_EBX] == 1 &&
417                             res->saved [X86_EDI] == 1 &&
418                             res->saved [X86_EBP] == 1 &&
419                             res->saved [X86_ESI] == 1 &&
420                             (ji = mono_jit_info_table_find (domain, frame->return_address))) {
421                                 //printf ("FRAME CFA %s\n", mono_method_full_name (ji->method, TRUE));
422                                 return ji;
423                         }
424
425                 } else {
426                         //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)));
427
428                         MONO_CONTEXT_SET_IP (new_ctx, frame->return_address);
429                         frame = frame->next;
430                         MONO_CONTEXT_SET_BP (new_ctx, frame);
431
432                         /* stop if !frame or when we detect an unexpected managed frame */
433                         if (!frame || mono_jit_info_table_find (domain, frame->return_address)) {
434                                 if (trace) {
435                                         g_free (*trace);
436                                         *trace = NULL;
437                                 }
438                                 return NULL;
439                         }
440                 }
441         }
442
443         //if (!lmf)
444         //g_assert_not_reached ();
445
446         if (trace) {
447                 g_free (*trace);
448                 *trace = NULL;
449         }
450         return NULL;
451 }
452
453 #endif
454
455 /*
456  * arch_get_restore_context:
457  *
458  * Returns a pointer to a method which restores a previously saved sigcontext.
459  */
460 static gpointer
461 arch_get_restore_context (void)
462 {
463         static guint8 *start = NULL;
464         guint8 *code;
465
466         if (start)
467                 return start;
468
469         /* restore_contect (struct sigcontext *ctx) */
470         /* we do not restore X86_EAX, X86_EDX */
471
472         start = code = g_malloc (1024);
473         
474         /* load ctx */
475         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
476
477         /* get return address, stored in EDX */
478         x86_mov_reg_membase (code, X86_EDX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4);
479         /* restore EBX */
480         x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
481         /* restore EDI */
482         x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
483         /* restore ESI */
484         x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
485         /* restore ESP */
486         x86_mov_reg_membase (code, X86_ESP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
487         /* restore EBP */
488         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
489
490         /* jump to the saved IP */
491         x86_jump_reg (code, X86_EDX);
492
493         return start;
494 }
495
496 /*
497  * arch_get_call_filter:
498  *
499  * Returns a pointer to a method which calls an exception filter. We
500  * also use this function to call finally handlers (we pass NULL as 
501  * @exc object in this case).
502  */
503 static gpointer
504 arch_get_call_filter (void)
505 {
506         static guint8 start [64];
507         static int inited = 0;
508         guint8 *code;
509
510         if (inited)
511                 return start;
512
513         inited = 1;
514         /* call_filter (struct sigcontext *ctx, unsigned long eip) */
515         code = start;
516
517         x86_push_reg (code, X86_EBP);
518         x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
519         x86_push_reg (code, X86_EBX);
520         x86_push_reg (code, X86_EDI);
521         x86_push_reg (code, X86_ESI);
522
523         /* load ctx */
524         x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
525         /* load eip */
526         x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
527         /* save EBP */
528         x86_push_reg (code, X86_EBP);
529
530         /* set new EBP */
531         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
532         /* restore registers used by global register allocation (EBX & ESI) */
533         x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
534         x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
535         x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
536
537         /* call the handler */
538         x86_call_reg (code, X86_ECX);
539
540         /* restore EBP */
541         x86_pop_reg (code, X86_EBP);
542
543         /* restore saved regs */
544         x86_pop_reg (code, X86_ESI);
545         x86_pop_reg (code, X86_EDI);
546         x86_pop_reg (code, X86_EBX);
547         x86_leave (code);
548         x86_ret (code);
549
550         g_assert ((code - start) < 64);
551         return start;
552 }
553
554 static void
555 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
556                  unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
557                  unsigned long eip,  unsigned long esp)
558 {
559         static void (*restore_context) (struct sigcontext *);
560         struct sigcontext ctx;
561
562         if (!restore_context)
563                 restore_context = arch_get_restore_context ();
564
565         /* adjust eip so that it point into the call instruction */
566         eip -= 1;
567
568         ctx.SC_ESP = esp;
569         ctx.SC_EIP = eip;
570         ctx.SC_EBP = ebp;
571         ctx.SC_EDI = edi;
572         ctx.SC_ESI = esi;
573         ctx.SC_EBX = ebx;
574         ctx.SC_EDX = edx;
575         ctx.SC_ECX = ecx;
576         ctx.SC_EAX = eax;
577         
578         mono_arch_handle_exception (&ctx, exc, FALSE);
579         restore_context (&ctx);
580
581         g_assert_not_reached ();
582 }
583
584 /**
585  * arch_get_throw_exception:
586  *
587  * Returns a function pointer which can be used to raise 
588  * exceptions. The returned function has the following 
589  * signature: void (*func) (MonoException *exc); 
590  * For example to raise an arithmetic exception you can use:
591  *
592  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
593  * x86_call_code (code, arch_get_throw_exception ()); 
594  *
595  */
596 gpointer 
597 mono_arch_get_throw_exception (void)
598 {
599         static guint8 start [24];
600         static int inited = 0;
601         guint8 *code;
602
603         if (inited)
604                 return start;
605
606         inited = 1;
607         code = start;
608
609         x86_push_reg (code, X86_ESP);
610         x86_push_membase (code, X86_ESP, 4); /* IP */
611         x86_push_membase (code, X86_ESP, 12); /* exception */
612         x86_push_reg (code, X86_EBP);
613         x86_push_reg (code, X86_EDI);
614         x86_push_reg (code, X86_ESI);
615         x86_push_reg (code, X86_EBX);
616         x86_push_reg (code, X86_EDX);
617         x86_push_reg (code, X86_ECX);
618         x86_push_reg (code, X86_EAX);
619         x86_call_code (code, throw_exception);
620         /* we should never reach this breakpoint */
621         x86_breakpoint (code);
622
623         g_assert ((code - start) < 24);
624         return start;
625 }
626
627 /**
628  * arch_get_throw_exception_by_name:
629  *
630  * Returns a function pointer which can be used to raise 
631  * corlib exceptions. The returned function has the following 
632  * signature: void (*func) (char *exc_name); 
633  * For example to raise an arithmetic exception you can use:
634  *
635  * x86_push_imm (code, "ArithmeticException"); 
636  * x86_call_code (code, arch_get_throw_exception_by_name ()); 
637  *
638  */
639 gpointer 
640 mono_arch_get_throw_exception_by_name (void)
641 {
642         static guint8 start [32];
643         static int inited = 0;
644         guint8 *code;
645
646         if (inited)
647                 return start;
648
649         inited = 1;
650         code = start;
651
652         x86_push_membase (code, X86_ESP, 4); /* exception name */
653         x86_push_imm (code, "System");
654         x86_push_imm (code, mono_defaults.exception_class->image);
655         x86_call_code (code, mono_exception_from_name);
656         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
657         /* save the newly create object (overwrite exception name)*/
658         x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
659         x86_jump_code (code, mono_arch_get_throw_exception ());
660
661         g_assert ((code - start) < 32);
662
663         return start;
664 }       
665
666 static MonoArray *
667 glist_to_array (GList *list) 
668 {
669         MonoDomain *domain = mono_domain_get ();
670         MonoArray *res;
671         int len, i;
672
673         if (!list)
674                 return NULL;
675
676         len = g_list_length (list);
677         res = mono_array_new (domain, mono_defaults.int_class, len);
678
679         for (i = 0; list; list = list->next, i++)
680                 mono_array_set (res, gpointer, i, list->data);
681
682         return res;
683 }
684
685 /* mono_arch_find_jit_info:
686  *
687  * This function is used to gather information from @ctx. It return the 
688  * MonoJitInfo of the corresponding function, unwinds one stack frame and
689  * stores the resulting context into @new_ctx. It also stores a string 
690  * describing the stack location into @trace (if not NULL), and modifies
691  * the @lmf if necessary. @native_offset return the IP offset from the 
692  * start of the function or -1 if that info is not available.
693  */
694 static MonoJitInfo *
695 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoContext *ctx, 
696                          MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
697                          gboolean *managed)
698 {
699         MonoJitInfo *ji;
700         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
701
702         ji = mono_jit_info_table_find (domain, ip);
703
704         if (trace)
705                 *trace = NULL;
706
707         if (native_offset)
708                 *native_offset = -1;
709
710         if (managed)
711                 *managed = FALSE;
712
713         if (ji != NULL) {
714                 char *source_location, *tmpaddr, *fname;
715                 gint32 address, iloffset;
716                 int offset;
717
718                 *new_ctx = *ctx;
719
720                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
721                         /* remove any unused lmf */
722                         *lmf = (*lmf)->previous_lmf;
723                 }
724
725                 address = (char *)ip - (char *)ji->code_start;
726
727                 if (native_offset)
728                         *native_offset = address;
729
730                 if (managed)
731                         if (!ji->method->wrapper_type)
732                                 *managed = TRUE;
733
734                 if (trace) {
735                         source_location = mono_debug_source_location_from_address (ji->method, address, NULL, domain);
736                         iloffset = mono_debug_il_offset_from_address (ji->method, address, domain);
737
738                         if (iloffset < 0)
739                                 tmpaddr = g_strdup_printf ("<0x%05x>", address);
740                         else
741                                 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
742                 
743                         fname = mono_method_full_name (ji->method, TRUE);
744
745                         if (source_location)
746                                 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
747                         else
748                                 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
749
750                         g_free (fname);
751                         g_free (source_location);
752                         g_free (tmpaddr);
753                 }
754                                 
755                 offset = -1;
756                 /* restore caller saved registers */
757                 if (ji->used_regs & X86_EBX_MASK) {
758                         new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
759                         offset--;
760                 }
761                 if (ji->used_regs & X86_EDI_MASK) {
762                         new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
763                         offset--;
764                 }
765                 if (ji->used_regs & X86_ESI_MASK) {
766                         new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
767                 }
768
769                 new_ctx->SC_ESP = ctx->SC_EBP;
770                 /* we substract 1, so that the IP points into the call instruction */
771                 new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1;
772                 new_ctx->SC_EBP = *((int *)ctx->SC_EBP);
773
774                 *res = *ji;
775                 return res;
776 #ifdef MONO_USE_EXC_TABLES
777         } else if ((ji = x86_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) {
778                 *res = *ji;             
779                 return res;
780 #endif
781         } else if (*lmf) {
782                 
783                 *new_ctx = *ctx;
784
785                 if (!(*lmf)->method)
786                         return (gpointer)-1;
787
788                 if (trace)
789                         *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
790                 
791                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
792                         *res = *ji;
793                 } else {
794                         memset (res, 0, sizeof (MonoJitInfo));
795                         res->method = (*lmf)->method;
796                 }
797
798                 new_ctx->SC_ESI = (*lmf)->esi;
799                 new_ctx->SC_EDI = (*lmf)->edi;
800                 new_ctx->SC_EBX = (*lmf)->ebx;
801                 new_ctx->SC_EBP = (*lmf)->ebp;
802                 new_ctx->SC_EIP = (*lmf)->eip;
803                 /* the lmf is always stored on the stack, so the following
804                  * expression points to a stack location which can be used as ESP */
805                 new_ctx->SC_ESP = (unsigned long)&((*lmf)->eip);
806
807                 *lmf = (*lmf)->previous_lmf;
808
809                 return res;
810                 
811         }
812
813         return NULL;
814 }
815
816 MonoArray *
817 ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
818 {
819         MonoDomain *domain = mono_domain_get ();
820         MonoArray *res;
821         MonoArray *ta = exc->trace_ips;
822         int i, len;
823
824         if (ta == NULL) {
825                 /* Exception is not thrown yet */
826                 return mono_array_new (domain, mono_defaults.stack_frame_class, 0);
827         }
828         
829         len = mono_array_length (ta);
830
831         res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0);
832
833         for (i = skip; i < len; i++) {
834                 MonoJitInfo *ji;
835                 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
836                 gpointer ip = mono_array_get (ta, gpointer, i);
837
838                 ji = mono_jit_info_table_find (domain, ip);
839                 if (ji == NULL) {
840                         /* Unmanaged frame */
841                         mono_array_set (res, gpointer, i, sf);
842                         continue;
843                 }
844
845                 g_assert (ji != NULL);
846
847                 sf->method = mono_method_get_object (domain, ji->method, NULL);
848                 sf->native_offset = (char *)ip - (char *)ji->code_start;
849
850                 sf->il_offset = mono_debug_il_offset_from_address (ji->method, sf->native_offset, domain);
851
852                 if (need_file_info) {
853                         gchar *filename;
854                         
855                         filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line, domain);
856
857                         sf->filename = filename? mono_string_new (domain, filename): NULL;
858                         sf->column = 0;
859
860                         g_free (filename);
861                 }
862
863                 mono_array_set (res, gpointer, i, sf);
864         }
865
866         return res;
867 }
868
869 void
870 mono_jit_walk_stack (MonoStackWalk func, gpointer user_data) {
871         MonoDomain *domain = mono_domain_get ();
872         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
873         MonoLMF *lmf = jit_tls->lmf;
874         MonoJitInfo *ji, rji;
875         gint native_offset, il_offset;
876         gboolean managed;
877
878         MonoContext ctx, new_ctx;
879
880         MONO_CONTEXT_SET_IP (&ctx, __builtin_return_address (0));
881         MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (1));
882
883         while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) {
884                 
885                 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
886                 g_assert (ji);
887
888                 if (ji == (gpointer)-1)
889                         return;
890
891                 il_offset = mono_debug_il_offset_from_address (ji->method, native_offset, domain);
892
893                 if (func (ji->method, native_offset, il_offset, managed, user_data))
894                         return;
895                 
896                 ctx = new_ctx;
897         }
898 }
899
900 MonoBoolean
901 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, 
902                           MonoReflectionMethod **method, 
903                           gint32 *iloffset, gint32 *native_offset,
904                           MonoString **file, gint32 *line, gint32 *column)
905 {
906         MonoDomain *domain = mono_domain_get ();
907         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
908         MonoLMF *lmf = jit_tls->lmf;
909         MonoJitInfo *ji, rji;
910         MonoContext ctx, new_ctx;
911
912         MONO_CONTEXT_SET_IP (&ctx, ves_icall_get_frame_info);
913         MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (0));
914
915         skip++;
916
917         do {
918                 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &ctx, &new_ctx, NULL, &lmf, native_offset, NULL);
919
920                 ctx = new_ctx;
921                 
922                 if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_BP (&ctx) >= jit_tls->end_of_stack)
923                         return FALSE;
924
925                 /* skip all wrappers ??*/
926                 if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
927                     ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK ||
928                     ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE)
929                         continue;
930
931                 skip--;
932
933         } while (skip >= 0);
934
935         *method = mono_method_get_object (domain, ji->method, NULL);
936         *iloffset = mono_debug_il_offset_from_address (ji->method, *native_offset, domain);
937
938         if (need_file_info) {
939                 gchar *filename;
940
941                 filename = mono_debug_source_location_from_address (ji->method, *native_offset, line, domain);
942
943                 *file = filename? mono_string_new (domain, filename): NULL;
944                 *column = 0;
945
946                 g_free (filename);
947         }
948
949         return TRUE;
950 }
951
952 /**
953  * arch_handle_exception:
954  * @ctx: saved processor state
955  * @obj: the exception object
956  * @test_only: only test if the exception is caught, but dont call handlers
957  *
958  *
959  */
960 gboolean
961 mono_arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
962 {
963         MonoDomain *domain = mono_domain_get ();
964         MonoJitInfo *ji, rji;
965         static int (*call_filter) (MonoContext *, gpointer) = NULL;
966         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
967         MonoLMF *lmf = jit_tls->lmf;            
968         GList *trace_ips = NULL;
969
970         g_assert (ctx != NULL);
971         if (!obj) {
972                 MonoException *ex = mono_get_exception_null_reference ();
973                 ex->message = mono_string_new (domain, 
974                         "Object reference not set to an instance of an object");
975                 obj = (MonoObject *)ex;
976         } 
977
978         g_assert (mono_object_isinst (obj, mono_defaults.exception_class));
979
980         if (!call_filter)
981                 call_filter = arch_get_call_filter ();
982
983         g_assert (jit_tls->end_of_stack);
984         g_assert (jit_tls->abort_func);
985
986         if (!test_only) {
987                 MonoContext ctx_cp = *ctx;
988                 if (mono_jit_trace_calls)
989                         g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name);
990                 if (!mono_arch_handle_exception (&ctx_cp, obj, TRUE)) {
991                         if (mono_break_on_exc)
992                                 G_BREAKPOINT ();
993                         mono_unhandled_exception (obj);
994                 }
995         }
996
997         while (1) {
998                 MonoContext new_ctx;
999                 char *trace = NULL;
1000                 
1001                 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, ctx, &new_ctx, 
1002                                               test_only ? &trace : NULL, &lmf, NULL, NULL);
1003                 if (!ji) {
1004                         g_warning ("Exception inside function without unwind info");
1005                         g_assert_not_reached ();
1006                 }
1007
1008                 if (ji != (gpointer)-1) {
1009                         
1010                         if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) {
1011                                 char *tmp, *strace;
1012
1013                                 trace_ips = g_list_append (trace_ips, MONO_CONTEXT_GET_IP (ctx));
1014
1015                                 if (!((MonoException*)obj)->stack_trace)
1016                                         strace = g_strdup ("");
1017                                 else
1018                                         strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
1019                         
1020                                 tmp = g_strdup_printf ("%s%s\n", strace, trace);
1021                                 g_free (strace);
1022
1023                                 ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
1024
1025                                 g_free (tmp);
1026                         }
1027
1028                         if (ji->num_clauses) {
1029                                 int i;
1030                                 
1031                                 g_assert (ji->clauses);
1032                         
1033                                 for (i = 0; i < ji->num_clauses; i++) {
1034                                         MonoJitExceptionInfo *ei = &ji->clauses [i];
1035
1036                                         if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
1037                                             MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) { 
1038                                                 /* catch block */
1039
1040                                                 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE) || (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)) {
1041                                                         /* store the exception object int cfg->excvar */
1042                                                         g_assert (ji->exvar_offset);
1043                                                         *((gpointer *)((char *)MONO_CONTEXT_GET_BP (ctx) + ji->exvar_offset)) = obj;
1044                                                 }
1045
1046                                                 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE && 
1047                                                      mono_object_isinst (obj, mono_class_get (ji->method->klass->image, ei->data.token))) ||
1048                                                     ((ei->flags == MONO_EXCEPTION_CLAUSE_FILTER &&
1049                                                       call_filter (ctx, ei->data.filter)))) {
1050                                                         if (test_only) {
1051                                                                 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
1052                                                                 g_list_free (trace_ips);
1053                                                                 g_free (trace);
1054                                                                 return TRUE;
1055                                                         }
1056                                                         if (mono_jit_trace_calls)
1057                                                                 g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
1058                                                         MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
1059                                                         jit_tls->lmf = lmf;
1060                                                         g_free (trace);
1061                                                         return 0;
1062                                                 }
1063                                                 if (!test_only && ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
1064                                                     MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
1065                                                     (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
1066                                                         if (mono_jit_trace_calls)
1067                                                                 g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
1068                                                         call_filter (ctx, ei->handler_start);
1069                                                 }
1070                                                 
1071                                         }
1072                                 }
1073                         }
1074                 }
1075
1076                 g_free (trace);
1077                         
1078                 *ctx = new_ctx;
1079
1080                 if ((ji == (gpointer)-1) || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) {
1081                         if (!test_only) {
1082                                 jit_tls->lmf = lmf;
1083                                 jit_tls->abort_func (obj);
1084                                 g_assert_not_reached ();
1085                         } else {
1086                                 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
1087                                 g_list_free (trace_ips);
1088                                 return FALSE;
1089                         }
1090                 }
1091         }
1092
1093         g_assert_not_reached ();
1094 }
1095
1096