e640c0ad516de488191573b997ae937d336c5d90
[mono.git] / mono / mini / exceptions-sparc.c
1 /*
2  * exceptions-sparc.c: exception support for 64 bit sparc
3  *
4  * Authors:
5  *   Mark Crichton (crichton@gimp.org)
6  *   Dietmar Maurer (dietmar@ximian.com)
7  *
8  * (C) 2003 Ximian, Inc.
9  */
10
11 #include <config.h>
12 #include <glib.h>
13 #include <signal.h>
14 #include <string.h>
15 #include <sys/ucontext.h>
16
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>
25
26 #include "mini.h"
27 #include "mini-sparc.h"
28
29 gboolean  mono_sparc_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only);
30
31 #define MONO_CONTEXT_SET_IP(ctx,eip) do { (ctx)->ip = (long)(eip); } while (0); 
32 #define MONO_CONTEXT_SET_BP(ctx,ebp) do { (ctx)->fp = (long)(ebp); } while (0); 
33
34 #define MONO_CONTEXT_GET_IP(ctx) ((gpointer)((ctx)->ip))
35 #define MONO_CONTEXT_GET_BP(ctx) ((gpointer)((ctx)->fp))
36
37 #define IS_ON_SIGALTSTACK(jit_tls) FALSE
38
39 /*
40  * arch_get_restore_context:
41  *
42  * Returns a pointer to a method which restores a previously saved sigcontext.
43  */
44 static gpointer
45 arch_get_restore_context (void)
46 {
47         static guint32 start [32];
48         static int inited = 0;
49         guint32 *code;
50
51         if (inited)
52                 return start;
53
54         code = start;
55
56         sparc_ld_imm (code, sparc_o0, G_STRUCT_OFFSET (MonoContext, ip), sparc_i7);
57         sparc_ld_imm (code, sparc_o0, G_STRUCT_OFFSET (MonoContext, sp), sparc_i6);
58
59         sparc_jmpl_imm (code, sparc_i7, 0, sparc_g0);
60         /* FIXME: This does not return to the correct window */
61         sparc_restore_imm (code, sparc_g0, 0, sparc_g0);
62
63         g_assert ((code - start) < 32);
64
65         inited = 1;
66
67         return start;
68 }
69
70 /*
71  * arch_get_call_filter:
72  *
73  * Returns a pointer to a method which calls an exception filter. We
74  * also use this function to call finally handlers (we pass NULL as 
75  * @exc object in this case).
76  *
77  * call_filter (MonoContext *ctx, gpointer ip)
78  */
79 static gpointer
80 arch_get_call_filter (void)
81 {
82         static guint32 start [64];
83         static int inited = 0;
84         guint32 *code;
85         int i;
86
87         if (inited)
88                 return start;
89
90         code = start;
91
92         /*
93          * There are two frames here:
94          * - the first frame is used by call_filter
95          * - the second frame is used to run the filter code
96          */
97
98         /* Create first frame */
99         sparc_save_imm (code, sparc_sp, -160, sparc_sp);
100
101         sparc_mov_reg_reg (code, sparc_i1, sparc_o0);
102         sparc_ld_imm (code, sparc_i0, G_STRUCT_OFFSET (MonoContext, sp), sparc_o1);
103
104         /* Create second frame */
105         sparc_save_imm (code, sparc_sp, -160, sparc_sp);
106
107         sparc_mov_reg_reg (code, sparc_i0, sparc_o0);
108         sparc_mov_reg_reg (code, sparc_i1, sparc_o1);
109
110         /*
111          * We need to change %fp to point to the stack frame of the method
112          * containing the filter. But changing %fp also changes the %sp of
113          * the parent frame (the first frame), so if the OS saves the first frame,
114          * it saves it to the stack frame of the method, which is not good.
115          * So flush all register windows to memory before changing %fp.
116          */
117         sparc_flushw (code);
118
119         sparc_mov_reg_reg (code, sparc_fp, sparc_o7);
120
121         /* 
122          * Modify the second frame so it is identical to the one used in the
123          * method containing the filter.
124          */
125         for (i = 0; i < 16; ++i)
126                 sparc_ld_imm (code, sparc_o1, i * 4, sparc_l0 + i);
127
128         /* Save %fp to a location reserved in mono_arch_allocate_vars */
129         sparc_st_imm (code, sparc_o7, sparc_fp, -4);
130
131         /* Call the filter code, after this returns, %o0 will hold the result */
132         sparc_call_imm (code, sparc_o0, 0);
133         sparc_nop (code);
134
135         /* Restore original %fp */
136         sparc_ld_imm (code, sparc_fp, -4, sparc_fp);
137
138         sparc_mov_reg_reg (code, sparc_o0, sparc_i0);
139
140         /* Return to first frame */
141         sparc_restore (code, sparc_g0, sparc_g0, sparc_g0);
142
143         /* FIXME: Save locals to the stack */
144
145         /* Return to caller */
146         sparc_ret (code);
147         /* Return result in delay slot */
148         sparc_restore (code, sparc_o0, sparc_g0, sparc_o0);
149
150         g_assert ((code - start) < 64);
151
152         inited = 1;
153
154         return start;
155 }
156
157 static void
158 throw_exception (MonoObject *ex, guint32 sp, guint32 ip)
159 {
160         MonoContext ctx;
161         static void (*restore_context) (MonoContext *);
162
163         if (!restore_context)
164                 restore_context = arch_get_restore_context ();
165
166         ctx.sp = (guint32*)sp;
167         ctx.ip = ip;
168         ctx.fp = (guint32*)ctx.sp [sparc_i6 - 16];
169
170         mono_sparc_handle_exception (&ctx, ex, FALSE);
171         restore_context (&ctx);
172
173         g_assert_not_reached ();
174 }
175
176 /**
177  * arch_get_throw_exception_by_name:
178  *
179  * Returns a function pointer which can be used to raise exceptions.
180  * The returned function has the following 
181  * signature: void (*func) (char *exc_name); 
182  */
183 gpointer 
184 mono_arch_get_throw_exception (void)
185 {
186         static guint32 start [32];
187         static int inited = 0;
188         guint32 *code;
189
190         if (inited)
191                 return start;
192
193         inited = 1;
194         code = start;
195
196         sparc_save_imm (code, sparc_sp, -160, sparc_sp);
197
198         sparc_flushw (code);
199         sparc_mov_reg_reg (code, sparc_i0, sparc_o0);
200         sparc_mov_reg_reg (code, sparc_fp, sparc_o1);
201         sparc_mov_reg_reg (code, sparc_i7, sparc_o2);
202         sparc_set (code, throw_exception, sparc_o7);
203         sparc_jmpl (code, sparc_o7, sparc_g0, sparc_callsite);
204         sparc_nop (code);
205
206         g_assert ((code - start) < 32);
207
208         return start;
209 }
210
211 /**
212  * arch_get_throw_exception_by_name:
213  *
214  * Returns a function pointer which can be used to raise 
215  * corlib exceptions. The returned function has the following 
216  * signature: void (*func) (char *exc_name, gpointer ip); 
217  */
218 gpointer 
219 mono_arch_get_throw_exception_by_name (void)
220 {
221         static guint32 start [32];
222         static int inited = 0;
223         guint32 *code;
224
225         if (inited)
226                 return start;
227
228         inited = 1;
229         code = start;
230
231         sparc_save_imm (code, sparc_sp, -160, sparc_sp);
232
233         sparc_mov_reg_reg (code, sparc_i0, sparc_o2);
234         sparc_set (code, mono_defaults.corlib, sparc_o0);
235         sparc_set (code, "System", sparc_o1);
236         sparc_set (code, mono_exception_from_name, sparc_o7);
237         sparc_jmpl (code, sparc_o7, sparc_g0, sparc_callsite);
238         sparc_nop (code);
239
240         /* Return to the caller, so exception handling does not see this frame */
241         sparc_restore (code, sparc_o0, sparc_g0, sparc_o0);
242
243         /* Put original return address into %o7 */
244         sparc_mov_reg_reg (code, sparc_o1, sparc_o7);
245         sparc_set (code, mono_arch_get_throw_exception (), sparc_g1);
246         /* Use a jmp instead of a call so o7 is preserved */
247         sparc_jmpl_imm (code, sparc_g1, 0, sparc_g0);
248         sparc_nop (code);
249
250         g_assert ((code - start) < 32);
251
252         return start;
253 }       
254
255 /* mono_arch_find_jit_info:
256  *
257  * This function is used to gather information from @ctx. It return the 
258  * MonoJitInfo of the corresponding function, unwinds one stack frame and
259  * stores the resulting context into @new_ctx. It also stores a string 
260  * describing the stack location into @trace (if not NULL), and modifies
261  * the @lmf if necessary. @native_offset return the IP offset from the 
262  * start of the function or -1 if that info is not available.
263  */
264 MonoJitInfo *
265 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
266                          MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
267                          gboolean *managed)
268 {
269         MonoJitInfo *ji;
270         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
271         guint32 *window;
272
273         /* Avoid costly table lookup during stack overflow */
274         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
275                 ji = prev_ji;
276         else
277                 ji = mono_jit_info_table_find (domain, ip);
278
279         if (trace)
280                 *trace = NULL;
281
282         if (native_offset)
283                 *native_offset = -1;
284
285         if (managed)
286                 *managed = FALSE;
287
288         if (ji != NULL) {
289                 char *source_location, *tmpaddr, *fname;
290                 gint32 address, iloffset;
291
292                 *new_ctx = *ctx;
293
294                 address = (char *)ip - (char *)ji->code_start;
295
296                 if (native_offset)
297                         *native_offset = address;
298
299                 if (managed)
300                         if (!ji->method->wrapper_type)
301                                 *managed = TRUE;
302
303                 if (trace) {
304                         source_location = mono_debug_source_location_from_address (ji->method, address, NULL, domain);
305                         iloffset = mono_debug_il_offset_from_address (ji->method, address, domain);
306
307                         if (iloffset < 0)
308                                 tmpaddr = g_strdup_printf ("<0x%05x>", address);
309                         else
310                                 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
311                 
312                         fname = mono_method_full_name (ji->method, TRUE);
313
314                         if (source_location)
315                                 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
316                         else
317                                 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
318
319                         g_free (fname);
320                         g_free (source_location);
321                         g_free (tmpaddr);
322                 }
323
324                 /* FIXME: lmf */
325
326                 /* Restore ip and sp from the saved register window */
327                 window = (guint32*)ctx->sp;
328                 new_ctx->ip = window [sparc_i7 - 16];
329                 new_ctx->sp = (guint32*)(window [sparc_i6 - 16]);
330                 new_ctx->fp = (guint32*)(new_ctx->sp [sparc_i6 - 16]);
331
332                 *res = *ji;
333                 return res;
334         }
335         else {
336                 if (!(*lmf))
337                         return NULL;
338
339                 *new_ctx = *ctx;
340
341                 if (!(*lmf)->method)
342                         return (gpointer)-1;
343
344                 if (trace)
345                         *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
346                 
347                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->ip))) {
348                         *res = *ji;
349                 } else {
350                         memset (res, 0, sizeof (MonoJitInfo));
351                         res->method = (*lmf)->method;
352                 }
353
354                 new_ctx->ip = (*lmf)->ip;
355                 new_ctx->sp = (*lmf)->sp;
356                 new_ctx->fp = (*lmf)->ebp;
357
358                 *lmf = (*lmf)->previous_lmf;
359
360                 return res;
361         }
362 }
363
364 gboolean
365 mono_arch_has_unwind_info (gconstpointer addr)
366 {
367         return FALSE;
368 }
369
370 static MonoArray *
371 glist_to_array (GList *list) 
372 {
373         MonoDomain *domain = mono_domain_get ();
374         MonoArray *res;
375         int len, i;
376
377         if (!list)
378                 return NULL;
379
380         len = g_list_length (list);
381         res = mono_array_new (domain, mono_defaults.int_class, len);
382
383         for (i = 0; list; list = list->next, i++)
384                 mono_array_set (res, gpointer, i, list->data);
385
386         return res;
387 }
388
389 MonoArray *
390 ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
391 {
392         return NULL;
393 }
394
395 void
396 mono_jit_walk_stack (MonoStackWalk func, gpointer user_data)
397 {
398         MonoDomain *domain = mono_domain_get ();
399         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
400         MonoLMF *lmf = jit_tls->lmf;
401         MonoJitInfo *ji, rji;
402         gint native_offset, il_offset;
403         gboolean managed;
404
405         MonoContext ctx, new_ctx;
406
407         mono_sparc_flushw ();
408
409         MONO_CONTEXT_SET_IP (&ctx, __builtin_return_address (0));
410         MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (0));
411
412         while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) {
413                 
414                 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
415                 g_assert (ji);
416
417                 if (ji == (gpointer)-1)
418                         return;
419
420                 il_offset = mono_debug_il_offset_from_address (ji->method, native_offset, domain);
421
422                 if (func (ji->method, native_offset, il_offset, managed, user_data))
423                         return;
424                 
425                 ctx = new_ctx;
426         }
427 }
428
429 MonoBoolean
430 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, 
431                           MonoReflectionMethod **method, 
432                           gint32 *iloffset, gint32 *native_offset,
433                           MonoString **file, gint32 *line, gint32 *column)
434 {
435         return FALSE;
436 }
437
438 /**
439  * mono_sparc_handle_exception:
440  * @ctx: saved processor state
441  * @obj: the exception object
442  * @test_only: only test if the exception is caught, but dont call handlers
443  *
444  *
445  */
446 gboolean
447 mono_sparc_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
448 {
449         MonoDomain *domain = mono_domain_get ();
450         MonoJitInfo *ji, rji;
451         static int (*call_filter) (MonoContext *, gpointer) = NULL;
452         static void (*restore_context) (MonoContext *);
453         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
454         MonoLMF *lmf = jit_tls->lmf;            
455         GList *trace_ips = NULL;
456         MonoException *mono_ex;
457         gboolean stack_overflow = FALSE;
458         MonoContext initial_ctx;
459         int frame_count = 0;
460         gboolean gc_disabled = FALSE;
461         MonoString *initial_stack_trace;
462
463         /*
464          * This function might execute on an alternate signal stack, and Boehm GC
465          * can't handle that.
466          * Also, since the altstack is small, stack space intensive operations like
467          * JIT compilation should be avoided.
468          */
469         if (IS_ON_SIGALTSTACK (jit_tls)) {
470                 /* 
471                  * FIXME: disabling/enabling GC while already on a signal stack might
472                  * not be safe either.
473                  */
474                 /* Have to reenable it later */
475                 gc_disabled = TRUE;
476                 mono_gc_disable ();
477         }
478
479         g_assert (ctx != NULL);
480         if (!obj) {
481                 MonoException *ex = mono_get_exception_null_reference ();
482                 ex->message = mono_string_new (domain, "Object reference not set to an instance of an object");
483                 obj = (MonoObject *)ex;
484         } 
485
486         if (mono_object_isinst (obj, mono_defaults.exception_class)) {
487                 mono_ex = (MonoException*)obj;
488                 mono_ex->stack_trace = NULL;
489                 initial_stack_trace = mono_ex->stack_trace;
490         } else {
491                 mono_ex = NULL;
492         }
493
494         //printf ("HANDLING EXCEPTION: %s\n", ((MonoObject*)obj)->vtable->klass->name);
495         
496         if (obj == domain->stack_overflow_ex)
497                 stack_overflow = TRUE;
498
499         if (!call_filter)
500                 call_filter = arch_get_call_filter ();
501
502         if (!restore_context)
503                 restore_context = arch_get_restore_context ();
504
505         g_assert (jit_tls->end_of_stack);
506         g_assert (jit_tls->abort_func);
507
508         if (!test_only) {
509                 MonoContext ctx_cp = *ctx;
510                 if (mono_jit_trace_calls != NULL)
511                         g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name);
512                 if (!mono_sparc_handle_exception (&ctx_cp, obj, TRUE)) {
513                         if (mono_break_on_exc)
514                                 G_BREAKPOINT ();
515                         mono_unhandled_exception (obj);
516                 }
517         }
518
519         initial_ctx = *ctx;
520         memset (&rji, 0, sizeof (rji));
521
522         while (1) {
523                 MonoContext new_ctx;
524                 char *trace = NULL;
525                 gboolean need_trace = FALSE;
526                 guint32 free_stack;
527
528                 if (test_only && (frame_count < 1000))
529                         need_trace = TRUE;
530
531                 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &rji, ctx, &new_ctx, 
532                                               need_trace ? &trace : NULL, &lmf, NULL, NULL);
533                 if (!ji) {
534                         g_warning ("Exception inside function without unwind info");
535                         g_assert_not_reached ();
536                 }
537
538                 //printf ("JI: %p IP: %p SP: %p.\n", ji, (gpointer)new_ctx.ip, new_ctx.sp);
539
540                 if (ji != (gpointer)-1) {
541                         frame_count ++;
542                         //printf ("M: %s %p %p %d.\n", mono_method_full_name (ji->method, TRUE), jit_tls->end_of_stack, ctx->ebp, count);
543
544                         if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE && mono_ex) {
545                                 char *tmp, *strace;
546
547                                 /* 
548                                  * Avoid overwriting the stack trace if the exception is
549                                  * rethrown. Also avoid giant stack traces during a stack
550                                  * overflow.
551                                  */
552                                 if (!initial_stack_trace && (frame_count < 1000)) {
553                                         trace_ips = g_list_append (trace_ips, MONO_CONTEXT_GET_IP (ctx));
554
555                                         if (!mono_ex->stack_trace)
556                                                 strace = g_strdup ("");
557                                         else
558                                                 strace = mono_string_to_utf8 (mono_ex->stack_trace);
559                         
560                                         tmp = g_strdup_printf ("%s%s\n", strace, trace);
561                                         g_free (strace);
562                                         
563                                         mono_ex->stack_trace = mono_string_new (domain, tmp);
564
565                                         g_free (tmp);
566                                 }
567                         }
568
569 /*
570                         if (stack_overflow)
571                                 free_stack = (guint8*)(MONO_CONTEXT_GET_BP (ctx)) - (guint8*)(MONO_CONTEXT_GET_BP (&initial_ctx));
572                         else
573                                 free_stack = 0xffffff;
574 */
575                         free_stack = 0xffffff;
576
577                         /* 
578                          * During stack overflow, wait till the unwinding frees some stack
579                          * space before running handlers/finalizers.
580                          */
581                         if ((free_stack > (64 * 1024)) && ji->num_clauses) {
582                                 int i;
583                                 
584                                 g_assert (ji->clauses);
585                         
586                                 for (i = 0; i < ji->num_clauses; i++) {
587                                         MonoJitExceptionInfo *ei = &ji->clauses [i];
588
589                                         if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
590                                             MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) { 
591                                                 /* catch block */
592
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;
597                                                 }
598
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)))) {
603                                                         if (test_only) {
604                                                                 if (mono_ex)
605                                                                         mono_ex->trace_ips = glist_to_array (trace_ips);
606                                                                 g_list_free (trace_ips);
607                                                                 g_free (trace);
608
609                                                                 if (gc_disabled)
610                                                                         mono_gc_enable ();
611                                                                 return TRUE;
612                                                         }
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);
616                                                         jit_tls->lmf = lmf;
617                                                         g_free (trace);
618
619                                                         if (gc_disabled)
620                                                                 mono_gc_enable ();
621                                                         return 0;
622                                                 }
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);
629                                                 }
630                                                 
631                                         }
632                                 }
633                         }
634                 }
635
636                 g_free (trace);
637                         
638                 *ctx = new_ctx;
639
640                 if ((ji == (gpointer)-1) || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) {
641                         if (gc_disabled)
642                                 mono_gc_enable ();
643
644                         if (!test_only) {
645                                 jit_tls->lmf = lmf;
646
647                                 if (IS_ON_SIGALTSTACK (jit_tls)) {
648                                         /* Switch back to normal stack */
649                                         if (stack_overflow)
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);
654                                 }
655                                 else
656                                         jit_tls->abort_func (obj);
657                                 g_assert_not_reached ();
658                         } else {
659                                 if (mono_ex)
660                                         mono_ex->trace_ips = glist_to_array (trace_ips);
661                                 g_list_free (trace_ips);
662                                 return FALSE;
663                         }
664                 }
665         }
666
667         g_assert_not_reached ();
668 }
669
670 gboolean
671 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
672 {
673         MonoContext mctx;
674         ucontext_t *ctx = (ucontext_t*)sigctx;
675
676         /*
677          * Access to the machine state using the ucontext_t parameter is somewhat
678          * under documented under solaris. The code below seems to work under
679          * Solaris 9.
680          */
681         g_assert (!ctx->uc_mcontext.gwins);
682
683         mctx.ip = ctx->uc_mcontext.gregs [REG_PC];
684         mctx.sp = ctx->uc_mcontext.gregs [REG_SP];
685         mctx.fp = mctx.sp [sparc_fp - 16];
686
687         mono_sparc_handle_exception (&mctx, obj, test_only);
688         
689         /* We can't use restore_context to return from a signal handler */
690         ctx->uc_mcontext.gregs [REG_PC] = mctx.ip;
691         ctx->uc_mcontext.gregs [REG_nPC] = mctx.ip + 4;
692         ctx->uc_mcontext.gregs [REG_SP] = mctx.sp;
693         mctx.sp [sparc_fp - 16] = mctx.fp;
694
695         return TRUE;
696 }