[xbuild] Make Engine.DefaultToolsVersion 2.0 .
[mono.git] / mono / mini / exceptions-arm.c
1 /*
2  * exceptions-arm.c: exception support for ARM
3  *
4  * Authors:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *   Paolo Molaro (lupus@ximian.com)
7  *
8  * (C) 2001 Ximian, Inc.
9  */
10
11 #include <config.h>
12 #include <glib.h>
13 #include <signal.h>
14 #include <string.h>
15
16 #ifndef MONO_CROSS_COMPILE
17 #ifdef HAVE_ASM_SIGCONTEXT_H
18 #include <asm/sigcontext.h>
19 #endif  /* def HAVE_ASM_SIGCONTEXT_H */
20 #endif
21
22 #ifdef HAVE_UCONTEXT_H
23 #include <ucontext.h>
24 #endif  /* def HAVE_UCONTEXT_H */
25
26 #include <mono/arch/arm/arm-codegen.h>
27 #include <mono/metadata/appdomain.h>
28 #include <mono/metadata/tabledefs.h>
29 #include <mono/metadata/threads.h>
30 #include <mono/metadata/debug-helpers.h>
31 #include <mono/metadata/exception.h>
32 #include <mono/metadata/mono-debug.h>
33
34 #include "mini.h"
35 #include "mini-arm.h"
36 #include "mono/utils/mono-sigcontext.h"
37
38 /*
39  * arch_get_restore_context:
40  *
41  * Returns a pointer to a method which restores a previously saved sigcontext.
42  * The first argument in r0 is the pointer to the context.
43  */
44 gpointer
45 mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
46 {
47         guint8 *code;
48         guint8 *start;
49         int ctx_reg;
50         MonoJumpInfo *ji = NULL;
51         GSList *unwind_ops = NULL;
52
53         start = code = mono_global_codeman_reserve (128);
54
55         /* 
56          * Move things to their proper place so we can restore all the registers with
57          * one instruction.
58          */
59
60         ctx_reg = ARMREG_R0;
61
62         /* move eip to PC */
63         ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, eip));
64         ARM_STR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_PC * sizeof (mgreg_t)));
65         /* move sp to SP */
66         ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, esp));
67         ARM_STR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_SP * sizeof (mgreg_t)));
68
69         /* restore everything */
70         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET(MonoContext, regs));
71         ARM_LDM (code, ARMREG_IP, 0xffff);
72
73         /* never reached */
74         ARM_DBRK (code);
75
76         g_assert ((code - start) < 128);
77
78         mono_arch_flush_icache (start, code - start);
79
80         if (info)
81                 *info = mono_tramp_info_create (g_strdup_printf ("restore_context"), start, code - start, ji, unwind_ops);
82
83         return start;
84 }
85
86 /*
87  * arch_get_call_filter:
88  *
89  * Returns a pointer to a method which calls an exception filter. We
90  * also use this function to call finally handlers (we pass NULL as 
91  * @exc object in this case).
92  */
93 gpointer
94 mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
95 {
96         guint8 *code;
97         guint8* start;
98         int ctx_reg;
99         MonoJumpInfo *ji = NULL;
100         GSList *unwind_ops = NULL;
101
102         /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */
103         start = code = mono_global_codeman_reserve (320);
104
105         /* save all the regs on the stack */
106         ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
107         ARM_PUSH (code, MONO_ARM_REGSAVE_MASK);
108
109         /* restore all the regs from ctx (in r0), but not sp, the stack pointer */
110         ctx_reg = ARMREG_R0;
111         ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, eip));
112         ARM_ADD_REG_IMM8 (code, ARMREG_LR, ctx_reg, G_STRUCT_OFFSET(MonoContext, regs) + (MONO_ARM_FIRST_SAVED_REG * sizeof (mgreg_t)));
113         ARM_LDM (code, ARMREG_LR, MONO_ARM_REGSAVE_MASK);
114         /* call handler at eip (r1) and set the first arg with the exception (r2) */
115         ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_R2);
116         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
117         ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_R1);
118
119         /* epilog */
120         ARM_POP_NWB (code, 0xff0 | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
121
122         g_assert ((code - start) < 320);
123
124         mono_arch_flush_icache (start, code - start);
125
126         if (info)
127                 *info = mono_tramp_info_create (g_strdup_printf ("call_filter"), start, code - start, ji, unwind_ops);
128
129         return start;
130 }
131
132 void
133 mono_arm_throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs)
134 {
135         static void (*restore_context) (MonoContext *);
136         MonoContext ctx;
137         gboolean rethrow = eip & 1;
138
139         if (!restore_context)
140                 restore_context = mono_get_restore_context ();
141
142         eip &= ~1; /* clear the optional rethrow bit */
143         /* adjust eip so that it point into the call instruction */
144         eip -= 4;
145
146         /*printf ("stack in throw: %p\n", esp);*/
147         MONO_CONTEXT_SET_BP (&ctx, int_regs [ARMREG_FP - 4]);
148         MONO_CONTEXT_SET_SP (&ctx, esp);
149         MONO_CONTEXT_SET_IP (&ctx, eip);
150         memcpy (((guint8*)&ctx.regs) + (ARMREG_R4 * sizeof (mgreg_t)), int_regs, 8 * sizeof (mgreg_t));
151         /* memcpy (&ctx.fregs, fp_regs, sizeof (double) * MONO_SAVED_FREGS); */
152
153         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
154                 MonoException *mono_ex = (MonoException*)exc;
155                 if (!rethrow)
156                         mono_ex->stack_trace = NULL;
157         }
158         mono_handle_exception (&ctx, exc, (gpointer)(eip + 4), FALSE);
159         restore_context (&ctx);
160         g_assert_not_reached ();
161 }
162
163 void
164 mono_arm_throw_exception_by_token (guint32 type_token, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs)
165 {
166         /* Clear thumb bit */
167         eip &= ~1;
168
169         mono_arm_throw_exception ((MonoObject*)mono_exception_from_token (mono_defaults.corlib, type_token), eip, esp, int_regs, fp_regs);
170 }
171
172 void
173 mono_arm_resume_unwind (guint32 dummy1, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs)
174 {
175         MonoContext ctx;
176
177         eip &= ~1; /* clear the optional rethrow bit */
178         /* adjust eip so that it point into the call instruction */
179         eip -= 4;
180
181         MONO_CONTEXT_SET_BP (&ctx, int_regs [ARMREG_FP - 4]);
182         MONO_CONTEXT_SET_SP (&ctx, esp);
183         MONO_CONTEXT_SET_IP (&ctx, eip);
184         memcpy (((guint8*)&ctx.regs) + (ARMREG_R4 * sizeof (mgreg_t)), int_regs, 8 * sizeof (mgreg_t));
185
186         mono_resume_unwind (&ctx);
187 }
188
189 /**
190  * get_throw_trampoline:
191  *
192  * Returns a function pointer which can be used to raise 
193  * exceptions. The returned function has the following 
194  * signature: void (*func) (MonoException *exc); or
195  * void (*func) (guint32 ex_token, guint8* ip);
196  *
197  */
198 static gpointer 
199 get_throw_trampoline (int size, gboolean corlib, gboolean rethrow, gboolean llvm, gboolean resume_unwind, const char *tramp_name, MonoTrampInfo **info, gboolean aot)
200 {
201         guint8 *start;
202         guint8 *code;
203         MonoJumpInfo *ji = NULL;
204         GSList *unwind_ops = NULL;
205
206         code = start = mono_global_codeman_reserve (size);
207
208         mono_add_unwind_op_def_cfa (unwind_ops, code, start, ARMREG_SP, 0);
209
210         /* save all the regs on the stack */
211         ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
212         ARM_PUSH (code, MONO_ARM_REGSAVE_MASK);
213
214         mono_add_unwind_op_def_cfa (unwind_ops, code, start, ARMREG_SP, MONO_ARM_NUM_SAVED_REGS * sizeof (mgreg_t));
215         mono_add_unwind_op_offset (unwind_ops, code, start, ARMREG_LR, - sizeof (mgreg_t));
216
217         /* call throw_exception (exc, ip, sp, int_regs, fp_regs) */
218         /* caller sp */
219         ARM_ADD_REG_IMM8 (code, ARMREG_R2, ARMREG_SP, MONO_ARM_NUM_SAVED_REGS * sizeof (mgreg_t));
220         /* exc is already in place in r0 */
221         if (corlib) {
222                 /* The caller ip is already in R1 */
223                 if (llvm)
224                         /* Negate the ip adjustment done in mono_arm_throw_exception */
225                         ARM_ADD_REG_IMM8 (code, ARMREG_R1, ARMREG_R1, 4);
226         } else {
227                 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR); /* caller ip */
228         }
229         /* FIXME: pointer to the saved fp regs */
230         /*pos = alloc_size - sizeof (double) * MONO_SAVED_FREGS;
231         ppc_addi (code, ppc_r7, ppc_sp, pos);*/
232         /* pointer to the saved int regs */
233         ARM_MOV_REG_REG (code, ARMREG_R3, ARMREG_SP); /* the pushed regs */
234         /* we encode rethrow in the ip, so we avoid args on the stack */
235         ARM_ORR_REG_IMM8 (code, ARMREG_R1, ARMREG_R1, rethrow);
236
237         if (aot) {
238                 const char *icall_name;
239
240                 if (resume_unwind)
241                         icall_name = "mono_arm_resume_unwind";
242                 else if (corlib)
243                         icall_name = "mono_arm_throw_exception_by_token";
244                 else
245                         icall_name = "mono_arm_throw_exception";
246
247                 ji = mono_patch_info_list_prepend (ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
248                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
249                 ARM_B (code, 0);
250                 *(gpointer*)(gpointer)code = NULL;
251                 code += 4;
252                 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
253         } else {
254                 code = mono_arm_emit_load_imm (code, ARMREG_IP, GPOINTER_TO_UINT (resume_unwind ? (gpointer)mono_arm_resume_unwind : (corlib ? (gpointer)mono_arm_throw_exception_by_token : (gpointer)mono_arm_throw_exception)));
255         }
256         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
257         ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
258         /* we should never reach this breakpoint */
259         ARM_DBRK (code);
260         g_assert ((code - start) < size);
261         mono_arch_flush_icache (start, code - start);
262
263         if (info)
264                 *info = mono_tramp_info_create (g_strdup_printf (tramp_name), start, code - start, ji, unwind_ops);
265
266         return start;
267 }
268
269 /**
270  * arch_get_throw_exception:
271  *
272  * Returns a function pointer which can be used to raise 
273  * exceptions. The returned function has the following 
274  * signature: void (*func) (MonoException *exc); 
275  * For example to raise an arithmetic exception you can use:
276  *
277  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
278  * x86_call_code (code, arch_get_throw_exception ()); 
279  *
280  */
281 gpointer 
282 mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
283 {
284         return get_throw_trampoline (132, FALSE, FALSE, FALSE, FALSE, "throw_exception", info, aot);
285 }
286
287 /**
288  * mono_arch_get_rethrow_exception:
289  *
290  * Returns a function pointer which can be used to rethrow 
291  * exceptions. The returned function has the following 
292  * signature: void (*func) (MonoException *exc); 
293  *
294  */
295 gpointer
296 mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot)
297 {
298         return get_throw_trampoline (132, FALSE, TRUE, FALSE, FALSE, "rethrow_exception", info, aot);
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  * On ARM, the ip is passed instead of an offset.
311  */
312 gpointer 
313 mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
314 {
315         return get_throw_trampoline (168, TRUE, FALSE, FALSE, FALSE, "throw_corlib_exception", info, aot);
316 }       
317
318 GSList*
319 mono_arm_get_exception_trampolines (gboolean aot)
320 {
321         MonoTrampInfo *info;
322         GSList *tramps = NULL;
323
324         /* LLVM uses the normal trampolines, but with a different name */
325         get_throw_trampoline (168, TRUE, FALSE, FALSE, FALSE, "llvm_throw_corlib_exception_trampoline", &info, aot);
326         tramps = g_slist_prepend (tramps, info);
327         
328         get_throw_trampoline (168, TRUE, FALSE, TRUE, FALSE, "llvm_throw_corlib_exception_abs_trampoline", &info, aot);
329         tramps = g_slist_prepend (tramps, info);
330
331         get_throw_trampoline (168, FALSE, FALSE, FALSE, TRUE, "llvm_resume_unwind_trampoline", &info, aot);
332         tramps = g_slist_prepend (tramps, info);
333
334         return tramps;
335 }
336
337 void
338 mono_arch_exceptions_init (void)
339 {
340         guint8 *tramp;
341         GSList *tramps, *l;
342         
343         if (mono_aot_only) {
344                 tramp = mono_aot_get_trampoline ("llvm_throw_corlib_exception_trampoline");
345                 mono_register_jit_icall (tramp, "llvm_throw_corlib_exception_trampoline", NULL, TRUE);
346                 tramp = mono_aot_get_trampoline ("llvm_throw_corlib_exception_abs_trampoline");
347                 mono_register_jit_icall (tramp, "llvm_throw_corlib_exception_abs_trampoline", NULL, TRUE);
348                 tramp = mono_aot_get_trampoline ("llvm_resume_unwind_trampoline");
349                 mono_register_jit_icall (tramp, "llvm_resume_unwind_trampoline", NULL, TRUE);
350         } else {
351                 tramps = mono_arm_get_exception_trampolines (FALSE);
352                 for (l = tramps; l; l = l->next) {
353                         MonoTrampInfo *info = l->data;
354
355                         mono_register_jit_icall (info->code, g_strdup (info->name), NULL, TRUE);
356                         mono_save_trampoline_xdebug_info (info);
357                         mono_tramp_info_free (info);
358                 }
359                 g_slist_free (tramps);
360         }
361 }
362
363 /* 
364  * mono_arch_find_jit_info:
365  *
366  * See exceptions-amd64.c for docs;
367  */
368 gboolean
369 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, 
370                                                          MonoJitInfo *ji, MonoContext *ctx, 
371                                                          MonoContext *new_ctx, MonoLMF **lmf,
372                                                          mgreg_t **save_locations,
373                                                          StackFrameInfo *frame)
374 {
375         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
376
377         memset (frame, 0, sizeof (StackFrameInfo));
378         frame->ji = ji;
379
380         *new_ctx = *ctx;
381
382         if (ji != NULL) {
383                 int i;
384                 gssize regs [MONO_MAX_IREGS + 1];
385                 guint8 *cfa;
386                 guint32 unwind_info_len;
387                 guint8 *unwind_info;
388
389                 frame->type = FRAME_TYPE_MANAGED;
390
391                 if (ji->from_aot)
392                         unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len);
393                 else
394                         unwind_info = mono_get_cached_unwind_info (ji->used_regs, &unwind_info_len);
395
396                 for (i = 0; i < 16; ++i)
397                         regs [i] = new_ctx->regs [i];
398                 regs [ARMREG_SP] = new_ctx->esp;
399
400                 mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, 
401                                                    (guint8*)ji->code_start + ji->code_size,
402                                                    ip, regs, MONO_MAX_IREGS,
403                                                    save_locations, MONO_MAX_IREGS, &cfa);
404
405                 for (i = 0; i < 16; ++i)
406                         new_ctx->regs [i] = regs [i];
407                 new_ctx->eip = regs [ARMREG_LR];
408                 new_ctx->esp = (gsize)cfa;
409
410                 if (*lmf && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->esp)) {
411                         /* remove any unused lmf */
412                         *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
413                 }
414
415                 /* Clear thumb bit */
416                 new_ctx->eip &= ~1;
417
418                 /* we substract 1, so that the IP points into the call instruction */
419                 new_ctx->eip--;
420
421                 return TRUE;
422         } else if (*lmf) {
423
424                 if (((gsize)(*lmf)->previous_lmf) & 2) {
425                         /* 
426                          * This LMF entry is created by the soft debug code to mark transitions to
427                          * managed code done during invokes.
428                          */
429                         MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
430
431                         g_assert (ext->debugger_invoke);
432
433                         memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
434
435                         *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
436
437                         frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
438
439                         return TRUE;
440                 }
441
442                 frame->type = FRAME_TYPE_MANAGED_TO_NATIVE;
443                 
444                 if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL))) {
445                         frame->ji = ji;
446                 } else {
447                         if (!(*lmf)->method)
448                                 return FALSE;
449                         frame->method = (*lmf)->method;
450                 }
451
452                 /*
453                  * The LMF is saved at the start of the method using:
454                  * ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP)
455                  * ARM_PUSH (code, 0x5ff0);
456                  * So it stores the register state as it existed at the caller.
457                  */
458                 memcpy (&new_ctx->regs [0], &(*lmf)->iregs [0], sizeof (mgreg_t) * 13);
459                 /* SP is skipped */
460                 new_ctx->regs [ARMREG_LR] = (*lmf)->iregs [ARMREG_LR - 1];
461                 new_ctx->esp = (*lmf)->iregs [ARMREG_IP];
462                 new_ctx->eip = new_ctx->regs [ARMREG_LR];
463
464                 /* Clear thumb bit */
465                 new_ctx->eip &= ~1;
466
467                 /* we substract 1, so that the IP points into the call instruction */
468                 new_ctx->eip--;
469
470                 *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
471
472                 return TRUE;
473         }
474
475         return FALSE;
476 }
477
478 void
479 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
480 {
481 #ifdef MONO_CROSS_COMPILE
482         g_assert_not_reached ();
483 #elif BROKEN_LINUX
484         g_assert_not_reached ();
485 #else
486         arm_ucontext *my_uc = sigctx;
487
488         mctx->eip = UCONTEXT_REG_PC (my_uc);
489         mctx->esp = UCONTEXT_REG_SP (my_uc);
490         memcpy (&mctx->regs, &UCONTEXT_REG_R0 (my_uc), sizeof (mgreg_t) * 16);
491 #endif
492 }
493
494 void
495 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *ctx)
496 {
497 #ifdef MONO_CROSS_COMPILE
498         g_assert_not_reached ();
499 #elif BROKEN_LINUX
500         g_assert_not_reached ();
501 #else
502         arm_ucontext *my_uc = ctx;
503
504         UCONTEXT_REG_PC (my_uc) = mctx->eip;
505         UCONTEXT_REG_SP (my_uc) = mctx->regs [ARMREG_FP];
506         /* The upper registers are not guaranteed to be valid */
507         memcpy (&UCONTEXT_REG_R0 (my_uc), &mctx->regs, sizeof (mgreg_t) * 12);
508 #endif
509 }
510
511 /*
512  * handle_exception:
513  *
514  *   Called by resuming from a signal handler.
515  */
516 static void
517 handle_signal_exception (gpointer obj, gboolean test_only)
518 {
519         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
520         MonoContext ctx;
521         static void (*restore_context) (MonoContext *);
522
523         if (!restore_context)
524                 restore_context = mono_get_restore_context ();
525
526         memcpy (&ctx, &jit_tls->ex_ctx, sizeof (MonoContext));
527
528         mono_handle_exception (&ctx, obj, MONO_CONTEXT_GET_IP (&ctx), test_only);
529
530         restore_context (&ctx);
531 }
532
533 /*
534  * This works around a gcc 4.5 bug:
535  * https://bugs.launchpad.net/ubuntu/+source/gcc-4.5/+bug/721531
536  */
537 #if defined(__GNUC__)
538 __attribute__((noinline))
539 #endif
540 static gpointer
541 get_handle_signal_exception_addr (void)
542 {
543         return handle_signal_exception;
544 }
545
546 /*
547  * This is the function called from the signal handler
548  */
549 gboolean
550 mono_arch_handle_exception (void *ctx, gpointer obj, gboolean test_only)
551 {
552 #if defined(MONO_CROSS_COMPILE)
553         g_assert_not_reached ();
554 #elif defined(MONO_ARCH_USE_SIGACTION)
555         arm_ucontext *sigctx = ctx;
556         /*
557          * Handling the exception in the signal handler is problematic, since the original
558          * signal is disabled, and we could run arbitrary code though the debugger. So
559          * resume into the normal stack and do most work there if possible.
560          */
561         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
562         guint64 sp = UCONTEXT_REG_SP (sigctx);
563
564         /* Pass the ctx parameter in TLS */
565         mono_arch_sigctx_to_monoctx (sigctx, &jit_tls->ex_ctx);
566         /* The others in registers */
567         UCONTEXT_REG_R0 (sigctx) = (gsize)obj;
568         UCONTEXT_REG_R1 (sigctx) = test_only;
569
570         /* Allocate a stack frame */
571         sp -= 16;
572         UCONTEXT_REG_SP (sigctx) = sp;
573
574         UCONTEXT_REG_PC (sigctx) = (gsize)get_handle_signal_exception_addr ();
575 #ifdef UCONTEXT_REG_CPSR
576         if ((gsize)UCONTEXT_REG_PC (sigctx) & 1)
577                 /* Transition to thumb */
578                 UCONTEXT_REG_CPSR (sigctx) |= (1 << 5);
579         else
580                 /* Transition to ARM */
581                 UCONTEXT_REG_CPSR (sigctx) &= ~(1 << 5);
582 #endif
583
584         return TRUE;
585 #else
586         MonoContext mctx;
587         gboolean result;
588
589         mono_arch_sigctx_to_monoctx (ctx, &mctx);
590
591         result = mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only);
592         /* restore the context so that returning from the signal handler will invoke
593          * the catch clause 
594          */
595         mono_arch_monoctx_to_sigctx (&mctx, ctx);
596         return result;
597 #endif
598 }
599
600 gpointer
601 mono_arch_ip_from_context (void *sigctx)
602 {
603 #ifdef MONO_CROSS_COMPILE
604         g_assert_not_reached ();
605 #elif BROKEN_LINUX
606         g_assert_not_reached ();
607 #else
608         arm_ucontext *my_uc = sigctx;
609         return (void*) UCONTEXT_REG_PC (my_uc);
610 #endif
611 }