2010-05-11 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / mini / exceptions-hppa.c
blobc8c5f3d9c8dc3486c9de1e3478328c64a717b81f
1 /*
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
22 * THE SOFTWARE.
26 #include <config.h>
27 #include <glib.h>
28 #include <signal.h>
29 #include <string.h>
30 #include <ucontext.h>
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>
40 #include "mini.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 { \
46 int reg, ofs; \
47 hppa_ldw (code, G_STRUCT_OFFSET (MonoContext, pc), \
48 ctx_reg, ip_reg); \
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); \
53 ofs += 4; \
54 } \
55 } \
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); \
61 } \
62 } \
63 } while (0)
66 /* HPPA bit ops */
67 static inline int
68 hppa_low_sign_extend (unsigned val, unsigned bits)
70 return (int) ((val & 0x1 ? (-1 << (bits - 1)) : 0) | val >> 1);
73 static inline int
74 hppa_sign_extend (unsigned val, unsigned bits)
76 return (int) (val >> (bits - 1) ? (-1 << bits) | val : val);
79 static inline int
80 hppa_get_field (unsigned word, int from, int to)
82 return ((word) >> (31 - (to)) & ((1 << ((to) - (from) + 1)) - 1));
85 static inline int
86 hppa_extract_14 (unsigned word)
88 return hppa_low_sign_extend (word & 0x3fff, 14);
91 static inline int
92 hppa_extract_21 (unsigned word)
94 int val;
96 word &= 0x1fffff;
97 word <<= 11;
98 val = hppa_get_field (word, 20, 20);
99 val <<= 11;
100 val |= hppa_get_field (word, 9, 19);
101 val <<= 2;
102 val |= hppa_get_field (word, 5, 6);
103 val <<= 5;
104 val |= hppa_get_field (word, 0, 4);
105 val <<= 2;
106 val |= hppa_get_field (word, 7, 8);
107 return hppa_sign_extend (val, 21) << 11;
110 static inline int
111 hppa_is_branch(unsigned int insn)
113 switch (insn >> 26)
115 case 0x20:
116 case 0x21:
117 case 0x22:
118 case 0x23:
119 case 0x27:
120 case 0x28:
121 case 0x29:
122 case 0x2a:
123 case 0x2b:
124 case 0x2f:
125 case 0x30:
126 case 0x31:
127 case 0x32:
128 case 0x33:
129 case 0x38:
130 case 0x39:
131 case 0x3a:
132 case 0x3b:
133 return 1;
135 default:
136 return 0;
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)
146 gpointer
147 mono_arch_get_restore_context (void)
149 guint8 *code;
150 static guint8 start [384];
151 static int inited = 0;
153 if (inited)
154 return start;
155 inited = 1;
157 code = start;
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);
163 hppa_nop (code);
165 /* not reached */
166 *(guint32 *)code = 0xdeadbeef;
168 g_assert ((code - start) < sizeof(start));
169 mono_arch_flush_icache (start, code - start);
170 return 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).
180 gpointer
181 mono_arch_get_call_filter (void)
183 static guint8 start [1024];
184 static int inited = 0;
185 guint8 *code;
186 int pos, i;
188 if (inited)
189 return start;
191 inited = 1;
192 /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */
193 code = start;
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 */
199 pos = 0;
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);
228 /* epilog */
229 hppa_ldo (code, -pos, hppa_sp, hppa_sp);
231 /* Restore registers */
232 pos = 0;
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);
251 hppa_nop (code);
253 g_assert ((code - start) < sizeof(start));
254 mono_arch_flush_icache (start, code - start);
255 return start;
258 static void
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 *);
262 MonoContext ctx;
264 if (!restore_context)
265 restore_context = mono_arch_get_restore_context ();
267 /* adjust eip so that it point into the call instruction */
268 eip &= ~3;
269 eip -= 8;
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;
278 if (!rethrow)
279 mono_ex->stack_trace = NULL;
281 mono_handle_exception (&ctx, exc, (gpointer)(eip + 8), FALSE);
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);
297 static gpointer
298 mono_arch_get_throw_exception_generic (guint8 *start, int size, int by_name, gboolean rethrow)
300 guint8 *code;
301 int pos, frpos, i;
303 code = start;
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 */
314 pos = 0;
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));
322 frpos = pos;
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);
338 if (by_name) {
339 /* mono_exception_from_name (MonoImage *image,
340 * const char *name_space,
341 * const char *name)
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);
368 hppa_nop (code);
370 /* not reached */
371 *(guint32 *)code = 0x88c0ffee;
373 g_assert ((code - start) < size);
374 mono_arch_flush_icache (start, code - start);
375 return 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);
386 gpointer
387 mono_arch_get_rethrow_exception (void)
389 static guint8 start [450];
390 static int inited = 0;
392 if (inited)
393 return start;
394 mono_arch_get_throw_exception_generic (start, sizeof (start), FALSE, TRUE);
395 inited = 1;
396 return start;
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 ());
410 gpointer
411 mono_arch_get_throw_exception (void)
413 static guint8 start [450];
414 static int inited = 0;
416 if (inited)
417 return start;
418 mono_arch_get_throw_exception_generic (start, sizeof (start), FALSE, FALSE);
419 inited = 1;
420 return start;
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 ());
435 gpointer
436 mono_arch_get_throw_exception_by_name (void)
438 static guint8 start [450];
439 static int inited = 0;
441 if (inited)
442 return start;
443 mono_arch_get_throw_exception_generic (start, sizeof (start), TRUE, FALSE);
444 inited = 1;
445 return start;
448 static int
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);
453 int saw_branch = 0;
454 int stack_size = 0;
456 /* Look for the instruction that adjusts the stack pointer */
457 while (insn < end) {
458 /* ldo X(sp), sp */
459 if ((insn[0] & 0xffffc000) == 0x37de0000) {
460 stack_size += hppa_extract_14 (insn [0]);
462 /* stwm X,D(sp) */
463 else if ((insn[0] & 0xffe00000) == 0x6fc00000) {
464 stack_size += hppa_extract_14 (insn [0]);
465 if (stored_fp)
466 *stored_fp = 1;
468 /* addil/ldo */
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]);
473 insn++;
476 insn++;
477 if (saw_branch)
478 break;
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);
486 return stack_size;
489 static void
490 hppa_analyze_frame (MonoJitInfo *ji, MonoContext *ctx, MonoContext *new_ctx)
492 unsigned char *sp;
493 int stack_size;
494 int stored_fp = 0;
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;
508 gulong val;
509 int i, j = 0;
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))
518 j++;
521 if (stored_fp) {
522 gulong val;
523 val = *(gulong *)sp;
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.
537 MonoJitInfo *
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)
541 MonoJitInfo *ji;
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)))
546 ji = prev_ji;
547 else
548 ji = mini_jit_info_table_find (domain, ip, NULL);
550 if (managed)
551 *managed = FALSE;
553 if (ji != NULL) {
554 gint32 address;
556 *new_ctx = *ctx;
558 address = (char *)ip - (char *)ji->code_start;
560 if (managed)
561 if (!ji->method->wrapper_type)
562 *managed = TRUE;
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);
572 return ji;
573 } else if (*lmf) {
575 *new_ctx = *ctx;
577 if (!(*lmf)->method)
578 return (gpointer)-1;
580 if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL))) {
581 } else {
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;
596 return NULL;
599 gboolean
600 mono_arch_has_unwind_info (gconstpointer addr)
602 return FALSE;
606 * This is the function called from the signal handler
608 gboolean
609 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
611 struct ucontext *uc = sigctx;
612 MonoContext mctx;
613 gboolean result;
614 int i, grs, frs;
616 mctx.pc = uc->uc_mcontext.sc_iaoq [0];
617 mctx.sp = uc->uc_mcontext.sc_gr [30];
619 grs = 0; frs = 0;
620 for (i = 0; i < 32; i++) {
621 if (HPPA_IS_SAVED_GREG (i))
622 mctx.regs[grs++] = uc->uc_mcontext.sc_gr [i];
623 if (HPPA_IS_SAVED_FREG (i))
624 mctx.fregs[frs++] = uc->uc_mcontext.sc_fr [i];
626 g_assert (grs == MONO_SAVED_GREGS && frs == MONO_SAVED_FREGS);
628 result = mono_handle_exception (&mctx, obj, (gpointer)mctx.pc, test_only);
630 /* restore the context so that returning from the signal handler will invoke
631 * the catch clause
633 uc->uc_mcontext.sc_iaoq [0] = mctx.pc;
634 uc->uc_mcontext.sc_iaoq [1] = mctx.pc + 4;
635 uc->uc_mcontext.sc_gr [30] = mctx.sp;
637 grs = 0; frs = 0;
638 for (i = 0; i < 32; i++) {
639 if (HPPA_IS_SAVED_GREG (i))
640 uc->uc_mcontext.sc_gr [i] = mctx.regs [grs++];
641 if (HPPA_IS_SAVED_FREG (i))
642 uc->uc_mcontext.sc_fr [i] = mctx.fregs [frs++];
645 return result;
648 gpointer
649 mono_arch_ip_from_context (void *sigctx)
651 struct ucontext *uc = sigctx;
652 return (gpointer)uc->uc_mcontext.sc_iaoq [0];