2 * exceptions-x86.c: exception support for x86
5 * Dietmar Maurer (dietmar@ximian.com)
7 * (C) 2001 Ximian, Inc.
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>
28 static MonoW32ExceptionHandler fpe_handler;
29 static MonoW32ExceptionHandler ill_handler;
30 static MonoW32ExceptionHandler segv_handler;
32 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
34 #define W32_SEH_HANDLE_EX(_ex) \
35 if (_ex##_handler) _ex##_handler((int)sctx)
38 * Unhandled Exception Filter
39 * Top-level per-process exception handler.
41 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
45 struct sigcontext* sctx;
48 res = EXCEPTION_CONTINUE_EXECUTION;
50 er = ep->ExceptionRecord;
51 ctx = ep->ContextRecord;
52 sctx = g_malloc(sizeof(struct sigcontext));
54 /* Copy Win32 context to UNIX style context */
65 switch (er->ExceptionCode) {
66 case EXCEPTION_ACCESS_VIOLATION:
67 W32_SEH_HANDLE_EX(segv);
69 case EXCEPTION_ILLEGAL_INSTRUCTION:
70 W32_SEH_HANDLE_EX(ill);
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);
84 /* Copy context back */
100 old_handler = SetUnhandledExceptionFilter(seh_handler);
103 void win32_seh_cleanup()
105 if (old_handler) SetUnhandledExceptionFilter(old_handler);
108 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
112 fpe_handler = handler;
115 ill_handler = handler;
118 segv_handler = handler;
125 #endif /* PLATFORM_WIN32 */
128 * mono_arch_get_restore_context:
130 * Returns a pointer to a method which restores a previously saved sigcontext.
133 mono_arch_get_restore_context (void)
135 static guint8 *start = NULL;
141 /* restore_contect (struct sigcontext *ctx) */
142 /* we do not restore X86_EAX, X86_EDX */
144 start = code = g_malloc (1024);
147 x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
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);
152 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
154 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
156 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
158 x86_mov_reg_membase (code, X86_ESP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
160 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
162 /* jump to the saved IP */
163 x86_jump_reg (code, X86_EDX);
169 * mono_arch_get_call_filter:
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).
176 mono_arch_get_call_filter (void)
178 static guint8 start [64];
179 static int inited = 0;
186 /* call_filter (struct sigcontext *ctx, unsigned long eip) */
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);
196 x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
198 x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
200 x86_push_reg (code, X86_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);
209 /* call the handler */
210 x86_call_reg (code, X86_ECX);
213 x86_pop_reg (code, X86_EBP);
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);
222 g_assert ((code - start) < 64);
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)
231 static void (*restore_context) (struct sigcontext *);
232 struct sigcontext ctx;
234 if (!restore_context)
235 restore_context = mono_arch_get_restore_context ();
237 /* adjust eip so that it point into the call instruction */
240 /* Pop argument and return address */
241 ctx.SC_ESP = esp + (2 * sizeof (gpointer));
251 if (mono_object_isinst (exc, mono_defaults.exception_class)) {
252 MonoException *mono_ex = (MonoException*)exc;
253 mono_ex->stack_trace = NULL;
255 mono_handle_exception (&ctx, exc, eip + 1, FALSE);
256 restore_context (&ctx);
258 g_assert_not_reached ();
262 * mono_arch_get_throw_exception:
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:
269 * x86_push_imm (code, mono_get_exception_arithmetic ());
270 * x86_call_code (code, arch_get_throw_exception ());
274 mono_arch_get_throw_exception (void)
276 static guint8 start [24];
277 static int inited = 0;
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);
300 g_assert ((code - start) < 24);
305 * mono_arch_get_throw_exception_by_name:
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:
312 * x86_push_imm (code, "ArithmeticException");
313 * x86_call_code (code, arch_get_throw_exception_by_name ());
317 mono_arch_get_throw_exception_by_name (void)
319 static guint8 start [32];
320 static int inited = 0;
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 ());
338 g_assert ((code - start) < 32);
343 /* mono_arch_find_jit_info:
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.
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,
358 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
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)))
364 ji = mono_jit_info_table_find (domain, ip);
376 char *source_location, *tmpaddr, *fname;
377 gint32 address, iloffset;
382 address = (char *)ip - (char *)ji->code_start;
385 *native_offset = address;
388 if (!ji->method->wrapper_type)
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);
396 tmpaddr = g_strdup_printf ("<0x%05x>", address);
398 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
400 fname = mono_method_full_name (ji->method, TRUE);
403 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
405 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
408 g_free (source_location);
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.
417 if (ji->method->save_lmf) {
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.
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;
430 /* restore caller saved registers */
431 if (ji->used_regs & X86_EBX_MASK) {
432 new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
435 if (ji->used_regs & X86_EDI_MASK) {
436 new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
439 if (ji->used_regs & X86_ESI_MASK) {
440 new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
444 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
445 /* remove any unused lmf */
446 *lmf = (*lmf)->previous_lmf;
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);
457 #ifdef MONO_USE_EXC_TABLES
458 } else if ((ji = x86_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) {
469 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
472 memset (res, 0, sizeof (MonoJitInfo));
473 res->method = (*lmf)->method;
477 *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name (res->method, TRUE));
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);
488 *lmf = (*lmf)->previous_lmf;
498 * mono_arch_handle_exception:
500 * @ctx: saved processor state
501 * @obj: the exception object
504 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
506 return mono_handle_exception (sigctx, obj, mono_arch_ip_from_context (sigctx), test_only);
510 mono_arch_ip_from_context (void *sigctx)
512 struct sigcontext *ctx = sigctx;
513 return (gpointer)ctx->SC_EIP;