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