2009-08-11 Gonzalo Paniagua Javier <gonzalo@novell.com>
[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 #include <mono/utils/mono-mmap.h>
24
25 #include "mini.h"
26 #include "mini-x86.h"
27 #include "tasklets.h"
28 #include "debug-mini.h"
29
30 #ifdef PLATFORM_WIN32
31 static void (*restore_stack) (void *);
32
33 static MonoW32ExceptionHandler fpe_handler;
34 static MonoW32ExceptionHandler ill_handler;
35 static MonoW32ExceptionHandler segv_handler;
36
37 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
38
39 #define W32_SEH_HANDLE_EX(_ex) \
40         if (_ex##_handler) _ex##_handler((int)sctx)
41
42 /*
43  * mono_win32_get_handle_stackoverflow (void):
44  *
45  * Returns a pointer to a method which restores the current context stack
46  * and calls handle_exceptions, when done restores the original stack.
47  */
48 static gpointer
49 mono_win32_get_handle_stackoverflow (void)
50 {
51         static guint8 *start = NULL;
52         guint8 *code;
53
54         if (start)
55                 return start;
56
57         /* restore_contect (void *sigctx) */
58         start = code = mono_global_codeman_reserve (128);
59
60         /* load context into ebx */
61         x86_mov_reg_membase (code, X86_EBX, X86_ESP, 4, 4);
62
63         /* move current stack into edi for later restore */
64         x86_mov_reg_reg (code, X86_EDI, X86_ESP, 4);
65
66         /* use the new freed stack from sigcontext */
67         x86_mov_reg_membase (code, X86_ESP, X86_EBX,  G_STRUCT_OFFSET (struct sigcontext, esp), 4);
68
69         /* get the current domain */
70         x86_call_code (code, mono_domain_get);
71
72         /* get stack overflow exception from domain object */
73         x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoDomain, stack_overflow_ex), 4);
74
75         /* call mono_arch_handle_exception (sctx, stack_overflow_exception_obj, FALSE) */
76         x86_push_imm (code, 0);
77         x86_push_reg (code, X86_EAX);
78         x86_push_reg (code, X86_EBX);
79         x86_call_code (code, mono_arch_handle_exception);
80
81         /* restore the SEH handler stack */
82         x86_mov_reg_reg (code, X86_ESP, X86_EDI, 4);
83
84         /* return */
85         x86_ret (code);
86
87         return start;
88 }
89
90 /* Special hack to workaround the fact that the
91  * when the SEH handler is called the stack is
92  * to small to recover.
93  *
94  * Stack walking part of this method is from mono_handle_exception
95  *
96  * The idea is simple; 
97  *  - walk the stack to free some space (64k)
98  *  - set esp to new stack location
99  *  - call mono_arch_handle_exception with stack overflow exception
100  *  - set esp to SEH handlers stack
101  *  - done
102  */
103 static void 
104 win32_handle_stack_overflow (EXCEPTION_POINTERS* ep, struct sigcontext *sctx) 
105 {
106     SYSTEM_INFO si;
107     DWORD page_size;
108         MonoDomain *domain = mono_domain_get ();
109         MonoJitInfo *ji, rji;
110         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
111         MonoLMF *lmf = jit_tls->lmf;            
112         MonoContext initial_ctx;
113         MonoContext ctx;
114         guint32 free_stack = 0;
115
116         /* convert sigcontext to MonoContext (due to reuse of stack walking helpers */
117         mono_arch_sigctx_to_monoctx (sctx, &ctx);
118         
119         /* get our os page size */
120     GetSystemInfo(&si);
121         page_size = si.dwPageSize;
122
123         /* Let's walk the stack to recover
124          * the needed stack space (if possible)
125          */
126         memset (&rji, 0, sizeof (rji));
127
128         initial_ctx = ctx;
129         free_stack = (guint8*)(MONO_CONTEXT_GET_BP (&ctx)) - (guint8*)(MONO_CONTEXT_GET_BP (&initial_ctx));
130
131         /* try to free 64kb from our stack */
132         do {
133                 MonoContext new_ctx;
134
135                 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &rji, &ctx, &new_ctx, &lmf, NULL);
136                 if (!ji) {
137                         g_warning ("Exception inside function without unwind info");
138                         g_assert_not_reached ();
139                 }
140
141                 if (ji != (gpointer)-1) {
142                         free_stack = (guint8*)(MONO_CONTEXT_GET_BP (&ctx)) - (guint8*)(MONO_CONTEXT_GET_BP (&initial_ctx));
143                 }
144
145                 /* todo: we should call abort if ji is -1 */
146                 ctx = new_ctx;
147         } while (free_stack < 64 * 1024 && ji != (gpointer) -1);
148
149         /* convert into sigcontext to be used in mono_arch_handle_exception */
150         mono_arch_monoctx_to_sigctx (&ctx, sctx);
151
152         /* todo: install new stack-guard page */
153
154         /* use the new stack and call mono_arch_handle_exception () */
155         restore_stack (sctx);
156 }
157
158 /*
159  * Unhandled Exception Filter
160  * Top-level per-process exception handler.
161  */
162 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
163 {
164         EXCEPTION_RECORD* er;
165         CONTEXT* ctx;
166         struct sigcontext* sctx;
167         LONG res;
168
169         res = EXCEPTION_CONTINUE_EXECUTION;
170
171         er = ep->ExceptionRecord;
172         ctx = ep->ContextRecord;
173         sctx = g_malloc(sizeof(struct sigcontext));
174
175         /* Copy Win32 context to UNIX style context */
176         sctx->eax = ctx->Eax;
177         sctx->ebx = ctx->Ebx;
178         sctx->ecx = ctx->Ecx;
179         sctx->edx = ctx->Edx;
180         sctx->ebp = ctx->Ebp;
181         sctx->esp = ctx->Esp;
182         sctx->esi = ctx->Esi;
183         sctx->edi = ctx->Edi;
184         sctx->eip = ctx->Eip;
185
186         switch (er->ExceptionCode) {
187         case EXCEPTION_STACK_OVERFLOW:
188                 win32_handle_stack_overflow (ep, sctx);
189                 break;
190         case EXCEPTION_ACCESS_VIOLATION:
191                 W32_SEH_HANDLE_EX(segv);
192                 break;
193         case EXCEPTION_ILLEGAL_INSTRUCTION:
194                 W32_SEH_HANDLE_EX(ill);
195                 break;
196         case EXCEPTION_INT_DIVIDE_BY_ZERO:
197         case EXCEPTION_INT_OVERFLOW:
198         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
199         case EXCEPTION_FLT_OVERFLOW:
200         case EXCEPTION_FLT_UNDERFLOW:
201         case EXCEPTION_FLT_INEXACT_RESULT:
202                 W32_SEH_HANDLE_EX(fpe);
203                 break;
204         default:
205                 break;
206         }
207
208         /* Copy context back */
209         ctx->Eax = sctx->eax;
210         ctx->Ebx = sctx->ebx;
211         ctx->Ecx = sctx->ecx;
212         ctx->Edx = sctx->edx;
213         ctx->Ebp = sctx->ebp;
214         ctx->Esp = sctx->esp;
215         ctx->Esi = sctx->esi;
216         ctx->Edi = sctx->edi;
217         ctx->Eip = sctx->eip;
218
219         g_free (sctx);
220
221         return res;
222 }
223
224 void win32_seh_init()
225 {
226         /* install restore stack helper */
227         if (!restore_stack)
228                 restore_stack = mono_win32_get_handle_stackoverflow ();
229
230         old_handler = SetUnhandledExceptionFilter(seh_handler);
231 }
232
233 void win32_seh_cleanup()
234 {
235         if (old_handler) SetUnhandledExceptionFilter(old_handler);
236 }
237
238 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
239 {
240         switch (type) {
241         case SIGFPE:
242                 fpe_handler = handler;
243                 break;
244         case SIGILL:
245                 ill_handler = handler;
246                 break;
247         case SIGSEGV:
248                 segv_handler = handler;
249                 break;
250         default:
251                 break;
252         }
253 }
254
255 #endif /* PLATFORM_WIN32 */
256
257 /*
258  * mono_arch_get_restore_context:
259  *
260  * Returns a pointer to a method which restores a previously saved sigcontext.
261  */
262 gpointer
263 mono_arch_get_restore_context (void)
264 {
265         static guint8 *start = NULL;
266         guint8 *code;
267
268         if (start)
269                 return start;
270
271         /* restore_contect (MonoContext *ctx) */
272         /* we do not restore X86_EAX, X86_EDX */
273
274         start = code = mono_global_codeman_reserve (128);
275         
276         /* load ctx */
277         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
278
279         /* get return address, stored in EDX */
280         x86_mov_reg_membase (code, X86_EDX, X86_EAX,  G_STRUCT_OFFSET (MonoContext, eip), 4);
281         /* restore EBX */
282         x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (MonoContext, ebx), 4);
283         /* restore EDI */
284         x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (MonoContext, edi), 4);
285         /* restore ESI */
286         x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (MonoContext, esi), 4);
287         /* restore ESP */
288         x86_mov_reg_membase (code, X86_ESP, X86_EAX,  G_STRUCT_OFFSET (MonoContext, esp), 4);
289         /* restore EBP */
290         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (MonoContext, ebp), 4);
291
292         /* jump to the saved IP */
293         x86_jump_reg (code, X86_EDX);
294
295         return start;
296 }
297
298 /*
299  * mono_arch_get_call_filter:
300  *
301  * Returns a pointer to a method which calls an exception filter. We
302  * also use this function to call finally handlers (we pass NULL as 
303  * @exc object in this case).
304  */
305 gpointer
306 mono_arch_get_call_filter (void)
307 {
308         static guint8* start;
309         static int inited = 0;
310         guint8 *code;
311
312         if (inited)
313                 return start;
314
315         inited = 1;
316         /* call_filter (MonoContext *ctx, unsigned long eip) */
317         start = code = mono_global_codeman_reserve (64);
318
319         x86_push_reg (code, X86_EBP);
320         x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
321         x86_push_reg (code, X86_EBX);
322         x86_push_reg (code, X86_EDI);
323         x86_push_reg (code, X86_ESI);
324
325         /* load ctx */
326         x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
327         /* load eip */
328         x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
329         /* save EBP */
330         x86_push_reg (code, X86_EBP);
331
332         /* set new EBP */
333         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (MonoContext, ebp), 4);
334         /* restore registers used by global register allocation (EBX & ESI) */
335         x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (MonoContext, ebx), 4);
336         x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (MonoContext, esi), 4);
337         x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (MonoContext, edi), 4);
338
339         /* align stack and save ESP */
340         x86_mov_reg_reg (code, X86_EDX, X86_ESP, 4);
341         x86_alu_reg_imm (code, X86_AND, X86_ESP, -MONO_ARCH_FRAME_ALIGNMENT);
342         g_assert (MONO_ARCH_FRAME_ALIGNMENT >= 8);
343         x86_alu_reg_imm (code, X86_SUB, X86_ESP, MONO_ARCH_FRAME_ALIGNMENT - 8);
344         x86_push_reg (code, X86_EDX);
345
346         /* call the handler */
347         x86_call_reg (code, X86_ECX);
348
349         /* restore ESP */
350         x86_pop_reg (code, X86_ESP);
351
352         /* restore EBP */
353         x86_pop_reg (code, X86_EBP);
354
355         /* restore saved regs */
356         x86_pop_reg (code, X86_ESI);
357         x86_pop_reg (code, X86_EDI);
358         x86_pop_reg (code, X86_EBX);
359         x86_leave (code);
360         x86_ret (code);
361
362         g_assert ((code - start) < 64);
363         return start;
364 }
365
366 static void
367 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
368                  unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
369                  unsigned long eip,  unsigned long esp, gboolean rethrow)
370 {
371         static void (*restore_context) (MonoContext *);
372         MonoContext ctx;
373
374         if (!restore_context)
375                 restore_context = mono_arch_get_restore_context ();
376
377         /* Pop argument and return address */
378         ctx.esp = esp + (2 * sizeof (gpointer));
379         ctx.eip = eip;
380         ctx.ebp = ebp;
381         ctx.edi = edi;
382         ctx.esi = esi;
383         ctx.ebx = ebx;
384         ctx.edx = edx;
385         ctx.ecx = ecx;
386         ctx.eax = eax;
387
388         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
389                 MonoException *mono_ex = (MonoException*)exc;
390                 if (!rethrow)
391                         mono_ex->stack_trace = NULL;
392         }
393
394         if (mono_debug_using_mono_debugger ()) {
395                 guint8 buf [16], *code;
396
397                 mono_breakpoint_clean_code (NULL, (gpointer)eip, 8, buf, sizeof (buf));
398                 code = buf + 8;
399
400                 if (buf [3] == 0xe8) {
401                         MonoContext ctx_cp = ctx;
402                         ctx_cp.eip = eip - 5;
403
404                         if (mono_debugger_handle_exception (&ctx_cp, exc)) {
405                                 restore_context (&ctx_cp);
406                                 g_assert_not_reached ();
407                         }
408                 }
409         }
410
411         /* adjust eip so that it point into the call instruction */
412         ctx.eip -= 1;
413
414         mono_handle_exception (&ctx, exc, (gpointer)eip, FALSE);
415         restore_context (&ctx);
416
417         g_assert_not_reached ();
418 }
419
420 static guint8*
421 get_throw_exception (gboolean rethrow)
422 {
423         guint8 *start, *code;
424
425         start = code = mono_global_codeman_reserve (64);
426
427         x86_push_reg (code, X86_ESP);
428         x86_push_membase (code, X86_ESP, 4); /* IP */
429         x86_push_membase (code, X86_ESP, 12); /* exception */
430         x86_push_reg (code, X86_EBP);
431         x86_push_reg (code, X86_EDI);
432         x86_push_reg (code, X86_ESI);
433         x86_push_reg (code, X86_EBX);
434         x86_push_reg (code, X86_EDX);
435         x86_push_reg (code, X86_ECX);
436         x86_push_reg (code, X86_EAX);
437         x86_call_code (code, throw_exception);
438         /* we should never reach this breakpoint */
439         x86_breakpoint (code);
440
441         g_assert ((code - start) < 64);
442
443         return start;
444 }
445
446 /**
447  * mono_arch_get_throw_exception:
448  *
449  * Returns a function pointer which can be used to raise 
450  * exceptions. The returned function has the following 
451  * signature: void (*func) (MonoException *exc); 
452  * For example to raise an arithmetic exception you can use:
453  *
454  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
455  * x86_call_code (code, arch_get_throw_exception ()); 
456  *
457  */
458 gpointer 
459 mono_arch_get_throw_exception (void)
460 {
461         static guint8 *start;
462         static int inited = 0;
463
464         if (inited)
465                 return start;
466
467         start = get_throw_exception (FALSE);
468
469         inited = 1;
470
471         return start;
472 }
473
474 gpointer 
475 mono_arch_get_rethrow_exception (void)
476 {
477         static guint8 *start;
478         static int inited = 0;
479
480         if (inited)
481                 return start;
482
483         start = get_throw_exception (TRUE);
484
485         inited = 1;
486
487         return start;
488 }
489
490 /**
491  * mono_arch_get_throw_exception_by_name:
492  *
493  * Returns a function pointer which can be used to raise 
494  * corlib exceptions. The returned function has the following 
495  * signature: void (*func) (gpointer ip, char *exc_name); 
496  * For example to raise an arithmetic exception you can use:
497  *
498  * x86_push_imm (code, "ArithmeticException"); 
499  * x86_push_imm (code, <IP>)
500  * x86_jump_code (code, arch_get_throw_exception_by_name ()); 
501  *
502  */
503 gpointer 
504 mono_arch_get_throw_exception_by_name (void)
505 {
506         guint8* start;
507         guint8 *code;
508
509         start = code = mono_global_codeman_reserve (32);
510
511         /* Not used */
512         x86_breakpoint (code);
513
514         mono_arch_flush_icache (start, code - start);
515
516         return start;
517 }
518
519 /**
520  * mono_arch_get_throw_corlib_exception:
521  *
522  * Returns a function pointer which can be used to raise 
523  * corlib exceptions. The returned function has the following 
524  * signature: void (*func) (guint32 ex_token, guint32 offset); 
525  * Here, offset is the offset which needs to be substracted from the caller IP 
526  * to get the IP of the throw. Passing the offset has the advantage that it 
527  * needs no relocations in the caller.
528  */
529 gpointer 
530 mono_arch_get_throw_corlib_exception (void)
531 {
532         static guint8* start;
533         static int inited = 0;
534         guint8 *code;
535
536         if (inited)
537                 return start;
538
539         inited = 1;
540         code = start = mono_global_codeman_reserve (64);
541
542         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4); /* token */
543         x86_alu_reg_imm (code, X86_ADD, X86_EAX, MONO_TOKEN_TYPE_DEF);
544         x86_push_reg (code, X86_EAX);
545         x86_push_imm (code, mono_defaults.exception_class->image);
546         x86_call_code (code, mono_exception_from_token);
547         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8);
548         /* Compute caller ip */
549         x86_pop_reg (code, X86_ECX);
550         /* Pop token */
551         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
552         x86_pop_reg (code, X86_EDX);
553         x86_alu_reg_reg (code, X86_SUB, X86_ECX, X86_EDX);
554         /* Push exception object */
555         x86_push_reg (code, X86_EAX);
556         /* Push throw IP */
557         x86_push_reg (code, X86_ECX);
558         x86_jump_code (code, mono_arch_get_throw_exception ());
559
560         g_assert ((code - start) < 64);
561
562         return start;
563 }
564
565 /* mono_arch_find_jit_info:
566  *
567  * This function is used to gather information from @ctx. It return the 
568  * MonoJitInfo of the corresponding function, unwinds one stack frame and
569  * stores the resulting context into @new_ctx. It also stores a string 
570  * describing the stack location into @trace (if not NULL), and modifies
571  * the @lmf if necessary. @native_offset return the IP offset from the 
572  * start of the function or -1 if that info is not available.
573  */
574 MonoJitInfo *
575 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
576                          MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed)
577 {
578         MonoJitInfo *ji;
579         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
580
581         /* Avoid costly table lookup during stack overflow */
582         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
583                 ji = prev_ji;
584         else
585                 ji = mono_jit_info_table_find (domain, ip);
586
587         if (managed)
588                 *managed = FALSE;
589
590         *new_ctx = *ctx;
591
592         if (ji != NULL) {
593                 gssize regs [MONO_MAX_IREGS + 1];
594                 guint8 *cfa;
595                 guint32 unwind_info_len;
596                 guint8 *unwind_info;
597
598                 if (managed)
599                         if (!ji->method->wrapper_type)
600                                 *managed = TRUE;
601
602                 if (ji->from_aot)
603                         unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len);
604                 else
605                         unwind_info = mono_get_cached_unwind_info (ji->used_regs, &unwind_info_len);
606
607                 regs [X86_EAX] = new_ctx->eax;
608                 regs [X86_EBX] = new_ctx->ebx;
609                 regs [X86_ECX] = new_ctx->ecx;
610                 regs [X86_EDX] = new_ctx->edx;
611                 regs [X86_ESP] = new_ctx->esp;
612                 regs [X86_EBP] = new_ctx->ebp;
613                 regs [X86_ESI] = new_ctx->esi;
614                 regs [X86_EDI] = new_ctx->edi;
615                 regs [X86_NREG] = new_ctx->eip;
616
617                 mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, 
618                                                    (guint8*)ji->code_start + ji->code_size,
619                                                    ip, regs, MONO_MAX_IREGS + 1, &cfa);
620
621                 new_ctx->eax = regs [X86_EAX];
622                 new_ctx->ebx = regs [X86_EBX];
623                 new_ctx->ecx = regs [X86_ECX];
624                 new_ctx->edx = regs [X86_EDX];
625                 new_ctx->esp = regs [X86_ESP];
626                 new_ctx->ebp = regs [X86_EBP];
627                 new_ctx->esi = regs [X86_ESI];
628                 new_ctx->edi = regs [X86_EDI];
629                 new_ctx->eip = regs [X86_NREG];
630
631                 /* The CFA becomes the new SP value */
632                 new_ctx->esp = (gssize)cfa;
633
634                 /* Adjust IP */
635                 new_ctx->eip --;
636
637                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
638                         /* remove any unused lmf */
639                         *lmf = (gpointer)(((guint32)(*lmf)->previous_lmf) & ~1);
640                 }
641
642                 /* Pop arguments off the stack */
643                 {
644                         MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
645
646                         guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (ji->method), mono_method_signature (ji->method)->param_count, arg_info);
647                         new_ctx->esp += stack_to_pop;
648                 }
649
650                 return ji;
651         } else if (*lmf) {
652                 
653                 *new_ctx = *ctx;
654
655                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
656                 } else {
657                         if (!((guint32)((*lmf)->previous_lmf) & 1))
658                                 /* Top LMF entry */
659                                 return (gpointer)-1;
660                         /* Trampoline lmf frame */
661                         memset (res, 0, MONO_SIZEOF_JIT_INFO);
662                         res->method = (*lmf)->method;
663                 }
664
665                 new_ctx->esi = (*lmf)->esi;
666                 new_ctx->edi = (*lmf)->edi;
667                 new_ctx->ebx = (*lmf)->ebx;
668                 new_ctx->ebp = (*lmf)->ebp;
669                 new_ctx->eip = (*lmf)->eip;
670
671                 /* Check if we are in a trampoline LMF frame */
672                 if ((guint32)((*lmf)->previous_lmf) & 1) {
673                         /* lmf->esp is set by the trampoline code */
674                         new_ctx->esp = (*lmf)->esp;
675
676                         /* Pop arguments off the stack */
677                         /* FIXME: Handle the delegate case too ((*lmf)->method == NULL) */
678                         /* FIXME: Handle the IMT/vtable case too */
679                         if ((*lmf)->method && (*lmf)->method != MONO_FAKE_IMT_METHOD && (*lmf)->method != MONO_FAKE_VTABLE_METHOD) {
680                                 MonoMethod *method = (*lmf)->method;
681                                 MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (method)->param_count + 1);
682
683                                 guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (method), mono_method_signature (method)->param_count, arg_info);
684                                 new_ctx->esp += stack_to_pop;
685                         }
686                 }
687                 else
688                         /* the lmf is always stored on the stack, so the following
689                          * expression points to a stack location which can be used as ESP */
690                         new_ctx->esp = (unsigned long)&((*lmf)->eip);
691
692                 *lmf = (gpointer)(((guint32)(*lmf)->previous_lmf) & ~1);
693
694                 return ji ? ji : res;
695         }
696
697         return NULL;
698 }
699
700 #ifdef __sun
701 #define REG_EAX EAX
702 #define REG_EBX EBX
703 #define REG_ECX ECX
704 #define REG_EDX EDX
705 #define REG_EBP EBP
706 #define REG_ESP ESP
707 #define REG_ESI ESI
708 #define REG_EDI EDI
709 #define REG_EIP EIP
710 #endif
711
712 void
713 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
714 {
715 #ifdef MONO_ARCH_USE_SIGACTION
716         ucontext_t *ctx = (ucontext_t*)sigctx;
717         
718         mctx->eax = UCONTEXT_REG_EAX (ctx);
719         mctx->ebx = UCONTEXT_REG_EBX (ctx);
720         mctx->ecx = UCONTEXT_REG_ECX (ctx);
721         mctx->edx = UCONTEXT_REG_EDX (ctx);
722         mctx->ebp = UCONTEXT_REG_EBP (ctx);
723         mctx->esp = UCONTEXT_REG_ESP (ctx);
724         mctx->esi = UCONTEXT_REG_ESI (ctx);
725         mctx->edi = UCONTEXT_REG_EDI (ctx);
726         mctx->eip = UCONTEXT_REG_EIP (ctx);
727 #else   
728         struct sigcontext *ctx = (struct sigcontext *)sigctx;
729
730         mctx->eax = ctx->SC_EAX;
731         mctx->ebx = ctx->SC_EBX;
732         mctx->ecx = ctx->SC_ECX;
733         mctx->edx = ctx->SC_EDX;
734         mctx->ebp = ctx->SC_EBP;
735         mctx->esp = ctx->SC_ESP;
736         mctx->esi = ctx->SC_ESI;
737         mctx->edi = ctx->SC_EDI;
738         mctx->eip = ctx->SC_EIP;
739 #endif
740 }
741
742 void
743 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
744 {
745 #ifdef MONO_ARCH_USE_SIGACTION
746         ucontext_t *ctx = (ucontext_t*)sigctx;
747
748         UCONTEXT_REG_EAX (ctx) = mctx->eax;
749         UCONTEXT_REG_EBX (ctx) = mctx->ebx;
750         UCONTEXT_REG_ECX (ctx) = mctx->ecx;
751         UCONTEXT_REG_EDX (ctx) = mctx->edx;
752         UCONTEXT_REG_EBP (ctx) = mctx->ebp;
753         UCONTEXT_REG_ESP (ctx) = mctx->esp;
754         UCONTEXT_REG_ESI (ctx) = mctx->esi;
755         UCONTEXT_REG_EDI (ctx) = mctx->edi;
756         UCONTEXT_REG_EIP (ctx) = mctx->eip;
757 #else
758         struct sigcontext *ctx = (struct sigcontext *)sigctx;
759
760         ctx->SC_EAX = mctx->eax;
761         ctx->SC_EBX = mctx->ebx;
762         ctx->SC_ECX = mctx->ecx;
763         ctx->SC_EDX = mctx->edx;
764         ctx->SC_EBP = mctx->ebp;
765         ctx->SC_ESP = mctx->esp;
766         ctx->SC_ESI = mctx->esi;
767         ctx->SC_EDI = mctx->edi;
768         ctx->SC_EIP = mctx->eip;
769 #endif
770 }       
771
772 gpointer
773 mono_arch_ip_from_context (void *sigctx)
774 {
775 #ifdef MONO_ARCH_USE_SIGACTION
776         ucontext_t *ctx = (ucontext_t*)sigctx;
777         return (gpointer)UCONTEXT_REG_EIP (ctx);
778 #else
779         struct sigcontext *ctx = sigctx;
780         return (gpointer)ctx->SC_EIP;
781 #endif  
782 }
783
784 gboolean
785 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
786 {
787         MonoContext mctx;
788
789         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
790
791         if (mono_debugger_handle_exception (&mctx, (MonoObject *)obj))
792                 return TRUE;
793
794         mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only);
795
796         mono_arch_monoctx_to_sigctx (&mctx, sigctx);
797
798         return TRUE;
799 }
800
801 static void
802 restore_soft_guard_pages (void)
803 {
804         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
805         if (jit_tls->stack_ovf_guard_base)
806                 mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_NONE);
807 }
808
809 /* 
810  * this function modifies mctx so that when it is restored, it
811  * won't execcute starting at mctx.eip, but in a function that
812  * will restore the protection on the soft-guard pages and return back to
813  * continue at mctx.eip.
814  */
815 static void
816 prepare_for_guard_pages (MonoContext *mctx)
817 {
818         gpointer *sp;
819         sp = (gpointer)(mctx->esp);
820         sp -= 1;
821         /* the resturn addr */
822         sp [0] = (gpointer)(mctx->eip);
823         mctx->eip = (unsigned long)restore_soft_guard_pages;
824         mctx->esp = (unsigned long)sp;
825 }
826
827 static void
828 altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean stack_ovf)
829 {
830         void (*restore_context) (MonoContext *);
831         MonoContext mctx;
832
833         restore_context = mono_arch_get_restore_context ();
834         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
835
836         if (mono_debugger_handle_exception (&mctx, (MonoObject *)obj)) {
837                 if (stack_ovf)
838                         prepare_for_guard_pages (&mctx);
839                 restore_context (&mctx);
840         }
841
842         mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, FALSE);
843         if (stack_ovf)
844                 prepare_for_guard_pages (&mctx);
845         restore_context (&mctx);
846 }
847
848 void
849 mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean stack_ovf)
850 {
851 #ifdef MONO_ARCH_USE_SIGACTION
852         MonoException *exc = NULL;
853         ucontext_t *ctx = (ucontext_t*)sigctx;
854         MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)UCONTEXT_REG_EIP (ctx));
855         gpointer *sp;
856         int frame_size;
857
858         /* if we didn't find a managed method for the ip address and it matches the fault
859          * address, we assume we followed a broken pointer during an indirect call, so
860          * we try the lookup again with the return address pushed on the stack
861          */
862         if (!ji && fault_addr == (gpointer)UCONTEXT_REG_EIP (ctx)) {
863                 glong *sp = (gpointer)UCONTEXT_REG_ESP (ctx);
864                 ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)sp [0]);
865                 if (ji)
866                         UCONTEXT_REG_EIP (ctx) = sp [0];
867         }
868         if (stack_ovf)
869                 exc = mono_domain_get ()->stack_overflow_ex;
870         if (!ji)
871                 mono_handle_native_sigsegv (SIGSEGV, sigctx);
872         /* setup a call frame on the real stack so that control is returned there
873          * and exception handling can continue.
874          * If this was a stack overflow the caller already ensured the stack pages
875          * needed have been unprotected.
876          * The frame looks like:
877          *   ucontext struct
878          *   test_only arg
879          *   exception arg
880          *   ctx arg
881          *   return ip
882          */
883         frame_size = sizeof (ucontext_t) + sizeof (gpointer) * 4;
884         frame_size += 15;
885         frame_size &= ~15;
886         sp = (gpointer)(UCONTEXT_REG_ESP (ctx) & ~15);
887         sp = (gpointer)((char*)sp - frame_size);
888         /* the incoming arguments are aligned to 16 bytes boundaries, so the return address IP
889          * goes at sp [-1]
890          */
891         sp [-1] = (gpointer)UCONTEXT_REG_EIP (ctx);
892         sp [0] = sp + 4;
893         sp [1] = exc;
894         sp [2] = (gpointer)stack_ovf;
895         /* may need to adjust pointers in the new struct copy, depending on the OS */
896         memcpy (sp + 4, ctx, sizeof (ucontext_t));
897         /* at the return form the signal handler execution starts in altstack_handle_and_restore() */
898         UCONTEXT_REG_EIP (ctx) = (unsigned long)altstack_handle_and_restore;
899         UCONTEXT_REG_ESP (ctx) = (unsigned long)(sp - 1);
900 #endif
901 }
902
903 #if MONO_SUPPORT_TASKLETS
904 MonoContinuationRestore
905 mono_tasklets_arch_restore (void)
906 {
907         static guint8* saved = NULL;
908         guint8 *code, *start;
909
910         if (saved)
911                 return (MonoContinuationRestore)saved;
912         code = start = mono_global_codeman_reserve (48);
913         /* the signature is: restore (MonoContinuation *cont, int state, MonoLMF **lmf_addr) */
914         /* put cont in edx */
915         x86_mov_reg_membase (code, X86_EDX, X86_ESP, 4, 4);
916         /* setup the copy of the stack */
917         x86_mov_reg_membase (code, X86_ECX, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, stack_used_size), 4);
918         x86_shift_reg_imm (code, X86_SHR, X86_ECX, 2);
919         x86_cld (code);
920         x86_mov_reg_membase (code, X86_ESI, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, saved_stack), 4);
921         x86_mov_reg_membase (code, X86_EDI, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, return_sp), 4);
922         x86_prefix (code, X86_REP_PREFIX);
923         x86_movsl (code);
924
925         /* now restore the registers from the LMF */
926         x86_mov_reg_membase (code, X86_ECX, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, lmf), 4);
927         x86_mov_reg_membase (code, X86_EBX, X86_ECX, G_STRUCT_OFFSET (MonoLMF, ebx), 4);
928         x86_mov_reg_membase (code, X86_EBP, X86_ECX, G_STRUCT_OFFSET (MonoLMF, ebp), 4);
929         x86_mov_reg_membase (code, X86_ESI, X86_ECX, G_STRUCT_OFFSET (MonoLMF, esi), 4);
930         x86_mov_reg_membase (code, X86_EDI, X86_ECX, G_STRUCT_OFFSET (MonoLMF, edi), 4);
931
932         /* restore the lmf chain */
933         /*x86_mov_reg_membase (code, X86_ECX, X86_ESP, 12, 4);
934         x86_mov_membase_reg (code, X86_ECX, 0, X86_EDX, 4);*/
935
936         /* state in eax, so it's setup as the return value */
937         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 8, 4);
938         x86_jump_membase (code, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, return_ip));
939         g_assert ((code - start) <= 48);
940         saved = start;
941         return (MonoContinuationRestore)saved;
942 }
943 #endif
944