2 * exceptions-sparc.c: exception support for 64 bit sparc
5 * Mark Crichton (crichton@gimp.org)
6 * Dietmar Maurer (dietmar@ximian.com)
8 * (C) 2003 Ximian, Inc.
15 #include <sys/ucontext.h>
17 #include <mono/arch/sparc/sparc-codegen.h>
18 #include <mono/metadata/appdomain.h>
19 #include <mono/metadata/tabledefs.h>
20 #include <mono/metadata/threads.h>
21 #include <mono/metadata/debug-helpers.h>
22 #include <mono/metadata/exception.h>
23 #include <mono/metadata/mono-debug.h>
24 #include <mono/metadata/gc-internal.h>
27 #include "mini-sparc.h"
29 typedef struct MonoContext {
35 gboolean mono_sparc_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only);
37 #define MONO_CONTEXT_SET_IP(ctx,eip) do { (ctx)->ip = (long)(eip); } while (0);
38 #define MONO_CONTEXT_SET_BP(ctx,ebp) do { (ctx)->fp = (long)(ebp); } while (0);
40 #define MONO_CONTEXT_GET_IP(ctx) ((gpointer)((ctx)->ip))
41 #define MONO_CONTEXT_GET_BP(ctx) ((gpointer)((ctx)->fp))
43 #define IS_ON_SIGALTSTACK(jit_tls) FALSE
46 * arch_get_restore_context:
48 * Returns a pointer to a method which restores a previously saved sigcontext.
51 arch_get_restore_context (void)
53 static guint32 start [32];
54 static int inited = 0;
62 sparc_ld_imm (code, sparc_o0, G_STRUCT_OFFSET (MonoContext, ip), sparc_i7);
63 sparc_ld_imm (code, sparc_o0, G_STRUCT_OFFSET (MonoContext, sp), sparc_i6);
65 sparc_jmpl_imm (code, sparc_i7, 0, sparc_g0);
66 /* FIXME: This does not return to the correct window */
67 sparc_restore_imm (code, sparc_g0, 0, sparc_g0);
69 g_assert ((code - start) < 32);
77 * arch_get_call_filter:
79 * Returns a pointer to a method which calls an exception filter. We
80 * also use this function to call finally handlers (we pass NULL as
81 * @exc object in this case).
83 * call_filter (MonoContext *ctx, gpointer ip)
86 arch_get_call_filter (void)
88 static guint32 start [64];
89 static int inited = 0;
99 * There are two frames here:
100 * - the first frame is used by call_filter
101 * - the second frame is used to run the filter code
104 /* Create first frame */
105 sparc_save_imm (code, sparc_sp, -160, sparc_sp);
107 sparc_mov_reg_reg (code, sparc_i1, sparc_o0);
108 sparc_ld_imm (code, sparc_i0, G_STRUCT_OFFSET (MonoContext, sp), sparc_o1);
110 /* Create second frame */
111 sparc_save_imm (code, sparc_sp, -160, sparc_sp);
113 sparc_mov_reg_reg (code, sparc_i0, sparc_o0);
114 sparc_mov_reg_reg (code, sparc_i1, sparc_o1);
117 * We need to change %fp to point to the stack frame of the method
118 * containing the filter. But changing %fp also changes the %sp of
119 * the parent frame (the first frame), so if the OS saves the first frame,
120 * it saves it to the stack frame of the method, which is not good.
121 * So flush all register windows to memory before changing %fp.
125 sparc_mov_reg_reg (code, sparc_fp, sparc_o7);
128 * Modify the second frame so it is identical to the one used in the
129 * method containing the filter.
131 for (i = 0; i < 16; ++i)
132 sparc_ld_imm (code, sparc_o1, i * 4, sparc_l0 + i);
134 /* Save %fp to a location reserved in mono_arch_allocate_vars */
135 sparc_st_imm (code, sparc_o7, sparc_fp, -4);
137 /* Call the filter code, after this returns, %o0 will hold the result */
138 sparc_call_imm (code, sparc_o0, 0);
141 /* Restore original %fp */
142 sparc_ld_imm (code, sparc_fp, -4, sparc_fp);
144 sparc_mov_reg_reg (code, sparc_o0, sparc_i0);
146 /* Return to first frame */
147 sparc_restore (code, sparc_g0, sparc_g0, sparc_g0);
149 /* FIXME: Save locals to the stack */
151 /* Return to caller */
153 /* Return result in delay slot */
154 sparc_restore (code, sparc_o0, sparc_g0, sparc_o0);
156 g_assert ((code - start) < 64);
164 throw_exception (MonoObject *ex, guint32 sp, guint32 ip)
167 static void (*restore_context) (MonoContext *);
169 if (!restore_context)
170 restore_context = arch_get_restore_context ();
172 ctx.sp = (guint32*)sp;
174 ctx.fp = (guint32*)ctx.sp [sparc_i6 - 16];
176 mono_sparc_handle_exception (&ctx, ex, FALSE);
177 restore_context (&ctx);
179 g_assert_not_reached ();
183 * arch_get_throw_exception_by_name:
185 * Returns a function pointer which can be used to raise exceptions.
186 * The returned function has the following
187 * signature: void (*func) (char *exc_name);
190 mono_arch_get_throw_exception (void)
192 static guint32 start [32];
193 static int inited = 0;
202 sparc_save_imm (code, sparc_sp, -160, sparc_sp);
205 sparc_mov_reg_reg (code, sparc_i0, sparc_o0);
206 sparc_mov_reg_reg (code, sparc_fp, sparc_o1);
207 sparc_mov_reg_reg (code, sparc_i7, sparc_o2);
208 sparc_set (code, throw_exception, sparc_o7);
209 sparc_jmpl (code, sparc_o7, sparc_g0, sparc_callsite);
212 g_assert ((code - start) < 32);
218 * arch_get_throw_exception_by_name:
220 * Returns a function pointer which can be used to raise
221 * corlib exceptions. The returned function has the following
222 * signature: void (*func) (char *exc_name, gpointer ip);
225 mono_arch_get_throw_exception_by_name (void)
227 static guint32 start [32];
228 static int inited = 0;
237 sparc_save_imm (code, sparc_sp, -160, sparc_sp);
239 sparc_mov_reg_reg (code, sparc_i0, sparc_o2);
240 sparc_set (code, mono_defaults.corlib, sparc_o0);
241 sparc_set (code, "System", sparc_o1);
242 sparc_set (code, mono_exception_from_name, sparc_o7);
243 sparc_jmpl (code, sparc_o7, sparc_g0, sparc_callsite);
246 /* Return to the caller, so exception handling does not see this frame */
247 sparc_restore (code, sparc_o0, sparc_g0, sparc_o0);
249 /* Put original return address into %o7 */
250 sparc_mov_reg_reg (code, sparc_o1, sparc_o7);
251 sparc_set (code, mono_arch_get_throw_exception (), sparc_g1);
252 /* Use a jmp instead of a call so o7 is preserved */
253 sparc_jmpl_imm (code, sparc_g1, 0, sparc_g0);
256 g_assert ((code - start) < 32);
261 /* mono_arch_find_jit_info:
263 * This function is used to gather information from @ctx. It return the
264 * MonoJitInfo of the corresponding function, unwinds one stack frame and
265 * stores the resulting context into @new_ctx. It also stores a string
266 * describing the stack location into @trace (if not NULL), and modifies
267 * the @lmf if necessary. @native_offset return the IP offset from the
268 * start of the function or -1 if that info is not available.
271 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx,
272 MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
276 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
279 /* Avoid costly table lookup during stack overflow */
280 if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
283 ji = mono_jit_info_table_find (domain, ip);
295 char *source_location, *tmpaddr, *fname;
296 gint32 address, iloffset;
300 address = (char *)ip - (char *)ji->code_start;
303 *native_offset = address;
306 if (!ji->method->wrapper_type)
310 source_location = mono_debug_source_location_from_address (ji->method, address, NULL, domain);
311 iloffset = mono_debug_il_offset_from_address (ji->method, address, domain);
314 tmpaddr = g_strdup_printf ("<0x%05x>", address);
316 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
318 fname = mono_method_full_name (ji->method, TRUE);
321 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
323 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
326 g_free (source_location);
332 /* Restore ip and sp from the saved register window */
333 window = (guint32*)ctx->sp;
334 new_ctx->ip = window [sparc_i7 - 16];
335 new_ctx->sp = (guint32*)(window [sparc_i6 - 16]);
336 new_ctx->fp = (guint32*)(new_ctx->sp [sparc_i6 - 16]);
351 *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
353 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->ip))) {
356 memset (res, 0, sizeof (MonoJitInfo));
357 res->method = (*lmf)->method;
360 new_ctx->ip = (*lmf)->ip;
361 new_ctx->sp = (*lmf)->sp;
362 new_ctx->fp = (*lmf)->ebp;
364 *lmf = (*lmf)->previous_lmf;
371 mono_arch_has_unwind_info (gconstpointer addr)
377 glist_to_array (GList *list)
379 MonoDomain *domain = mono_domain_get ();
386 len = g_list_length (list);
387 res = mono_array_new (domain, mono_defaults.int_class, len);
389 for (i = 0; list; list = list->next, i++)
390 mono_array_set (res, gpointer, i, list->data);
396 ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
402 mono_jit_walk_stack (MonoStackWalk func, gpointer user_data)
404 MonoDomain *domain = mono_domain_get ();
405 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
406 MonoLMF *lmf = jit_tls->lmf;
407 MonoJitInfo *ji, rji;
408 gint native_offset, il_offset;
411 MonoContext ctx, new_ctx;
413 mono_sparc_flushw ();
415 MONO_CONTEXT_SET_IP (&ctx, __builtin_return_address (0));
416 MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (0));
418 while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) {
420 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
423 if (ji == (gpointer)-1)
426 il_offset = mono_debug_il_offset_from_address (ji->method, native_offset, domain);
428 if (func (ji->method, native_offset, il_offset, managed, user_data))
436 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
437 MonoReflectionMethod **method,
438 gint32 *iloffset, gint32 *native_offset,
439 MonoString **file, gint32 *line, gint32 *column)
445 * mono_sparc_handle_exception:
446 * @ctx: saved processor state
447 * @obj: the exception object
448 * @test_only: only test if the exception is caught, but dont call handlers
453 mono_sparc_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
455 MonoDomain *domain = mono_domain_get ();
456 MonoJitInfo *ji, rji;
457 static int (*call_filter) (MonoContext *, gpointer) = NULL;
458 static void (*restore_context) (MonoContext *);
459 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
460 MonoLMF *lmf = jit_tls->lmf;
461 GList *trace_ips = NULL;
462 MonoException *mono_ex;
463 gboolean stack_overflow = FALSE;
464 MonoContext initial_ctx;
466 gboolean gc_disabled = FALSE;
469 * This function might execute on an alternate signal stack, and Boehm GC
471 * Also, since the altstack is small, stack space intensive operations like
472 * JIT compilation should be avoided.
474 if (IS_ON_SIGALTSTACK (jit_tls)) {
476 * FIXME: disabling/enabling GC while already on a signal stack might
477 * not be safe either.
479 /* Have to reenable it later */
484 g_assert (ctx != NULL);
486 MonoException *ex = mono_get_exception_null_reference ();
487 ex->message = mono_string_new (domain, "Object reference not set to an instance of an object");
488 obj = (MonoObject *)ex;
491 if (mono_object_isinst (obj, mono_defaults.exception_class)) {
492 mono_ex = (MonoException*)obj;
493 mono_ex->stack_trace = NULL;
498 //printf ("HANDLING EXCEPTION: %s\n", ((MonoObject*)obj)->vtable->klass->name);
500 if (obj == domain->stack_overflow_ex)
501 stack_overflow = TRUE;
504 call_filter = arch_get_call_filter ();
506 if (!restore_context)
507 restore_context = arch_get_restore_context ();
509 g_assert (jit_tls->end_of_stack);
510 g_assert (jit_tls->abort_func);
513 MonoContext ctx_cp = *ctx;
514 if (mono_jit_trace_calls != NULL)
515 g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name);
516 if (!mono_sparc_handle_exception (&ctx_cp, obj, TRUE)) {
517 if (mono_break_on_exc)
519 mono_unhandled_exception (obj);
524 memset (&rji, 0, sizeof (rji));
529 gboolean need_trace = FALSE;
532 if (test_only && (frame_count < 1000))
535 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &rji, ctx, &new_ctx,
536 need_trace ? &trace : NULL, &lmf, NULL, NULL);
538 g_warning ("Exception inside function without unwind info");
539 g_assert_not_reached ();
542 //printf ("JI: %p IP: %p SP: %p.\n", ji, (gpointer)new_ctx.ip, new_ctx.sp);
544 if (ji != (gpointer)-1) {
546 //printf ("M: %s %p %p %d.\n", mono_method_full_name (ji->method, TRUE), jit_tls->end_of_stack, ctx->ebp, count);
548 if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE && mono_ex) {
551 /* Avoid giant stack traces */
552 if (frame_count < 1000) {
553 trace_ips = g_list_append (trace_ips, MONO_CONTEXT_GET_IP (ctx));
555 if (!mono_ex->stack_trace)
556 strace = g_strdup ("");
558 strace = mono_string_to_utf8 (mono_ex->stack_trace);
560 tmp = g_strdup_printf ("%s%s\n", strace, trace);
563 mono_ex->stack_trace = mono_string_new (domain, tmp);
571 free_stack = (guint8*)(MONO_CONTEXT_GET_BP (ctx)) - (guint8*)(MONO_CONTEXT_GET_BP (&initial_ctx));
573 free_stack = 0xffffff;
575 free_stack = 0xffffff;
578 * During stack overflow, wait till the unwinding frees some stack
579 * space before running handlers/finalizers.
581 if ((free_stack > (64 * 1024)) && ji->num_clauses) {
584 g_assert (ji->clauses);
586 for (i = 0; i < ji->num_clauses; i++) {
587 MonoJitExceptionInfo *ei = &ji->clauses [i];
589 if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
590 MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) {
593 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE) || (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)) {
594 /* store the exception object int cfg->excvar */
595 g_assert (ji->exvar_offset);
596 *((gpointer *)((char *)MONO_CONTEXT_GET_BP (ctx) + ji->exvar_offset)) = obj;
599 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE &&
600 mono_object_isinst (obj, mono_class_get (ji->method->klass->image, ei->data.token))) ||
601 ((ei->flags == MONO_EXCEPTION_CLAUSE_FILTER &&
602 call_filter (ctx, ei->data.filter)))) {
605 mono_ex->trace_ips = glist_to_array (trace_ips);
606 g_list_free (trace_ips);
613 if (mono_jit_trace_calls != NULL && mono_trace_eval (ji->method))
614 g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
615 MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
623 if (!test_only && ei->try_start <= MONO_CONTEXT_GET_IP (ctx) &&
624 MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
625 (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
626 if (mono_jit_trace_calls != NULL && mono_trace_eval (ji->method))
627 g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
628 call_filter (ctx, ei->handler_start);
640 if ((ji == (gpointer)-1) || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) {
647 if (IS_ON_SIGALTSTACK (jit_tls)) {
648 /* Switch back to normal stack */
650 /* Free up some stack space */
651 initial_ctx.sp += (64 * 1024);
652 initial_ctx.ip = (unsigned int)jit_tls->abort_func;
653 restore_context (&initial_ctx);
656 jit_tls->abort_func (obj);
657 g_assert_not_reached ();
660 mono_ex->trace_ips = glist_to_array (trace_ips);
661 g_list_free (trace_ips);
667 g_assert_not_reached ();
671 mono_arch_handle_exception (ucontext_t *ctx, gpointer obj, gboolean test_only)
676 * Access to the machine state using the ucontext_t parameter is somewhat
677 * under documented under solaris. The code below seems to work under
680 g_assert (!ctx->uc_mcontext.gwins);
682 mctx.ip = ctx->uc_mcontext.gregs [REG_PC];
683 mctx.sp = ctx->uc_mcontext.gregs [REG_SP];
684 mctx.fp = mctx.sp [sparc_fp - 16];
686 mono_sparc_handle_exception (&mctx, obj, test_only);
688 /* We can't use restore_context to return from a signal handler */
689 ctx->uc_mcontext.gregs [REG_PC] = mctx.ip;
690 ctx->uc_mcontext.gregs [REG_nPC] = mctx.ip + 4;
691 ctx->uc_mcontext.gregs [REG_SP] = mctx.sp;
692 mctx.sp [sparc_fp - 16] = mctx.fp;