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