2010-01-20 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 /*
430  * mono_x86_llvm_throw_exception:
431  *
432  *   Same as throw_exception, but called from LLVM compiled code.
433  * Moved into a separate function to avoid breaking the alignment stuff in
434  * throw_exception, which is hard to test.
435  */
436 static void
437 mono_x86_llvm_throw_exception (mgreg_t *regs, MonoObject *exc, 
438                                                            mgreg_t eip, gboolean rethrow)
439 {
440         static void (*restore_context) (MonoContext *);
441         MonoContext ctx;
442
443         if (!restore_context)
444                 restore_context = mono_arch_get_restore_context ();
445
446         /* Pop alignment added in get_throw_exception (), the return address, plus the argument and the alignment added at the call site */
447         ctx.esp = regs [X86_ESP];
448         ctx.eip = eip;
449         ctx.ebp = regs [X86_EBP];
450         ctx.edi = regs [X86_EDI];
451         ctx.esi = regs [X86_ESI];
452         ctx.ebx = regs [X86_EBX];
453         ctx.edx = regs [X86_EDX];
454         ctx.ecx = regs [X86_ECX];
455         ctx.eax = regs [X86_EAX];
456
457 #ifdef __APPLE__
458         /* The OSX ABI specifies 16 byte alignment at call sites */
459         g_assert ((ctx.esp % MONO_ARCH_FRAME_ALIGNMENT) == 0);
460 #endif
461
462         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
463                 MonoException *mono_ex = (MonoException*)exc;
464                 if (!rethrow)
465                         mono_ex->stack_trace = NULL;
466         }
467
468         if (mono_debug_using_mono_debugger ()) {
469                 guint8 buf [16], *code;
470
471                 mono_breakpoint_clean_code (NULL, (gpointer)eip, 8, buf, sizeof (buf));
472                 code = buf + 8;
473
474                 if (buf [3] == 0xe8) {
475                         MonoContext ctx_cp = ctx;
476                         ctx_cp.eip = eip - 5;
477
478                         if (mono_debugger_handle_exception (&ctx_cp, exc)) {
479                                 restore_context (&ctx_cp);
480                                 g_assert_not_reached ();
481                         }
482                 }
483         }
484
485         /* adjust eip so that it point into the call instruction */
486         ctx.eip -= 1;
487
488         mono_handle_exception (&ctx, exc, (gpointer)eip, FALSE);
489
490         restore_context (&ctx);
491
492         g_assert_not_reached ();
493 }
494
495 static void
496 mono_x86_llvm_throw_corlib_exception (mgreg_t *regs, guint32 ex_token_index, 
497                                                                           mgreg_t eip, guint32 pc_offset)
498 {
499         guint32 ex_token = MONO_TOKEN_TYPE_DEF | ex_token_index;
500         MonoException *ex;
501
502         ex = mono_exception_from_token (mono_defaults.exception_class->image, ex_token);
503
504         eip += pc_offset;
505
506         mono_x86_llvm_throw_exception (regs, (MonoObject*)ex, eip, FALSE);
507 }
508
509 static guint8*
510 get_throw_exception (gboolean rethrow)
511 {
512         guint8 *start, *code;
513
514         start = code = mono_global_codeman_reserve (64);
515
516         /* 
517          * Align the stack on apple, since we push 10 args, and the call pushed 4 bytes.
518          */
519         x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4);
520         x86_push_reg (code, X86_ESP);
521         x86_push_membase (code, X86_ESP, 8); /* IP */
522         x86_push_membase (code, X86_ESP, 16); /* exception */
523         x86_push_reg (code, X86_EBP);
524         x86_push_reg (code, X86_EDI);
525         x86_push_reg (code, X86_ESI);
526         x86_push_reg (code, X86_EBX);
527         x86_push_reg (code, X86_EDX);
528         x86_push_reg (code, X86_ECX);
529         x86_push_reg (code, X86_EAX);
530         x86_call_code (code, throw_exception);
531         /* we should never reach this breakpoint */
532         x86_breakpoint (code);
533
534         g_assert ((code - start) < 64);
535
536         return start;
537 }
538
539 /*
540  * get_llvm_throw_exception:
541  *
542  *  Generate a call to mono_x86_llvm_throw_exception/
543  * mono_x86_llvm_throw_corlib_exception.
544  */
545 static guint8*
546 get_llvm_throw_exception (gboolean rethrow, gboolean corlib)
547 {
548         guint8 *start, *code;
549         GSList *unwind_ops = NULL;
550         int i, stack_size, arg_offsets [5], regs_offset;
551
552         start = code = mono_global_codeman_reserve (128);
553
554         stack_size = 128;
555
556         /*
557          * The stack looks like this:
558          * <pc offset> (only if corlib is TRUE)
559          * <exception object>/<type token>
560          * <return addr> <- esp
561          */
562
563         mono_add_unwind_op_def_cfa (unwind_ops, (guint8*)NULL, (guint8*)NULL, X86_ESP, 4);
564         mono_add_unwind_op_offset (unwind_ops, (guint8*)NULL, (guint8*)NULL, X86_NREG, -4);
565
566         /* Alloc frame */
567         x86_alu_reg_imm (code, X86_SUB, X86_ESP, stack_size);
568         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, stack_size + 4);
569
570         arg_offsets [0] = 0;
571         arg_offsets [1] = 4;
572         arg_offsets [2] = 8;
573         arg_offsets [3] = 12;
574         regs_offset = 16;
575
576         /* Save registers */
577         for (i = 0; i < X86_NREG; ++i)
578                 if (i != X86_ESP)
579                         x86_mov_membase_reg (code, X86_ESP, regs_offset + (i * 4), i, 4);
580         /* Save ESP */
581         /* LLVM doesn't push the exception object */
582         x86_lea_membase (code, X86_EAX, X86_ESP, stack_size + 4);
583         x86_mov_membase_reg (code, X86_ESP, regs_offset + (X86_ESP * 4), X86_EAX, 4);
584
585         /* Set arg1 == regs */
586         x86_lea_membase (code, X86_EAX, X86_ESP, regs_offset);
587         x86_mov_membase_reg (code, X86_ESP, arg_offsets [0], X86_EAX, 4);
588         /* Set arg2 == exc */
589         x86_mov_reg_membase (code, X86_EAX, X86_ESP, stack_size + 4, 4);
590         x86_mov_membase_reg (code, X86_ESP, arg_offsets [1], X86_EAX, 4);
591         /* Set arg3 == eip */
592         x86_mov_reg_membase (code, X86_EAX, X86_ESP, stack_size, 4);
593         x86_mov_membase_reg (code, X86_ESP, arg_offsets [2], X86_EAX, 4);
594         if (corlib) {
595                 /* Set arg4 == offset */
596                 x86_mov_reg_membase (code, X86_EAX, X86_ESP, stack_size + 8, 4);
597                 x86_mov_membase_reg (code, X86_ESP, arg_offsets [3], X86_EAX, 4);
598         } else {
599                 /* Set arg4 == rethrow */
600                 x86_mov_membase_imm (code, X86_ESP, arg_offsets [3], rethrow, 4);
601         }
602         /* Make the call */
603         x86_call_code (code, corlib ? (gpointer)mono_x86_llvm_throw_corlib_exception : (gpointer)mono_x86_llvm_throw_exception);
604         x86_breakpoint (code);
605
606         g_assert ((code - start) < 128);
607
608         mono_save_trampoline_xdebug_info (corlib ? "llvm_throw_corlib_exception_trampoline" : "llvm_throw_exception_trampoline", start, code - start, unwind_ops);
609
610         return start;
611 }
612
613 /**
614  * mono_arch_get_throw_exception:
615  *
616  * Returns a function pointer which can be used to raise 
617  * exceptions. The returned function has the following 
618  * signature: void (*func) (MonoException *exc); 
619  * For example to raise an arithmetic exception you can use:
620  *
621  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
622  * x86_call_code (code, arch_get_throw_exception ()); 
623  *
624  */
625 gpointer 
626 mono_arch_get_throw_exception (void)
627 {
628         static guint8 *start;
629         static int inited = 0;
630
631         if (inited)
632                 return start;
633
634         start = get_throw_exception (FALSE);
635
636         inited = 1;
637
638         return start;
639 }
640
641 gpointer 
642 mono_arch_get_rethrow_exception (void)
643 {
644         static guint8 *start;
645         static int inited = 0;
646
647         if (inited)
648                 return start;
649
650         start = get_throw_exception (TRUE);
651
652         inited = 1;
653
654         return start;
655 }
656
657 /**
658  * mono_arch_get_throw_exception_by_name:
659  *
660  * Returns a function pointer which can be used to raise 
661  * corlib exceptions. The returned function has the following 
662  * signature: void (*func) (gpointer ip, char *exc_name); 
663  * For example to raise an arithmetic exception you can use:
664  *
665  * x86_push_imm (code, "ArithmeticException"); 
666  * x86_push_imm (code, <IP>)
667  * x86_jump_code (code, arch_get_throw_exception_by_name ()); 
668  *
669  */
670 gpointer 
671 mono_arch_get_throw_exception_by_name (void)
672 {
673         guint8* start;
674         guint8 *code;
675
676         start = code = mono_global_codeman_reserve (32);
677
678         /* Not used */
679         x86_breakpoint (code);
680
681         mono_arch_flush_icache (start, code - start);
682
683         return start;
684 }
685
686 /**
687  * mono_arch_get_throw_corlib_exception:
688  *
689  * Returns a function pointer which can be used to raise 
690  * corlib exceptions. The returned function has the following 
691  * signature: void (*func) (guint32 ex_token, guint32 offset); 
692  * Here, offset is the offset which needs to be substracted from the caller IP 
693  * to get the IP of the throw. Passing the offset has the advantage that it 
694  * needs no relocations in the caller.
695  */
696 gpointer 
697 mono_arch_get_throw_corlib_exception (void)
698 {
699         static guint8* start;
700         static int inited = 0;
701         guint8 *code;
702         GSList *unwind_ops = NULL;
703
704         if (inited)
705                 return start;
706
707         inited = 1;
708         code = start = mono_global_codeman_reserve (64);
709
710         /* 
711          * Align the stack on apple, the caller doesn't do this to save space,
712          * two arguments + the return addr are already on the stack.
713          */
714         x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4);
715         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4 + 4, 4); /* token */
716         x86_alu_reg_imm (code, X86_ADD, X86_EAX, MONO_TOKEN_TYPE_DEF);
717         /* Align the stack on apple */
718         x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8);
719         x86_push_reg (code, X86_EAX);
720         x86_push_imm (code, mono_defaults.exception_class->image);
721         x86_call_code (code, mono_exception_from_token);
722         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 16);
723         /* Compute caller ip */
724         x86_mov_reg_membase (code, X86_ECX, X86_ESP, 4, 4);
725         /* Compute offset */
726         x86_mov_reg_membase (code, X86_EDX, X86_ESP, 4 + 4 + 4, 4);
727         /* Pop everything */
728         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4 + 4 + 4 + 4);
729         x86_alu_reg_reg (code, X86_SUB, X86_ECX, X86_EDX);
730         /* Align the stack on apple, mirrors the sub in OP_THROW. */
731         x86_alu_reg_imm (code, X86_SUB, X86_ESP, MONO_ARCH_FRAME_ALIGNMENT - 4);
732         /* Push exception object */
733         x86_push_reg (code, X86_EAX);
734         /* Push throw IP */
735         x86_push_reg (code, X86_ECX);
736         x86_jump_code (code, mono_arch_get_throw_exception ());
737
738         g_assert ((code - start) < 64);
739
740         mono_add_unwind_op_def_cfa (unwind_ops, (guint8*)NULL, (guint8*)NULL, X86_ESP, 4);
741         mono_add_unwind_op_offset (unwind_ops, (guint8*)NULL, (guint8*)NULL, X86_NREG, -4);
742
743         mono_save_trampoline_xdebug_info ("throw_corlib_exception_trampoline", start, code - start, unwind_ops);
744
745         return start;
746 }
747
748 void
749 mono_arch_exceptions_init (void)
750 {
751         guint8 *tramp;
752
753         tramp = get_llvm_throw_exception (FALSE, FALSE);
754
755         mono_register_jit_icall (tramp, "mono_arch_llvm_throw_exception", NULL, TRUE);
756
757         tramp = get_llvm_throw_exception (FALSE, TRUE);
758
759         mono_register_jit_icall (tramp, "mono_arch_llvm_throw_corlib_exception", NULL, TRUE);
760 }
761
762 /*
763  * mono_arch_find_jit_info_ext:
764  *
765  * See exceptions-amd64.c for docs.
766  */
767 gboolean
768 mono_arch_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls, 
769                                                          MonoJitInfo *ji, MonoContext *ctx, 
770                                                          MonoContext *new_ctx, MonoLMF **lmf, 
771                                                          StackFrameInfo *frame)
772 {
773         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
774
775         memset (frame, 0, sizeof (StackFrameInfo));
776         frame->ji = ji;
777         frame->managed = FALSE;
778
779         *new_ctx = *ctx;
780
781         if (ji != NULL) {
782                 gssize regs [MONO_MAX_IREGS + 1];
783                 guint8 *cfa;
784                 guint32 unwind_info_len;
785                 guint8 *unwind_info;
786
787                 frame->type = FRAME_TYPE_MANAGED;
788
789                 if (!ji->method->wrapper_type || ji->method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
790                         frame->managed = TRUE;
791
792                 if (ji->from_aot)
793                         unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len);
794                 else
795                         unwind_info = mono_get_cached_unwind_info (ji->used_regs, &unwind_info_len);
796
797                 regs [X86_EAX] = new_ctx->eax;
798                 regs [X86_EBX] = new_ctx->ebx;
799                 regs [X86_ECX] = new_ctx->ecx;
800                 regs [X86_EDX] = new_ctx->edx;
801                 regs [X86_ESP] = new_ctx->esp;
802                 regs [X86_EBP] = new_ctx->ebp;
803                 regs [X86_ESI] = new_ctx->esi;
804                 regs [X86_EDI] = new_ctx->edi;
805                 regs [X86_NREG] = new_ctx->eip;
806
807                 mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, 
808                                                    (guint8*)ji->code_start + ji->code_size,
809                                                    ip, regs, MONO_MAX_IREGS + 1, &cfa);
810
811                 new_ctx->eax = regs [X86_EAX];
812                 new_ctx->ebx = regs [X86_EBX];
813                 new_ctx->ecx = regs [X86_ECX];
814                 new_ctx->edx = regs [X86_EDX];
815                 new_ctx->esp = regs [X86_ESP];
816                 new_ctx->ebp = regs [X86_EBP];
817                 new_ctx->esi = regs [X86_ESI];
818                 new_ctx->edi = regs [X86_EDI];
819                 new_ctx->eip = regs [X86_NREG];
820
821                 /* The CFA becomes the new SP value */
822                 new_ctx->esp = (gssize)cfa;
823
824                 /* Adjust IP */
825                 new_ctx->eip --;
826
827                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
828                         /* remove any unused lmf */
829                         *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
830                 }
831
832                 /* Pop arguments off the stack */
833                 /* 
834                  * FIXME: LLVM doesn't push these, we can't use ji->from_llvm as it describes
835                  * the caller.
836                  */
837 #ifndef ENABLE_LLVM
838                 {
839                         MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
840
841                         guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (ji->method), mono_method_signature (ji->method)->param_count, arg_info);
842                         new_ctx->esp += stack_to_pop;
843                 }
844 #endif
845
846                 return TRUE;
847         } else if (*lmf) {
848
849                 if (((guint64)(*lmf)->previous_lmf) & 2) {
850                         /* 
851                          * This LMF entry is created by the soft debug code to mark transitions to
852                          * managed code done during invokes.
853                          */
854                         MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
855
856                         g_assert (ext->debugger_invoke);
857
858                         memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
859
860                         *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
861
862                         frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
863
864                         return TRUE;
865                 }
866                 
867                 if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL))) {
868                 } else {
869                         if (!((guint32)((*lmf)->previous_lmf) & 1))
870                                 /* Top LMF entry */
871                                 return FALSE;
872                         /* Trampoline lmf frame */
873                         frame->method = (*lmf)->method;
874                 }
875
876                 new_ctx->esi = (*lmf)->esi;
877                 new_ctx->edi = (*lmf)->edi;
878                 new_ctx->ebx = (*lmf)->ebx;
879                 new_ctx->ebp = (*lmf)->ebp;
880                 new_ctx->eip = (*lmf)->eip;
881
882                 frame->ji = ji;
883                 frame->type = FRAME_TYPE_MANAGED_TO_NATIVE;
884
885                 /* Check if we are in a trampoline LMF frame */
886                 if ((guint32)((*lmf)->previous_lmf) & 1) {
887                         /* lmf->esp is set by the trampoline code */
888                         new_ctx->esp = (*lmf)->esp;
889
890                         /* Pop arguments off the stack */
891                         /* FIXME: Handle the delegate case too ((*lmf)->method == NULL) */
892                         /* FIXME: Handle the IMT/vtable case too */
893 #ifndef ENABLE_LLVM
894                         if ((*lmf)->method && (*lmf)->method != MONO_FAKE_IMT_METHOD && (*lmf)->method != MONO_FAKE_VTABLE_METHOD) {
895                                 MonoMethod *method = (*lmf)->method;
896                                 MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (method)->param_count + 1);
897
898                                 guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (method), mono_method_signature (method)->param_count, arg_info);
899                                 new_ctx->esp += stack_to_pop;
900                         }
901 #endif
902                 }
903                 else
904                         /* the lmf is always stored on the stack, so the following
905                          * expression points to a stack location which can be used as ESP */
906                         new_ctx->esp = (unsigned long)&((*lmf)->eip);
907
908                 *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
909
910                 return TRUE;
911         }
912
913         return FALSE;
914 }
915
916 #ifdef __sun
917 #define REG_EAX EAX
918 #define REG_EBX EBX
919 #define REG_ECX ECX
920 #define REG_EDX EDX
921 #define REG_EBP EBP
922 #define REG_ESP ESP
923 #define REG_ESI ESI
924 #define REG_EDI EDI
925 #define REG_EIP EIP
926 #endif
927
928 void
929 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
930 {
931 #ifdef MONO_ARCH_USE_SIGACTION
932         ucontext_t *ctx = (ucontext_t*)sigctx;
933         
934         mctx->eax = UCONTEXT_REG_EAX (ctx);
935         mctx->ebx = UCONTEXT_REG_EBX (ctx);
936         mctx->ecx = UCONTEXT_REG_ECX (ctx);
937         mctx->edx = UCONTEXT_REG_EDX (ctx);
938         mctx->ebp = UCONTEXT_REG_EBP (ctx);
939         mctx->esp = UCONTEXT_REG_ESP (ctx);
940         mctx->esi = UCONTEXT_REG_ESI (ctx);
941         mctx->edi = UCONTEXT_REG_EDI (ctx);
942         mctx->eip = UCONTEXT_REG_EIP (ctx);
943 #else   
944         struct sigcontext *ctx = (struct sigcontext *)sigctx;
945
946         mctx->eax = ctx->SC_EAX;
947         mctx->ebx = ctx->SC_EBX;
948         mctx->ecx = ctx->SC_ECX;
949         mctx->edx = ctx->SC_EDX;
950         mctx->ebp = ctx->SC_EBP;
951         mctx->esp = ctx->SC_ESP;
952         mctx->esi = ctx->SC_ESI;
953         mctx->edi = ctx->SC_EDI;
954         mctx->eip = ctx->SC_EIP;
955 #endif
956 }
957
958 void
959 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
960 {
961 #ifdef MONO_ARCH_USE_SIGACTION
962         ucontext_t *ctx = (ucontext_t*)sigctx;
963
964         UCONTEXT_REG_EAX (ctx) = mctx->eax;
965         UCONTEXT_REG_EBX (ctx) = mctx->ebx;
966         UCONTEXT_REG_ECX (ctx) = mctx->ecx;
967         UCONTEXT_REG_EDX (ctx) = mctx->edx;
968         UCONTEXT_REG_EBP (ctx) = mctx->ebp;
969         UCONTEXT_REG_ESP (ctx) = mctx->esp;
970         UCONTEXT_REG_ESI (ctx) = mctx->esi;
971         UCONTEXT_REG_EDI (ctx) = mctx->edi;
972         UCONTEXT_REG_EIP (ctx) = mctx->eip;
973 #else
974         struct sigcontext *ctx = (struct sigcontext *)sigctx;
975
976         ctx->SC_EAX = mctx->eax;
977         ctx->SC_EBX = mctx->ebx;
978         ctx->SC_ECX = mctx->ecx;
979         ctx->SC_EDX = mctx->edx;
980         ctx->SC_EBP = mctx->ebp;
981         ctx->SC_ESP = mctx->esp;
982         ctx->SC_ESI = mctx->esi;
983         ctx->SC_EDI = mctx->edi;
984         ctx->SC_EIP = mctx->eip;
985 #endif
986 }       
987
988 gpointer
989 mono_arch_ip_from_context (void *sigctx)
990 {
991 #ifdef MONO_ARCH_USE_SIGACTION
992         ucontext_t *ctx = (ucontext_t*)sigctx;
993         return (gpointer)UCONTEXT_REG_EIP (ctx);
994 #else
995         struct sigcontext *ctx = sigctx;
996         return (gpointer)ctx->SC_EIP;
997 #endif  
998 }
999
1000 gboolean
1001 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
1002 {
1003         MonoContext mctx;
1004
1005         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
1006
1007         if (mono_debugger_handle_exception (&mctx, (MonoObject *)obj))
1008                 return TRUE;
1009
1010         mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only);
1011
1012         mono_arch_monoctx_to_sigctx (&mctx, sigctx);
1013
1014         return TRUE;
1015 }
1016
1017 static void
1018 restore_soft_guard_pages (void)
1019 {
1020         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
1021         if (jit_tls->stack_ovf_guard_base)
1022                 mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_NONE);
1023 }
1024
1025 /* 
1026  * this function modifies mctx so that when it is restored, it
1027  * won't execcute starting at mctx.eip, but in a function that
1028  * will restore the protection on the soft-guard pages and return back to
1029  * continue at mctx.eip.
1030  */
1031 static void
1032 prepare_for_guard_pages (MonoContext *mctx)
1033 {
1034         gpointer *sp;
1035         sp = (gpointer)(mctx->esp);
1036         sp -= 1;
1037         /* the resturn addr */
1038         sp [0] = (gpointer)(mctx->eip);
1039         mctx->eip = (unsigned long)restore_soft_guard_pages;
1040         mctx->esp = (unsigned long)sp;
1041 }
1042
1043 static void
1044 altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean stack_ovf)
1045 {
1046         void (*restore_context) (MonoContext *);
1047         MonoContext mctx;
1048
1049         restore_context = mono_arch_get_restore_context ();
1050         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
1051
1052         if (mono_debugger_handle_exception (&mctx, (MonoObject *)obj)) {
1053                 if (stack_ovf)
1054                         prepare_for_guard_pages (&mctx);
1055                 restore_context (&mctx);
1056         }
1057
1058         mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, FALSE);
1059         if (stack_ovf)
1060                 prepare_for_guard_pages (&mctx);
1061         restore_context (&mctx);
1062 }
1063
1064 void
1065 mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean stack_ovf)
1066 {
1067 #ifdef MONO_ARCH_USE_SIGACTION
1068         MonoException *exc = NULL;
1069         ucontext_t *ctx = (ucontext_t*)sigctx;
1070         MonoJitInfo *ji = mini_jit_info_table_find (mono_domain_get (), (gpointer)UCONTEXT_REG_EIP (ctx), NULL);
1071         gpointer *sp;
1072         int frame_size;
1073
1074         /* if we didn't find a managed method for the ip address and it matches the fault
1075          * address, we assume we followed a broken pointer during an indirect call, so
1076          * we try the lookup again with the return address pushed on the stack
1077          */
1078         if (!ji && fault_addr == (gpointer)UCONTEXT_REG_EIP (ctx)) {
1079                 glong *sp = (gpointer)UCONTEXT_REG_ESP (ctx);
1080                 ji = mini_jit_info_table_find (mono_domain_get (), (gpointer)sp [0], NULL);
1081                 if (ji)
1082                         UCONTEXT_REG_EIP (ctx) = sp [0];
1083         }
1084         if (stack_ovf)
1085                 exc = mono_domain_get ()->stack_overflow_ex;
1086         if (!ji)
1087                 mono_handle_native_sigsegv (SIGSEGV, sigctx);
1088         /* setup a call frame on the real stack so that control is returned there
1089          * and exception handling can continue.
1090          * If this was a stack overflow the caller already ensured the stack pages
1091          * needed have been unprotected.
1092          * The frame looks like:
1093          *   ucontext struct
1094          *   test_only arg
1095          *   exception arg
1096          *   ctx arg
1097          *   return ip
1098          */
1099         frame_size = sizeof (ucontext_t) + sizeof (gpointer) * 4;
1100         frame_size += 15;
1101         frame_size &= ~15;
1102         sp = (gpointer)(UCONTEXT_REG_ESP (ctx) & ~15);
1103         sp = (gpointer)((char*)sp - frame_size);
1104         /* the incoming arguments are aligned to 16 bytes boundaries, so the return address IP
1105          * goes at sp [-1]
1106          */
1107         sp [-1] = (gpointer)UCONTEXT_REG_EIP (ctx);
1108         sp [0] = sp + 4;
1109         sp [1] = exc;
1110         sp [2] = (gpointer)stack_ovf;
1111         /* may need to adjust pointers in the new struct copy, depending on the OS */
1112         memcpy (sp + 4, ctx, sizeof (ucontext_t));
1113         /* at the return form the signal handler execution starts in altstack_handle_and_restore() */
1114         UCONTEXT_REG_EIP (ctx) = (unsigned long)altstack_handle_and_restore;
1115         UCONTEXT_REG_ESP (ctx) = (unsigned long)(sp - 1);
1116 #endif
1117 }
1118
1119 #if MONO_SUPPORT_TASKLETS
1120 MonoContinuationRestore
1121 mono_tasklets_arch_restore (void)
1122 {
1123         static guint8* saved = NULL;
1124         guint8 *code, *start;
1125
1126         if (saved)
1127                 return (MonoContinuationRestore)saved;
1128         code = start = mono_global_codeman_reserve (48);
1129         /* the signature is: restore (MonoContinuation *cont, int state, MonoLMF **lmf_addr) */
1130         /* put cont in edx */
1131         x86_mov_reg_membase (code, X86_EDX, X86_ESP, 4, 4);
1132         /* setup the copy of the stack */
1133         x86_mov_reg_membase (code, X86_ECX, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, stack_used_size), 4);
1134         x86_shift_reg_imm (code, X86_SHR, X86_ECX, 2);
1135         x86_cld (code);
1136         x86_mov_reg_membase (code, X86_ESI, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, saved_stack), 4);
1137         x86_mov_reg_membase (code, X86_EDI, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, return_sp), 4);
1138         x86_prefix (code, X86_REP_PREFIX);
1139         x86_movsl (code);
1140
1141         /* now restore the registers from the LMF */
1142         x86_mov_reg_membase (code, X86_ECX, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, lmf), 4);
1143         x86_mov_reg_membase (code, X86_EBX, X86_ECX, G_STRUCT_OFFSET (MonoLMF, ebx), 4);
1144         x86_mov_reg_membase (code, X86_EBP, X86_ECX, G_STRUCT_OFFSET (MonoLMF, ebp), 4);
1145         x86_mov_reg_membase (code, X86_ESI, X86_ECX, G_STRUCT_OFFSET (MonoLMF, esi), 4);
1146         x86_mov_reg_membase (code, X86_EDI, X86_ECX, G_STRUCT_OFFSET (MonoLMF, edi), 4);
1147
1148         /* restore the lmf chain */
1149         /*x86_mov_reg_membase (code, X86_ECX, X86_ESP, 12, 4);
1150         x86_mov_membase_reg (code, X86_ECX, 0, X86_EDX, 4);*/
1151
1152         /* state in eax, so it's setup as the return value */
1153         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 8, 4);
1154         x86_jump_membase (code, X86_EDX, G_STRUCT_OFFSET (MonoContinuation, return_ip));
1155         g_assert ((code - start) <= 48);
1156         saved = start;
1157         return (MonoContinuationRestore)saved;
1158 }
1159 #endif
1160