Fix problems with overlong directory names: phase #1
[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, char **trace, MonoLMF **lmf, int *native_offset,
372                          gboolean *managed)
373 {
374         MonoJitInfo *ji;
375         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
376         gpointer *window;
377
378         /* Avoid costly table lookup during stack overflow */
379         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
380                 ji = prev_ji;
381         else
382                 ji = mono_jit_info_table_find (domain, ip);
383
384         if (managed)
385                 *managed = FALSE;
386
387         if (ji != NULL) {
388                 *new_ctx = *ctx;
389
390                 if (managed)
391                         if (!ji->method->wrapper_type)
392                                 *managed = TRUE;
393
394                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
395                         /* remove any unused lmf */
396                         *lmf = (*lmf)->previous_lmf;
397                 }
398
399                 /* Restore ip and sp from the saved register window */
400                 window = MONO_SPARC_WINDOW_ADDR (ctx->sp);
401                 new_ctx->ip = window [sparc_i7 - 16];
402                 new_ctx->sp = (gpointer*)(window [sparc_i6 - 16]);
403                 new_ctx->fp = (gpointer*)(MONO_SPARC_WINDOW_ADDR (new_ctx->sp) [sparc_i6 - 16]);
404
405                 return ji;
406         }
407         else {
408                 if (!(*lmf))
409                         return NULL;
410
411                 *new_ctx = *ctx;
412
413                 if (!(*lmf)->method)
414                         return (gpointer)-1;
415
416                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->ip))) {
417                 } else {
418                         memset (res, 0, sizeof (MonoJitInfo));
419                         res->method = (*lmf)->method;
420                 }
421
422                 new_ctx->ip = (*lmf)->ip;
423                 new_ctx->sp = (*lmf)->sp;
424                 new_ctx->fp = (*lmf)->ebp;
425
426                 *lmf = (*lmf)->previous_lmf;
427
428                 return ji ? ji : res;
429         }
430 }
431
432 gboolean
433 mono_arch_has_unwind_info (gconstpointer addr)
434 {
435         return FALSE;
436 }
437
438 #ifdef __linux__
439
440 gboolean
441 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
442 {
443        MonoContext mctx;
444        struct sigcontext *sc = sigctx;
445        gpointer *window;
446
447 #ifdef SPARCV9
448        mctx.ip = (gpointer) sc->sigc_regs.tpc;
449        mctx.sp = (gpointer) sc->sigc_regs.u_regs[14];
450 #else
451        mctx.ip = (gpointer) sc->si_regs.pc;
452        mctx.sp = (gpointer) sc->si_regs.u_regs[14];
453 #endif
454
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 #ifdef SPARCV9
461        sc->sigc_regs.tpc = (unsigned long) mctx.ip;
462        sc->sigc_regs.tnpc = (unsigned long) (mctx.ip + 4);
463        sc->sigc_regs.u_regs[14] = (unsigned long) mctx.sp;
464 #else
465        sc->si_regs.pc = (unsigned long) mctx.ip;
466        sc->si_regs.npc = (unsigned long) (mctx.ip + 4);
467        sc->si_regs.u_regs[14] = (unsigned long) mctx.sp;
468 #endif
469
470        window = (gpointer*)(((guint8*)mctx.sp) + MONO_SPARC_STACK_BIAS);
471        window [sparc_fp - 16] = mctx.fp;
472
473        return TRUE;
474 }
475
476 gpointer
477 mono_arch_ip_from_context (void *sigctx)
478 {
479        struct sigcontext *sc = sigctx;
480        gpointer *ret;
481
482 #ifdef SPARCV9
483        ret = (gpointer) sc->sigc_regs.tpc;
484 #else
485        ret = (gpointer) sc->si_regs.pc;
486 #endif
487
488        return ret;
489 }
490
491 #else /* !__linux__ */
492
493 gboolean
494 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
495 {
496         MonoContext mctx;
497         ucontext_t *ctx = (ucontext_t*)sigctx;
498         gpointer *window;
499
500         /*
501          * Access to the machine state using the ucontext_t parameter is somewhat
502          * under documented under solaris. The code below seems to work under
503          * Solaris 9.
504          */
505         g_assert (!ctx->uc_mcontext.gwins);
506
507         mctx.ip = ctx->uc_mcontext.gregs [REG_PC];
508         mctx.sp = ctx->uc_mcontext.gregs [REG_SP];
509         window = (gpointer*)(((guint8*)mctx.sp) + MONO_SPARC_STACK_BIAS);
510         mctx.fp = window [sparc_fp - 16];
511
512         mono_handle_exception (&mctx, obj, mctx.ip, test_only);
513         
514         /* We can't use restore_context to return from a signal handler */
515         ctx->uc_mcontext.gregs [REG_PC] = mctx.ip;
516         ctx->uc_mcontext.gregs [REG_nPC] = mctx.ip + 4;
517         ctx->uc_mcontext.gregs [REG_SP] = mctx.sp;
518         window = (gpointer*)(((guint8*)mctx.sp) + MONO_SPARC_STACK_BIAS);
519         window [sparc_fp - 16] = mctx.fp;
520
521         return TRUE;
522 }
523
524 gpointer
525 mono_arch_ip_from_context (void *sigctx)
526 {
527         ucontext_t *ctx = (ucontext_t*)sigctx;
528         return (gpointer)ctx->uc_mcontext.gregs [REG_PC];
529 }
530
531 #endif