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