2005-04-04 Zoltan Varga <vargaz@freemail.hu>
[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/metadata/mono-debug-debugger.h>
24
25 #include "mini.h"
26 #include "mini-x86.h"
27
28 #ifdef PLATFORM_WIN32
29 static MonoW32ExceptionHandler fpe_handler;
30 static MonoW32ExceptionHandler ill_handler;
31 static MonoW32ExceptionHandler segv_handler;
32
33 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
34
35 #define W32_SEH_HANDLE_EX(_ex) \
36         if (_ex##_handler) _ex##_handler((int)sctx)
37
38 /*
39  * Unhandled Exception Filter
40  * Top-level per-process exception handler.
41  */
42 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
43 {
44         EXCEPTION_RECORD* er;
45         CONTEXT* ctx;
46         struct sigcontext* sctx;
47         LONG res;
48
49         res = EXCEPTION_CONTINUE_EXECUTION;
50
51         er = ep->ExceptionRecord;
52         ctx = ep->ContextRecord;
53         sctx = g_malloc(sizeof(struct sigcontext));
54
55         /* Copy Win32 context to UNIX style context */
56         sctx->eax = ctx->Eax;
57         sctx->ebx = ctx->Ebx;
58         sctx->ecx = ctx->Ecx;
59         sctx->edx = ctx->Edx;
60         sctx->ebp = ctx->Ebp;
61         sctx->esp = ctx->Esp;
62         sctx->esi = ctx->Esi;
63         sctx->edi = ctx->Edi;
64         sctx->eip = ctx->Eip;
65
66         switch (er->ExceptionCode) {
67         case EXCEPTION_ACCESS_VIOLATION:
68                 W32_SEH_HANDLE_EX(segv);
69                 break;
70         case EXCEPTION_ILLEGAL_INSTRUCTION:
71                 W32_SEH_HANDLE_EX(ill);
72                 break;
73         case EXCEPTION_INT_DIVIDE_BY_ZERO:
74         case EXCEPTION_INT_OVERFLOW:
75         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
76         case EXCEPTION_FLT_OVERFLOW:
77         case EXCEPTION_FLT_UNDERFLOW:
78         case EXCEPTION_FLT_INEXACT_RESULT:
79                 W32_SEH_HANDLE_EX(fpe);
80                 break;
81         default:
82                 break;
83         }
84
85         /* Copy context back */
86         ctx->Eax = sctx->eax;
87         ctx->Ebx = sctx->ebx;
88         ctx->Ecx = sctx->ecx;
89         ctx->Edx = sctx->edx;
90         ctx->Ebp = sctx->ebp;
91         ctx->Esp = sctx->esp;
92         ctx->Esi = sctx->esi;
93         ctx->Edi = sctx->edi;
94         ctx->Eip = sctx->eip;
95
96         return res;
97 }
98
99 void win32_seh_init()
100 {
101         old_handler = SetUnhandledExceptionFilter(seh_handler);
102 }
103
104 void win32_seh_cleanup()
105 {
106         if (old_handler) SetUnhandledExceptionFilter(old_handler);
107 }
108
109 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
110 {
111         switch (type) {
112         case SIGFPE:
113                 fpe_handler = handler;
114                 break;
115         case SIGILL:
116                 ill_handler = handler;
117                 break;
118         case SIGSEGV:
119                 segv_handler = handler;
120                 break;
121         default:
122                 break;
123         }
124 }
125
126 #endif /* PLATFORM_WIN32 */
127
128 /*
129  * mono_arch_get_restore_context:
130  *
131  * Returns a pointer to a method which restores a previously saved sigcontext.
132  */
133 gpointer
134 mono_arch_get_restore_context (void)
135 {
136         static guint8 *start = NULL;
137         guint8 *code;
138
139         if (start)
140                 return start;
141
142         /* restore_contect (MonoContext *ctx) */
143         /* we do not restore X86_EAX, X86_EDX */
144
145         start = code = mono_global_codeman_reserve (128);
146         
147         /* load ctx */
148         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
149
150         /* get return address, stored in EDX */
151         x86_mov_reg_membase (code, X86_EDX, X86_EAX,  G_STRUCT_OFFSET (MonoContext, eip), 4);
152         /* restore EBX */
153         x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (MonoContext, ebx), 4);
154         /* restore EDI */
155         x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (MonoContext, edi), 4);
156         /* restore ESI */
157         x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (MonoContext, esi), 4);
158         /* restore ESP */
159         x86_mov_reg_membase (code, X86_ESP, X86_EAX,  G_STRUCT_OFFSET (MonoContext, esp), 4);
160         /* restore EBP */
161         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (MonoContext, ebp), 4);
162
163         /* jump to the saved IP */
164         x86_jump_reg (code, X86_EDX);
165
166         return start;
167 }
168
169 /*
170  * mono_arch_get_call_filter:
171  *
172  * Returns a pointer to a method which calls an exception filter. We
173  * also use this function to call finally handlers (we pass NULL as 
174  * @exc object in this case).
175  */
176 gpointer
177 mono_arch_get_call_filter (void)
178 {
179         static guint8* start;
180         static int inited = 0;
181         guint8 *code;
182
183         if (inited)
184                 return start;
185
186         inited = 1;
187         /* call_filter (MonoContext *ctx, unsigned long eip) */
188         start = code = mono_global_codeman_reserve (64);
189
190         x86_push_reg (code, X86_EBP);
191         x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
192         x86_push_reg (code, X86_EBX);
193         x86_push_reg (code, X86_EDI);
194         x86_push_reg (code, X86_ESI);
195
196         /* load ctx */
197         x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
198         /* load eip */
199         x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
200         /* save EBP */
201         x86_push_reg (code, X86_EBP);
202
203         /* set new EBP */
204         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (MonoContext, ebp), 4);
205         /* restore registers used by global register allocation (EBX & ESI) */
206         x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (MonoContext, ebx), 4);
207         x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (MonoContext, esi), 4);
208         x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (MonoContext, edi), 4);
209
210         /* call the handler */
211         x86_call_reg (code, X86_ECX);
212
213         /* restore EBP */
214         x86_pop_reg (code, X86_EBP);
215
216         /* restore saved regs */
217         x86_pop_reg (code, X86_ESI);
218         x86_pop_reg (code, X86_EDI);
219         x86_pop_reg (code, X86_EBX);
220         x86_leave (code);
221         x86_ret (code);
222
223         g_assert ((code - start) < 64);
224         return start;
225 }
226
227 static void
228 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
229                  unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
230                  unsigned long eip,  unsigned long esp, gboolean rethrow)
231 {
232         static void (*restore_context) (MonoContext *);
233         MonoContext ctx;
234
235         if (!restore_context)
236                 restore_context = mono_arch_get_restore_context ();
237
238         /* Pop argument and return address */
239         ctx.esp = esp + (2 * sizeof (gpointer));
240         ctx.eip = eip;
241         ctx.ebp = ebp;
242         ctx.edi = edi;
243         ctx.esi = esi;
244         ctx.ebx = ebx;
245         ctx.edx = edx;
246         ctx.ecx = ecx;
247         ctx.eax = eax;
248
249         if (mono_debugger_throw_exception ((gpointer)(eip - 5), (gpointer)esp, exc)) {
250                 /*
251                  * The debugger wants us to stop on the `throw' instruction.
252                  * By the time we get here, it already inserted a breakpoint on
253                  * eip - 5 (which is the address of the call).
254                  */
255                 ctx.eip = eip - 5;
256                 ctx.esp = esp + sizeof (gpointer);
257                 restore_context (&ctx);
258                 g_assert_not_reached ();
259         }
260
261         /* adjust eip so that it point into the call instruction */
262         ctx.eip -= 1;
263
264         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
265                 MonoException *mono_ex = (MonoException*)exc;
266                 if (!rethrow)
267                         mono_ex->stack_trace = NULL;
268         }
269         mono_handle_exception (&ctx, exc, (gpointer)eip, FALSE);
270         restore_context (&ctx);
271
272         g_assert_not_reached ();
273 }
274
275 static guint8*
276 get_throw_exception (gboolean rethrow)
277 {
278         guint8 *start, *code;
279
280         start = code = mono_global_codeman_reserve (64);
281
282         x86_push_reg (code, X86_ESP);
283         x86_push_membase (code, X86_ESP, 4); /* IP */
284         x86_push_membase (code, X86_ESP, 12); /* exception */
285         x86_push_reg (code, X86_EBP);
286         x86_push_reg (code, X86_EDI);
287         x86_push_reg (code, X86_ESI);
288         x86_push_reg (code, X86_EBX);
289         x86_push_reg (code, X86_EDX);
290         x86_push_reg (code, X86_ECX);
291         x86_push_reg (code, X86_EAX);
292         x86_call_code (code, throw_exception);
293         /* we should never reach this breakpoint */
294         x86_breakpoint (code);
295
296         g_assert ((code - start) < 64);
297
298         return start;
299 }
300
301 /**
302  * mono_arch_get_throw_exception:
303  *
304  * Returns a function pointer which can be used to raise 
305  * exceptions. The returned function has the following 
306  * signature: void (*func) (MonoException *exc); 
307  * For example to raise an arithmetic exception you can use:
308  *
309  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
310  * x86_call_code (code, arch_get_throw_exception ()); 
311  *
312  */
313 gpointer 
314 mono_arch_get_throw_exception (void)
315 {
316         static guint8 *start;
317         static int inited = 0;
318
319         if (inited)
320                 return start;
321
322         start = get_throw_exception (FALSE);
323
324         inited = 1;
325
326         return start;
327 }
328
329 gpointer 
330 mono_arch_get_rethrow_exception (void)
331 {
332         static guint8 *start;
333         static int inited = 0;
334
335         if (inited)
336                 return start;
337
338         start = get_throw_exception (TRUE);
339
340         inited = 1;
341
342         return start;
343 }
344
345 /**
346  * mono_arch_get_throw_exception_by_name:
347  *
348  * Returns a function pointer which can be used to raise 
349  * corlib exceptions. The returned function has the following 
350  * signature: void (*func) (gpointer ip, char *exc_name); 
351  * For example to raise an arithmetic exception you can use:
352  *
353  * x86_push_imm (code, "ArithmeticException"); 
354  * x86_push_imm (code, <IP>)
355  * x86_jump_code (code, arch_get_throw_exception_by_name ()); 
356  *
357  */
358 gpointer 
359 mono_arch_get_throw_exception_by_name (void)
360 {
361         static guint8* start;
362         static int inited = 0;
363         guint8 *code;
364
365         if (inited)
366                 return start;
367
368         inited = 1;
369         code = start = mono_global_codeman_reserve (32);
370
371         x86_push_membase (code, X86_ESP, 4); /* exception name */
372         x86_push_imm (code, "System");
373         x86_push_imm (code, mono_defaults.exception_class->image);
374         x86_call_code (code, mono_exception_from_name);
375         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
376         /* save the newly create object (overwrite exception name)*/
377         x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
378         x86_jump_code (code, mono_arch_get_throw_exception ());
379
380         g_assert ((code - start) < 32);
381
382         return start;
383 }
384
385 /**
386  * mono_arch_get_throw_corlib_exception:
387  *
388  * Returns a function pointer which can be used to raise 
389  * corlib exceptions. The returned function has the following 
390  * signature: void (*func) (guint32 ex_token, guint32 offset); 
391  * Here, offset is the offset which needs to be substracted from the caller IP 
392  * to get the IP of the throw. Passing the offset has the advantage that it 
393  * needs no relocations in the caller.
394  */
395 gpointer 
396 mono_arch_get_throw_corlib_exception (void)
397 {
398         static guint8* start;
399         static int inited = 0;
400         guint8 *code;
401
402         if (inited)
403                 return start;
404
405         inited = 1;
406         code = start = mono_global_codeman_reserve (64);
407
408         x86_push_membase (code, X86_ESP, 4); /* token */
409         x86_push_imm (code, mono_defaults.exception_class->image);
410         x86_call_code (code, mono_exception_from_token);
411         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8);
412         /* Compute caller ip */
413         x86_pop_reg (code, X86_ECX);
414         /* Pop token */
415         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
416         x86_pop_reg (code, X86_EDX);
417         x86_alu_reg_reg (code, X86_SUB, X86_ECX, X86_EDX);
418         /* Push exception object */
419         x86_push_reg (code, X86_EAX);
420         /* Push throw IP */
421         x86_push_reg (code, X86_ECX);
422         x86_jump_code (code, mono_arch_get_throw_exception ());
423
424         g_assert ((code - start) < 64);
425
426         return start;
427 }
428
429 /* mono_arch_find_jit_info:
430  *
431  * This function is used to gather information from @ctx. It return the 
432  * MonoJitInfo of the corresponding function, unwinds one stack frame and
433  * stores the resulting context into @new_ctx. It also stores a string 
434  * describing the stack location into @trace (if not NULL), and modifies
435  * the @lmf if necessary. @native_offset return the IP offset from the 
436  * start of the function or -1 if that info is not available.
437  */
438 MonoJitInfo *
439 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
440                          MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
441                          gboolean *managed)
442 {
443         MonoJitInfo *ji;
444         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
445
446         /* Avoid costly table lookup during stack overflow */
447         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
448                 ji = prev_ji;
449         else
450                 ji = mono_jit_info_table_find (domain, ip);
451
452         if (managed)
453                 *managed = FALSE;
454
455         if (ji != NULL) {
456                 int offset;
457
458                 *new_ctx = *ctx;
459
460                 if (managed)
461                         if (!ji->method->wrapper_type)
462                                 *managed = TRUE;
463
464                 /*
465                  * Some managed methods like pinvoke wrappers might have save_lmf set.
466                  * In this case, register save/restore code is not generated by the 
467                  * JIT, so we have to restore callee saved registers from the lmf.
468                  */
469                 if (ji->method->save_lmf) {
470                         /* 
471                          * We only need to do this if the exception was raised in managed
472                          * code, since otherwise the lmf was already popped of the stack.
473                          */
474                         if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
475                                 new_ctx->esi = (*lmf)->esi;
476                                 new_ctx->edi = (*lmf)->edi;
477                                 new_ctx->ebx = (*lmf)->ebx;
478                         }
479                 }
480                 else {
481                         offset = -1;
482                         /* restore caller saved registers */
483                         if (ji->used_regs & X86_EBX_MASK) {
484                                 new_ctx->ebx = *((int *)ctx->ebp + offset);
485                                 offset--;
486                         }
487                         if (ji->used_regs & X86_EDI_MASK) {
488                                 new_ctx->edi = *((int *)ctx->ebp + offset);
489                                 offset--;
490                         }
491                         if (ji->used_regs & X86_ESI_MASK) {
492                                 new_ctx->esi = *((int *)ctx->ebp + offset);
493                         }
494                 }
495
496                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
497                         /* remove any unused lmf */
498                         *lmf = (*lmf)->previous_lmf;
499                 }
500
501                 /* Pop EBP and the return address */
502                 new_ctx->esp = ctx->SC_EBP + (2 * sizeof (gpointer));
503                 /* we substract 1, so that the IP points into the call instruction */
504                 new_ctx->eip = *((int *)ctx->ebp + 1) - 1;
505                 new_ctx->ebp = *((int *)ctx->ebp);
506
507                 /* Pop arguments off the stack */
508                 {
509                         MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
510
511                         guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (ji->method), mono_method_signature (ji->method)->param_count, arg_info);
512                         new_ctx->esp += stack_to_pop;
513                 }
514
515                 return ji;
516         } else if (*lmf) {
517                 
518                 *new_ctx = *ctx;
519
520                 if (!(*lmf)->method)
521                         return (gpointer)-1;
522
523                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
524                 } else {
525                         memset (res, 0, sizeof (MonoJitInfo));
526                         res->method = (*lmf)->method;
527                 }
528
529                 new_ctx->esi = (*lmf)->esi;
530                 new_ctx->edi = (*lmf)->edi;
531                 new_ctx->ebx = (*lmf)->ebx;
532                 new_ctx->ebp = (*lmf)->ebp;
533                 new_ctx->eip = (*lmf)->eip;
534                 /* the lmf is always stored on the stack, so the following
535                  * expression points to a stack location which can be used as ESP */
536                 new_ctx->esp = (unsigned long)&((*lmf)->eip);
537
538                 *lmf = (*lmf)->previous_lmf;
539
540                 return ji ? ji : res;
541         }
542
543         return NULL;
544 }
545
546 void
547 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
548 {
549 #ifdef MONO_ARCH_USE_SIGACTION
550         ucontext_t *ctx = (ucontext_t*)sigctx;
551         
552         mctx->eax = ctx->uc_mcontext.gregs [REG_EAX];
553         mctx->ebx = ctx->uc_mcontext.gregs [REG_EBX];
554         mctx->ecx = ctx->uc_mcontext.gregs [REG_ECX];
555         mctx->edx = ctx->uc_mcontext.gregs [REG_EDX];
556         mctx->ebp = ctx->uc_mcontext.gregs [REG_EBP];
557         mctx->esp = ctx->uc_mcontext.gregs [REG_ESP];
558         mctx->esi = ctx->uc_mcontext.gregs [REG_ESI];
559         mctx->edi = ctx->uc_mcontext.gregs [REG_EDI];
560         mctx->eip = ctx->uc_mcontext.gregs [REG_EIP];
561 #else   
562         struct sigcontext *ctx = (struct sigcontext *)sigctx;
563
564         mctx->eax = ctx->SC_EAX;
565         mctx->ebx = ctx->SC_EBX;
566         mctx->ecx = ctx->SC_ECX;
567         mctx->edx = ctx->SC_EDX;
568         mctx->ebp = ctx->SC_EBP;
569         mctx->esp = ctx->SC_ESP;
570         mctx->esi = ctx->SC_ESI;
571         mctx->edi = ctx->SC_EDI;
572         mctx->eip = ctx->SC_EIP;
573 #endif
574 }
575
576 void
577 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
578 {
579 #ifdef MONO_ARCH_USE_SIGACTION
580         ucontext_t *ctx = (ucontext_t*)sigctx;
581
582         ctx->uc_mcontext.gregs [REG_EAX] = mctx->eax;
583         ctx->uc_mcontext.gregs [REG_EBX] = mctx->ebx;
584         ctx->uc_mcontext.gregs [REG_ECX] = mctx->ecx;
585         ctx->uc_mcontext.gregs [REG_EDX] = mctx->edx;
586         ctx->uc_mcontext.gregs [REG_EBP] = mctx->ebp;
587         ctx->uc_mcontext.gregs [REG_ESP] = mctx->esp;
588         ctx->uc_mcontext.gregs [REG_ESI] = mctx->esi;
589         ctx->uc_mcontext.gregs [REG_EDI] = mctx->edi;
590         ctx->uc_mcontext.gregs [REG_EIP] = mctx->eip;
591 #else
592         struct sigcontext *ctx = (struct sigcontext *)sigctx;
593
594         ctx->SC_EAX = mctx->eax;
595         ctx->SC_EBX = mctx->ebx;
596         ctx->SC_ECX = mctx->ecx;
597         ctx->SC_EDX = mctx->edx;
598         ctx->SC_EBP = mctx->ebp;
599         ctx->SC_ESP = mctx->esp;
600         ctx->SC_ESI = mctx->esi;
601         ctx->SC_EDI = mctx->edi;
602         ctx->SC_EIP = mctx->eip;
603 #endif
604 }       
605
606 gpointer
607 mono_arch_ip_from_context (void *sigctx)
608 {
609 #ifdef MONO_ARCH_USE_SIGACTION
610         ucontext_t *ctx = (ucontext_t*)sigctx;
611         return (gpointer)ctx->uc_mcontext.gregs [REG_EIP];
612 #else
613         struct sigcontext *ctx = sigctx;
614         return (gpointer)ctx->SC_EIP;
615 #endif  
616 }
617
618 gboolean
619 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
620 {
621         MonoContext mctx;
622
623         mono_arch_sigctx_to_monoctx (sigctx, &mctx);
624
625         mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only);
626
627         mono_arch_monoctx_to_sigctx (&mctx, sigctx);
628
629         return TRUE;
630 }