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