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