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>
23 #include <mono/metadata/mono-debug-debugger.h>
29 static MonoW32ExceptionHandler fpe_handler;
30 static MonoW32ExceptionHandler ill_handler;
31 static MonoW32ExceptionHandler segv_handler;
33 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
35 #define W32_SEH_HANDLE_EX(_ex) \
36 if (_ex##_handler) _ex##_handler((int)sctx)
39 * Unhandled Exception Filter
40 * Top-level per-process exception handler.
42 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
46 struct sigcontext* sctx;
49 res = EXCEPTION_CONTINUE_EXECUTION;
51 er = ep->ExceptionRecord;
52 ctx = ep->ContextRecord;
53 sctx = g_malloc(sizeof(struct sigcontext));
55 /* Copy Win32 context to UNIX style context */
66 switch (er->ExceptionCode) {
67 case EXCEPTION_ACCESS_VIOLATION:
68 W32_SEH_HANDLE_EX(segv);
70 case EXCEPTION_ILLEGAL_INSTRUCTION:
71 W32_SEH_HANDLE_EX(ill);
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);
85 /* Copy context back */
101 old_handler = SetUnhandledExceptionFilter(seh_handler);
104 void win32_seh_cleanup()
106 if (old_handler) SetUnhandledExceptionFilter(old_handler);
109 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
113 fpe_handler = handler;
116 ill_handler = handler;
119 segv_handler = handler;
126 #endif /* PLATFORM_WIN32 */
129 * mono_arch_get_restore_context:
131 * Returns a pointer to a method which restores a previously saved sigcontext.
134 mono_arch_get_restore_context (void)
136 static guint8 *start = NULL;
142 /* restore_contect (MonoContext *ctx) */
143 /* we do not restore X86_EAX, X86_EDX */
145 start = code = mono_global_codeman_reserve (128);
148 x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
150 /* get return address, stored in EDX */
151 x86_mov_reg_membase (code, X86_EDX, X86_EAX, G_STRUCT_OFFSET (MonoContext, eip), 4);
153 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebx), 4);
155 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (MonoContext, edi), 4);
157 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (MonoContext, esi), 4);
159 x86_mov_reg_membase (code, X86_ESP, X86_EAX, G_STRUCT_OFFSET (MonoContext, esp), 4);
161 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebp), 4);
163 /* jump to the saved IP */
164 x86_jump_reg (code, X86_EDX);
170 * mono_arch_get_call_filter:
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).
177 mono_arch_get_call_filter (void)
179 static guint8* start;
180 static int inited = 0;
187 /* call_filter (MonoContext *ctx, unsigned long eip) */
188 start = code = mono_global_codeman_reserve (64);
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);
197 x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
199 x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
201 x86_push_reg (code, X86_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);
210 /* call the handler */
211 x86_call_reg (code, X86_ECX);
214 x86_pop_reg (code, X86_EBP);
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);
223 g_assert ((code - start) < 64);
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)
232 static void (*restore_context) (MonoContext *);
235 if (!restore_context)
236 restore_context = mono_arch_get_restore_context ();
238 /* Pop argument and return address */
239 ctx.esp = esp + (2 * sizeof (gpointer));
249 if (mono_debugger_throw_exception ((gpointer)(eip - 5), (gpointer)esp, exc)) {
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).
256 ctx.esp = esp + sizeof (gpointer);
257 restore_context (&ctx);
258 g_assert_not_reached ();
261 /* adjust eip so that it point into the call instruction */
264 if (mono_object_isinst (exc, mono_defaults.exception_class)) {
265 MonoException *mono_ex = (MonoException*)exc;
267 mono_ex->stack_trace = NULL;
269 mono_handle_exception (&ctx, exc, (gpointer)eip, FALSE);
270 restore_context (&ctx);
272 g_assert_not_reached ();
276 get_throw_exception (gboolean rethrow)
278 guint8 *start, *code;
280 start = code = mono_global_codeman_reserve (64);
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) < 64);
302 * mono_arch_get_throw_exception:
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:
309 * x86_push_imm (code, mono_get_exception_arithmetic ());
310 * x86_call_code (code, arch_get_throw_exception ());
314 mono_arch_get_throw_exception (void)
316 static guint8 *start;
317 static int inited = 0;
322 start = get_throw_exception (FALSE);
330 mono_arch_get_rethrow_exception (void)
332 static guint8 *start;
333 static int inited = 0;
338 start = get_throw_exception (TRUE);
346 * mono_arch_get_throw_exception_by_name:
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:
353 * x86_push_imm (code, "ArithmeticException");
354 * x86_push_imm (code, <IP>)
355 * x86_jump_code (code, arch_get_throw_exception_by_name ());
359 mono_arch_get_throw_exception_by_name (void)
361 static guint8* start;
362 static int inited = 0;
369 code = start = mono_global_codeman_reserve (32);
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 ());
380 g_assert ((code - start) < 32);
386 * mono_arch_get_throw_corlib_exception:
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.
396 mono_arch_get_throw_corlib_exception (void)
398 static guint8* start;
399 static int inited = 0;
406 code = start = mono_global_codeman_reserve (64);
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);
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);
421 x86_push_reg (code, X86_ECX);
422 x86_jump_code (code, mono_arch_get_throw_exception ());
424 g_assert ((code - start) < 64);
429 /* mono_arch_find_jit_info:
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.
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,
444 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
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)))
450 ji = mono_jit_info_table_find (domain, ip);
461 if (!ji->method->wrapper_type)
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.
469 if (ji->method->save_lmf) {
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.
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;
482 /* restore caller saved registers */
483 if (ji->used_regs & X86_EBX_MASK) {
484 new_ctx->ebx = *((int *)ctx->ebp + offset);
487 if (ji->used_regs & X86_EDI_MASK) {
488 new_ctx->edi = *((int *)ctx->ebp + offset);
491 if (ji->used_regs & X86_ESI_MASK) {
492 new_ctx->esi = *((int *)ctx->ebp + offset);
496 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
497 /* remove any unused lmf */
498 *lmf = (*lmf)->previous_lmf;
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);
507 /* Pop arguments off the stack */
509 MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
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;
523 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
525 memset (res, 0, sizeof (MonoJitInfo));
526 res->method = (*lmf)->method;
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);
538 *lmf = (*lmf)->previous_lmf;
540 return ji ? ji : res;
547 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
549 #ifdef MONO_ARCH_USE_SIGACTION
550 ucontext_t *ctx = (ucontext_t*)sigctx;
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];
562 memcpy (mctx, sigctx, sizeof (MonoContext));
567 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
569 #ifdef MONO_ARCH_USE_SIGACTION
570 ucontext_t *ctx = (ucontext_t*)sigctx;
572 ctx->uc_mcontext.gregs [REG_EAX] = mctx->eax;
573 ctx->uc_mcontext.gregs [REG_EBX] = mctx->ebx;
574 ctx->uc_mcontext.gregs [REG_ECX] = mctx->ecx;
575 ctx->uc_mcontext.gregs [REG_EDX] = mctx->edx;
576 ctx->uc_mcontext.gregs [REG_EBP] = mctx->ebp;
577 ctx->uc_mcontext.gregs [REG_ESP] = mctx->esp;
578 ctx->uc_mcontext.gregs [REG_ESI] = mctx->esi;
579 ctx->uc_mcontext.gregs [REG_EDI] = mctx->edi;
580 ctx->uc_mcontext.gregs [REG_EIP] = mctx->eip;
582 memcpy (sigctx, mctx, sizeof (MonoContext));
587 mono_arch_ip_from_context (void *sigctx)
589 #ifdef MONO_ARCH_USE_SIGACTION
590 ucontext_t *ctx = (ucontext_t*)sigctx;
591 return (gpointer)ctx->uc_mcontext.gregs [REG_EIP];
593 struct sigcontext *ctx = sigctx;
594 return (gpointer)ctx->SC_EIP;
599 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
603 mono_arch_sigctx_to_monoctx (sigctx, &mctx);
605 mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only);
607 mono_arch_monoctx_to_sigctx (&mctx, sigctx);