2004-08-05 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  * mono_arch_get_restore_context:
132  *
133  * Returns a pointer to a method which restores a previously saved sigcontext.
134  */
135 gpointer
136 mono_arch_get_restore_context (void)
137 {
138         static guint8 *start = NULL;
139         guint8 *code;
140
141         if (start)
142                 return start;
143
144         /* restore_contect (MonoContext *ctx) */
145
146         start = code = g_malloc (1024);
147
148         /* get return address */
149         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rip), 8);
150
151         /* Restore registers */
152         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rbp), 8);
153         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rbx), 8);
154         amd64_mov_reg_membase (code, AMD64_R12, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r12), 8);
155         amd64_mov_reg_membase (code, AMD64_R13, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r13), 8);
156         amd64_mov_reg_membase (code, AMD64_R14, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r14), 8);
157         amd64_mov_reg_membase (code, AMD64_R15, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, r15), 8);
158
159         amd64_mov_reg_membase (code, AMD64_RSP, AMD64_RDI,  G_STRUCT_OFFSET (MonoContext, rsp), 8);
160
161         /* jump to the saved IP */
162         amd64_jump_reg (code, AMD64_RAX);
163
164         return start;
165 }
166
167 /*
168  * mono_arch_get_call_filter:
169  *
170  * Returns a pointer to a method which calls an exception filter. We
171  * also use this function to call finally handlers (we pass NULL as 
172  * @exc object in this case).
173  */
174 gpointer
175 mono_arch_get_call_filter (void)
176 {
177         static guint8 start [64];
178         static int inited = 0;
179         int i;
180         guint8 *code;
181         guint32 pos;
182
183         if (inited)
184                 return start;
185
186         inited = 1;
187         /* call_filter (MonoContext *ctx, unsigned long eip) */
188         code = start;
189
190         /* Alloc new frame */
191         amd64_push_reg (code, AMD64_RBP);
192         amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);
193
194         /* Save callee saved regs */
195         pos = 0;
196         for (i = 0; i < AMD64_NREG; ++i)
197                 if (AMD64_IS_CALLEE_SAVED_REG (i)) {
198                         amd64_push_reg (code, i);
199                         pos += 8;
200                 }
201
202         /* Save EBP */
203         pos += 8;
204         amd64_push_reg (code, AMD64_RBP);
205
206         /* Make stack misaligned, the call will make it aligned again */
207         if (! (pos & 8))
208                 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);
209
210         /* set new EBP */
211         amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rbp), 8);
212         /* load callee saved regs */
213         amd64_mov_reg_membase (code, AMD64_RBX, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, rbx), 8);
214         amd64_mov_reg_membase (code, AMD64_R12, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r12), 8);
215         amd64_mov_reg_membase (code, AMD64_R13, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r13), 8);
216         amd64_mov_reg_membase (code, AMD64_R14, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r14), 8);
217         amd64_mov_reg_membase (code, AMD64_R15, AMD64_RDI, G_STRUCT_OFFSET (MonoContext, r15), 8);
218
219         /* call the handler */
220         amd64_call_reg (code, AMD64_RSI);
221
222         if (! (pos & 8))
223                 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);
224
225         /* restore RBP */
226         amd64_pop_reg (code, AMD64_RBP);
227
228         /* Restore callee saved regs */
229         for (i = AMD64_NREG; i >= 0; --i)
230                 if (AMD64_IS_CALLEE_SAVED_REG (i))
231                         amd64_pop_reg (code, i);
232
233         amd64_leave (code);
234         amd64_ret (code);
235
236         g_assert ((code - start) < 64);
237         return start;
238 }
239
240 static void
241 throw_exception (MonoObject *exc, guint64 rip, guint64 rsp,
242                                  guint64 rbx, guint64 rbp, guint64 r12, guint64 r13, 
243                                  guint64 r14, guint64 r15)
244 {
245         static void (*restore_context) (MonoContext *);
246         MonoContext ctx;
247
248         if (!restore_context)
249                 restore_context = mono_arch_get_restore_context ();
250
251         /* adjust eip so that it point into the call instruction */
252         rip -= 1;
253
254         ctx.rsp = rsp;
255         ctx.rip = rip;
256         ctx.rbx = rbx;
257         ctx.rbp = rbp;
258         ctx.r12 = r12;
259         ctx.r13 = r13;
260         ctx.r14 = r14;
261         ctx.r15 = r15;
262
263         mono_handle_exception (&ctx, exc, (gpointer)(rip + 1), FALSE);
264         restore_context (&ctx);
265
266         g_assert_not_reached ();
267 }
268
269 /**
270  * mono_arch_get_throw_exception:
271  *
272  * Returns a function pointer which can be used to raise 
273  * exceptions. The returned function has the following 
274  * signature: void (*func) (MonoException *exc); 
275  *
276  */
277 gpointer 
278 mono_arch_get_throw_exception (void)
279 {
280         static guint8 start [64];
281         static int inited = 0;
282         guint8 *code;
283
284         if (inited)
285                 return start;
286
287         inited = 1;
288         code = start;
289
290         /* Exception */
291         amd64_mov_reg_reg (code, AMD64_RDI, AMD64_RDI, 8);
292         /* IP */
293         amd64_mov_reg_membase (code, AMD64_RSI, AMD64_RSP, 0, 8);
294         /* SP */
295         amd64_lea_membase (code, AMD64_RDX, AMD64_RSP, 8);
296         /* Callee saved regs */
297         amd64_mov_reg_reg (code, AMD64_RCX, AMD64_RBX, 8);
298         amd64_mov_reg_reg (code, AMD64_R8, AMD64_RBP, 8);
299         amd64_mov_reg_reg (code, AMD64_R9, AMD64_R12, 8);
300         /* reverse order */
301         amd64_push_reg (code, AMD64_R15);
302         amd64_push_reg (code, AMD64_R14);
303         amd64_push_reg (code, AMD64_R13);
304
305         amd64_mov_reg_imm (code, AMD64_R11, throw_exception);
306         amd64_call_reg (code, AMD64_R11);
307         amd64_breakpoint (code);
308
309         g_assert ((code - start) < 64);
310         return start;
311 }
312
313 /**
314  * mono_arch_get_throw_exception_by_name:
315  *
316  * Returns a function pointer which can be used to raise 
317  * corlib exceptions. The returned function has the following 
318  * signature: void (*func) (char *exc_name); 
319  */
320 gpointer 
321 mono_arch_get_throw_exception_by_name (void)
322 {
323         static guint8 start [64];
324         static int inited = 0;
325         guint8 *code;
326         guint64 throw_ex;
327
328         if (inited)
329                 return start;
330
331         inited = 1;
332         code = start;
333
334         /* Push return address */
335         amd64_push_reg (code, AMD64_RSI);
336
337         /* Call exception_from_name */
338         amd64_mov_reg_reg (code, AMD64_RDX, AMD64_RDI, 8);
339         amd64_mov_reg_imm (code, AMD64_RSI, "System");
340         amd64_mov_reg_imm (code, AMD64_RDI, mono_defaults.exception_class->image);
341
342         amd64_mov_reg_imm (code, AMD64_R11, mono_exception_from_name);
343         amd64_call_reg (code, AMD64_R11);
344
345         /* Put the original return address at the top of the misaligned stack */
346         amd64_pop_reg (code, AMD64_RSI);
347         amd64_push_reg (code, AMD64_R11);
348         amd64_push_reg (code, AMD64_RSI);
349
350         throw_ex = (guint64)mono_arch_get_throw_exception ();
351
352         /* Call throw_exception */
353         amd64_mov_reg_reg (code, AMD64_RDI, AMD64_RAX, 8);
354         amd64_mov_reg_imm (code, AMD64_R11, throw_ex);
355         /* The original IP is on the stack */
356         amd64_jump_reg (code, AMD64_R11);
357
358         g_assert ((code - start) < 64);
359
360         return start;
361 }
362
363 /* mono_arch_find_jit_info:
364  *
365  * This function is used to gather information from @ctx. It return the 
366  * MonoJitInfo of the corresponding function, unwinds one stack frame and
367  * stores the resulting context into @new_ctx. It also stores a string 
368  * describing the stack location into @trace (if not NULL), and modifies
369  * the @lmf if necessary. @native_offset return the IP offset from the 
370  * start of the function or -1 if that info is not available.
371  */
372 MonoJitInfo *
373 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
374                          MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
375                          gboolean *managed)
376 {
377         MonoJitInfo *ji;
378         int i;
379         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
380
381         /* Avoid costly table lookup during stack overflow */
382         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
383                 ji = prev_ji;
384         else
385                 ji = mono_jit_info_table_find (domain, ip);
386
387         if (trace)
388                 *trace = NULL;
389
390         if (native_offset)
391                 *native_offset = -1;
392
393         if (managed)
394                 *managed = FALSE;
395
396         if (ji != NULL) {
397                 char *source_location, *tmpaddr, *fname;
398                 gint32 address, iloffset;
399                 int offset;
400
401                 *new_ctx = *ctx;
402
403                 address = (char *)ip - (char *)ji->code_start;
404
405                 if (native_offset)
406                         *native_offset = address;
407
408                 if (managed)
409                         if (!ji->method->wrapper_type)
410                                 *managed = TRUE;
411
412                 if (trace) {
413                         source_location = mono_debug_source_location_from_address (ji->method, address, NULL, domain);
414                         iloffset = mono_debug_il_offset_from_address (ji->method, address, domain);
415
416                         if (iloffset < 0)
417                                 tmpaddr = g_strdup_printf ("<0x%05x>", address);
418                         else
419                                 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
420                 
421                         fname = mono_method_full_name (ji->method, TRUE);
422
423                         if (source_location)
424                                 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
425                         else
426                                 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
427
428                         g_free (fname);
429                         g_free (source_location);
430                         g_free (tmpaddr);
431                 }
432
433                 /*
434                  * Some managed methods like pinvoke wrappers might have save_lmf set.
435                  * In this case, register save/restore code is not generated by the 
436                  * JIT, so we have to restore callee saved registers from the lmf.
437                  */
438                 if (ji->method->save_lmf) {
439                         /* 
440                          * We only need to do this if the exception was raised in managed
441                          * code, since otherwise the lmf was already popped of the stack.
442                          */
443                         if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
444                                 new_ctx->rbx = (*lmf)->rbx;
445                                 new_ctx->r12 = (*lmf)->r12;
446                                 new_ctx->r13 = (*lmf)->r13;
447                                 new_ctx->r14 = (*lmf)->r14;
448                                 new_ctx->r15 = (*lmf)->r15;
449                         }
450                 }
451                 else {
452                         offset = -1;
453                         /* restore caller saved registers */
454                         for (i = 0; i < AMD64_NREG; i ++)
455                                 if (AMD64_IS_CALLEE_SAVED_REG (i) && (ji->used_regs & (1 << i))) {
456                                         guint64 reg = *((guint64 *)ctx->SC_EBP + offset);
457                                         offset --;
458                                         switch (i) {
459                                         case AMD64_RBX:
460                                                 new_ctx->rbx = reg;
461                                                 break;
462                                         case AMD64_R12:
463                                                 new_ctx->r12 = reg;
464                                                 break;
465                                         case AMD64_R13:
466                                                 new_ctx->r13 = reg;
467                                                 break;
468                                         case AMD64_R14:
469                                                 new_ctx->r14 = reg;
470                                                 break;
471                                         case AMD64_R15:
472                                                 new_ctx->r15 = reg;
473                                                 break;
474                                         default:
475                                                 g_assert_not_reached ();
476                                         }
477                                 }
478                 }
479
480                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
481                         /* remove any unused lmf */
482                         *lmf = (*lmf)->previous_lmf;
483                 }
484
485                 /* Pop EBP and the return address */
486                 new_ctx->SC_ESP = ctx->SC_EBP + (2 * sizeof (gpointer));
487                 /* we substract 1, so that the IP points into the call instruction */
488                 new_ctx->SC_EIP = *((guint64 *)ctx->SC_EBP + 1) - 1;
489                 new_ctx->SC_EBP = *((guint64 *)ctx->SC_EBP);
490
491                 *res = *ji;
492                 return res;
493         } else if (*lmf) {
494                 
495                 *new_ctx = *ctx;
496
497                 if (!(*lmf)->method)
498                         return (gpointer)-1;
499
500                 if (trace)
501                         *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
502                 
503                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->rip))) {
504                         *res = *ji;
505                 } else {
506                         memset (res, 0, sizeof (MonoJitInfo));
507                         res->method = (*lmf)->method;
508                 }
509
510                 new_ctx->SC_RIP = (*lmf)->rip;
511                 new_ctx->SC_RBP = (*lmf)->ebp;
512
513                 new_ctx->SC_RBX = (*lmf)->rbx;
514                 new_ctx->SC_R12 = (*lmf)->r12;
515                 new_ctx->SC_R13 = (*lmf)->r13;
516                 new_ctx->SC_R14 = (*lmf)->r14;
517                 new_ctx->SC_R15 = (*lmf)->r15;
518
519                 /* the lmf is always stored on the stack, so the following
520                  * expression points to a stack location which can be used as ESP */
521                 new_ctx->SC_ESP = ALIGN_TO ((guint64)&((*lmf)->rip), 16);
522
523                 *lmf = (*lmf)->previous_lmf;
524
525                 return res;
526                 
527         }
528
529         return NULL;
530 }
531
532 /**
533  * mono_arch_handle_exception:
534  *
535  * @ctx: saved processor state
536  * @obj: the exception object
537  */
538 gboolean
539 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
540 {
541         ucontext_t *ctx = (ucontext_t*)sigctx;
542         MonoContext mctx;
543
544         mctx.rax = ctx->uc_mcontext.gregs [REG_RAX];
545         mctx.rbx = ctx->uc_mcontext.gregs [REG_RBX];
546         mctx.rcx = ctx->uc_mcontext.gregs [REG_RCX];
547         mctx.rdx = ctx->uc_mcontext.gregs [REG_RDX];
548         mctx.rbp = ctx->uc_mcontext.gregs [REG_RBP];
549         mctx.rsp = ctx->uc_mcontext.gregs [REG_RSP];
550         mctx.rsi = ctx->uc_mcontext.gregs [REG_RSI];
551         mctx.rdi = ctx->uc_mcontext.gregs [REG_RDI];
552         mctx.rip = ctx->uc_mcontext.gregs [REG_RIP];
553         mctx.r12 = ctx->uc_mcontext.gregs [REG_R12];
554         mctx.r13 = ctx->uc_mcontext.gregs [REG_R13];
555         mctx.r14 = ctx->uc_mcontext.gregs [REG_R14];
556         mctx.r15 = ctx->uc_mcontext.gregs [REG_R15];
557
558         mono_handle_exception (&mctx, obj, mctx.rip, test_only);
559
560         ctx->uc_mcontext.gregs [REG_RAX] = mctx.rax;
561         ctx->uc_mcontext.gregs [REG_RBX] = mctx.rbx;
562         ctx->uc_mcontext.gregs [REG_RCX] = mctx.rcx;
563         ctx->uc_mcontext.gregs [REG_RDX] = mctx.rdx;
564         ctx->uc_mcontext.gregs [REG_RBP] = mctx.rbp;
565         ctx->uc_mcontext.gregs [REG_RSP] = mctx.rsp;
566         ctx->uc_mcontext.gregs [REG_RSI] = mctx.rsi;
567         ctx->uc_mcontext.gregs [REG_RDI] = mctx.rdi;
568         ctx->uc_mcontext.gregs [REG_RIP] = mctx.rip;
569         ctx->uc_mcontext.gregs [REG_R12] = mctx.r12;
570         ctx->uc_mcontext.gregs [REG_R13] = mctx.r13;
571         ctx->uc_mcontext.gregs [REG_R14] = mctx.r14;
572         ctx->uc_mcontext.gregs [REG_R15] = mctx.r15;
573
574         return TRUE;
575 }
576
577 gpointer
578 mono_arch_ip_from_context (void *sigctx)
579 {
580         ucontext_t *ctx = (ucontext_t*)sigctx;
581         return (gpointer)ctx->uc_mcontext.gregs [REG_RIP];
582 }
583