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 mono_handle_exception (&ctx, exc, eip + 1, FALSE);
252 restore_context (&ctx);
254 g_assert_not_reached ();
258 * mono_arch_get_throw_exception:
260 * Returns a function pointer which can be used to raise
261 * exceptions. The returned function has the following
262 * signature: void (*func) (MonoException *exc);
263 * For example to raise an arithmetic exception you can use:
265 * x86_push_imm (code, mono_get_exception_arithmetic ());
266 * x86_call_code (code, arch_get_throw_exception ());
270 mono_arch_get_throw_exception (void)
272 static guint8 start [24];
273 static int inited = 0;
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);
296 g_assert ((code - start) < 24);
301 * mono_arch_get_throw_exception_by_name:
303 * Returns a function pointer which can be used to raise
304 * corlib exceptions. The returned function has the following
305 * signature: void (*func) (char *exc_name);
306 * For example to raise an arithmetic exception you can use:
308 * x86_push_imm (code, "ArithmeticException");
309 * x86_call_code (code, arch_get_throw_exception_by_name ());
313 mono_arch_get_throw_exception_by_name (void)
315 static guint8 start [32];
316 static int inited = 0;
325 x86_push_membase (code, X86_ESP, 4); /* exception name */
326 x86_push_imm (code, "System");
327 x86_push_imm (code, mono_defaults.exception_class->image);
328 x86_call_code (code, mono_exception_from_name);
329 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
330 /* save the newly create object (overwrite exception name)*/
331 x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
332 x86_jump_code (code, mono_arch_get_throw_exception ());
334 g_assert ((code - start) < 32);
339 /* mono_arch_find_jit_info:
341 * This function is used to gather information from @ctx. It return the
342 * MonoJitInfo of the corresponding function, unwinds one stack frame and
343 * stores the resulting context into @new_ctx. It also stores a string
344 * describing the stack location into @trace (if not NULL), and modifies
345 * the @lmf if necessary. @native_offset return the IP offset from the
346 * start of the function or -1 if that info is not available.
349 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
350 MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
354 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
356 /* Avoid costly table lookup during stack overflow */
357 if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
360 ji = mono_jit_info_table_find (domain, ip);
372 char *source_location, *tmpaddr, *fname;
373 gint32 address, iloffset;
378 address = (char *)ip - (char *)ji->code_start;
381 *native_offset = address;
384 if (!ji->method->wrapper_type)
388 source_location = mono_debug_source_location_from_address (ji->method, address, NULL, domain);
389 iloffset = mono_debug_il_offset_from_address (ji->method, address, domain);
392 tmpaddr = g_strdup_printf ("<0x%05x>", address);
394 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
396 fname = mono_method_full_name (ji->method, TRUE);
399 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
401 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
404 g_free (source_location);
409 * Some managed methods like pinvoke wrappers might have save_lmf set.
410 * In this case, register save/restore code is not generated by the
411 * JIT, so we have to restore callee saved registers from the lmf.
413 if (ji->method->save_lmf) {
415 * We only need to do this if the exception was raised in managed
416 * code, since otherwise the lmf was already popped of the stack.
418 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
419 new_ctx->SC_ESI = (*lmf)->esi;
420 new_ctx->SC_EDI = (*lmf)->edi;
421 new_ctx->SC_EBX = (*lmf)->ebx;
426 /* restore caller saved registers */
427 if (ji->used_regs & X86_EBX_MASK) {
428 new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
431 if (ji->used_regs & X86_EDI_MASK) {
432 new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
435 if (ji->used_regs & X86_ESI_MASK) {
436 new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
440 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
441 /* remove any unused lmf */
442 *lmf = (*lmf)->previous_lmf;
445 /* Pop EBP and the return address */
446 new_ctx->SC_ESP = ctx->SC_EBP + (2 * sizeof (gpointer));
447 /* we substract 1, so that the IP points into the call instruction */
448 new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1;
449 new_ctx->SC_EBP = *((int *)ctx->SC_EBP);
453 #ifdef MONO_USE_EXC_TABLES
454 } else if ((ji = x86_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) {
465 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
468 memset (res, 0, sizeof (MonoJitInfo));
469 res->method = (*lmf)->method;
473 *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name (res->method, TRUE));
475 new_ctx->SC_ESI = (*lmf)->esi;
476 new_ctx->SC_EDI = (*lmf)->edi;
477 new_ctx->SC_EBX = (*lmf)->ebx;
478 new_ctx->SC_EBP = (*lmf)->ebp;
479 new_ctx->SC_EIP = (*lmf)->eip;
480 /* the lmf is always stored on the stack, so the following
481 * expression points to a stack location which can be used as ESP */
482 new_ctx->SC_ESP = (unsigned long)&((*lmf)->eip);
484 *lmf = (*lmf)->previous_lmf;
494 * mono_arch_handle_exception:
496 * @ctx: saved processor state
497 * @obj: the exception object
500 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
502 return mono_handle_exception (sigctx, obj, mono_arch_ip_from_context (sigctx), test_only);
506 mono_arch_ip_from_context (void *sigctx)
508 struct sigcontext *ctx = sigctx;
509 return (gpointer)ctx->SC_EIP;