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