2004-11-07 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / mini / exceptions-amd64.c
1 /*
2  * exceptions-amd64.c: exception support for AMD64
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 #include <sys/ucontext.h>
15
16 #include <mono/arch/amd64/amd64-codegen.h>
17 #include <mono/metadata/appdomain.h>
18 #include <mono/metadata/tabledefs.h>
19 #include <mono/metadata/threads.h>
20 #include <mono/metadata/debug-helpers.h>
21 #include <mono/metadata/exception.h>
22 #include <mono/metadata/gc-internal.h>
23 #include <mono/metadata/mono-debug.h>
24
25 #include "mini.h"
26 #include "mini-amd64.h"
27
28 #define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
29
30 #ifdef PLATFORM_WIN32
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  * Unhandled Exception Filter
42  * Top-level per-process exception handler.
43  */
44 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
45 {
46         EXCEPTION_RECORD* er;
47         CONTEXT* ctx;
48         MonoContext* sctx;
49         LONG res;
50
51         res = EXCEPTION_CONTINUE_EXECUTION;
52
53         er = ep->ExceptionRecord;
54         ctx = ep->ContextRecord;
55         sctx = g_malloc(sizeof(MonoContext));
56
57         /* Copy Win32 context to UNIX style context */
58         sctx->eax = ctx->Eax;
59         sctx->ebx = ctx->Ebx;
60         sctx->ecx = ctx->Ecx;
61         sctx->edx = ctx->Edx;
62         sctx->ebp = ctx->Ebp;
63         sctx->esp = ctx->Esp;
64         sctx->esi = ctx->Esi;
65         sctx->edi = ctx->Edi;
66         sctx->eip = ctx->Eip;
67
68         switch (er->ExceptionCode) {
69         case EXCEPTION_ACCESS_VIOLATION:
70                 W32_SEH_HANDLE_EX(segv);
71                 break;
72         case EXCEPTION_ILLEGAL_INSTRUCTION:
73                 W32_SEH_HANDLE_EX(ill);
74                 break;
75         case EXCEPTION_INT_DIVIDE_BY_ZERO:
76         case EXCEPTION_INT_OVERFLOW:
77         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
78         case EXCEPTION_FLT_OVERFLOW:
79         case EXCEPTION_FLT_UNDERFLOW:
80         case EXCEPTION_FLT_INEXACT_RESULT:
81                 W32_SEH_HANDLE_EX(fpe);
82                 break;
83         default:
84                 break;
85         }
86
87         /* Copy context back */
88         ctx->Eax = sctx->eax;
89         ctx->Ebx = sctx->ebx;
90         ctx->Ecx = sctx->ecx;
91         ctx->Edx = sctx->edx;
92         ctx->Ebp = sctx->ebp;
93         ctx->Esp = sctx->esp;
94         ctx->Esi = sctx->esi;
95         ctx->Edi = sctx->edi;
96         ctx->Eip = sctx->eip;
97
98         return res;
99 }
100
101 void win32_seh_init()
102 {
103         old_handler = SetUnhandledExceptionFilter(seh_handler);
104 }
105
106 void win32_seh_cleanup()
107 {
108         if (old_handler) SetUnhandledExceptionFilter(old_handler);
109 }
110
111 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
112 {
113         switch (type) {
114         case SIGFPE:
115                 fpe_handler = handler;
116                 break;
117         case SIGILL:
118                 ill_handler = handler;
119                 break;
120         case SIGSEGV:
121                 segv_handler = handler;
122                 break;
123         default:
124                 break;
125         }
126 }
127
128 #endif /* PLATFORM_WIN32 */
129
130 /*
131  * Can't allocate the helper methods in static arrays as on other platforms.
132  */
133 static MonoCodeManager *code_manager = NULL;
134 static CRITICAL_SECTION code_manager_mutex;
135
136 void
137 mono_amd64_exceptions_init ()
138 {
139         InitializeCriticalSection (&code_manager_mutex);
140         code_manager = mono_code_manager_new ();
141 }
142
143 /*
144  * mono_arch_get_restore_context:
145  *
146  * Returns a pointer to a method which restores a previously saved sigcontext.
147  */
148 gpointer
149 mono_arch_get_restore_context (void)
150 {
151         static guint8 *start = NULL;
152         static gboolean inited = FALSE;
153         guint8 *code;
154
155         if (inited)
156                 return start;
157
158         /* restore_contect (MonoContext *ctx) */
159
160         EnterCriticalSection (&code_manager_mutex);
161         start = code = mono_code_manager_reserve (code_manager, 1024);
162         LeaveCriticalSection (&code_manager_mutex);
163
164         /* get return address */
165         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rip), 8);
166
167         /* Restore registers */
168         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rbp), 8);
169         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rbx), 8);
170         amd64_mov_reg_membase (code, AMD64_R12, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r12), 8);
171         amd64_mov_reg_membase (code, AMD64_R13, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r13), 8);
172         amd64_mov_reg_membase (code, AMD64_R14, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r14), 8);
173         amd64_mov_reg_membase (code, AMD64_R15, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r15), 8);
174
175         amd64_mov_reg_membase (code, AMD64_RSP, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rsp), 8);
176
177         /* jump to the saved IP */
178         amd64_jump_reg (code, AMD64_RAX);
179
180         inited = TRUE;
181
182         return start;
183 }
184
185 /*
186  * mono_arch_get_call_filter:
187  *
188  * Returns a pointer to a method which calls an exception filter. We
189  * also use this function to call finally handlers (we pass NULL as 
190  * @exc object in this case).
191  */
192 gpointer
193 mono_arch_get_call_filter (void)
194 {
195         static guint8 *start;
196         static gboolean inited = FALSE;
197         int i;
198         guint8 *code;
199         guint32 pos;
200
201         if (inited)
202                 return start;
203
204         EnterCriticalSection (&code_manager_mutex);
205         start = code = mono_code_manager_reserve (code_manager, 64);
206         LeaveCriticalSection (&code_manager_mutex);
207
208         /* call_filter (MonoContext *ctx, unsigned long eip) */
209         code = start;
210
211         /* Alloc new frame */
212         amd64_push_reg (code, AMD64_RBP);
213         amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);
214
215         /* Save callee saved regs */
216         pos = 0;
217         for (i = 0; i < AMD64_NREG; ++i)
218                 if (AMD64_IS_CALLEE_SAVED_REG (i)) {
219                         amd64_push_reg (code, i);
220                         pos += 8;
221                 }
222
223         /* Save EBP */
224         pos += 8;
225         amd64_push_reg (code, AMD64_RBP);
226
227         /* Make stack misaligned, the call will make it aligned again */
228         if (! (pos & 8))
229                 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
230
231         /* set new EBP */
232         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rbp), 8);
233         /* load callee saved regs */
234         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rbx), 8);
235         amd64_mov_reg_membase (code, AMD64_R12, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r12), 8);
236         amd64_mov_reg_membase (code, AMD64_R13, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r13), 8);
237         amd64_mov_reg_membase (code, AMD64_R14, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r14), 8);
238         amd64_mov_reg_membase (code, AMD64_R15, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r15), 8);
239
240         /* call the handler */
241         amd64_call_reg (code, AMD64_RSI);
242
243         if (! (pos & 8))
244                 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
245
246         /* restore RBP */
247         amd64_pop_reg (code, AMD64_RBP);
248
249         /* Restore callee saved regs */
250         for (i = AMD64_NREG; i >= 0; --i)
251                 if (AMD64_IS_CALLEE_SAVED_REG (i))
252                         amd64_pop_reg (code, i);
253
254         amd64_leave (code);
255         amd64_ret (code);
256
257         g_assert ((code - start) < 64);
258
259         inited = TRUE;
260
261         return start;
262 }
263
264 static void
265 throw_exception (MonoObject *exc, guint64 rip, guint64 rsp,
266                                  guint64 rbx, guint64 rbp, guint64 r12, guint64 r13, 
267                                  guint64 r14, guint64 r15, guint64 rethrow)
268 {
269         static void (*restore_context) (MonoContext *);
270         MonoContext ctx;
271
272         if (!restore_context)
273                 restore_context = mono_arch_get_restore_context ();
274
275         /* adjust eip so that it point into the call instruction */
276         rip -= 1;
277
278         ctx.rsp = rsp;
279         ctx.rip = rip;
280         ctx.rbx = rbx;
281         ctx.rbp = rbp;
282         ctx.r12 = r12;
283         ctx.r13 = r13;
284         ctx.r14 = r14;
285         ctx.r15 = r15;
286
287         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
288                 MonoException *mono_ex = (MonoException*)exc;
289                 if (!rethrow)
290                         mono_ex->stack_trace = NULL;
291         }
292         mono_handle_exception (&ctx, exc, (gpointer)(rip + 1), FALSE);
293         restore_context (&ctx);
294
295         g_assert_not_reached ();
296 }
297
298 static gpointer
299 get_throw_trampoline (gboolean rethrow)
300 {
301         guint8* start;
302         guint8 *code;
303
304         EnterCriticalSection (&code_manager_mutex);
305         start = code = mono_code_manager_reserve (code_manager, 64);
306         LeaveCriticalSection (&code_manager_mutex);
307
308         code = start;
309
310         /* Exception */
311         amd64_mov_reg_reg (code, AMD64_RDI, AMD64_RDI, 8);
312         /* IP */
313         amd64_mov_reg_membase (code, AMD64_RSI, AMD64_RSP, 0, 8);
314         /* SP */
315         amd64_lea_membase (code, AMD64_RDX, AMD64_RSP, 8);
316         /* Callee saved regs */
317         amd64_mov_reg_reg (code, AMD64_RCX, AMD64_RBX, 8);
318         amd64_mov_reg_reg (code, AMD64_R8, AMD64_RBP, 8);
319         amd64_mov_reg_reg (code, AMD64_R9, AMD64_R12, 8);
320         /* reverse order */
321         amd64_push_imm (code, rethrow);
322         amd64_push_reg (code, AMD64_R15);
323         amd64_push_reg (code, AMD64_R14);
324         amd64_push_reg (code, AMD64_R13);
325
326         amd64_mov_reg_imm (code, AMD64_R11, throw_exception);
327         amd64_call_reg (code, AMD64_R11);
328         amd64_breakpoint (code);
329
330         g_assert ((code - start) < 64);
331
332         return start;
333 }
334
335 /**
336  * mono_arch_get_throw_exception:
337  *
338  * Returns a function pointer which can be used to raise 
339  * exceptions. The returned function has the following 
340  * signature: void (*func) (MonoException *exc); 
341  *
342  */
343 gpointer 
344 mono_arch_get_throw_exception (void)
345 {
346         static guint8* start;
347         static gboolean inited = FALSE;
348
349         if (inited)
350                 return start;
351
352         start = get_throw_trampoline (FALSE);
353
354         inited = TRUE;
355
356         return start;
357 }
358
359 gpointer 
360 mono_arch_get_rethrow_exception (void)
361 {
362         static guint8* start;
363         static gboolean inited = FALSE;
364
365         if (inited)
366                 return start;
367
368         start = get_throw_trampoline (TRUE);
369
370         inited = TRUE;
371
372         return start;
373 }
374
375 /**
376  * mono_arch_get_throw_exception_by_name:
377  *
378  * Returns a function pointer which can be used to raise 
379  * corlib exceptions. The returned function has the following 
380  * signature: void (*func) (char *exc_name); 
381  */
382 gpointer 
383 mono_arch_get_throw_exception_by_name (void)
384 {
385         static guint8* start;
386         static gboolean inited = FALSE;
387         guint8 *code;
388         guint64 throw_ex;
389
390         if (inited)
391                 return start;
392
393         EnterCriticalSection (&code_manager_mutex);
394         start = code = mono_code_manager_reserve (code_manager, 64);
395         LeaveCriticalSection (&code_manager_mutex);
396
397         code = start;
398
399         /* Push return address */
400         amd64_push_reg (code, AMD64_RSI);
401
402         /* Call exception_from_name */
403         amd64_mov_reg_reg (code, AMD64_RDX, AMD64_RDI, 8);
404         amd64_mov_reg_imm (code, AMD64_RSI, "System");
405         amd64_mov_reg_imm (code, AMD64_RDI, mono_defaults.exception_class->image);
406
407         amd64_mov_reg_imm (code, AMD64_R11, mono_exception_from_name);
408         amd64_call_reg (code, AMD64_R11);
409
410         /* Put the original return address at the top of the misaligned stack */
411         amd64_pop_reg (code, AMD64_RSI);
412         amd64_push_reg (code, AMD64_R11);
413         amd64_push_reg (code, AMD64_RSI);
414
415         throw_ex = (guint64)mono_arch_get_throw_exception ();
416
417         /* Call throw_exception */
418         amd64_mov_reg_reg (code, AMD64_RDI, AMD64_RAX, 8);
419         amd64_mov_reg_imm (code, AMD64_R11, throw_ex);
420         /* The original IP is on the stack */
421         amd64_jump_reg (code, AMD64_R11);
422
423         g_assert ((code - start) < 64);
424
425         inited = TRUE;
426
427         return start;
428 }
429
430 /* mono_arch_find_jit_info:
431  *
432  * This function is used to gather information from @ctx. It return the 
433  * MonoJitInfo of the corresponding function, unwinds one stack frame and
434  * stores the resulting context into @new_ctx. It also stores a string 
435  * describing the stack location into @trace (if not NULL), and modifies
436  * the @lmf if necessary. @native_offset return the IP offset from the 
437  * start of the function or -1 if that info is not available.
438  */
439 MonoJitInfo *
440 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
441                          MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
442                          gboolean *managed)
443 {
444         MonoJitInfo *ji;
445         int i;
446         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
447
448         /* Avoid costly table lookup during stack overflow */
449         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
450                 ji = prev_ji;
451         else
452                 ji = mono_jit_info_table_find (domain, ip);
453
454         if (managed)
455                 *managed = FALSE;
456
457         if (ji != NULL) {
458                 int offset;
459
460                 *new_ctx = *ctx;
461
462                 if (managed)
463                         if (!ji->method->wrapper_type)
464                                 *managed = TRUE;
465
466                 /*
467                  * Some managed methods like pinvoke wrappers might have save_lmf set.
468                  * In this case, register save/restore code is not generated by the 
469                  * JIT, so we have to restore callee saved registers from the lmf.
470                  */
471                 if (ji->method->save_lmf) {
472                         /* 
473                          * We only need to do this if the exception was raised in managed
474                          * code, since otherwise the lmf was already popped of the stack.
475                          */
476                         if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
477                                 new_ctx->rbx = (*lmf)->rbx;
478                                 new_ctx->r12 = (*lmf)->r12;
479                                 new_ctx->r13 = (*lmf)->r13;
480                                 new_ctx->r14 = (*lmf)->r14;
481                                 new_ctx->r15 = (*lmf)->r15;
482                         }
483                 }
484                 else {
485                         offset = -1;
486                         /* restore caller saved registers */
487                         for (i = 0; i < AMD64_NREG; i ++)
488                                 if (AMD64_IS_CALLEE_SAVED_REG (i) && (ji->used_regs & (1 << i))) {
489                                         guint64 reg = *((guint64 *)ctx->SC_EBP + offset);
490                                         offset --;
491                                         switch (i) {
492                                         case AMD64_RBX:
493                                                 new_ctx->rbx = reg;
494                                                 break;
495                                         case AMD64_R12:
496                                                 new_ctx->r12 = reg;
497                                                 break;
498                                         case AMD64_R13:
499                                                 new_ctx->r13 = reg;
500                                                 break;
501                                         case AMD64_R14:
502                                                 new_ctx->r14 = reg;
503                                                 break;
504                                         case AMD64_R15:
505                                                 new_ctx->r15 = reg;
506                                                 break;
507                                         default:
508                                                 g_assert_not_reached ();
509                                         }
510                                 }
511                 }
512
513                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
514                         /* remove any unused lmf */
515                         *lmf = (*lmf)->previous_lmf;
516                 }
517
518                 /* Pop EBP and the return address */
519                 new_ctx->SC_ESP = ctx->SC_EBP + (2 * sizeof (gpointer));
520                 /* we substract 1, so that the IP points into the call instruction */
521                 new_ctx->SC_EIP = *((guint64 *)ctx->SC_EBP + 1) - 1;
522                 new_ctx->SC_EBP = *((guint64 *)ctx->SC_EBP);
523
524                 /* Pop arguments off the stack */
525                 {
526                         MonoJitArgumentInfo *arg_info = alloca (sizeof (MonoJitArgumentInfo) * (ji->method->signature->param_count + 1));
527
528                         guint32 stack_to_pop = mono_arch_get_argument_info (ji->method->signature, ji->method->signature->param_count, arg_info);
529                         new_ctx->SC_ESP += stack_to_pop;
530                 }
531
532                 *res = *ji;
533                 return res;
534         } else if (*lmf) {
535                 
536                 *new_ctx = *ctx;
537
538                 if (!(*lmf)->method)
539                         return (gpointer)-1;
540
541                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->rip))) {
542                         *res = *ji;
543                 } else {
544                         memset (res, 0, sizeof (MonoJitInfo));
545                         res->method = (*lmf)->method;
546                 }
547
548                 new_ctx->SC_RIP = (*lmf)->rip;
549                 new_ctx->SC_RBP = (*lmf)->ebp;
550
551                 new_ctx->SC_RBX = (*lmf)->rbx;
552                 new_ctx->SC_R12 = (*lmf)->r12;
553                 new_ctx->SC_R13 = (*lmf)->r13;
554                 new_ctx->SC_R14 = (*lmf)->r14;
555                 new_ctx->SC_R15 = (*lmf)->r15;
556
557                 /* the lmf is always stored on the stack, so the following
558                  * expression points to a stack location which can be used as ESP */
559                 new_ctx->SC_ESP = ALIGN_TO ((guint64)&((*lmf)->rip), 16);
560
561                 *lmf = (*lmf)->previous_lmf;
562
563                 return res;
564                 
565         }
566
567         return NULL;
568 }
569
570 /**
571  * mono_arch_handle_exception:
572  *
573  * @ctx: saved processor state
574  * @obj: the exception object
575  */
576 gboolean
577 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
578 {
579         ucontext_t *ctx = (ucontext_t*)sigctx;
580         MonoContext mctx;
581
582         mctx.rax = ctx->uc_mcontext.gregs [REG_RAX];
583         mctx.rbx = ctx->uc_mcontext.gregs [REG_RBX];
584         mctx.rcx = ctx->uc_mcontext.gregs [REG_RCX];
585         mctx.rdx = ctx->uc_mcontext.gregs [REG_RDX];
586         mctx.rbp = ctx->uc_mcontext.gregs [REG_RBP];
587         mctx.rsp = ctx->uc_mcontext.gregs [REG_RSP];
588         mctx.rsi = ctx->uc_mcontext.gregs [REG_RSI];
589         mctx.rdi = ctx->uc_mcontext.gregs [REG_RDI];
590         mctx.rip = ctx->uc_mcontext.gregs [REG_RIP];
591         mctx.r12 = ctx->uc_mcontext.gregs [REG_R12];
592         mctx.r13 = ctx->uc_mcontext.gregs [REG_R13];
593         mctx.r14 = ctx->uc_mcontext.gregs [REG_R14];
594         mctx.r15 = ctx->uc_mcontext.gregs [REG_R15];
595
596         mono_handle_exception (&mctx, obj, (gpointer)mctx.rip, test_only);
597
598         ctx->uc_mcontext.gregs [REG_RAX] = mctx.rax;
599         ctx->uc_mcontext.gregs [REG_RBX] = mctx.rbx;
600         ctx->uc_mcontext.gregs [REG_RCX] = mctx.rcx;
601         ctx->uc_mcontext.gregs [REG_RDX] = mctx.rdx;
602         ctx->uc_mcontext.gregs [REG_RBP] = mctx.rbp;
603         ctx->uc_mcontext.gregs [REG_RSP] = mctx.rsp;
604         ctx->uc_mcontext.gregs [REG_RSI] = mctx.rsi;
605         ctx->uc_mcontext.gregs [REG_RDI] = mctx.rdi;
606         ctx->uc_mcontext.gregs [REG_RIP] = mctx.rip;
607         ctx->uc_mcontext.gregs [REG_R12] = mctx.r12;
608         ctx->uc_mcontext.gregs [REG_R13] = mctx.r13;
609         ctx->uc_mcontext.gregs [REG_R14] = mctx.r14;
610         ctx->uc_mcontext.gregs [REG_R15] = mctx.r15;
611
612         return TRUE;
613 }
614
615 gpointer
616 mono_arch_ip_from_context (void *sigctx)
617 {
618         ucontext_t *ctx = (ucontext_t*)sigctx;
619         return (gpointer)ctx->uc_mcontext.gregs [REG_RIP];
620 }
621