2 * exceptions-hppa.c: exception support for HPPA
4 * Copyright (c) 2007 Randolph Chung
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32 #include <mono/arch/hppa/hppa-codegen.h>
33 #include <mono/metadata/appdomain.h>
34 #include <mono/metadata/tabledefs.h>
35 #include <mono/metadata/threads.h>
36 #include <mono/metadata/debug-helpers.h>
37 #include <mono/metadata/exception.h>
38 #include <mono/metadata/mono-debug.h>
41 #include "mini-hppa.h"
43 #define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
45 #define restore_regs_from_context(ctx_reg,ip_reg) do { \
47 hppa_ldw (code, G_STRUCT_OFFSET (MonoContext, pc), \
49 ofs = G_STRUCT_OFFSET (MonoContext, regs); \
50 for (reg = 0; reg < 32; ++reg) { \
51 if (HPPA_IS_SAVED_GREG (reg)) { \
52 hppa_ldw (code, ofs, ctx_reg, reg); \
56 hppa_set (code, G_STRUCT_OFFSET (MonoContext, fregs), hppa_r1); \
57 for (reg = 4; reg < 32; ++reg) { \
58 if (HPPA_IS_SAVED_FREG (reg)) { \
59 hppa_flddx (code, hppa_r1, ctx_reg, reg); \
60 hppa_ldo (code, sizeof(double), hppa_r1, hppa_r1); \
68 hppa_low_sign_extend (unsigned val
, unsigned bits
)
70 return (int) ((val
& 0x1 ? (-1 << (bits
- 1)) : 0) | val
>> 1);
74 hppa_sign_extend (unsigned val
, unsigned bits
)
76 return (int) (val
>> (bits
- 1) ? (-1 << bits
) | val
: val
);
80 hppa_get_field (unsigned word
, int from
, int to
)
82 return ((word
) >> (31 - (to
)) & ((1 << ((to
) - (from
) + 1)) - 1));
86 hppa_extract_14 (unsigned word
)
88 return hppa_low_sign_extend (word
& 0x3fff, 14);
92 hppa_extract_21 (unsigned word
)
98 val
= hppa_get_field (word
, 20, 20);
100 val
|= hppa_get_field (word
, 9, 19);
102 val
|= hppa_get_field (word
, 5, 6);
104 val
|= hppa_get_field (word
, 0, 4);
106 val
|= hppa_get_field (word
, 7, 8);
107 return hppa_sign_extend (val
, 21) << 11;
111 hppa_is_branch(unsigned int insn
)
141 * arch_get_restore_context:
143 * Returns a pointer to a method which restores a previously saved sigcontext.
144 * called as restore_context(MonoContext *ctx)
147 mono_arch_get_restore_context (void)
150 static guint8 start
[384];
151 static int inited
= 0;
158 restore_regs_from_context (hppa_r26
, hppa_r20
);
159 /* restore also the stack pointer */
160 hppa_ldw (code
, G_STRUCT_OFFSET (MonoContext
, sp
), hppa_r26
, hppa_r30
);
161 /* jump to the saved IP */
162 hppa_bv (code
, hppa_r0
, hppa_r20
);
166 *(guint32
*)code
= 0xdeadbeef;
168 g_assert ((code
- start
) < sizeof(start
));
169 mono_arch_flush_icache (start
, code
- start
);
174 * arch_get_call_filter:
176 * Returns a pointer to a method which calls an exception filter. We
177 * also use this function to call finally handlers (we pass NULL as
178 * @exc object in this case).
181 mono_arch_get_call_filter (void)
183 static guint8 start
[1024];
184 static int inited
= 0;
192 /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */
195 /* Save the return pointer in its regular location */
196 hppa_stw (code
, hppa_r2
, -20, hppa_r30
);
198 /* Save all the registers on the stack */
200 for (i
= 0; i
< 32; i
++) {
201 if (HPPA_IS_SAVED_GREG (i
)) {
202 hppa_stw (code
, i
, pos
, hppa_sp
);
203 pos
+= sizeof(gulong
);
206 pos
= ALIGN_TO (pos
, sizeof(double));
207 hppa_set (code
, pos
, hppa_r1
);
208 for (i
= 0; i
< 32; i
++) {
209 if (HPPA_IS_SAVED_FREG (i
)) {
210 hppa_fstdx (code
, i
, hppa_r1
, hppa_sp
);
211 hppa_ldo (code
, sizeof(double), hppa_r1
, hppa_r1
);
212 pos
+= sizeof(double);
216 pos
+= 64; /* Leave space for the linkage area */
217 pos
= ALIGN_TO (pos
, MONO_ARCH_FRAME_ALIGNMENT
);
219 hppa_ldo (code
, pos
, hppa_sp
, hppa_sp
);
221 /* restore all the regs from ctx (in r26), but not the stack pointer */
222 restore_regs_from_context (hppa_r26
, hppa_r20
);
224 /* call handler at the saved IP (r25) */
225 hppa_ble (code
, 0, hppa_r25
);
226 hppa_copy (code
, hppa_r31
, hppa_r2
);
229 hppa_ldo (code
, -pos
, hppa_sp
, hppa_sp
);
231 /* Restore registers */
233 for (i
= 0; i
< 32; i
++) {
234 if (HPPA_IS_SAVED_GREG (i
)) {
235 hppa_ldw (code
, pos
, hppa_sp
, i
);
236 pos
+= sizeof(gulong
);
239 pos
= ALIGN_TO (pos
, sizeof(double));
240 hppa_set (code
, pos
, hppa_r1
);
241 for (i
= 0; i
< 32; i
++) {
242 if (HPPA_IS_SAVED_FREG (i
)) {
243 hppa_flddx (code
, hppa_r1
, hppa_sp
, i
);
244 hppa_ldo (code
, sizeof(double), hppa_r1
, hppa_r1
);
245 pos
+= sizeof(double);
249 hppa_ldw (code
, -20, hppa_sp
, hppa_r2
);
250 hppa_bv (code
, hppa_r0
, hppa_r2
);
253 g_assert ((code
- start
) < sizeof(start
));
254 mono_arch_flush_icache (start
, code
- start
);
259 throw_exception (MonoObject
*exc
, unsigned long eip
, unsigned long esp
, gulong
*int_regs
, gdouble
*fp_regs
, gboolean rethrow
)
261 static void (*restore_context
) (MonoContext
*);
264 if (!restore_context
)
265 restore_context
= mono_arch_get_restore_context ();
267 /* adjust eip so that it point into the call instruction */
271 MONO_CONTEXT_SET_BP (&ctx
, esp
);
272 MONO_CONTEXT_SET_IP (&ctx
, eip
);
273 memcpy (&ctx
.regs
, int_regs
, sizeof (gulong
) * MONO_SAVED_GREGS
);
274 memcpy (&ctx
.fregs
, fp_regs
, sizeof (double) * MONO_SAVED_FREGS
);
276 if (mono_object_isinst (exc
, mono_defaults
.exception_class
)) {
277 MonoException
*mono_ex
= (MonoException
*)exc
;
279 mono_ex
->stack_trace
= NULL
;
281 mono_handle_exception (&ctx
, exc
);
283 restore_context (&ctx
);
285 g_assert_not_reached ();
289 * arch_get_throw_exception_generic:
291 * Returns a function pointer which can be used to raise
292 * exceptions. The returned function has the following
293 * signature: void (*func) (MonoException *exc); or
294 * void (*func) (char *exc_name);
298 mono_arch_get_throw_exception_generic (guint8
*start
, int size
, int by_name
, gboolean rethrow
)
305 /* We are called with r26 = exception */
306 /* Stash the call site IP in the "return pointer" slot */
307 hppa_stw (code
, hppa_r2
, -20, hppa_sp
);
309 /* Non-standard prologue - we don't want to clobber r3 */
310 hppa_copy (code
, hppa_sp
, hppa_r1
);
311 hppa_ldo (code
, 512, hppa_sp
, hppa_sp
);
313 /* Save all the registers on the stack */
315 for (i
= 0; i
< 32; i
++) {
316 if (HPPA_IS_SAVED_GREG (i
)) {
317 hppa_stw (code
, i
, pos
, hppa_r1
);
318 pos
+= sizeof(gulong
);
321 pos
= ALIGN_TO (pos
, sizeof(double));
323 hppa_set (code
, pos
, hppa_r20
);
324 for (i
= 0; i
< 32; i
++) {
325 if (HPPA_IS_SAVED_FREG (i
)) {
326 hppa_fstdx (code
, i
, hppa_r20
, hppa_r1
);
327 hppa_ldo (code
, sizeof(double), hppa_r20
, hppa_r20
);
328 pos
+= sizeof(double);
332 /* Now that we have saved r4, we copy the stack pointer to it for
333 * use below - we want a callee save register in case we do the
334 * function call below
336 hppa_copy (code
, hppa_r1
, hppa_r4
);
339 /* mono_exception_from_name (MonoImage *image,
340 * const char *name_space,
343 void *func
= __canonicalize_funcptr_for_compare (mono_exception_from_name
);
344 hppa_copy (code
, hppa_r26
, hppa_r24
);
345 hppa_set (code
, mono_defaults
.corlib
, hppa_r26
);
346 hppa_set (code
, "System", hppa_r25
);
347 hppa_ldil (code
, hppa_lsel (func
), hppa_r1
);
348 hppa_ble (code
, hppa_rsel (func
), hppa_r1
);
349 hppa_copy (code
, hppa_r31
, hppa_r2
);
350 hppa_copy (code
, hppa_r28
, hppa_r26
);
353 /* call throw_exception (exc, ip, sp, int_regs, fp_regs, rethrow) */
354 /* exc is already in place in r26 */
356 hppa_ldw (code
, -20, hppa_r4
, hppa_r25
); /* ip */
357 hppa_copy (code
, hppa_r4
, hppa_r24
); /* sp */
358 hppa_ldo (code
, 0, hppa_r4
, hppa_r23
);
359 hppa_ldo (code
, frpos
, hppa_r4
, hppa_r22
);
360 hppa_stw (code
, hppa_r22
, -52, hppa_sp
);
361 hppa_ldo (code
, rethrow
, hppa_r0
, hppa_r22
);
362 hppa_stw (code
, hppa_r22
, -56, hppa_sp
);
364 hppa_set (code
, throw_exception
, hppa_r1
);
365 hppa_depi (code
, 0, 31, 2, hppa_r1
);
366 hppa_ldw (code
, 0, hppa_r1
, hppa_r1
);
367 hppa_bv (code
, hppa_r0
, hppa_r1
);
371 *(guint32
*)code
= 0x88c0ffee;
373 g_assert ((code
- start
) < size
);
374 mono_arch_flush_icache (start
, code
- start
);
379 * mono_arch_get_rethrow_exception:
381 * Returns a function pointer which can be used to rethrow
382 * exceptions. The returned function has the following
383 * signature: void (*func) (MonoException *exc);
387 mono_arch_get_rethrow_exception (void)
389 static guint8 start
[450];
390 static int inited
= 0;
394 mono_arch_get_throw_exception_generic (start
, sizeof (start
), FALSE
, TRUE
);
399 * arch_get_throw_exception:
401 * Returns a function pointer which can be used to raise
402 * exceptions. The returned function has the following
403 * signature: void (*func) (MonoException *exc);
404 * For example to raise an arithmetic exception you can use:
406 * x86_push_imm (code, mono_get_exception_arithmetic ());
407 * x86_call_code (code, arch_get_throw_exception ());
411 mono_arch_get_throw_exception (void)
413 static guint8 start
[450];
414 static int inited
= 0;
418 mono_arch_get_throw_exception_generic (start
, sizeof (start
), FALSE
, FALSE
);
424 * arch_get_throw_exception_by_name:
426 * Returns a function pointer which can be used to raise
427 * corlib exceptions. The returned function has the following
428 * signature: void (*func) (char *exc_name);
429 * For example to raise an arithmetic exception you can use:
431 * x86_push_imm (code, "ArithmeticException");
432 * x86_call_code (code, arch_get_throw_exception_by_name ());
436 mono_arch_get_throw_exception_by_name (void)
438 static guint8 start
[450];
439 static int inited
= 0;
443 mono_arch_get_throw_exception_generic (start
, sizeof (start
), TRUE
, FALSE
);
449 hppa_get_size_of_frame (MonoJitInfo
*ji
, int *stored_fp
)
451 guint32
*insn
= (guint32
*)((unsigned long)ji
->code_start
& ~3);
452 guint32
*end
= (guint32
*)((unsigned long)ji
->code_start
+ ji
->code_size
);
456 /* Look for the instruction that adjusts the stack pointer */
459 if ((insn
[0] & 0xffffc000) == 0x37de0000) {
460 stack_size
+= hppa_extract_14 (insn
[0]);
463 else if ((insn
[0] & 0xffe00000) == 0x6fc00000) {
464 stack_size
+= hppa_extract_14 (insn
[0]);
469 else if (((insn
[0] & 0xffe00000) == 0x28200000) &&
470 ((insn
[1] & 0xffff0000) == 0x343e0000)) {
471 stack_size
+= hppa_extract_21 (insn
[0]);
472 stack_size
+= hppa_extract_14 (insn
[1]);
479 saw_branch
= hppa_is_branch (insn
[0]);
482 if (stack_size
== 0) {
483 g_print ("No stack frame found for function at %p\n", ji
->code_start
);
490 hppa_analyze_frame (MonoJitInfo
*ji
, MonoContext
*ctx
, MonoContext
*new_ctx
)
496 stack_size
= hppa_get_size_of_frame (ji
, &stored_fp
);
498 sp
= (unsigned char *)MONO_CONTEXT_GET_BP (ctx
) - stack_size
;
499 MONO_CONTEXT_SET_BP (new_ctx
, sp
);
500 MONO_CONTEXT_SET_IP (new_ctx
, *(unsigned int *)(sp
- 20));
502 if (ji
->method
->save_lmf
) {
503 memcpy (&new_ctx
->regs
, (char *)sp
+ HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, regs
), sizeof (gulong
) * MONO_SAVED_GREGS
);
504 memcpy (&new_ctx
->fregs
, (char *)sp
+ HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, fregs
), sizeof (double) * MONO_SAVED_FREGS
);
506 else if (ji
->used_regs
) {
507 char *pos
= (char *)sp
+ HPPA_STACK_LMF_OFFSET
;
511 for (i
= 0; i
<= 32; i
++) {
512 if ((1 << i
) & ji
->used_regs
) {
513 val
= *(gulong
*)pos
;
514 pos
+= sizeof (gulong
);
515 new_ctx
->regs
[j
] = val
;
517 if (HPPA_IS_SAVED_GREG (i
))
524 new_ctx
->regs
[0] = val
;
528 /* mono_arch_find_jit_info:
530 * This function is used to gather information from @ctx. It return the
531 * MonoJitInfo of the corresponding function, unwinds one stack frame and
532 * stores the resulting context into @new_ctx. It also stores a string
533 * describing the stack location into @trace (if not NULL), and modifies
534 * the @lmf if necessary. @native_offset return the IP offset from the
535 * start of the function or -1 if that info is not available.
538 mono_arch_find_jit_info (MonoDomain
*domain
, MonoJitTlsData
*jit_tls
, MonoJitInfo
*res
, MonoJitInfo
*prev_ji
,
539 MonoContext
*ctx
, MonoContext
*new_ctx
, MonoLMF
**lmf
, gboolean
*managed
)
542 gpointer ip
= MONO_CONTEXT_GET_IP (ctx
);
544 /* Avoid costly table lookup during stack overflow */
545 if (prev_ji
&& (ip
> prev_ji
->code_start
&& ((guint8
*)ip
< ((guint8
*)prev_ji
->code_start
) + prev_ji
->code_size
)))
548 ji
= mini_jit_info_table_find (domain
, ip
, NULL
);
558 address
= (char *)ip
- (char *)ji
->code_start
;
561 if (!ji
->method
->wrapper_type
)
564 if (*lmf
&& (*lmf
)->ebp
!= 0xffffffff && (MONO_CONTEXT_GET_BP (ctx
) <= (gpointer
)(*lmf
)->ebp
)) {
565 /* remove any unused lmf */
566 *lmf
= (*lmf
)->previous_lmf
;
569 hppa_analyze_frame (ji
, ctx
, new_ctx
);
570 //printf("Managed frame: rewound (ip,sp)=(%x [%s],%x) to (%x,%x)\n", ctx->pc, mono_method_full_name (ji->method, FALSE), ctx->sp, new_ctx->pc, new_ctx->sp);
580 if ((ji
= mini_jit_info_table_find (domain
, (gpointer
)(*lmf
)->eip
, NULL
))) {
582 memset (res
, 0, MONO_SIZEOF_JIT_INFO
);
583 res
->method
= (*lmf
)->method
;
586 MONO_CONTEXT_SET_BP (new_ctx
, (*lmf
)->ebp
);
587 MONO_CONTEXT_SET_IP (new_ctx
, (*lmf
)->eip
);
588 memcpy (&new_ctx
->regs
, (*lmf
)->regs
, sizeof (gulong
) * MONO_SAVED_GREGS
);
589 memcpy (&new_ctx
->fregs
, (*lmf
)->regs
, sizeof (double) * MONO_SAVED_FREGS
);
590 *lmf
= (*lmf
)->previous_lmf
;
591 //printf("Unmanaged frame: rewound to (ip,sp)=(%x [%s],%x)\n", new_ctx->pc, mono_method_full_name (ji ? ji->method : res->method, FALSE), new_ctx->sp);
593 return ji
? ji
: res
;
600 * This is the function called from the signal handler
603 mono_arch_handle_exception (void *sigctx
, gpointer obj
)
605 struct ucontext
*uc
= sigctx
;
610 mctx
.pc
= uc
->uc_mcontext
.sc_iaoq
[0];
611 mctx
.sp
= uc
->uc_mcontext
.sc_gr
[30];
614 for (i
= 0; i
< 32; i
++) {
615 if (HPPA_IS_SAVED_GREG (i
))
616 mctx
.regs
[grs
++] = uc
->uc_mcontext
.sc_gr
[i
];
617 if (HPPA_IS_SAVED_FREG (i
))
618 mctx
.fregs
[frs
++] = uc
->uc_mcontext
.sc_fr
[i
];
620 g_assert (grs
== MONO_SAVED_GREGS
&& frs
== MONO_SAVED_FREGS
);
622 result
= mono_handle_exception (&mctx
, obj
);
624 /* restore the context so that returning from the signal handler will invoke
627 uc
->uc_mcontext
.sc_iaoq
[0] = mctx
.pc
;
628 uc
->uc_mcontext
.sc_iaoq
[1] = mctx
.pc
+ 4;
629 uc
->uc_mcontext
.sc_gr
[30] = mctx
.sp
;
632 for (i
= 0; i
< 32; i
++) {
633 if (HPPA_IS_SAVED_GREG (i
))
634 uc
->uc_mcontext
.sc_gr
[i
] = mctx
.regs
[grs
++];
635 if (HPPA_IS_SAVED_FREG (i
))
636 uc
->uc_mcontext
.sc_fr
[i
] = mctx
.fregs
[frs
++];
643 mono_arch_ip_from_context (void *sigctx
)
645 struct ucontext
*uc
= sigctx
;
646 return (gpointer
)uc
->uc_mcontext
.sc_iaoq
[0];