2 * exceptions-sparc.c: exception support for sparc
5 * Mark Crichton (crichton@gimp.org)
6 * Dietmar Maurer (dietmar@ximian.com)
8 * (C) 2003 Ximian, Inc.
15 #include <sys/ucontext.h>
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>
28 #include "mini-sparc.h"
34 #define MONO_SPARC_WINDOW_ADDR(sp) ((gpointer*)(((guint8*)(sp)) + MONO_SPARC_STACK_BIAS))
37 * mono_arch_get_restore_context:
39 * Returns a pointer to a method which restores a previously saved sigcontext.
42 mono_arch_get_restore_context (void)
44 static guint32
*start
;
45 static int inited
= 0;
51 code
= start
= mono_global_codeman_reserve (32 * sizeof (guint32
));
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
);
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
);
60 g_assert ((code
- start
) < 32);
62 mono_arch_flush_icache ((guint8
*)start
, (guint8
*)code
- (guint8
*)start
);
70 * mono_arch_get_call_filter:
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).
76 * call_filter (MonoContext *ctx, gpointer ip)
79 mono_arch_get_call_filter (void)
81 static guint32
*start
;
82 static int inited
= 0;
89 code
= start
= mono_global_codeman_reserve (64 * sizeof (guint32
));
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
97 /* Create first frame */
98 sparc_save_imm (code
, sparc_sp
, -256, sparc_sp
);
100 sparc_mov_reg_reg (code
, sparc_i1
, sparc_o0
);
101 sparc_ldi_imm (code
, sparc_i0
, G_STRUCT_OFFSET (MonoContext
, sp
), sparc_o1
);
103 /* Create second frame */
104 sparc_save_imm (code
, sparc_sp
, -256, sparc_sp
);
106 sparc_mov_reg_reg (code
, sparc_i0
, sparc_o0
);
107 sparc_mov_reg_reg (code
, sparc_i1
, sparc_o1
);
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.
118 sparc_mov_reg_reg (code
, sparc_fp
, sparc_o7
);
121 * Modify the second frame so it is identical to the one used in the
122 * method containing the filter.
124 for (i
= 0; i
< 16; ++i
)
125 sparc_ldi_imm (code
, sparc_o1
, MONO_SPARC_STACK_BIAS
+ i
* sizeof (gpointer
), sparc_l0
+ i
);
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
));
130 /* Call the filter code, after this returns, %o0 will hold the result */
131 sparc_call_imm (code
, sparc_o0
, 0);
134 /* Restore original %fp */
135 sparc_ldi_imm (code
, sparc_fp
, MONO_SPARC_STACK_BIAS
- sizeof (gpointer
), sparc_fp
);
137 sparc_mov_reg_reg (code
, sparc_o0
, sparc_i0
);
139 /* Return to first frame */
140 sparc_restore (code
, sparc_g0
, sparc_g0
, sparc_g0
);
142 /* FIXME: Save locals to the stack */
144 /* Return to caller */
146 /* Return result in delay slot */
147 sparc_restore (code
, sparc_o0
, sparc_g0
, sparc_o0
);
149 g_assert ((code
- start
) < 64);
151 mono_arch_flush_icache ((guint8
*)start
, (guint8
*)code
- (guint8
*)start
);
159 throw_exception (MonoObject
*exc
, gpointer sp
, gpointer ip
, gboolean rethrow
)
162 static void (*restore_context
) (MonoContext
*);
165 if (!restore_context
)
166 restore_context
= mono_arch_get_restore_context ();
168 window
= MONO_SPARC_WINDOW_ADDR (sp
);
169 ctx
.sp
= (gpointer
*)sp
;
171 ctx
.fp
= (gpointer
*)(MONO_SPARC_WINDOW_ADDR (sp
) [sparc_i6
- 16]);
173 if (mono_object_isinst (exc
, mono_defaults
.exception_class
)) {
174 MonoException
*mono_ex
= (MonoException
*)exc
;
176 mono_ex
->stack_trace
= NULL
;
178 mono_handle_exception (&ctx
, exc
, ip
, FALSE
);
179 restore_context (&ctx
);
181 g_assert_not_reached ();
185 get_throw_exception (gboolean rethrow
)
187 guint32
*start
, *code
;
189 code
= start
= mono_global_codeman_reserve (16 * sizeof (guint32
));
191 sparc_save_imm (code
, sparc_sp
, -512, sparc_sp
);
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
);
202 g_assert ((code
- start
) <= 16);
204 mono_arch_flush_icache ((guint8
*)start
, (guint8
*)code
- (guint8
*)start
);
210 * mono_arch_get_throw_exception:
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);
217 mono_arch_get_throw_exception (void)
219 static guint32
* start
;
220 static int inited
= 0;
227 start
= get_throw_exception (FALSE
);
233 mono_arch_get_rethrow_exception (void)
235 static guint32
* start
;
236 static int inited
= 0;
243 start
= get_throw_exception (TRUE
);
249 * mono_arch_get_throw_exception_by_name:
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);
256 mono_arch_get_throw_exception_by_name (void)
258 static guint32
*start
;
259 static int inited
= 0;
267 code
= start
= mono_global_codeman_reserve (64 * sizeof (guint32
));
275 sparc_save_imm (code
, sparc_sp
, -160, sparc_sp
);
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
);
284 /* Return to the caller, so exception handling does not see this frame */
285 sparc_restore (code
, sparc_o0
, sparc_g0
, sparc_o0
);
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
);
294 g_assert ((code
- start
) < 32);
296 mono_arch_flush_icache ((guint8
*)start
, (guint8
*)code
- (guint8
*)start
);
302 * mono_arch_get_throw_corlib_exception:
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.
312 mono_arch_get_throw_corlib_exception (void)
314 static guint32
*start
;
315 static int inited
= 0;
323 code
= start
= mono_global_codeman_reserve (64 * sizeof (guint32
));
331 sparc_mov_reg_reg (code
, sparc_o7
, sparc_o2
);
332 sparc_save_imm (code
, sparc_sp
, -160, sparc_sp
);
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
);
341 /* Return to the caller, so exception handling does not see this frame */
342 sparc_restore (code
, sparc_o0
, sparc_g0
, sparc_o0
);
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
);
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
);
353 g_assert ((code
- start
) < 32);
355 mono_arch_flush_icache ((guint8
*)start
, (guint8
*)code
- (guint8
*)start
);
360 /* mono_arch_find_jit_info:
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.
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
)
374 gpointer ip
= MONO_CONTEXT_GET_IP (ctx
);
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
)))
381 ji
= mono_jit_info_table_find (domain
, ip
);
390 if (!ji
->method
->wrapper_type
)
393 if (*lmf
&& (MONO_CONTEXT_GET_BP (ctx
) >= (gpointer
)(*lmf
)->ebp
)) {
394 /* remove any unused lmf */
395 *lmf
= (*lmf
)->previous_lmf
;
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]);
415 if ((ji
= mono_jit_info_table_find (domain
, (gpointer
)(*lmf
)->ip
))) {
417 memset (res
, 0, sizeof (MonoJitInfo
));
418 res
->method
= (*lmf
)->method
;
421 new_ctx
->ip
= (*lmf
)->ip
;
422 new_ctx
->sp
= (*lmf
)->sp
;
423 new_ctx
->fp
= (*lmf
)->ebp
;
425 *lmf
= (*lmf
)->previous_lmf
;
427 return ji
? ji
: res
;
432 mono_arch_has_unwind_info (gconstpointer addr
)
440 mono_arch_handle_exception (void *sigctx
, gpointer obj
, gboolean test_only
)
443 struct sigcontext
*sc
= sigctx
;
447 mctx
.ip
= (gpointer
) sc
->sigc_regs
.tpc
;
448 mctx
.sp
= (gpointer
) sc
->sigc_regs
.u_regs
[14];
450 mctx
.ip
= (gpointer
) sc
->si_regs
.pc
;
451 mctx
.sp
= (gpointer
) sc
->si_regs
.u_regs
[14];
454 window
= (gpointer
*)(((guint8
*)mctx
.sp
) + MONO_SPARC_STACK_BIAS
);
455 mctx
.fp
= window
[sparc_fp
- 16];
457 mono_handle_exception (&mctx
, obj
, mctx
.ip
, test_only
);
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
;
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
;
469 window
= (gpointer
*)(((guint8
*)mctx
.sp
) + MONO_SPARC_STACK_BIAS
);
470 window
[sparc_fp
- 16] = mctx
.fp
;
476 mono_arch_ip_from_context (void *sigctx
)
478 struct sigcontext
*sc
= sigctx
;
482 ret
= (gpointer
) sc
->sigc_regs
.tpc
;
484 ret
= (gpointer
) sc
->si_regs
.pc
;
490 #else /* !__linux__ */
493 mono_arch_handle_exception (void *sigctx
, gpointer obj
, gboolean test_only
)
496 ucontext_t
*ctx
= (ucontext_t
*)sigctx
;
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
504 g_assert (!ctx
->uc_mcontext
.gwins
);
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];
511 mono_handle_exception (&mctx
, obj
, mctx
.ip
, test_only
);
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
;
524 mono_arch_ip_from_context (void *sigctx
)
526 ucontext_t
*ctx
= (ucontext_t
*)sigctx
;
527 return (gpointer
)ctx
->uc_mcontext
.gregs
[REG_PC
];