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