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_corlib_exception:
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.
259 mono_arch_get_throw_corlib_exception (void)
261 static guint32
*start
;
262 static int inited
= 0;
270 code
= start
= mono_global_codeman_reserve (64 * sizeof (guint32
));
278 sparc_mov_reg_reg (code
, sparc_o7
, sparc_o2
);
279 sparc_save_imm (code
, sparc_sp
, -160, sparc_sp
);
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
);
288 /* Return to the caller, so exception handling does not see this frame */
289 sparc_restore (code
, sparc_o0
, sparc_g0
, sparc_o0
);
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
);
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
);
300 g_assert ((code
- start
) < 32);
302 mono_arch_flush_icache ((guint8
*)start
, (guint8
*)code
- (guint8
*)start
);
307 /* mono_arch_find_jit_info:
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.
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
)
321 gpointer ip
= MONO_CONTEXT_GET_IP (ctx
);
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
)))
328 ji
= mini_jit_info_table_find (domain
, ip
, NULL
);
337 if (!ji
->method
->wrapper_type
)
340 if (*lmf
&& (MONO_CONTEXT_GET_BP (ctx
) >= (gpointer
)(*lmf
)->ebp
)) {
341 /* remove any unused lmf */
342 *lmf
= (*lmf
)->previous_lmf
;
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]);
362 if ((ji
= mini_jit_info_table_find (domain
, (gpointer
)(*lmf
)->ip
, NULL
))) {
364 memset (res
, 0, MONO_SIZEOF_JIT_INFO
);
365 res
->method
= (*lmf
)->method
;
368 new_ctx
->ip
= (*lmf
)->ip
;
369 new_ctx
->sp
= (*lmf
)->sp
;
370 new_ctx
->fp
= (*lmf
)->ebp
;
372 *lmf
= (*lmf
)->previous_lmf
;
374 return ji
? ji
: res
;
379 mono_arch_has_unwind_info (gconstpointer addr
)
387 mono_arch_handle_exception (void *sigctx
, gpointer obj
, gboolean test_only
)
390 struct sigcontext
*sc
= sigctx
;
394 mctx
.ip
= (gpointer
) sc
->sigc_regs
.tpc
;
395 mctx
.sp
= (gpointer
) sc
->sigc_regs
.u_regs
[14];
397 mctx
.ip
= (gpointer
) sc
->si_regs
.pc
;
398 mctx
.sp
= (gpointer
) sc
->si_regs
.u_regs
[14];
401 window
= (gpointer
*)(((guint8
*)mctx
.sp
) + MONO_SPARC_STACK_BIAS
);
402 mctx
.fp
= window
[sparc_fp
- 16];
404 mono_handle_exception (&mctx
, obj
, mctx
.ip
, test_only
);
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
;
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
;
416 window
= (gpointer
*)(((guint8
*)mctx
.sp
) + MONO_SPARC_STACK_BIAS
);
417 window
[sparc_fp
- 16] = mctx
.fp
;
423 mono_arch_ip_from_context (void *sigctx
)
425 struct sigcontext
*sc
= sigctx
;
429 ret
= (gpointer
) sc
->sigc_regs
.tpc
;
431 ret
= (gpointer
) sc
->si_regs
.pc
;
437 #else /* !__linux__ */
440 mono_arch_handle_exception (void *sigctx
, gpointer obj
, gboolean test_only
)
443 ucontext_t
*ctx
= (ucontext_t
*)sigctx
;
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
451 g_assert (!ctx
->uc_mcontext
.gwins
);
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];
458 mono_handle_exception (&mctx
, obj
, mctx
.ip
, test_only
);
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
;
471 mono_arch_ip_from_context (void *sigctx
)
473 ucontext_t
*ctx
= (ucontext_t
*)sigctx
;
474 return (gpointer
)ctx
->uc_mcontext
.gregs
[REG_PC
];