Make sure x86 ATOMIC_CAS doesn't overwrite its own operands.
[mono-debugger.git] / mono / mini / exceptions-x86.c
blobf0ec4e000ef3fd6fb11b1ccba6e3f0390992f190
1 /*
2 * exceptions-x86.c: exception support for x86
4 * Authors:
5 * Dietmar Maurer (dietmar@ximian.com)
7 * (C) 2001 Ximian, Inc.
8 */
10 #include <config.h>
11 #include <glib.h>
12 #include <signal.h>
13 #include <string.h>
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/utils/mono-mmap.h>
25 #include "mini.h"
26 #include "mini-x86.h"
27 #include "debug-mini.h"
29 #ifdef PLATFORM_WIN32
30 static void (*restore_stack) (void *);
32 static MonoW32ExceptionHandler fpe_handler;
33 static MonoW32ExceptionHandler ill_handler;
34 static MonoW32ExceptionHandler segv_handler;
36 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
38 #define W32_SEH_HANDLE_EX(_ex) \
39 if (_ex##_handler) _ex##_handler((int)sctx)
42 * mono_win32_get_handle_stackoverflow (void):
44 * Returns a pointer to a method which restores the current context stack
45 * and calls handle_exceptions, when done restores the original stack.
47 static gpointer
48 mono_win32_get_handle_stackoverflow (void)
50 static guint8 *start = NULL;
51 guint8 *code;
53 if (start)
54 return start;
56 /* restore_contect (void *sigctx) */
57 start = code = mono_global_codeman_reserve (128);
59 /* load context into ebx */
60 x86_mov_reg_membase (code, X86_EBX, X86_ESP, 4, 4);
62 /* move current stack into edi for later restore */
63 x86_mov_reg_reg (code, X86_EDI, X86_ESP, 4);
65 /* use the new freed stack from sigcontext */
66 x86_mov_reg_membase (code, X86_ESP, X86_EBX, G_STRUCT_OFFSET (struct sigcontext, esp), 4);
68 /* get the current domain */
69 x86_call_code (code, mono_domain_get);
71 /* get stack overflow exception from domain object */
72 x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoDomain, stack_overflow_ex), 4);
74 /* call mono_arch_handle_exception (sctx, stack_overflow_exception_obj, FALSE) */
75 x86_push_imm (code, 0);
76 x86_push_reg (code, X86_EAX);
77 x86_push_reg (code, X86_EBX);
78 x86_call_code (code, mono_arch_handle_exception);
80 /* restore the SEH handler stack */
81 x86_mov_reg_reg (code, X86_ESP, X86_EDI, 4);
83 /* return */
84 x86_ret (code);
86 return start;
89 /* Special hack to workaround the fact that the
90 * when the SEH handler is called the stack is
91 * to small to recover.
93 * Stack walking part of this method is from mono_handle_exception
95 * The idea is simple;
96 * - walk the stack to free some space (64k)
97 * - set esp to new stack location
98 * - call mono_arch_handle_exception with stack overflow exception
99 * - set esp to SEH handlers stack
100 * - done
102 static void
103 win32_handle_stack_overflow (EXCEPTION_POINTERS* ep, struct sigcontext *sctx)
105 SYSTEM_INFO si;
106 DWORD page_size;
107 MonoDomain *domain = mono_domain_get ();
108 MonoJitInfo *ji, rji;
109 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
110 MonoLMF *lmf = jit_tls->lmf;
111 MonoContext initial_ctx;
112 MonoContext ctx;
113 guint32 free_stack = 0;
115 /* convert sigcontext to MonoContext (due to reuse of stack walking helpers */
116 mono_arch_sigctx_to_monoctx (sctx, &ctx);
118 /* get our os page size */
119 GetSystemInfo(&si);
120 page_size = si.dwPageSize;
122 /* Let's walk the stack to recover
123 * the needed stack space (if possible)
125 memset (&rji, 0, sizeof (rji));
127 initial_ctx = ctx;
128 free_stack = (guint8*)(MONO_CONTEXT_GET_BP (&ctx)) - (guint8*)(MONO_CONTEXT_GET_BP (&initial_ctx));
130 /* try to free 64kb from our stack */
131 do {
132 MonoContext new_ctx;
134 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &rji, &ctx, &new_ctx, &lmf, NULL);
135 if (!ji) {
136 g_warning ("Exception inside function without unwind info");
137 g_assert_not_reached ();
140 if (ji != (gpointer)-1) {
141 free_stack = (guint8*)(MONO_CONTEXT_GET_BP (&ctx)) - (guint8*)(MONO_CONTEXT_GET_BP (&initial_ctx));
144 /* todo: we should call abort if ji is -1 */
145 ctx = new_ctx;
146 } while (free_stack < 64 * 1024 && ji != (gpointer) -1);
148 /* convert into sigcontext to be used in mono_arch_handle_exception */
149 mono_arch_monoctx_to_sigctx (&ctx, sctx);
151 /* todo: install new stack-guard page */
153 /* use the new stack and call mono_arch_handle_exception () */
154 restore_stack (sctx);
158 * Unhandled Exception Filter
159 * Top-level per-process exception handler.
161 LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep)
163 EXCEPTION_RECORD* er;
164 CONTEXT* ctx;
165 struct sigcontext* sctx;
166 LONG res;
168 res = EXCEPTION_CONTINUE_EXECUTION;
170 er = ep->ExceptionRecord;
171 ctx = ep->ContextRecord;
172 sctx = g_malloc(sizeof(struct sigcontext));
174 /* Copy Win32 context to UNIX style context */
175 sctx->eax = ctx->Eax;
176 sctx->ebx = ctx->Ebx;
177 sctx->ecx = ctx->Ecx;
178 sctx->edx = ctx->Edx;
179 sctx->ebp = ctx->Ebp;
180 sctx->esp = ctx->Esp;
181 sctx->esi = ctx->Esi;
182 sctx->edi = ctx->Edi;
183 sctx->eip = ctx->Eip;
185 switch (er->ExceptionCode) {
186 case EXCEPTION_STACK_OVERFLOW:
187 win32_handle_stack_overflow (ep, sctx);
188 break;
189 case EXCEPTION_ACCESS_VIOLATION:
190 W32_SEH_HANDLE_EX(segv);
191 break;
192 case EXCEPTION_ILLEGAL_INSTRUCTION:
193 W32_SEH_HANDLE_EX(ill);
194 break;
195 case EXCEPTION_INT_DIVIDE_BY_ZERO:
196 case EXCEPTION_INT_OVERFLOW:
197 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
198 case EXCEPTION_FLT_OVERFLOW:
199 case EXCEPTION_FLT_UNDERFLOW:
200 case EXCEPTION_FLT_INEXACT_RESULT:
201 W32_SEH_HANDLE_EX(fpe);
202 break;
203 default:
204 break;
207 /* Copy context back */
208 ctx->Eax = sctx->eax;
209 ctx->Ebx = sctx->ebx;
210 ctx->Ecx = sctx->ecx;
211 ctx->Edx = sctx->edx;
212 ctx->Ebp = sctx->ebp;
213 ctx->Esp = sctx->esp;
214 ctx->Esi = sctx->esi;
215 ctx->Edi = sctx->edi;
216 ctx->Eip = sctx->eip;
218 g_free (sctx);
220 return res;
223 void win32_seh_init()
225 /* install restore stack helper */
226 if (!restore_stack)
227 restore_stack = mono_win32_get_handle_stackoverflow ();
229 old_handler = SetUnhandledExceptionFilter(seh_handler);
232 void win32_seh_cleanup()
234 if (old_handler) SetUnhandledExceptionFilter(old_handler);
237 void win32_seh_set_handler(int type, MonoW32ExceptionHandler handler)
239 switch (type) {
240 case SIGFPE:
241 fpe_handler = handler;
242 break;
243 case SIGILL:
244 ill_handler = handler;
245 break;
246 case SIGSEGV:
247 segv_handler = handler;
248 break;
249 default:
250 break;
254 #endif /* PLATFORM_WIN32 */
257 * mono_arch_get_restore_context:
259 * Returns a pointer to a method which restores a previously saved sigcontext.
261 gpointer
262 mono_arch_get_restore_context (void)
264 static guint8 *start = NULL;
265 guint8 *code;
267 if (start)
268 return start;
270 /* restore_contect (MonoContext *ctx) */
271 /* we do not restore X86_EAX, X86_EDX */
273 start = code = mono_global_codeman_reserve (128);
275 /* load ctx */
276 x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
278 /* get return address, stored in EDX */
279 x86_mov_reg_membase (code, X86_EDX, X86_EAX, G_STRUCT_OFFSET (MonoContext, eip), 4);
280 /* restore EBX */
281 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebx), 4);
282 /* restore EDI */
283 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (MonoContext, edi), 4);
284 /* restore ESI */
285 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (MonoContext, esi), 4);
286 /* restore ESP */
287 x86_mov_reg_membase (code, X86_ESP, X86_EAX, G_STRUCT_OFFSET (MonoContext, esp), 4);
288 /* restore EBP */
289 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebp), 4);
291 /* jump to the saved IP */
292 x86_jump_reg (code, X86_EDX);
294 return start;
298 * mono_arch_get_call_filter:
300 * Returns a pointer to a method which calls an exception filter. We
301 * also use this function to call finally handlers (we pass NULL as
302 * @exc object in this case).
304 gpointer
305 mono_arch_get_call_filter (void)
307 static guint8* start;
308 static int inited = 0;
309 guint8 *code;
311 if (inited)
312 return start;
314 inited = 1;
315 /* call_filter (MonoContext *ctx, unsigned long eip) */
316 start = code = mono_global_codeman_reserve (64);
318 x86_push_reg (code, X86_EBP);
319 x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
320 x86_push_reg (code, X86_EBX);
321 x86_push_reg (code, X86_EDI);
322 x86_push_reg (code, X86_ESI);
324 /* load ctx */
325 x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
326 /* load eip */
327 x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
328 /* save EBP */
329 x86_push_reg (code, X86_EBP);
331 /* set new EBP */
332 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebp), 4);
333 /* restore registers used by global register allocation (EBX & ESI) */
334 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (MonoContext, ebx), 4);
335 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (MonoContext, esi), 4);
336 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (MonoContext, edi), 4);
338 /* align stack and save ESP */
339 x86_mov_reg_reg (code, X86_EDX, X86_ESP, 4);
340 x86_alu_reg_imm (code, X86_AND, X86_ESP, -MONO_ARCH_FRAME_ALIGNMENT);
341 g_assert (MONO_ARCH_FRAME_ALIGNMENT >= 8);
342 x86_alu_reg_imm (code, X86_SUB, X86_ESP, MONO_ARCH_FRAME_ALIGNMENT - 8);
343 x86_push_reg (code, X86_EDX);
345 /* call the handler */
346 x86_call_reg (code, X86_ECX);
348 /* restore ESP */
349 x86_pop_reg (code, X86_ESP);
351 /* restore EBP */
352 x86_pop_reg (code, X86_EBP);
354 /* restore saved regs */
355 x86_pop_reg (code, X86_ESI);
356 x86_pop_reg (code, X86_EDI);
357 x86_pop_reg (code, X86_EBX);
358 x86_leave (code);
359 x86_ret (code);
361 g_assert ((code - start) < 64);
362 return start;
365 static void
366 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
367 unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
368 unsigned long eip, unsigned long esp, gboolean rethrow)
370 static void (*restore_context) (MonoContext *);
371 MonoContext ctx;
373 if (!restore_context)
374 restore_context = mono_arch_get_restore_context ();
376 /* Pop argument and return address */
377 ctx.esp = esp + (2 * sizeof (gpointer));
378 ctx.eip = eip;
379 ctx.ebp = ebp;
380 ctx.edi = edi;
381 ctx.esi = esi;
382 ctx.ebx = ebx;
383 ctx.edx = edx;
384 ctx.ecx = ecx;
385 ctx.eax = eax;
387 if (mono_object_isinst (exc, mono_defaults.exception_class)) {
388 MonoException *mono_ex = (MonoException*)exc;
389 if (!rethrow)
390 mono_ex->stack_trace = NULL;
393 if (mono_debug_using_mono_debugger ()) {
394 guint8 buf [16], *code;
396 mono_breakpoint_clean_code (NULL, (gpointer)eip, 8, buf, sizeof (buf));
397 code = buf + 8;
399 if (buf [3] == 0xe8) {
400 MonoContext ctx_cp = ctx;
401 ctx_cp.eip = eip - 5;
403 if (mono_debugger_handle_exception (&ctx_cp, exc)) {
404 restore_context (&ctx_cp);
405 g_assert_not_reached ();
410 /* adjust eip so that it point into the call instruction */
411 ctx.eip -= 1;
413 mono_handle_exception (&ctx, exc, (gpointer)eip, FALSE);
414 restore_context (&ctx);
416 g_assert_not_reached ();
419 static guint8*
420 get_throw_exception (gboolean rethrow)
422 guint8 *start, *code;
424 start = code = mono_global_codeman_reserve (64);
426 x86_push_reg (code, X86_ESP);
427 x86_push_membase (code, X86_ESP, 4); /* IP */
428 x86_push_membase (code, X86_ESP, 12); /* exception */
429 x86_push_reg (code, X86_EBP);
430 x86_push_reg (code, X86_EDI);
431 x86_push_reg (code, X86_ESI);
432 x86_push_reg (code, X86_EBX);
433 x86_push_reg (code, X86_EDX);
434 x86_push_reg (code, X86_ECX);
435 x86_push_reg (code, X86_EAX);
436 x86_call_code (code, throw_exception);
437 /* we should never reach this breakpoint */
438 x86_breakpoint (code);
440 g_assert ((code - start) < 64);
442 return start;
446 * mono_arch_get_throw_exception:
448 * Returns a function pointer which can be used to raise
449 * exceptions. The returned function has the following
450 * signature: void (*func) (MonoException *exc);
451 * For example to raise an arithmetic exception you can use:
453 * x86_push_imm (code, mono_get_exception_arithmetic ());
454 * x86_call_code (code, arch_get_throw_exception ());
457 gpointer
458 mono_arch_get_throw_exception (void)
460 static guint8 *start;
461 static int inited = 0;
463 if (inited)
464 return start;
466 start = get_throw_exception (FALSE);
468 inited = 1;
470 return start;
473 gpointer
474 mono_arch_get_rethrow_exception (void)
476 static guint8 *start;
477 static int inited = 0;
479 if (inited)
480 return start;
482 start = get_throw_exception (TRUE);
484 inited = 1;
486 return start;
490 * mono_arch_get_throw_exception_by_name:
492 * Returns a function pointer which can be used to raise
493 * corlib exceptions. The returned function has the following
494 * signature: void (*func) (gpointer ip, char *exc_name);
495 * For example to raise an arithmetic exception you can use:
497 * x86_push_imm (code, "ArithmeticException");
498 * x86_push_imm (code, <IP>)
499 * x86_jump_code (code, arch_get_throw_exception_by_name ());
502 gpointer
503 mono_arch_get_throw_exception_by_name (void)
505 static guint8* start;
506 static int inited = 0;
507 guint8 *code;
509 if (inited)
510 return start;
512 inited = 1;
513 code = start = mono_global_codeman_reserve (32);
515 x86_push_membase (code, X86_ESP, 4); /* exception name */
516 x86_push_imm (code, "System");
517 x86_push_imm (code, mono_defaults.exception_class->image);
518 x86_call_code (code, mono_exception_from_name);
519 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
520 /* save the newly create object (overwrite exception name)*/
521 x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
522 x86_jump_code (code, mono_arch_get_throw_exception ());
524 g_assert ((code - start) < 32);
526 return start;
530 * mono_arch_get_throw_corlib_exception:
532 * Returns a function pointer which can be used to raise
533 * corlib exceptions. The returned function has the following
534 * signature: void (*func) (guint32 ex_token, guint32 offset);
535 * Here, offset is the offset which needs to be substracted from the caller IP
536 * to get the IP of the throw. Passing the offset has the advantage that it
537 * needs no relocations in the caller.
539 gpointer
540 mono_arch_get_throw_corlib_exception (void)
542 static guint8* start;
543 static int inited = 0;
544 guint8 *code;
546 if (inited)
547 return start;
549 inited = 1;
550 code = start = mono_global_codeman_reserve (64);
552 x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4); /* token */
553 x86_alu_reg_imm (code, X86_ADD, X86_EAX, MONO_TOKEN_TYPE_DEF);
554 x86_push_reg (code, X86_EAX);
555 x86_push_imm (code, mono_defaults.exception_class->image);
556 x86_call_code (code, mono_exception_from_token);
557 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 8);
558 /* Compute caller ip */
559 x86_pop_reg (code, X86_ECX);
560 /* Pop token */
561 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
562 x86_pop_reg (code, X86_EDX);
563 x86_alu_reg_reg (code, X86_SUB, X86_ECX, X86_EDX);
564 /* Push exception object */
565 x86_push_reg (code, X86_EAX);
566 /* Push throw IP */
567 x86_push_reg (code, X86_ECX);
568 x86_jump_code (code, mono_arch_get_throw_exception ());
570 g_assert ((code - start) < 64);
572 return start;
575 /* mono_arch_find_jit_info:
577 * This function is used to gather information from @ctx. It return the
578 * MonoJitInfo of the corresponding function, unwinds one stack frame and
579 * stores the resulting context into @new_ctx. It also stores a string
580 * describing the stack location into @trace (if not NULL), and modifies
581 * the @lmf if necessary. @native_offset return the IP offset from the
582 * start of the function or -1 if that info is not available.
584 MonoJitInfo *
585 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
586 MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed)
588 MonoJitInfo *ji;
589 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
591 /* Avoid costly table lookup during stack overflow */
592 if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
593 ji = prev_ji;
594 else
595 ji = mono_jit_info_table_find (domain, ip);
597 if (managed)
598 *managed = FALSE;
600 *new_ctx = *ctx;
602 if (ji != NULL) {
603 gssize regs [MONO_MAX_IREGS + 1];
604 guint8 *cfa;
605 guint32 unwind_info_len;
606 guint8 *unwind_info;
608 if (managed)
609 if (!ji->method->wrapper_type)
610 *managed = TRUE;
612 if (ji->from_aot)
613 unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len);
614 else
615 unwind_info = mono_get_cached_unwind_info (ji->used_regs, &unwind_info_len);
617 regs [X86_EAX] = new_ctx->eax;
618 regs [X86_EBX] = new_ctx->ebx;
619 regs [X86_ECX] = new_ctx->ecx;
620 regs [X86_EDX] = new_ctx->edx;
621 regs [X86_ESP] = new_ctx->esp;
622 regs [X86_EBP] = new_ctx->ebp;
623 regs [X86_ESI] = new_ctx->esi;
624 regs [X86_EDI] = new_ctx->edi;
625 regs [X86_NREG] = new_ctx->eip;
627 mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start,
628 (guint8*)ji->code_start + ji->code_size,
629 ip, regs, MONO_MAX_IREGS + 1, &cfa);
631 new_ctx->eax = regs [X86_EAX];
632 new_ctx->ebx = regs [X86_EBX];
633 new_ctx->ecx = regs [X86_ECX];
634 new_ctx->edx = regs [X86_EDX];
635 new_ctx->esp = regs [X86_ESP];
636 new_ctx->ebp = regs [X86_EBP];
637 new_ctx->esi = regs [X86_ESI];
638 new_ctx->edi = regs [X86_EDI];
639 new_ctx->eip = regs [X86_NREG];
641 /* The CFA becomes the new SP value */
642 new_ctx->esp = (gssize)cfa;
644 /* Adjust IP */
645 new_ctx->eip --;
647 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
648 /* remove any unused lmf */
649 *lmf = (gpointer)(((guint32)(*lmf)->previous_lmf) & ~1);
652 /* Pop arguments off the stack */
654 MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (ji->method)->param_count + 1);
656 guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (ji->method), mono_method_signature (ji->method)->param_count, arg_info);
657 new_ctx->esp += stack_to_pop;
660 return ji;
661 } else if (*lmf) {
663 *new_ctx = *ctx;
665 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
666 } else {
667 if (!((guint32)((*lmf)->previous_lmf) & 1))
668 /* Top LMF entry */
669 return (gpointer)-1;
670 /* Trampoline lmf frame */
671 memset (res, 0, sizeof (MonoJitInfo));
672 res->method = (*lmf)->method;
675 new_ctx->esi = (*lmf)->esi;
676 new_ctx->edi = (*lmf)->edi;
677 new_ctx->ebx = (*lmf)->ebx;
678 new_ctx->ebp = (*lmf)->ebp;
679 new_ctx->eip = (*lmf)->eip;
681 /* Check if we are in a trampoline LMF frame */
682 if ((guint32)((*lmf)->previous_lmf) & 1) {
683 /* lmf->esp is set by the trampoline code */
684 new_ctx->esp = (*lmf)->esp;
686 /* Pop arguments off the stack */
687 /* FIXME: Handle the delegate case too ((*lmf)->method == NULL) */
688 /* FIXME: Handle the IMT/vtable case too */
689 if ((*lmf)->method && (*lmf)->method != MONO_FAKE_IMT_METHOD && (*lmf)->method != MONO_FAKE_VTABLE_METHOD) {
690 MonoMethod *method = (*lmf)->method;
691 MonoJitArgumentInfo *arg_info = g_newa (MonoJitArgumentInfo, mono_method_signature (method)->param_count + 1);
693 guint32 stack_to_pop = mono_arch_get_argument_info (mono_method_signature (method), mono_method_signature (method)->param_count, arg_info);
694 new_ctx->esp += stack_to_pop;
697 else
698 /* the lmf is always stored on the stack, so the following
699 * expression points to a stack location which can be used as ESP */
700 new_ctx->esp = (unsigned long)&((*lmf)->eip);
702 *lmf = (gpointer)(((guint32)(*lmf)->previous_lmf) & ~1);
704 return ji ? ji : res;
707 return NULL;
710 #ifdef __sun
711 #define REG_EAX EAX
712 #define REG_EBX EBX
713 #define REG_ECX ECX
714 #define REG_EDX EDX
715 #define REG_EBP EBP
716 #define REG_ESP ESP
717 #define REG_ESI ESI
718 #define REG_EDI EDI
719 #define REG_EIP EIP
720 #endif
722 void
723 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
725 #ifdef MONO_ARCH_USE_SIGACTION
726 ucontext_t *ctx = (ucontext_t*)sigctx;
728 mctx->eax = UCONTEXT_REG_EAX (ctx);
729 mctx->ebx = UCONTEXT_REG_EBX (ctx);
730 mctx->ecx = UCONTEXT_REG_ECX (ctx);
731 mctx->edx = UCONTEXT_REG_EDX (ctx);
732 mctx->ebp = UCONTEXT_REG_EBP (ctx);
733 mctx->esp = UCONTEXT_REG_ESP (ctx);
734 mctx->esi = UCONTEXT_REG_ESI (ctx);
735 mctx->edi = UCONTEXT_REG_EDI (ctx);
736 mctx->eip = UCONTEXT_REG_EIP (ctx);
737 #else
738 struct sigcontext *ctx = (struct sigcontext *)sigctx;
740 mctx->eax = ctx->SC_EAX;
741 mctx->ebx = ctx->SC_EBX;
742 mctx->ecx = ctx->SC_ECX;
743 mctx->edx = ctx->SC_EDX;
744 mctx->ebp = ctx->SC_EBP;
745 mctx->esp = ctx->SC_ESP;
746 mctx->esi = ctx->SC_ESI;
747 mctx->edi = ctx->SC_EDI;
748 mctx->eip = ctx->SC_EIP;
749 #endif
752 void
753 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
755 #ifdef MONO_ARCH_USE_SIGACTION
756 ucontext_t *ctx = (ucontext_t*)sigctx;
758 UCONTEXT_REG_EAX (ctx) = mctx->eax;
759 UCONTEXT_REG_EBX (ctx) = mctx->ebx;
760 UCONTEXT_REG_ECX (ctx) = mctx->ecx;
761 UCONTEXT_REG_EDX (ctx) = mctx->edx;
762 UCONTEXT_REG_EBP (ctx) = mctx->ebp;
763 UCONTEXT_REG_ESP (ctx) = mctx->esp;
764 UCONTEXT_REG_ESI (ctx) = mctx->esi;
765 UCONTEXT_REG_EDI (ctx) = mctx->edi;
766 UCONTEXT_REG_EIP (ctx) = mctx->eip;
767 #else
768 struct sigcontext *ctx = (struct sigcontext *)sigctx;
770 ctx->SC_EAX = mctx->eax;
771 ctx->SC_EBX = mctx->ebx;
772 ctx->SC_ECX = mctx->ecx;
773 ctx->SC_EDX = mctx->edx;
774 ctx->SC_EBP = mctx->ebp;
775 ctx->SC_ESP = mctx->esp;
776 ctx->SC_ESI = mctx->esi;
777 ctx->SC_EDI = mctx->edi;
778 ctx->SC_EIP = mctx->eip;
779 #endif
782 gpointer
783 mono_arch_ip_from_context (void *sigctx)
785 #ifdef MONO_ARCH_USE_SIGACTION
786 ucontext_t *ctx = (ucontext_t*)sigctx;
787 return (gpointer)UCONTEXT_REG_EIP (ctx);
788 #else
789 struct sigcontext *ctx = sigctx;
790 return (gpointer)ctx->SC_EIP;
791 #endif
794 gboolean
795 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
797 MonoContext mctx;
799 mono_arch_sigctx_to_monoctx (sigctx, &mctx);
801 if (mono_debugger_handle_exception (&mctx, (MonoObject *)obj))
802 return TRUE;
804 mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only);
806 mono_arch_monoctx_to_sigctx (&mctx, sigctx);
808 return TRUE;
811 static void
812 restore_soft_guard_pages (void)
814 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
815 if (jit_tls->stack_ovf_guard_base)
816 mono_mprotect (jit_tls->stack_ovf_guard_base, jit_tls->stack_ovf_guard_size, MONO_MMAP_NONE);
820 * this function modifies mctx so that when it is restored, it
821 * won't execcute starting at mctx.eip, but in a function that
822 * will restore the protection on the soft-guard pages and return back to
823 * continue at mctx.eip.
825 static void
826 prepare_for_guard_pages (MonoContext *mctx)
828 gpointer *sp;
829 sp = (gpointer)(mctx->esp);
830 sp -= 1;
831 /* the resturn addr */
832 sp [0] = (gpointer)(mctx->eip);
833 mctx->eip = (unsigned long)restore_soft_guard_pages;
834 mctx->esp = (unsigned long)sp;
837 static void
838 altstack_handle_and_restore (void *sigctx, gpointer obj, gboolean stack_ovf)
840 void (*restore_context) (MonoContext *);
841 MonoContext mctx;
843 restore_context = mono_arch_get_restore_context ();
844 mono_arch_sigctx_to_monoctx (sigctx, &mctx);
846 if (mono_debugger_handle_exception (&mctx, (MonoObject *)obj)) {
847 if (stack_ovf)
848 prepare_for_guard_pages (&mctx);
849 restore_context (&mctx);
852 mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, FALSE);
853 if (stack_ovf)
854 prepare_for_guard_pages (&mctx);
855 restore_context (&mctx);
858 void
859 mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean stack_ovf)
861 #ifdef MONO_ARCH_USE_SIGACTION
862 MonoException *exc = NULL;
863 ucontext_t *ctx = (ucontext_t*)sigctx;
864 MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)UCONTEXT_REG_EIP (ctx));
865 gpointer *sp;
866 int frame_size;
868 /* if we didn't find a managed method for the ip address and it matches the fault
869 * address, we assume we followed a broken pointer during an indirect call, so
870 * we try the lookup again with the return address pushed on the stack
872 if (!ji && fault_addr == (gpointer)UCONTEXT_REG_EIP (ctx)) {
873 glong *sp = (gpointer)UCONTEXT_REG_ESP (ctx);
874 ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)sp [0]);
875 if (ji)
876 UCONTEXT_REG_EIP (ctx) = sp [0];
878 if (stack_ovf)
879 exc = mono_domain_get ()->stack_overflow_ex;
880 if (!ji)
881 mono_handle_native_sigsegv (SIGSEGV, sigctx);
882 /* setup a call frame on the real stack so that control is returned there
883 * and exception handling can continue.
884 * If this was a stack overflow the caller already ensured the stack pages
885 * needed have been unprotected.
886 * The frame looks like:
887 * ucontext struct
888 * test_only arg
889 * exception arg
890 * ctx arg
891 * return ip
893 frame_size = sizeof (ucontext_t) + sizeof (gpointer) * 4;
894 frame_size += 15;
895 frame_size &= ~15;
896 sp = (gpointer)(UCONTEXT_REG_ESP (ctx) & ~15);
897 sp = (gpointer)((char*)sp - frame_size);
898 /* the incoming arguments are aligned to 16 bytes boundaries, so the return address IP
899 * goes at sp [-1]
901 sp [-1] = (gpointer)UCONTEXT_REG_EIP (ctx);
902 sp [0] = sp + 4;
903 sp [1] = exc;
904 sp [2] = (gpointer)stack_ovf;
905 /* may need to adjust pointers in the new struct copy, depending on the OS */
906 memcpy (sp + 4, ctx, sizeof (ucontext_t));
907 /* at the return form the signal handler execution starts in altstack_handle_and_restore() */
908 UCONTEXT_REG_EIP (ctx) = (unsigned long)altstack_handle_and_restore;
909 UCONTEXT_REG_ESP (ctx) = (unsigned long)(sp - 1);
910 #endif