2009-08-11 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mono / mini / exceptions-sparc.c
1 /*
2  * exceptions-sparc.c: exception support for 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 #include <mono/metadata/tokentype.h>
26
27 #include "mini.h"
28 #include "mini-sparc.h"
29
30 #ifndef REG_SP
31 #define REG_SP REG_O6
32 #endif
33
34 #define MONO_SPARC_WINDOW_ADDR(sp) ((gpointer*)(((guint8*)(sp)) + MONO_SPARC_STACK_BIAS))
35
36 /*
37  * mono_arch_get_restore_context:
38  *
39  * Returns a pointer to a method which restores a previously saved sigcontext.
40  */
41 gpointer
42 mono_arch_get_restore_context (void)
43 {
44         static guint32 *start;
45         static int inited = 0;
46         guint32 *code;
47
48         if (inited)
49                 return start;
50
51         code = start = mono_global_codeman_reserve (32 * sizeof (guint32));
52
53         sparc_ldi_imm (code, sparc_o0, G_STRUCT_OFFSET (MonoContext, ip), sparc_i7);
54         sparc_ldi_imm (code, sparc_o0, G_STRUCT_OFFSET (MonoContext, sp), sparc_i6);
55
56         sparc_jmpl_imm (code, sparc_i7, 0, sparc_g0);
57         /* FIXME: This does not return to the correct window */
58         sparc_restore_imm (code, sparc_g0, 0, sparc_g0);
59
60         g_assert ((code - start) < 32);
61
62         mono_arch_flush_icache ((guint8*)start, (guint8*)code - (guint8*)start);
63
64         inited = 1;
65
66         return start;
67 }
68
69 /*
70  * mono_arch_get_call_filter:
71  *
72  * Returns a pointer to a method which calls an exception filter. We
73  * also use this function to call finally handlers (we pass NULL as 
74  * @exc object in this case).
75  *
76  * call_filter (MonoContext *ctx, gpointer ip)
77  */
78 gpointer
79 mono_arch_get_call_filter (void)
80 {
81         static guint32 *start;
82         static int inited = 0;
83         guint32 *code;
84         int i;
85
86         if (inited)
87                 return start;
88
89         code = start = mono_global_codeman_reserve (64 * sizeof (guint32));
90
91         /*
92          * There are two frames here:
93          * - the first frame is used by call_filter
94          * - the second frame is used to run the filter code
95          */
96
97         /* Create first frame */
98         sparc_save_imm (code, sparc_sp, -256, sparc_sp);
99
100         sparc_mov_reg_reg (code, sparc_i1, sparc_o0);
101         sparc_ldi_imm (code, sparc_i0, G_STRUCT_OFFSET (MonoContext, sp), sparc_o1);
102
103         /* Create second frame */
104         sparc_save_imm (code, sparc_sp, -256, sparc_sp);
105
106         sparc_mov_reg_reg (code, sparc_i0, sparc_o0);
107         sparc_mov_reg_reg (code, sparc_i1, sparc_o1);
108
109         /*
110          * We need to change %fp to point to the stack frame of the method
111          * containing the filter. But changing %fp also changes the %sp of
112          * the parent frame (the first frame), so if the OS saves the first frame,
113          * it saves it to the stack frame of the method, which is not good.
114          * So flush all register windows to memory before changing %fp.
115          */
116         sparc_flushw (code);
117
118         sparc_mov_reg_reg (code, sparc_fp, sparc_o7);
119
120         /* 
121          * Modify the second frame so it is identical to the one used in the
122          * method containing the filter.
123          */
124         for (i = 0; i < 16; ++i)
125                 sparc_ldi_imm (code, sparc_o1, MONO_SPARC_STACK_BIAS + i * sizeof (gpointer), sparc_l0 + i);
126
127         /* Save %fp to a location reserved in mono_arch_allocate_vars */
128         sparc_sti_imm (code, sparc_o7, sparc_fp, MONO_SPARC_STACK_BIAS - sizeof (gpointer));
129
130         /* Call the filter code, after this returns, %o0 will hold the result */
131         sparc_call_imm (code, sparc_o0, 0);
132         sparc_nop (code);
133
134         /* Restore original %fp */
135         sparc_ldi_imm (code, sparc_fp, MONO_SPARC_STACK_BIAS - sizeof (gpointer), sparc_fp);
136
137         sparc_mov_reg_reg (code, sparc_o0, sparc_i0);
138
139         /* Return to first frame */
140         sparc_restore (code, sparc_g0, sparc_g0, sparc_g0);
141
142         /* FIXME: Save locals to the stack */
143
144         /* Return to caller */
145         sparc_ret (code);
146         /* Return result in delay slot */
147         sparc_restore (code, sparc_o0, sparc_g0, sparc_o0);
148
149         g_assert ((code - start) < 64);
150
151         mono_arch_flush_icache ((guint8*)start, (guint8*)code - (guint8*)start);
152
153         inited = 1;
154
155         return start;
156 }
157
158 static void
159 throw_exception (MonoObject *exc, gpointer sp, gpointer ip, gboolean rethrow)
160 {
161         MonoContext ctx;
162         static void (*restore_context) (MonoContext *);
163         gpointer *window;
164         
165         if (!restore_context)
166                 restore_context = mono_arch_get_restore_context ();
167
168         window = MONO_SPARC_WINDOW_ADDR (sp);
169         ctx.sp = (gpointer*)sp;
170         ctx.ip = ip;
171         ctx.fp = (gpointer*)(MONO_SPARC_WINDOW_ADDR (sp) [sparc_i6 - 16]);
172
173         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
174                 MonoException *mono_ex = (MonoException*)exc;
175                 if (!rethrow)
176                         mono_ex->stack_trace = NULL;
177         }
178         mono_handle_exception (&ctx, exc, ip, FALSE);
179         restore_context (&ctx);
180
181         g_assert_not_reached ();
182 }
183
184 static gpointer 
185 get_throw_exception (gboolean rethrow)
186 {
187         guint32 *start, *code;
188
189         code = start = mono_global_codeman_reserve (16 * sizeof (guint32));
190
191         sparc_save_imm (code, sparc_sp, -512, sparc_sp);
192
193         sparc_flushw (code);
194         sparc_mov_reg_reg (code, sparc_i0, sparc_o0);
195         sparc_mov_reg_reg (code, sparc_fp, sparc_o1);
196         sparc_mov_reg_reg (code, sparc_i7, sparc_o2);
197         sparc_set (code, rethrow, sparc_o3);
198         sparc_set (code, throw_exception, sparc_o7);
199         sparc_jmpl (code, sparc_o7, sparc_g0, sparc_callsite);
200         sparc_nop (code);
201
202         g_assert ((code - start) <= 16);
203
204         mono_arch_flush_icache ((guint8*)start, (guint8*)code - (guint8*)start);
205
206         return start;
207 }
208
209 /**
210  * mono_arch_get_throw_exception:
211  *
212  * Returns a function pointer which can be used to raise exceptions.
213  * The returned function has the following 
214  * signature: void (*func) (MonoException *exc); 
215  */
216 gpointer 
217 mono_arch_get_throw_exception (void)
218 {
219         static guint32* start;
220         static int inited = 0;
221
222         if (inited)
223                 return start;
224
225         inited = 1;
226
227         start = get_throw_exception (FALSE);
228
229         return start;
230 }
231
232 gpointer 
233 mono_arch_get_rethrow_exception (void)
234 {
235         static guint32* start;
236         static int inited = 0;
237
238         if (inited)
239                 return start;
240
241         inited = 1;
242
243         start = get_throw_exception (TRUE);
244
245         return start;
246 }
247
248 /**
249  * mono_arch_get_throw_exception_by_name:
250  *
251  * Returns a function pointer which can be used to raise 
252  * corlib exceptions. The returned function has the following 
253  * signature: void (*func) (char *exc_name, gpointer ip); 
254  */
255 gpointer 
256 mono_arch_get_throw_exception_by_name (void)
257 {
258         static guint32 *start;
259         static int inited = 0;
260         guint32 *code;
261         int reg;
262
263         if (inited)
264                 return start;
265
266         inited = 1;
267         code = start = mono_global_codeman_reserve (64 * sizeof (guint32));
268
269 #ifdef SPARCV9
270         reg = sparc_g4;
271 #else
272         reg = sparc_g1;
273 #endif
274
275         sparc_save_imm (code, sparc_sp, -160, sparc_sp);
276
277         sparc_mov_reg_reg (code, sparc_i0, sparc_o2);
278         sparc_set (code, mono_defaults.corlib, sparc_o0);
279         sparc_set (code, "System", sparc_o1);
280         sparc_set (code, mono_exception_from_name, sparc_o7);
281         sparc_jmpl (code, sparc_o7, sparc_g0, sparc_callsite);
282         sparc_nop (code);
283
284         /* Return to the caller, so exception handling does not see this frame */
285         sparc_restore (code, sparc_o0, sparc_g0, sparc_o0);
286
287         /* Put original return address into %o7 */
288         sparc_mov_reg_reg (code, sparc_o1, sparc_o7);
289         sparc_set (code, mono_arch_get_throw_exception (), reg);
290         /* Use a jmp instead of a call so o7 is preserved */
291         sparc_jmpl_imm (code, reg, 0, sparc_g0);
292         sparc_nop (code);
293
294         g_assert ((code - start) < 32);
295
296         mono_arch_flush_icache ((guint8*)start, (guint8*)code - (guint8*)start);
297
298         return start;
299 }
300
301 /**
302  * mono_arch_get_throw_corlib_exception:
303  *
304  * Returns a function pointer which can be used to raise 
305  * corlib exceptions. The returned function has the following 
306  * signature: void (*func) (guint32 ex_token, guint32 offset); 
307  * Here, offset is the offset which needs to be substracted from the caller IP 
308  * to get the IP of the throw. Passing the offset has the advantage that it 
309  * needs no relocations in the caller.
310  */
311 gpointer 
312 mono_arch_get_throw_corlib_exception (void)
313 {
314         static guint32 *start;
315         static int inited = 0;
316         guint32 *code;
317         int reg;
318
319         if (inited)
320                 return start;
321
322         inited = 1;
323         code = start = mono_global_codeman_reserve (64 * sizeof (guint32));
324
325 #ifdef SPARCV9
326         reg = sparc_g4;
327 #else
328         reg = sparc_g1;
329 #endif
330
331         sparc_mov_reg_reg (code, sparc_o7, sparc_o2);
332         sparc_save_imm (code, sparc_sp, -160, sparc_sp);
333
334         sparc_set (code, MONO_TOKEN_TYPE_DEF, sparc_o7);
335         sparc_add (code, FALSE, sparc_i0, sparc_o7, sparc_o1);
336         sparc_set (code, mono_defaults.exception_class->image, sparc_o0);
337         sparc_set (code, mono_exception_from_token, sparc_o7);
338         sparc_jmpl (code, sparc_o7, sparc_g0, sparc_callsite);
339         sparc_nop (code);
340
341         /* Return to the caller, so exception handling does not see this frame */
342         sparc_restore (code, sparc_o0, sparc_g0, sparc_o0);
343
344         /* Compute throw ip */
345         sparc_sll_imm (code, sparc_o1, 2, sparc_o1);
346         sparc_sub (code, 0, sparc_o2, sparc_o1, sparc_o7);
347
348         sparc_set (code, mono_arch_get_throw_exception (), reg);
349         /* Use a jmp instead of a call so o7 is preserved */
350         sparc_jmpl_imm (code, reg, 0, sparc_g0);
351         sparc_nop (code);
352
353         g_assert ((code - start) < 32);
354
355         mono_arch_flush_icache ((guint8*)start, (guint8*)code - (guint8*)start);
356
357         return start;
358 }
359
360 /* mono_arch_find_jit_info:
361  *
362  * This function is used to gather information from @ctx. It return the 
363  * MonoJitInfo of the corresponding function, unwinds one stack frame and
364  * stores the resulting context into @new_ctx. It also stores a string 
365  * describing the stack location into @trace (if not NULL), and modifies
366  * the @lmf if necessary. @native_offset return the IP offset from the 
367  * start of the function or -1 if that info is not available.
368  */
369 MonoJitInfo *
370 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
371                          MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed)
372 {
373         MonoJitInfo *ji;
374         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
375         gpointer *window;
376
377         /* Avoid costly table lookup during stack overflow */
378         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
379                 ji = prev_ji;
380         else
381                 ji = mono_jit_info_table_find (domain, ip);
382
383         if (managed)
384                 *managed = FALSE;
385
386         if (ji != NULL) {
387                 *new_ctx = *ctx;
388
389                 if (managed)
390                         if (!ji->method->wrapper_type)
391                                 *managed = TRUE;
392
393                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
394                         /* remove any unused lmf */
395                         *lmf = (*lmf)->previous_lmf;
396                 }
397
398                 /* Restore ip and sp from the saved register window */
399                 window = MONO_SPARC_WINDOW_ADDR (ctx->sp);
400                 new_ctx->ip = window [sparc_i7 - 16];
401                 new_ctx->sp = (gpointer*)(window [sparc_i6 - 16]);
402                 new_ctx->fp = (gpointer*)(MONO_SPARC_WINDOW_ADDR (new_ctx->sp) [sparc_i6 - 16]);
403
404                 return ji;
405         }
406         else {
407                 if (!(*lmf))
408                         return NULL;
409
410                 *new_ctx = *ctx;
411
412                 if (!(*lmf)->method)
413                         return (gpointer)-1;
414
415                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->ip))) {
416                 } else {
417                         memset (res, 0, MONO_SIZEOF_JIT_INFO);
418                         res->method = (*lmf)->method;
419                 }
420
421                 new_ctx->ip = (*lmf)->ip;
422                 new_ctx->sp = (*lmf)->sp;
423                 new_ctx->fp = (*lmf)->ebp;
424
425                 *lmf = (*lmf)->previous_lmf;
426
427                 return ji ? ji : res;
428         }
429 }
430
431 gboolean
432 mono_arch_has_unwind_info (gconstpointer addr)
433 {
434         return FALSE;
435 }
436
437 #ifdef __linux__
438
439 gboolean
440 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
441 {
442        MonoContext mctx;
443        struct sigcontext *sc = sigctx;
444        gpointer *window;
445
446 #ifdef SPARCV9
447        mctx.ip = (gpointer) sc->sigc_regs.tpc;
448        mctx.sp = (gpointer) sc->sigc_regs.u_regs[14];
449 #else
450        mctx.ip = (gpointer) sc->si_regs.pc;
451        mctx.sp = (gpointer) sc->si_regs.u_regs[14];
452 #endif
453
454        window = (gpointer*)(((guint8*)mctx.sp) + MONO_SPARC_STACK_BIAS);
455        mctx.fp = window [sparc_fp - 16];
456
457        mono_handle_exception (&mctx, obj, mctx.ip, test_only);
458
459 #ifdef SPARCV9
460        sc->sigc_regs.tpc = (unsigned long) mctx.ip;
461        sc->sigc_regs.tnpc = (unsigned long) (mctx.ip + 4);
462        sc->sigc_regs.u_regs[14] = (unsigned long) mctx.sp;
463 #else
464        sc->si_regs.pc = (unsigned long) mctx.ip;
465        sc->si_regs.npc = (unsigned long) (mctx.ip + 4);
466        sc->si_regs.u_regs[14] = (unsigned long) mctx.sp;
467 #endif
468
469        window = (gpointer*)(((guint8*)mctx.sp) + MONO_SPARC_STACK_BIAS);
470        window [sparc_fp - 16] = mctx.fp;
471
472        return TRUE;
473 }
474
475 gpointer
476 mono_arch_ip_from_context (void *sigctx)
477 {
478        struct sigcontext *sc = sigctx;
479        gpointer *ret;
480
481 #ifdef SPARCV9
482        ret = (gpointer) sc->sigc_regs.tpc;
483 #else
484        ret = (gpointer) sc->si_regs.pc;
485 #endif
486
487        return ret;
488 }
489
490 #else /* !__linux__ */
491
492 gboolean
493 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
494 {
495         MonoContext mctx;
496         ucontext_t *ctx = (ucontext_t*)sigctx;
497         gpointer *window;
498
499         /*
500          * Access to the machine state using the ucontext_t parameter is somewhat
501          * under documented under solaris. The code below seems to work under
502          * Solaris 9.
503          */
504         g_assert (!ctx->uc_mcontext.gwins);
505
506         mctx.ip = ctx->uc_mcontext.gregs [REG_PC];
507         mctx.sp = ctx->uc_mcontext.gregs [REG_SP];
508         window = (gpointer*)(((guint8*)mctx.sp) + MONO_SPARC_STACK_BIAS);
509         mctx.fp = window [sparc_fp - 16];
510
511         mono_handle_exception (&mctx, obj, mctx.ip, test_only);
512         
513         /* We can't use restore_context to return from a signal handler */
514         ctx->uc_mcontext.gregs [REG_PC] = mctx.ip;
515         ctx->uc_mcontext.gregs [REG_nPC] = mctx.ip + 4;
516         ctx->uc_mcontext.gregs [REG_SP] = mctx.sp;
517         window = (gpointer*)(((guint8*)mctx.sp) + MONO_SPARC_STACK_BIAS);
518         window [sparc_fp - 16] = mctx.fp;
519
520         return TRUE;
521 }
522
523 gpointer
524 mono_arch_ip_from_context (void *sigctx)
525 {
526         ucontext_t *ctx = (ucontext_t*)sigctx;
527         return (gpointer)ctx->uc_mcontext.gregs [REG_PC];
528 }
529
530 #endif