[System] Add new 'Mono.Security.Interface.MonoTlsProviderFactory' callback to let...
[mono.git] / mono / mini / exceptions-arm64.c
1 /*
2  * exceptions-arm64.c: exception support for ARM64
3  *
4  * Copyright 2013 Xamarin Inc
5  *
6  * Based on exceptions-arm.c:
7  *
8  * Authors:
9  *   Dietmar Maurer (dietmar@ximian.com)
10  *   Paolo Molaro (lupus@ximian.com)
11  *
12  * (C) 2001 Ximian, Inc.
13  */
14
15 #include "mini.h"
16
17 #include <mono/arch/arm64/arm64-codegen.h>
18 #include <mono/metadata/abi-details.h>
19
20 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
21
22 #ifndef DISABLE_JIT
23
24 gpointer
25 mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
26 {
27         guint8 *start, *code;
28         MonoJumpInfo *ji = NULL;
29         GSList *unwind_ops = NULL;
30         int i, ctx_reg, size;
31
32         size = 256;
33         code = start = mono_global_codeman_reserve (size);
34
35         arm_movx (code, ARMREG_IP0, ARMREG_R0);
36         ctx_reg = ARMREG_IP0;
37         /* Restore fregs */
38         for (i = 0; i < 32; ++i)
39                 arm_ldrfpx (code, i, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, fregs) + (i * 8));
40         /* Restore gregs */
41         // FIXME: Restore less registers
42         // FIXME: fp should be restored later
43         code = mono_arm_emit_load_regarray (code, 0xffffffff & ~(1 << ctx_reg) & ~(1 << ARMREG_SP), ctx_reg, MONO_STRUCT_OFFSET (MonoContext, regs));
44         /* ip0/ip1 doesn't need to be restored */
45         /* ip1 = pc */
46         arm_ldrx (code, ARMREG_IP1, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, pc));
47         /* ip0 = sp */
48         arm_ldrx (code, ARMREG_IP0, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_SP * 8));
49         /* Restore sp, ctx is no longer valid */
50         arm_movspx (code, ARMREG_SP, ARMREG_IP0); 
51         /* Branch to pc */
52         arm_brx (code, ARMREG_IP1);
53         /* Not reached */
54         arm_brk (code, 0);
55
56         g_assert ((code - start) < size);
57         mono_arch_flush_icache (start, code - start);
58         mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
59
60         if (info)
61                 *info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops);
62
63         return start;
64 }
65
66 gpointer
67 mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
68 {
69         guint8 *code;
70         guint8* start;
71         int size, offset, gregs_offset, fregs_offset, ctx_offset, num_fregs, frame_size;
72         MonoJumpInfo *ji = NULL;
73         GSList *unwind_ops = NULL;
74
75         size = 512;
76         start = code = mono_global_codeman_reserve (size);
77
78         /* Compute stack frame size and offsets */
79         offset = 0;
80         /* frame block */
81         offset += 2 * 8;
82         /* gregs */
83         gregs_offset = offset;
84         offset += 32 * 8;
85         /* fregs */
86         num_fregs = 8;
87         fregs_offset = offset;
88         offset += num_fregs * 8;
89         ctx_offset = offset;
90         ctx_offset += 8;
91         frame_size = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT);
92
93         /*
94          * We are being called from C code, ctx is in r0, the address to call is in r1.
95          * We need to save state, restore ctx, make the call, then restore the previous state,
96          * returning the value returned by the call.
97          */
98
99         /* Setup a frame */
100         arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -frame_size);
101         arm_movspx (code, ARMREG_FP, ARMREG_SP);
102
103         /* Save ctx */
104         arm_strx (code, ARMREG_R0, ARMREG_FP, ctx_offset);
105         /* Save gregs */
106         code = mono_arm_emit_store_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS | (1 << ARMREG_FP), ARMREG_FP, gregs_offset);
107         /* No need to save/restore fregs, since we don't currently use them */
108
109         /* Load regs from ctx */
110         code = mono_arm_emit_load_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, regs));
111         /* Load fp */
112         arm_ldrx (code, ARMREG_FP, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_FP * 8));
113
114         /* Make the call */
115         arm_blrx (code, ARMREG_R1);
116         /* For filters, the result is in R0 */
117
118         /* Restore fp */
119         arm_ldrx (code, ARMREG_FP, ARMREG_SP, gregs_offset + (ARMREG_FP * 8));
120         /* Load ctx */
121         arm_ldrx (code, ARMREG_IP0, ARMREG_FP, ctx_offset);
122         /* Save registers back to ctx */
123         /* This isn't strictly neccessary since we don't allocate variables used in eh clauses to registers */
124         code = mono_arm_emit_store_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoContext, regs));
125
126         /* Restore regs */
127         code = mono_arm_emit_load_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_FP, gregs_offset);
128         /* Destroy frame */
129         code = mono_arm_emit_destroy_frame (code, frame_size, (1 << ARMREG_IP0));
130         arm_retx (code, ARMREG_LR);
131
132         g_assert ((code - start) < size);
133         mono_arch_flush_icache (start, code - start);
134         mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
135
136         if (info)
137                 *info = mono_tramp_info_create ("call_filter", start, code - start, ji, unwind_ops);
138
139         return start;
140 }
141
142 static gpointer 
143 get_throw_trampoline (int size, gboolean corlib, gboolean rethrow, gboolean llvm, gboolean resume_unwind, const char *tramp_name, MonoTrampInfo **info, gboolean aot)
144 {
145         guint8 *start, *code;
146         MonoJumpInfo *ji = NULL;
147         GSList *unwind_ops = NULL;
148         int i, offset, gregs_offset, fregs_offset, frame_size, num_fregs;
149
150         code = start = mono_global_codeman_reserve (size);
151
152         /* We are being called by JITted code, the exception object/type token is in R0 */
153
154         /* Compute stack frame size and offsets */
155         offset = 0;
156         /* frame block */
157         offset += 2 * 8;
158         /* gregs */
159         gregs_offset = offset;
160         offset += 32 * 8;
161         /* fregs */
162         num_fregs = 8;
163         fregs_offset = offset;
164         offset += num_fregs * 8;
165         frame_size = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT);
166
167         /* Setup a frame */
168         arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -frame_size);
169         arm_movspx (code, ARMREG_FP, ARMREG_SP);
170
171         /* Save gregs */
172         code = mono_arm_emit_store_regarray (code, 0xffffffff, ARMREG_FP, gregs_offset);
173         if (corlib && !llvm)
174                 /* The real LR is in R1 */
175                 arm_strx (code, ARMREG_R1, ARMREG_FP, gregs_offset + (ARMREG_LR * 8));
176         /* Save fp/sp */
177         arm_ldrx (code, ARMREG_IP0, ARMREG_FP, 0);
178         arm_strx (code, ARMREG_IP0, ARMREG_FP, gregs_offset + (ARMREG_FP * 8));
179         arm_addx_imm (code, ARMREG_IP0, ARMREG_FP, frame_size);
180         arm_strx (code, ARMREG_IP0, ARMREG_FP, gregs_offset + (ARMREG_SP * 8)); 
181         /* Save fregs */
182         for (i = 0; i < num_fregs; ++i)
183                 arm_strfpx (code, ARMREG_D8 + i, ARMREG_FP, fregs_offset + (i * 8));
184
185         /* Call the C trampoline function */
186         /* Arg1 =  exception object/type token */
187         arm_movx (code, ARMREG_R0, ARMREG_R0);
188         /* Arg2 = caller ip */
189         if (corlib) {
190                 if (llvm)
191                         arm_ldrx (code, ARMREG_R1, ARMREG_FP, gregs_offset + (ARMREG_LR * 8));
192                 else
193                         arm_movx (code, ARMREG_R1, ARMREG_R1);
194         } else {
195                 arm_ldrx (code, ARMREG_R1, ARMREG_FP, 8);
196         }
197         /* Arg 3 = gregs */
198         arm_addx_imm (code, ARMREG_R2, ARMREG_FP, gregs_offset);
199         /* Arg 4 = fregs */
200         arm_addx_imm (code, ARMREG_R3, ARMREG_FP, fregs_offset);
201         /* Arg 5 = corlib */
202         arm_movzx (code, ARMREG_R4, corlib ? 1 : 0, 0);
203         /* Arg 6 = rethrow */
204         arm_movzx (code, ARMREG_R5, rethrow ? 1 : 0, 0);
205         /* Call the function */
206         if (aot) {
207                 const char *icall_name;
208
209                 if (resume_unwind)
210                         icall_name = "mono_arm_resume_unwind";
211                 else
212                         icall_name = "mono_arm_throw_exception";
213
214                 code = mono_arm_emit_aotconst (&ji, code, start, ARMREG_LR, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
215         } else {
216                 gpointer icall_func;
217
218                 if (resume_unwind)
219                         icall_func = mono_arm_resume_unwind;
220                 else
221                         icall_func = mono_arm_throw_exception;
222
223                 code = mono_arm_emit_imm64 (code, ARMREG_LR, (guint64)icall_func);
224         }
225         arm_blrx (code, ARMREG_LR);
226         /* This shouldn't return */
227         arm_brk (code, 0x0);
228
229         g_assert ((code - start) < size);
230         mono_arch_flush_icache (start, code - start);
231         mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
232
233         if (info)
234                 *info = mono_tramp_info_create (tramp_name, start, code - start, ji, unwind_ops);
235
236         return start;
237 }
238
239 gpointer 
240 mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
241 {
242         return get_throw_trampoline (256, FALSE, FALSE, FALSE, FALSE, "throw_exception", info, aot);
243 }
244
245 gpointer
246 mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot)
247 {
248         return get_throw_trampoline (256, FALSE, TRUE, FALSE, FALSE, "rethrow_exception", info, aot);
249 }
250
251 gpointer 
252 mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
253 {
254         return get_throw_trampoline (256, TRUE, FALSE, FALSE, FALSE, "throw_corlib_exception", info, aot);
255 }
256
257 GSList*
258 mono_arm_get_exception_trampolines (gboolean aot)
259 {
260         MonoTrampInfo *info;
261         GSList *tramps = NULL;
262
263         /* LLVM uses the normal trampolines, but with a different name */
264         get_throw_trampoline (256, TRUE, FALSE, FALSE, FALSE, "llvm_throw_corlib_exception_trampoline", &info, aot);
265         tramps = g_slist_prepend (tramps, info);
266         
267         get_throw_trampoline (256, TRUE, FALSE, TRUE, FALSE, "llvm_throw_corlib_exception_abs_trampoline", &info, aot);
268         tramps = g_slist_prepend (tramps, info);
269
270         get_throw_trampoline (256, FALSE, FALSE, FALSE, TRUE, "llvm_resume_unwind_trampoline", &info, aot);
271         tramps = g_slist_prepend (tramps, info);
272
273         return tramps;
274 }
275
276 #else /* DISABLE_JIT */
277
278 gpointer
279 mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
280 {
281         g_assert_not_reached ();
282         return NULL;
283 }
284
285 gpointer
286 mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
287 {
288         g_assert_not_reached ();
289         return NULL;
290 }
291
292 gpointer 
293 mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
294 {
295         g_assert_not_reached ();
296         return NULL;
297 }
298
299 gpointer
300 mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot)
301 {
302         g_assert_not_reached ();
303         return NULL;
304 }
305
306 gpointer 
307 mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
308 {
309         g_assert_not_reached ();
310         return NULL;
311 }       
312
313 GSList*
314 mono_arm_get_exception_trampolines (gboolean aot)
315 {
316         g_assert_not_reached ();
317         return NULL;
318 }
319
320 #endif /* !DISABLE_JIT */
321
322 void
323 mono_arch_exceptions_init (void)
324 {
325         guint8 *tramp;
326         GSList *tramps, *l;
327
328         if (mono_aot_only) {
329                 tramp = mono_aot_get_trampoline ("llvm_throw_corlib_exception_trampoline");
330                 mono_register_jit_icall (tramp, "llvm_throw_corlib_exception_trampoline", NULL, TRUE);
331                 tramp = mono_aot_get_trampoline ("llvm_throw_corlib_exception_abs_trampoline");
332                 mono_register_jit_icall (tramp, "llvm_throw_corlib_exception_abs_trampoline", NULL, TRUE);
333                 tramp = mono_aot_get_trampoline ("llvm_resume_unwind_trampoline");
334                 mono_register_jit_icall (tramp, "llvm_resume_unwind_trampoline", NULL, TRUE);
335         } else {
336                 tramps = mono_arm_get_exception_trampolines (FALSE);
337                 for (l = tramps; l; l = l->next) {
338                         MonoTrampInfo *info = l->data;
339
340                         mono_register_jit_icall (info->code, g_strdup (info->name), NULL, TRUE);
341                         mono_tramp_info_register (info, NULL);
342                 }
343                 g_slist_free (tramps);
344         }
345 }
346
347 /*
348  * mono_arm_throw_exception:
349  *
350  *   This function is called by the exception trampolines.
351  * FP_REGS points to the 8 callee saved fp regs.
352  */
353 void
354 mono_arm_throw_exception (gpointer arg, mgreg_t pc, mgreg_t *int_regs, gdouble *fp_regs, gboolean corlib, gboolean rethrow)
355 {
356         MonoContext ctx;
357         MonoObject *exc = NULL;
358         guint32 ex_token_index, ex_token;
359
360         if (!corlib)
361                 exc = arg;
362         else {
363                 ex_token_index = (guint64)arg;
364                 ex_token = MONO_TOKEN_TYPE_DEF | ex_token_index;
365                 exc = (MonoObject*)mono_exception_from_token (mono_defaults.corlib, ex_token);
366         }
367
368         /* Adjust pc so it points into the call instruction */
369         pc -= 4;
370
371         /* Initialize a ctx based on the arguments */
372         memset (&ctx, 0, sizeof (MonoContext));
373         memcpy (&(ctx.regs [0]), int_regs, sizeof (mgreg_t) * 32);
374         memcpy (&(ctx.fregs [ARMREG_D8]), fp_regs, sizeof (double) * 8);
375         ctx.pc = pc;
376
377         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
378                 MonoException *mono_ex = (MonoException*)exc;
379                 if (!rethrow)
380                         mono_ex->stack_trace = NULL;
381         }
382
383         mono_handle_exception (&ctx, exc);
384
385         mono_restore_context (&ctx);
386 }
387
388 void
389 mono_arm_resume_unwind (gpointer arg, mgreg_t pc, mgreg_t *int_regs, gdouble *fp_regs, gboolean corlib, gboolean rethrow)
390 {
391         MonoContext ctx;
392
393         /* Adjust pc so it points into the call instruction */
394         pc -= 4;
395
396         /* Initialize a ctx based on the arguments */
397         memset (&ctx, 0, sizeof (MonoContext));
398         memcpy (&(ctx.regs [0]), int_regs, sizeof (mgreg_t) * 32);
399         memcpy (&(ctx.fregs [ARMREG_D8]), fp_regs, sizeof (double) * 8);
400         ctx.pc = pc;
401
402         mono_resume_unwind (&ctx);
403 }
404
405 /* 
406  * mono_arch_unwind_frame:
407  *
408  * See exceptions-amd64.c for docs;
409  */
410 gboolean
411 mono_arch_unwind_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, 
412                                                          MonoJitInfo *ji, MonoContext *ctx, 
413                                                          MonoContext *new_ctx, MonoLMF **lmf,
414                                                          mgreg_t **save_locations,
415                                                          StackFrameInfo *frame)
416 {
417         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
418
419         memset (frame, 0, sizeof (StackFrameInfo));
420         frame->ji = ji;
421
422         *new_ctx = *ctx;
423
424         if (ji != NULL) {
425                 mgreg_t regs [MONO_MAX_IREGS + 8 + 1];
426                 guint8 *cfa;
427                 guint32 unwind_info_len;
428                 guint8 *unwind_info;
429
430                 frame->type = FRAME_TYPE_MANAGED;
431
432                 unwind_info = mono_jinfo_get_unwind_info (ji, &unwind_info_len);
433
434                 memcpy (regs, &new_ctx->regs, sizeof (mgreg_t) * 32);
435                 /* v8..v15 are callee saved */
436                 memcpy (regs + MONO_MAX_IREGS, &(new_ctx->fregs [8]), sizeof (mgreg_t) * 8);
437
438                 mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, 
439                                                    (guint8*)ji->code_start + ji->code_size,
440                                                    ip, NULL, regs, MONO_MAX_IREGS + 8,
441                                                    save_locations, MONO_MAX_IREGS, &cfa);
442
443                 memcpy (&new_ctx->regs, regs, sizeof (mgreg_t) * 32);
444                 memcpy (&(new_ctx->fregs [8]), regs + MONO_MAX_IREGS, sizeof (mgreg_t) * 8);
445
446                 new_ctx->pc = regs [ARMREG_LR];
447                 new_ctx->regs [ARMREG_SP] = (mgreg_t)cfa;
448
449                 if (*lmf && (*lmf)->gregs [MONO_ARCH_LMF_REG_SP] && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->gregs [MONO_ARCH_LMF_REG_SP])) {
450                         /* remove any unused lmf */
451                         *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
452                 }
453
454                 /* we substract 1, so that the IP points into the call instruction */
455                 new_ctx->pc--;
456
457                 return TRUE;
458         } else if (*lmf) {
459                 if (((gsize)(*lmf)->previous_lmf) & 2) {
460                         /* 
461                          * This LMF entry is created by the soft debug code to mark transitions to
462                          * managed code done during invokes.
463                          */
464                         MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
465
466                         g_assert (ext->debugger_invoke);
467
468                         memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
469
470                         *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
471
472                         frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
473
474                         return TRUE;
475                 }
476
477                 frame->type = FRAME_TYPE_MANAGED_TO_NATIVE;
478
479                 ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->pc, NULL);
480                 if (!ji)
481                         return FALSE;
482
483                 g_assert (MONO_ARCH_LMF_REGS == ((0x3ff << 19) | (1 << ARMREG_FP) | (1 << ARMREG_SP)));
484                 memcpy (&new_ctx->regs [ARMREG_R19], &(*lmf)->gregs [0], sizeof (mgreg_t) * 10);
485                 new_ctx->regs [ARMREG_FP] = (*lmf)->gregs [MONO_ARCH_LMF_REG_FP];
486                 new_ctx->regs [ARMREG_SP] = (*lmf)->gregs [MONO_ARCH_LMF_REG_SP];
487                 new_ctx->pc = (*lmf)->pc;
488
489                 /* we substract 1, so that the IP points into the call instruction */
490                 new_ctx->pc--;
491
492                 *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
493
494                 return TRUE;
495         }
496
497         return FALSE;
498 }
499
500 void
501 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
502 {
503         mono_sigctx_to_monoctx (sigctx, mctx);
504 }
505
506 void
507 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
508 {
509         mono_monoctx_to_sigctx (mctx, sigctx);
510 }
511
512 /*
513  * handle_exception:
514  *
515  *   Called by resuming from a signal handler.
516  */
517 static void
518 handle_signal_exception (gpointer obj)
519 {
520         MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id);
521         MonoContext ctx;
522
523         memcpy (&ctx, &jit_tls->ex_ctx, sizeof (MonoContext));
524
525         mono_handle_exception (&ctx, obj);
526
527         mono_restore_context (&ctx);
528 }
529
530 /*
531  * This is the function called from the signal handler
532  */
533 gboolean
534 mono_arch_handle_exception (void *ctx, gpointer obj)
535 {
536 #if defined(MONO_CROSS_COMPILE)
537         g_assert_not_reached ();
538 #else
539         MonoJitTlsData *jit_tls;
540         void *sigctx = ctx;
541
542         /*
543          * Resume into the normal stack and handle the exception there.
544          */
545         jit_tls = mono_native_tls_get_value (mono_jit_tls_id);
546
547         /* Pass the ctx parameter in TLS */
548         mono_arch_sigctx_to_monoctx (sigctx, &jit_tls->ex_ctx);
549         /* The others in registers */
550         UCONTEXT_REG_R0 (sigctx) = (gsize)obj;
551
552         UCONTEXT_REG_PC (sigctx) = (gsize)handle_signal_exception;
553         UCONTEXT_REG_SP (sigctx) = UCONTEXT_REG_SP (sigctx) - MONO_ARCH_REDZONE_SIZE;
554 #endif
555
556         return TRUE;
557 }
558
559 gpointer
560 mono_arch_ip_from_context (void *sigctx)
561 {
562 #ifdef MONO_CROSS_COMPILE
563         g_assert_not_reached ();
564         return NULL;
565 #else
566         return (gpointer)UCONTEXT_REG_PC (sigctx);
567 #endif
568 }
569
570 void
571 mono_arch_setup_async_callback (MonoContext *ctx, void (*async_cb)(void *fun), gpointer user_data)
572 {
573         mgreg_t sp = (mgreg_t)MONO_CONTEXT_GET_SP (ctx);
574
575         // FIXME:
576         g_assert (!user_data);
577
578         /* Allocate a stack frame */
579         sp -= 32;
580         MONO_CONTEXT_SET_SP (ctx, sp);
581
582         mono_arch_setup_resume_sighandler_ctx (ctx, async_cb);
583 }
584
585 /*
586  * mono_arch_setup_resume_sighandler_ctx:
587  *
588  *   Setup CTX so execution continues at FUNC.
589  */
590 void
591 mono_arch_setup_resume_sighandler_ctx (MonoContext *ctx, gpointer func)
592 {
593         MONO_CONTEXT_SET_IP (ctx,func);
594 }