2010-01-20 Zoltan Varga <vargaz@gmail.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_corlib_exception:
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) (guint32 ex_token, guint32 offset); 
254  * Here, offset is the offset which needs to be substracted from the caller IP 
255  * to get the IP of the throw. Passing the offset has the advantage that it 
256  * needs no relocations in the caller.
257  */
258 gpointer 
259 mono_arch_get_throw_corlib_exception (void)
260 {
261         static guint32 *start;
262         static int inited = 0;
263         guint32 *code;
264         int reg;
265
266         if (inited)
267                 return start;
268
269         inited = 1;
270         code = start = mono_global_codeman_reserve (64 * sizeof (guint32));
271
272 #ifdef SPARCV9
273         reg = sparc_g4;
274 #else
275         reg = sparc_g1;
276 #endif
277
278         sparc_mov_reg_reg (code, sparc_o7, sparc_o2);
279         sparc_save_imm (code, sparc_sp, -160, sparc_sp);
280
281         sparc_set (code, MONO_TOKEN_TYPE_DEF, sparc_o7);
282         sparc_add (code, FALSE, sparc_i0, sparc_o7, sparc_o1);
283         sparc_set (code, mono_defaults.exception_class->image, sparc_o0);
284         sparc_set (code, mono_exception_from_token, sparc_o7);
285         sparc_jmpl (code, sparc_o7, sparc_g0, sparc_callsite);
286         sparc_nop (code);
287
288         /* Return to the caller, so exception handling does not see this frame */
289         sparc_restore (code, sparc_o0, sparc_g0, sparc_o0);
290
291         /* Compute throw ip */
292         sparc_sll_imm (code, sparc_o1, 2, sparc_o1);
293         sparc_sub (code, 0, sparc_o2, sparc_o1, sparc_o7);
294
295         sparc_set (code, mono_arch_get_throw_exception (), reg);
296         /* Use a jmp instead of a call so o7 is preserved */
297         sparc_jmpl_imm (code, reg, 0, sparc_g0);
298         sparc_nop (code);
299
300         g_assert ((code - start) < 32);
301
302         mono_arch_flush_icache ((guint8*)start, (guint8*)code - (guint8*)start);
303
304         return start;
305 }
306
307 /* mono_arch_find_jit_info:
308  *
309  * This function is used to gather information from @ctx. It return the 
310  * MonoJitInfo of the corresponding function, unwinds one stack frame and
311  * stores the resulting context into @new_ctx. It also stores a string 
312  * describing the stack location into @trace (if not NULL), and modifies
313  * the @lmf if necessary. @native_offset return the IP offset from the 
314  * start of the function or -1 if that info is not available.
315  */
316 MonoJitInfo *
317 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
318                          MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed)
319 {
320         MonoJitInfo *ji;
321         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
322         gpointer *window;
323
324         /* Avoid costly table lookup during stack overflow */
325         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
326                 ji = prev_ji;
327         else
328                 ji = mini_jit_info_table_find (domain, ip, NULL);
329
330         if (managed)
331                 *managed = FALSE;
332
333         if (ji != NULL) {
334                 *new_ctx = *ctx;
335
336                 if (managed)
337                         if (!ji->method->wrapper_type)
338                                 *managed = TRUE;
339
340                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
341                         /* remove any unused lmf */
342                         *lmf = (*lmf)->previous_lmf;
343                 }
344
345                 /* Restore ip and sp from the saved register window */
346                 window = MONO_SPARC_WINDOW_ADDR (ctx->sp);
347                 new_ctx->ip = window [sparc_i7 - 16];
348                 new_ctx->sp = (gpointer*)(window [sparc_i6 - 16]);
349                 new_ctx->fp = (gpointer*)(MONO_SPARC_WINDOW_ADDR (new_ctx->sp) [sparc_i6 - 16]);
350
351                 return ji;
352         }
353         else {
354                 if (!(*lmf))
355                         return NULL;
356
357                 *new_ctx = *ctx;
358
359                 if (!(*lmf)->method)
360                         return (gpointer)-1;
361
362                 if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->ip, NULL))) {
363                 } else {
364                         memset (res, 0, MONO_SIZEOF_JIT_INFO);
365                         res->method = (*lmf)->method;
366                 }
367
368                 new_ctx->ip = (*lmf)->ip;
369                 new_ctx->sp = (*lmf)->sp;
370                 new_ctx->fp = (*lmf)->ebp;
371
372                 *lmf = (*lmf)->previous_lmf;
373
374                 return ji ? ji : res;
375         }
376 }
377
378 gboolean
379 mono_arch_has_unwind_info (gconstpointer addr)
380 {
381         return FALSE;
382 }
383
384 #ifdef __linux__
385
386 gboolean
387 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
388 {
389        MonoContext mctx;
390        struct sigcontext *sc = sigctx;
391        gpointer *window;
392
393 #ifdef SPARCV9
394        mctx.ip = (gpointer) sc->sigc_regs.tpc;
395        mctx.sp = (gpointer) sc->sigc_regs.u_regs[14];
396 #else
397        mctx.ip = (gpointer) sc->si_regs.pc;
398        mctx.sp = (gpointer) sc->si_regs.u_regs[14];
399 #endif
400
401        window = (gpointer*)(((guint8*)mctx.sp) + MONO_SPARC_STACK_BIAS);
402        mctx.fp = window [sparc_fp - 16];
403
404        mono_handle_exception (&mctx, obj, mctx.ip, test_only);
405
406 #ifdef SPARCV9
407        sc->sigc_regs.tpc = (unsigned long) mctx.ip;
408        sc->sigc_regs.tnpc = (unsigned long) (mctx.ip + 4);
409        sc->sigc_regs.u_regs[14] = (unsigned long) mctx.sp;
410 #else
411        sc->si_regs.pc = (unsigned long) mctx.ip;
412        sc->si_regs.npc = (unsigned long) (mctx.ip + 4);
413        sc->si_regs.u_regs[14] = (unsigned long) mctx.sp;
414 #endif
415
416        window = (gpointer*)(((guint8*)mctx.sp) + MONO_SPARC_STACK_BIAS);
417        window [sparc_fp - 16] = mctx.fp;
418
419        return TRUE;
420 }
421
422 gpointer
423 mono_arch_ip_from_context (void *sigctx)
424 {
425        struct sigcontext *sc = sigctx;
426        gpointer *ret;
427
428 #ifdef SPARCV9
429        ret = (gpointer) sc->sigc_regs.tpc;
430 #else
431        ret = (gpointer) sc->si_regs.pc;
432 #endif
433
434        return ret;
435 }
436
437 #else /* !__linux__ */
438
439 gboolean
440 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
441 {
442         MonoContext mctx;
443         ucontext_t *ctx = (ucontext_t*)sigctx;
444         gpointer *window;
445
446         /*
447          * Access to the machine state using the ucontext_t parameter is somewhat
448          * under documented under solaris. The code below seems to work under
449          * Solaris 9.
450          */
451         g_assert (!ctx->uc_mcontext.gwins);
452
453         mctx.ip = ctx->uc_mcontext.gregs [REG_PC];
454         mctx.sp = ctx->uc_mcontext.gregs [REG_SP];
455         window = (gpointer*)(((guint8*)mctx.sp) + MONO_SPARC_STACK_BIAS);
456         mctx.fp = window [sparc_fp - 16];
457
458         mono_handle_exception (&mctx, obj, mctx.ip, test_only);
459         
460         /* We can't use restore_context to return from a signal handler */
461         ctx->uc_mcontext.gregs [REG_PC] = mctx.ip;
462         ctx->uc_mcontext.gregs [REG_nPC] = mctx.ip + 4;
463         ctx->uc_mcontext.gregs [REG_SP] = mctx.sp;
464         window = (gpointer*)(((guint8*)mctx.sp) + MONO_SPARC_STACK_BIAS);
465         window [sparc_fp - 16] = mctx.fp;
466
467         return TRUE;
468 }
469
470 gpointer
471 mono_arch_ip_from_context (void *sigctx)
472 {
473         ucontext_t *ctx = (ucontext_t*)sigctx;
474         return (gpointer)ctx->uc_mcontext.gregs [REG_PC];
475 }
476
477 #endif