Merge pull request #588 from Daniel15/bug-10872
[mono.git] / mono / mini / tramp-x86.c
1 /*
2  * tramp-x86.c: JIT trampoline code for x86
3  *
4  * Authors:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12
13 #include <mono/metadata/appdomain.h>
14 #include <mono/metadata/metadata-internals.h>
15 #include <mono/metadata/marshal.h>
16 #include <mono/metadata/tabledefs.h>
17 #include <mono/metadata/mono-debug.h>
18 #include <mono/metadata/mono-debug-debugger.h>
19 #include <mono/metadata/monitor.h>
20 #include <mono/metadata/gc-internal.h>
21 #include <mono/arch/x86/x86-codegen.h>
22
23 #include <mono/utils/memcheck.h>
24
25 #include "mini.h"
26 #include "mini-x86.h"
27
28 static guint8* nullified_class_init_trampoline;
29
30 /*
31  * mono_arch_get_unbox_trampoline:
32  * @m: method pointer
33  * @addr: pointer to native code for @m
34  *
35  * when value type methods are called through the vtable we need to unbox the
36  * this argument. This method returns a pointer to a trampoline which does
37  * unboxing before calling the method
38  */
39 gpointer
40 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
41 {
42         guint8 *code, *start;
43         int this_pos = 4;
44         MonoDomain *domain = mono_domain_get ();
45
46         start = code = mono_domain_code_reserve (domain, 16);
47
48         x86_alu_membase_imm (code, X86_ADD, X86_ESP, this_pos, sizeof (MonoObject));
49         x86_jump_code (code, addr);
50         g_assert ((code - start) < 16);
51
52         nacl_domain_code_validate (domain, &start, 16, &code);
53
54         return start;
55 }
56
57 gpointer
58 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
59 {
60         guint8 *code, *start;
61         int buf_len;
62
63         MonoDomain *domain = mono_domain_get ();
64
65         buf_len = 10;
66
67         start = code = mono_domain_code_reserve (domain, buf_len);
68
69         x86_mov_reg_imm (code, MONO_ARCH_RGCTX_REG, mrgctx);
70         x86_jump_code (code, addr);
71         g_assert ((code - start) <= buf_len);
72
73         nacl_domain_code_validate (domain, &start, buf_len, &code);
74         mono_arch_flush_icache (start, code - start);
75
76         return start;
77 }
78
79 gpointer
80 mono_arch_get_llvm_imt_trampoline (MonoDomain *domain, MonoMethod *m, int vt_offset)
81 {
82         guint8 *code, *start;
83         int buf_len;
84         int this_offset;
85
86         buf_len = 32;
87
88         start = code = mono_domain_code_reserve (domain, buf_len);
89
90         this_offset = mono_x86_get_this_arg_offset (NULL, mono_method_signature (m));
91
92         /* Set imt arg */
93         x86_mov_reg_imm (code, MONO_ARCH_IMT_REG, m);
94         /* Load this */
95         x86_mov_reg_membase (code, X86_EAX, X86_ESP, this_offset + 4, 4);
96         /* Load vtable address */
97         x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, 4);
98         x86_jump_membase (code, X86_EAX, vt_offset);
99
100         g_assert ((code - start) < buf_len);
101
102         nacl_domain_code_validate (domain, &start, buf_len, &code);
103
104         mono_arch_flush_icache (start, code - start);
105
106         return start;
107 }
108
109 void
110 mono_arch_patch_callsite (guint8 *method_start, guint8 *orig_code, guint8 *addr)
111 {
112 #if defined(__default_codegen__)
113         guint8 *code;
114         guint8 buf [8];
115         gboolean can_write = mono_breakpoint_clean_code (method_start, orig_code, 8, buf, sizeof (buf));
116
117         code = buf + 8;
118
119         /* go to the start of the call instruction
120          *
121          * address_byte = (m << 6) | (o << 3) | reg
122          * call opcode: 0xff address_byte displacement
123          * 0xff m=1,o=2 imm8
124          * 0xff m=2,o=2 imm32
125          */
126         code -= 6;
127         orig_code -= 6;
128         if (code [1] == 0xe8) {
129                 if (can_write) {
130                         InterlockedExchange ((gint32*)(orig_code + 2), (guint)addr - ((guint)orig_code + 1) - 5);
131
132                         /* Tell valgrind to recompile the patched code */
133                         VALGRIND_DISCARD_TRANSLATIONS (orig_code + 2, 4);
134                 }
135         } else if (code [1] == 0xe9) {
136                 /* A PLT entry: jmp <DISP> */
137                 if (can_write)
138                         InterlockedExchange ((gint32*)(orig_code + 2), (guint)addr - ((guint)orig_code + 1) - 5);
139         } else {
140                 printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
141                                 code [4], code [5], code [6]);
142                 g_assert_not_reached ();
143         }
144 #elif defined(__native_client__)
145         /* Target must be bundle-aligned */
146         g_assert (((guint32)addr & kNaClAlignmentMask) == 0);
147
148         /* 0xe8 = call <DISP>, 0xe9 = jump <DISP> */
149         if ((orig_code [-5] == 0xe8) || orig_code [-6] == 0xe9) {
150                 int ret;
151                 gint32 offset = (gint32)addr - (gint32)orig_code;
152                 guint8 buf[sizeof(gint32)];
153                 *((gint32*)(buf)) = offset;
154                 ret = nacl_dyncode_modify (orig_code - sizeof(gint32), buf, sizeof(gint32));
155                 g_assert (ret == 0);
156         } else {
157                 printf ("Invalid trampoline sequence %p: %02x %02x %02x %02x %02x\n", orig_code, orig_code [-5], orig_code [-4], orig_code [-3], orig_code [-2], orig_code[-1]);
158                 g_assert_not_reached ();
159         }
160 #endif
161 }
162
163 void
164 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
165 {
166         guint32 offset;
167
168         /* Patch the jump table entry used by the plt entry */
169
170 #if defined(__native_client_codegen__) || defined(__native_client__)
171         /* for both compiler and runtime      */
172         /* A PLT entry:                       */
173         /*        mov <DISP>(%ebx), %ecx      */
174         /*        and 0xffffffe0, %ecx        */
175         /*        jmp *%ecx                   */
176         g_assert (code [0] == 0x8b);
177         g_assert (code [1] == 0x8b);
178
179         offset = *(guint32*)(code + 2);
180 #elif defined(__default_codegen__)
181         /* A PLT entry: jmp *<DISP>(%ebx) */
182         g_assert (code [0] == 0xff);
183         g_assert (code [1] == 0xa3);
184
185         offset = *(guint32*)(code + 2);
186 #endif  /* __native_client_codegen__ */
187         if (!got)
188                 got = (gpointer*)(gsize) regs [MONO_ARCH_GOT_REG];
189         *(guint8**)((guint8*)got + offset) = addr;
190 }
191
192 static gpointer
193 get_vcall_slot (guint8 *code, mgreg_t *regs, int *displacement)
194 {
195         const int kBufSize = NACL_SIZE (8, 16);
196         guint8 buf [64];
197         guint8 reg = 0;
198         gint32 disp = 0;
199
200         mono_breakpoint_clean_code (NULL, code, kBufSize, buf, kBufSize);
201         code = buf + 8;
202
203         *displacement = 0;
204
205         if ((code [0] == 0xff) && ((code [1] & 0x18) == 0x10) && ((code [1] >> 6) == 2)) {
206                 reg = code [1] & 0x07;
207                 disp = *((gint32*)(code + 2));
208 #if defined(__native_client_codegen__) || defined(__native_client__)
209         } else if ((code[1] == 0x83) && (code[2] == 0xe1) && (code[4] == 0xff) &&
210                            (code[5] == 0xd1) && (code[-5] == 0x8b)) {
211                 disp = *((gint32*)(code - 3));
212                 reg = code[-4] & 0x07;
213         } else if ((code[-2] == 0x8b) && (code[1] == 0x83) && (code[4] == 0xff)) {
214                 reg = code[-1] & 0x07;
215                 disp = (signed char)code[0];
216 #endif
217         } else {
218                 g_assert_not_reached ();
219                 return NULL;
220         }
221
222         *displacement = disp;
223         return (gpointer)regs [reg];
224 }
225
226 static gpointer*
227 get_vcall_slot_addr (guint8* code, mgreg_t *regs)
228 {
229         gpointer vt;
230         int displacement;
231         vt = get_vcall_slot (code, regs, &displacement);
232         if (!vt)
233                 return NULL;
234         return (gpointer*)((char*)vt + displacement);
235 }
236
237 void
238 mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs)
239 {
240         guint8 buf [16];
241         gboolean can_write = mono_breakpoint_clean_code (NULL, code, 6, buf, sizeof (buf));
242
243         if (!can_write)
244                 return;
245
246         code -= 5;
247         if (code [0] == 0xe8) {
248 #if defined(__default_codegen__)
249                 if (!mono_running_on_valgrind ()) {
250                         guint32 ops;
251                         /*
252                          * Thread safe code patching using the algorithm from the paper
253                          * 'Practicing JUDO: Java Under Dynamic Optimizations'
254                          */
255                         /* 
256                          * First atomically change the the first 2 bytes of the call to a
257                          * spinning jump.
258                          */
259                         ops = 0xfeeb;
260                         InterlockedExchange ((gint32*)code, ops);
261
262                         /* Then change the other bytes to a nop */
263                         code [2] = 0x90;
264                         code [3] = 0x90;
265                         code [4] = 0x90;
266
267                         /* Then atomically change the first 4 bytes to a nop as well */
268                         ops = 0x90909090;
269                         InterlockedExchange ((gint32*)code, ops);
270                         /* FIXME: the calltree skin trips on the self modifying code above */
271
272                         /* Tell valgrind to recompile the patched code */
273                         //VALGRIND_DISCARD_TRANSLATIONS (code, 8);
274                 }
275 #elif defined(__native_client_codegen__)
276                 mono_arch_patch_callsite (code, code + 5, nullified_class_init_trampoline);
277 #endif
278         } else if (code [0] == 0x90 || code [0] == 0xeb) {
279                 /* Already changed by another thread */
280                 ;
281         } else if ((code [-1] == 0xff) && (x86_modrm_reg (code [0]) == 0x2)) {
282                 /* call *<OFFSET>(<REG>) -> Call made from AOT code */
283                 gpointer *vtable_slot;
284
285                 vtable_slot = get_vcall_slot_addr (code + 5, regs);
286                 g_assert (vtable_slot);
287
288                 *vtable_slot = nullified_class_init_trampoline;
289         } else {
290                         printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
291                                 code [4], code [5], code [6]);
292                         g_assert_not_reached ();
293                 }
294 }
295
296 void
297 mono_arch_nullify_plt_entry (guint8 *code, mgreg_t *regs)
298 {
299         if (mono_aot_only && !nullified_class_init_trampoline)
300                 nullified_class_init_trampoline = mono_aot_get_trampoline ("nullified_class_init_trampoline");
301
302         mono_arch_patch_plt_entry (code, NULL, regs, nullified_class_init_trampoline);
303 }
304
305 guchar*
306 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
307 {
308         guint8 *buf, *code, *tramp;
309         int pushed_args, pushed_args_caller_saved;
310         GSList *unwind_ops = NULL;
311         MonoJumpInfo *ji = NULL;
312
313         unwind_ops = mono_arch_get_cie_program ();
314
315         code = buf = mono_global_codeman_reserve (256);
316
317         /* Note that there is a single argument to the trampoline
318          * and it is stored at: esp + pushed_args * sizeof (gpointer)
319          * the ret address is at: esp + (pushed_args + 1) * sizeof (gpointer)
320          */
321
322         /* Put all registers into an array on the stack
323          * If this code is changed, make sure to update the offset value in
324          * mono_arch_get_this_arg_from_call () in mini-x86.c.
325          */
326         x86_push_reg (code, X86_EDI);
327         x86_push_reg (code, X86_ESI);
328         x86_push_reg (code, X86_EBP);
329         x86_push_reg (code, X86_ESP);
330         x86_push_reg (code, X86_EBX);
331         x86_push_reg (code, X86_EDX);
332         x86_push_reg (code, X86_ECX);
333         x86_push_reg (code, X86_EAX);
334
335         pushed_args_caller_saved = pushed_args = 8;
336
337         /* Align stack on apple */
338         x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4);
339
340         pushed_args ++;
341
342         /* save LMF begin */
343
344         /* save the IP (caller ip) */
345         if (tramp_type == MONO_TRAMPOLINE_JUMP)
346                 x86_push_imm (code, 0);
347         else
348                 x86_push_membase (code, X86_ESP, (pushed_args + 1) * sizeof (gpointer));
349
350         pushed_args++;
351
352         x86_push_reg (code, X86_EBP);
353         x86_push_reg (code, X86_ESI);
354         x86_push_reg (code, X86_EDI);
355         x86_push_reg (code, X86_EBX);
356
357         pushed_args += 4;
358
359         /* save ESP */
360         x86_push_reg (code, X86_ESP);
361         /* Adjust ESP so it points to the previous frame */
362         x86_alu_membase_imm (code, X86_ADD, X86_ESP, 0, (pushed_args + 2) * 4);
363
364         pushed_args ++;
365
366         /* save method info */
367         if ((tramp_type == MONO_TRAMPOLINE_JIT) || (tramp_type == MONO_TRAMPOLINE_JUMP))
368                 x86_push_membase (code, X86_ESP, pushed_args * sizeof (gpointer));
369         else
370                 x86_push_imm (code, 0);
371
372         pushed_args++;
373
374         /* On apple, the stack is correctly aligned to 16 bytes because pushed_args is
375          * 16 and there is the extra trampoline arg + the return ip pushed by call
376          * FIXME: Note that if an exception happens while some args are pushed
377          * on the stack, the stack will be misaligned.
378          */
379         g_assert (pushed_args == 16);
380
381         /* get the address of lmf for the current thread */
382         if (aot) {
383                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_get_lmf_addr");
384                 x86_call_reg (code, X86_EAX);
385         } else {
386                 x86_call_code (code, mono_get_lmf_addr);
387         }
388         /* push lmf */
389         x86_push_reg (code, X86_EAX); 
390         /* push *lfm (previous_lmf) */
391         x86_push_membase (code, X86_EAX, 0);
392         /* Signal to mono_arch_find_jit_info () that this is a trampoline frame */
393         x86_alu_membase_imm (code, X86_ADD, X86_ESP, 0, 1);
394         /* *(lmf) = ESP */
395         x86_mov_membase_reg (code, X86_EAX, 0, X86_ESP, 4);
396         /* save LFM end */
397
398         pushed_args += 2;
399
400         /* starting the call sequence */
401
402         /* FIXME: Push the trampoline address */
403         x86_push_imm (code, 0);
404
405         pushed_args++;
406
407         /* push the method info */
408         x86_push_membase (code, X86_ESP, pushed_args * sizeof (gpointer));
409
410         pushed_args++;
411
412         /* push the return address onto the stack */
413         if (tramp_type == MONO_TRAMPOLINE_JUMP)
414                 x86_push_imm (code, 0);
415         else
416                 x86_push_membase (code, X86_ESP, (pushed_args + 1) * sizeof (gpointer));
417         pushed_args++;
418         /* push the address of the register array */
419         x86_lea_membase (code, X86_EAX, X86_ESP, (pushed_args - 8) * sizeof (gpointer));
420         x86_push_reg (code, X86_EAX);
421
422         pushed_args++;
423
424 #ifdef __APPLE__
425         /* check the stack is aligned after the ret ip is pushed */
426         /*x86_mov_reg_reg (buf, X86_EDX, X86_ESP, 4);
427         x86_alu_reg_imm (buf, X86_AND, X86_EDX, 15);
428         x86_alu_reg_imm (buf, X86_CMP, X86_EDX, 0);
429         x86_branch_disp (buf, X86_CC_Z, 3, FALSE);
430         x86_breakpoint (buf);*/
431 #endif
432
433         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, X86_ESP, ((pushed_args + 2) * 4));
434
435         if (aot) {
436                 char *icall_name = g_strdup_printf ("trampoline_func_%d", tramp_type);
437                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
438                 x86_call_reg (code, X86_EAX);
439         } else {
440                 tramp = (guint8*)mono_get_trampoline_func (tramp_type);
441                 x86_call_code (code, tramp);
442         }
443
444         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4*4);
445
446         pushed_args -= 4;
447
448         /* Check for thread interruption */
449         /* This is not perf critical code so no need to check the interrupt flag */
450         /* Align the stack on osx */
451         x86_alu_reg_imm (code, X86_SUB, X86_ESP, 3 * 4);
452         x86_push_reg (code, X86_EAX);
453         if (aot) {
454                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_thread_force_interruption_checkpoint");
455                 x86_call_reg (code, X86_EAX);
456         } else {
457                 x86_call_code (code, (guint8*)mono_thread_force_interruption_checkpoint);
458         }
459         x86_pop_reg (code, X86_EAX);
460         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 3 * 4);
461
462         /* Restore LMF */
463
464         /* ebx = previous_lmf */
465         x86_pop_reg (code, X86_EBX);
466         pushed_args--;
467         x86_alu_reg_imm (code, X86_SUB, X86_EBX, 1);
468
469         /* edi = lmf */
470         x86_pop_reg (code, X86_EDI);
471         pushed_args--;
472
473         /* *(lmf) = previous_lmf */
474         x86_mov_membase_reg (code, X86_EDI, 0, X86_EBX, 4);
475
476         /* discard method info */
477         x86_pop_reg (code, X86_ESI);
478         pushed_args--;
479
480         /* discard ESP */
481         x86_pop_reg (code, X86_ESI);
482         pushed_args--;
483
484         /* restore caller saved regs */
485         x86_pop_reg (code, X86_EBX);
486         x86_pop_reg (code, X86_EDI);
487         x86_pop_reg (code, X86_ESI);
488         x86_pop_reg (code, X86_EBP);
489
490         pushed_args -= 4;
491
492         /* discard save IP */
493         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
494         pushed_args--;
495
496         /* restore LMF end */
497
498         if (!MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type)) {
499                 /* 
500                  * Overwrite the method ptr with the address we need to jump to,
501                  * to free %eax.
502                  */
503                 x86_mov_membase_reg (code, X86_ESP, pushed_args * sizeof (gpointer), X86_EAX, 4);
504         }
505
506         /* Restore caller saved registers */
507         x86_mov_reg_membase (code, X86_ECX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_ECX) * 4, 4);
508         x86_mov_reg_membase (code, X86_EDX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_EDX) * 4, 4);
509         if ((tramp_type == MONO_TRAMPOLINE_RESTORE_STACK_PROT) || (tramp_type == MONO_TRAMPOLINE_AOT_PLT))
510                 x86_mov_reg_membase (code, X86_EAX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_EAX) * 4, 4);
511
512         if (!MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type)) {
513                 /* Pop saved reg array + stack align */
514                 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 9 * 4);
515                 pushed_args -= 9;
516                 g_assert (pushed_args == 0);
517         } else {
518                 /* Pop saved reg array + stack align + method ptr */
519                 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 10 * 4);
520                 pushed_args -= 10;
521
522                 /* We've popped one more stack item than we've pushed (the
523                    method ptr argument), so we must end up at -1. */
524                 g_assert (pushed_args == -1);
525         }
526
527         x86_ret (code);
528
529         nacl_global_codeman_validate (&buf, 256, &code);
530         g_assert ((code - buf) <= 256);
531
532         if (info)
533                 *info = mono_tramp_info_create (mono_get_generic_trampoline_name (tramp_type), buf, code - buf, ji, unwind_ops);
534
535         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
536                 /* Initialize the nullified class init trampoline used in the AOT case */
537                 nullified_class_init_trampoline = mono_arch_get_nullified_class_init_trampoline (NULL);
538         }
539
540         return buf;
541 }
542
543 gpointer
544 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
545 {
546         guint8 *code, *buf;
547         int tramp_size = NACL_SIZE (16, kNaClAlignment);                
548
549         code = buf = mono_global_codeman_reserve (tramp_size);
550         x86_ret (code);
551
552         nacl_global_codeman_validate (&buf, tramp_size, &code);
553
554         mono_arch_flush_icache (buf, code - buf);
555
556         if (info)
557                 *info = mono_tramp_info_create (g_strdup_printf ("nullified_class_init_trampoline"), buf, code - buf, NULL, NULL);
558
559         if (mono_jit_map_is_enabled ())
560                 mono_emit_jit_tramp (buf, code - buf, "nullified_class_init_trampoline");
561
562         return buf;
563 }
564
565 #define TRAMPOLINE_SIZE 10
566
567 gpointer
568 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
569 {
570         guint8 *code, *buf, *tramp;
571         
572         tramp = mono_get_trampoline_code (tramp_type);
573
574         code = buf = mono_domain_code_reserve_align (domain, TRAMPOLINE_SIZE, NACL_SIZE (4, kNaClAlignment));
575
576         x86_push_imm (buf, arg1);
577         x86_jump_code (buf, tramp);
578         g_assert ((buf - code) <= TRAMPOLINE_SIZE);
579
580         nacl_domain_code_validate (domain, &code, NACL_SIZE (4, kNaClAlignment), &buf);
581
582         mono_arch_flush_icache (code, buf - code);
583
584         if (code_len)
585                 *code_len = buf - code;
586
587         return code;
588 }
589
590 gpointer
591 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
592 {
593         guint8 *tramp;
594         guint8 *code, *buf;
595         guint8 **rgctx_null_jumps;
596         int tramp_size;
597         int depth, index;
598         int i;
599         gboolean mrgctx;
600         MonoJumpInfo *ji = NULL;
601         GSList *unwind_ops = NULL;
602
603         unwind_ops = mono_arch_get_cie_program ();
604
605         mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
606         index = MONO_RGCTX_SLOT_INDEX (slot);
607         if (mrgctx)
608                 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
609         for (depth = 0; ; ++depth) {
610                 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
611
612                 if (index < size - 1)
613                         break;
614                 index -= size - 1;
615         }
616
617 #if defined(__default_codegen__)
618         tramp_size = (aot ? 64 : 36) + 6 * depth;
619 #elif defined(__native_client_codegen__)
620         tramp_size = (aot ? 64 : 36) + 2 * kNaClAlignment +
621           6 * (depth + kNaClAlignment);
622 #endif
623
624         code = buf = mono_global_codeman_reserve (tramp_size);
625
626         rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
627
628         /* load vtable/mrgctx ptr */
629         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
630         if (!mrgctx) {
631                 /* load rgctx ptr from vtable */
632                 x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context), 4);
633                 /* is the rgctx ptr null? */
634                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
635                 /* if yes, jump to actual trampoline */
636                 rgctx_null_jumps [0] = code;
637                 x86_branch8 (code, X86_CC_Z, -1, 1);
638         }
639
640         for (i = 0; i < depth; ++i) {
641                 /* load ptr to next array */
642                 if (mrgctx && i == 0)
643                         x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT, 4);
644                 else
645                         x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, 4);
646                 /* is the ptr null? */
647                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
648                 /* if yes, jump to actual trampoline */
649                 rgctx_null_jumps [i + 1] = code;
650                 x86_branch8 (code, X86_CC_Z, -1, 1);
651         }
652
653         /* fetch slot */
654         x86_mov_reg_membase (code, X86_EAX, X86_EAX, sizeof (gpointer) * (index + 1), 4);
655         /* is the slot null? */
656         x86_test_reg_reg (code, X86_EAX, X86_EAX);
657         /* if yes, jump to actual trampoline */
658         rgctx_null_jumps [depth + 1] = code;
659         x86_branch8 (code, X86_CC_Z, -1, 1);
660         /* otherwise return */
661         x86_ret (code);
662
663         for (i = mrgctx ? 1 : 0; i <= depth + 1; ++i)
664                 x86_patch (rgctx_null_jumps [i], code);
665
666         g_free (rgctx_null_jumps);
667
668         x86_mov_reg_membase (code, MONO_ARCH_VTABLE_REG, X86_ESP, 4, 4);
669
670         if (aot) {
671                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
672                 x86_jump_reg (code, X86_EAX);
673         } else {
674                 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), NULL);
675
676                 /* jump to the actual trampoline */
677                 x86_jump_code (code, tramp);
678         }
679
680         nacl_global_codeman_validate (&buf, tramp_size, &code);
681         mono_arch_flush_icache (buf, code - buf);
682
683         g_assert (code - buf <= tramp_size);
684
685         if (info)
686                 *info = mono_tramp_info_create (mono_get_rgctx_fetch_trampoline_name (slot), buf, code - buf, ji, unwind_ops);
687
688         return buf;
689 }
690
691 /*
692  * mono_arch_create_general_rgctx_lazy_fetch_trampoline:
693  *
694  *   This is a general variant of the rgctx fetch trampolines. It receives a pointer to gpointer[2] in the rgctx reg. The first entry contains the slot, the second
695  * the trampoline to call if the slot is not filled.
696  */
697 gpointer
698 mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboolean aot)
699 {
700         guint8 *code, *buf;
701         int tramp_size;
702         MonoJumpInfo *ji = NULL;
703         GSList *unwind_ops = NULL;
704
705         g_assert (aot);
706
707         unwind_ops = mono_arch_get_cie_program ();
708
709         tramp_size = 64;
710
711         code = buf = mono_global_codeman_reserve (tramp_size);
712
713         // FIXME: Currently, we always go to the slow path.
714         
715         /* Load trampoline addr */
716         x86_mov_reg_membase (code, X86_EAX, MONO_ARCH_RGCTX_REG, 4, 4);
717         /* Load mrgctx/vtable */
718         x86_mov_reg_membase (code, MONO_ARCH_VTABLE_REG, X86_ESP, 4, 4);
719
720         x86_jump_reg (code, X86_EAX);
721
722         nacl_global_codeman_validate (&buf, tramp_size, &code);
723         mono_arch_flush_icache (buf, code - buf);
724
725         g_assert (code - buf <= tramp_size);
726
727         if (info)
728                 *info = mono_tramp_info_create ("rgctx_fetch_trampoline_general", buf, code - buf, ji, unwind_ops);
729
730         return buf;
731 }
732
733 gpointer
734 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
735 {
736         guint8 *tramp;
737         guint8 *code, *buf;
738         static int byte_offset = -1;
739         static guint8 bitmask;
740         guint8 *jump;
741         int tramp_size;
742         GSList *unwind_ops = NULL;
743         MonoJumpInfo *ji = NULL;
744
745         tramp_size = 64;
746
747         code = buf = mono_global_codeman_reserve (tramp_size);
748
749         unwind_ops = mono_arch_get_cie_program ();
750
751         if (byte_offset < 0)
752                 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
753
754         x86_test_membase_imm (code, MONO_ARCH_VTABLE_REG, byte_offset, bitmask);
755         jump = code;
756         x86_branch8 (code, X86_CC_Z, -1, 1);
757
758         x86_ret (code);
759
760         x86_patch (jump, code);
761
762         /* Push the vtable so the stack is the same as in a specific trampoline */
763         x86_push_reg (code, MONO_ARCH_VTABLE_REG);
764
765         if (aot) {
766                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_generic_class_init");
767                 x86_jump_reg (code, X86_EAX);
768         } else {
769                 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC_CLASS_INIT);
770
771                 /* jump to the actual trampoline */
772                 x86_jump_code (code, tramp);
773         }
774
775         mono_arch_flush_icache (code, code - buf);
776
777         g_assert (code - buf <= tramp_size);
778 #ifdef __native_client_codegen__
779         g_assert (code - buf <= kNaClAlignment);
780 #endif
781
782         nacl_global_codeman_validate (&buf, tramp_size, &code);
783
784         if (info)
785                 *info = mono_tramp_info_create (g_strdup_printf ("generic_class_init_trampoline"), buf, code - buf, ji, unwind_ops);
786
787         return buf;
788 }
789
790 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
791 /*
792  * The code produced by this trampoline is equivalent to this:
793  *
794  * if (obj) {
795  *      if (obj->synchronisation) {
796  *              if (obj->synchronisation->owner == 0) {
797  *                      if (cmpxch (&obj->synchronisation->owner, TID, 0) == 0)
798  *                              return;
799  *              }
800  *              if (obj->synchronisation->owner == TID) {
801  *                      ++obj->synchronisation->nest;
802  *                      return;
803  *              }
804  *      }
805  * }
806  * return full_monitor_enter ();
807  *
808  */
809 gpointer
810 mono_arch_create_monitor_enter_trampoline (MonoTrampInfo **info, gboolean aot)
811 {
812         guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_MONITOR_ENTER);
813         guint8 *code, *buf;
814         guint8 *jump_obj_null, *jump_sync_null, *jump_other_owner, *jump_cmpxchg_failed, *jump_tid, *jump_sync_thin_hash = NULL;
815         int tramp_size;
816         int owner_offset, nest_offset, dummy;
817         MonoJumpInfo *ji = NULL;
818         GSList *unwind_ops = NULL;
819
820         g_assert (MONO_ARCH_MONITOR_OBJECT_REG == X86_EAX);
821
822         mono_monitor_threads_sync_members_offset (&owner_offset, &nest_offset, &dummy);
823         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset) == sizeof (gpointer));
824         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32));
825         owner_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset);
826         nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset);
827
828         tramp_size = NACL_SIZE (96, 128);
829
830         code = buf = mono_global_codeman_reserve (tramp_size);
831
832         if (mono_thread_get_tls_offset () != -1) {
833                 /* MonoObject* obj is in EAX */
834                 /* is obj null? */
835                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
836                 /* if yes, jump to actual trampoline */
837                 jump_obj_null = code;
838                 x86_branch8 (code, X86_CC_Z, -1, 1);
839
840                 /* load obj->synchronization to ECX */
841                 x86_mov_reg_membase (code, X86_ECX, X86_EAX, G_STRUCT_OFFSET (MonoObject, synchronisation), 4);
842
843                 if (mono_gc_is_moving ()) {
844                         /*if bit zero is set it's a thin hash*/
845                         /*FIXME use testb encoding*/
846                         x86_test_reg_imm (code, X86_ECX, 0x01);
847                         jump_sync_thin_hash = code;
848                         x86_branch8 (code, X86_CC_NE, -1, 1);
849
850                         /*clear bits used by the gc*/
851                         x86_alu_reg_imm (code, X86_AND, X86_ECX, ~0x3);
852                 }
853
854                 /* is synchronization null? */
855                 x86_test_reg_reg (code, X86_ECX, X86_ECX);
856
857                 /* if yes, jump to actual trampoline */
858                 jump_sync_null = code;
859                 x86_branch8 (code, X86_CC_Z, -1, 1);
860
861                 /* load MonoInternalThread* into EDX */
862                 code = mono_x86_emit_tls_get (code, X86_EDX, mono_thread_get_tls_offset ());
863                 /* load TID into EDX */
864                 x86_mov_reg_membase (code, X86_EDX, X86_EDX, G_STRUCT_OFFSET (MonoInternalThread, tid), 4);
865
866                 /* is synchronization->owner null? */
867                 x86_alu_membase_imm (code, X86_CMP, X86_ECX, owner_offset, 0);
868                 /* if not, jump to next case */
869                 jump_tid = code;
870                 x86_branch8 (code, X86_CC_NZ, -1, 1);
871
872                 /* if yes, try a compare-exchange with the TID */
873                 /* free up register EAX, needed for the zero */
874                 x86_push_reg (code, X86_EAX);
875                 /* zero EAX */
876                 x86_alu_reg_reg (code, X86_XOR, X86_EAX, X86_EAX);
877                 /* compare and exchange */
878                 x86_prefix (code, X86_LOCK_PREFIX);
879                 x86_cmpxchg_membase_reg (code, X86_ECX, owner_offset, X86_EDX);
880                 /* if not successful, jump to actual trampoline */
881                 jump_cmpxchg_failed = code;
882                 x86_branch8 (code, X86_CC_NZ, -1, 1);
883                 /* if successful, pop and return */
884                 x86_pop_reg (code, X86_EAX);
885                 x86_ret (code);
886
887                 /* next case: synchronization->owner is not null */
888                 x86_patch (jump_tid, code);
889                 /* is synchronization->owner == TID? */
890                 x86_alu_membase_reg (code, X86_CMP, X86_ECX, owner_offset, X86_EDX);
891                 /* if not, jump to actual trampoline */
892                 jump_other_owner = code;
893                 x86_branch8 (code, X86_CC_NZ, -1, 1);
894                 /* if yes, increment nest */
895                 x86_inc_membase (code, X86_ECX, nest_offset);
896                 /* return */
897                 x86_ret (code);
898
899                 /* push obj */
900                 x86_patch (jump_obj_null, code);
901                 if (jump_sync_thin_hash)
902                         x86_patch (jump_sync_thin_hash, code);
903                 x86_patch (jump_sync_null, code);
904                 x86_patch (jump_other_owner, code);
905                 x86_push_reg (code, X86_EAX);
906                 /* jump to the actual trampoline */
907                 x86_patch (jump_cmpxchg_failed, code);
908                 if (aot) {
909                         /* We are calling the generic trampoline directly, the argument is pushed
910                          * on the stack just like a specific trampoline.
911                          */
912                         code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_monitor_enter");
913                         x86_jump_reg (code, X86_EAX);
914                 } else {
915                         x86_jump_code (code, tramp);
916                 }
917         } else {
918                 /* push obj and jump to the actual trampoline */
919                 x86_push_reg (code, X86_EAX);
920                 if (aot) {
921                         code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_monitor_enter");
922                         x86_jump_reg (code, X86_EAX);
923                 } else {
924                         x86_jump_code (code, tramp);
925                 }
926         }
927
928         mono_arch_flush_icache (buf, code - buf);
929         g_assert (code - buf <= tramp_size);
930
931         nacl_global_codeman_validate (&buf, tramp_size, &code);
932
933         if (info)
934                 *info = mono_tramp_info_create (g_strdup_printf ("monitor_enter_trampoline"), buf, code - buf, ji, unwind_ops);
935
936         return buf;
937 }
938
939 gpointer
940 mono_arch_create_monitor_exit_trampoline (MonoTrampInfo **info, gboolean aot)
941 {
942         guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_MONITOR_EXIT);
943         guint8 *code, *buf;
944         guint8 *jump_obj_null, *jump_have_waiters, *jump_sync_null, *jump_not_owned, *jump_sync_thin_hash = NULL;
945         guint8 *jump_next;
946         int tramp_size;
947         int owner_offset, nest_offset, entry_count_offset;
948         MonoJumpInfo *ji = NULL;
949         GSList *unwind_ops = NULL;
950
951         g_assert (MONO_ARCH_MONITOR_OBJECT_REG == X86_EAX);
952
953         mono_monitor_threads_sync_members_offset (&owner_offset, &nest_offset, &entry_count_offset);
954         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset) == sizeof (gpointer));
955         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32));
956         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (entry_count_offset) == sizeof (gint32));
957         owner_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset);
958         nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset);
959         entry_count_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (entry_count_offset);
960
961         tramp_size = NACL_SIZE (96, 128);
962
963         code = buf = mono_global_codeman_reserve (tramp_size);
964
965         if (mono_thread_get_tls_offset () != -1) {
966                 /* MonoObject* obj is in EAX */
967                 /* is obj null? */
968                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
969                 /* if yes, jump to actual trampoline */
970                 jump_obj_null = code;
971                 x86_branch8 (code, X86_CC_Z, -1, 1);
972
973                 /* load obj->synchronization to ECX */
974                 x86_mov_reg_membase (code, X86_ECX, X86_EAX, G_STRUCT_OFFSET (MonoObject, synchronisation), 4);
975
976                 if (mono_gc_is_moving ()) {
977                         /*if bit zero is set it's a thin hash*/
978                         /*FIXME use testb encoding*/
979                         x86_test_reg_imm (code, X86_ECX, 0x01);
980                         jump_sync_thin_hash = code;
981                         x86_branch8 (code, X86_CC_NE, -1, 1);
982
983                         /*clear bits used by the gc*/
984                         x86_alu_reg_imm (code, X86_AND, X86_ECX, ~0x3);
985                 }
986
987                 /* is synchronization null? */
988                 x86_test_reg_reg (code, X86_ECX, X86_ECX);
989                 /* if yes, jump to actual trampoline */
990                 jump_sync_null = code;
991                 x86_branch8 (code, X86_CC_Z, -1, 1);
992
993                 /* next case: synchronization is not null */
994                 /* load MonoInternalThread* into EDX */
995                 code = mono_x86_emit_tls_get (code, X86_EDX, mono_thread_get_tls_offset ());
996                 /* load TID into EDX */
997                 x86_mov_reg_membase (code, X86_EDX, X86_EDX, G_STRUCT_OFFSET (MonoInternalThread, tid), 4);
998                 /* is synchronization->owner == TID */
999                 x86_alu_membase_reg (code, X86_CMP, X86_ECX, owner_offset, X86_EDX);
1000                 /* if no, jump to actual trampoline */
1001                 jump_not_owned = code;
1002                 x86_branch8 (code, X86_CC_NZ, -1, 1);
1003
1004                 /* next case: synchronization->owner == TID */
1005                 /* is synchronization->nest == 1 */
1006                 x86_alu_membase_imm (code, X86_CMP, X86_ECX, nest_offset, 1);
1007                 /* if not, jump to next case */
1008                 jump_next = code;
1009                 x86_branch8 (code, X86_CC_NZ, -1, 1);
1010                 /* if yes, is synchronization->entry_count zero? */
1011                 x86_alu_membase_imm (code, X86_CMP, X86_ECX, entry_count_offset, 0);
1012                 /* if not, jump to actual trampoline */
1013                 jump_have_waiters = code;
1014                 x86_branch8 (code, X86_CC_NZ, -1 , 1);
1015                 /* if yes, set synchronization->owner to null and return */
1016                 x86_mov_membase_imm (code, X86_ECX, owner_offset, 0, 4);
1017                 x86_ret (code);
1018
1019                 /* next case: synchronization->nest is not 1 */
1020                 x86_patch (jump_next, code);
1021                 /* decrease synchronization->nest and return */
1022                 x86_dec_membase (code, X86_ECX, nest_offset);
1023                 x86_ret (code);
1024
1025                 /* push obj and jump to the actual trampoline */
1026                 x86_patch (jump_obj_null, code);
1027                 if (jump_sync_thin_hash)
1028                         x86_patch (jump_sync_thin_hash, code);
1029                 x86_patch (jump_have_waiters, code);
1030                 x86_patch (jump_not_owned, code);
1031                 x86_patch (jump_sync_null, code);
1032         }
1033
1034         /* push obj and jump to the actual trampoline */
1035         x86_push_reg (code, X86_EAX);
1036         if (aot) {
1037                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_monitor_exit");
1038                 x86_jump_reg (code, X86_EAX);
1039         } else {
1040                 x86_jump_code (code, tramp);
1041         }
1042
1043         nacl_global_codeman_validate (&buf, tramp_size, &code);
1044
1045         mono_arch_flush_icache (buf, code - buf);
1046         g_assert (code - buf <= tramp_size);
1047
1048         if (info)
1049                 *info = mono_tramp_info_create (g_strdup_printf ("monitor_exit_trampoline"), buf, code - buf, ji, unwind_ops);
1050
1051         return buf;
1052 }
1053
1054 #else
1055
1056 gpointer
1057 mono_arch_create_monitor_enter_trampoline (MonoTrampInfo **info, gboolean aot)
1058 {
1059         g_assert_not_reached ();
1060         return NULL;
1061 }
1062
1063 gpointer
1064 mono_arch_create_monitor_exit_trampoline (MonoTrampInfo **info, gboolean aot)
1065 {
1066         g_assert_not_reached ();
1067         return NULL;
1068 }
1069
1070 #endif
1071
1072 void
1073 mono_arch_invalidate_method (MonoJitInfo *ji, void *func, gpointer func_arg)
1074 {
1075         /* FIXME: This is not thread safe */
1076         guint8 *code = ji->code_start;
1077
1078         x86_push_imm (code, func_arg);
1079         x86_call_code (code, (guint8*)func);
1080 }
1081
1082 static void
1083 handler_block_trampoline_helper (gpointer *ptr)
1084 {
1085         MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id);
1086         *ptr = jit_tls->handler_block_return_address;
1087 }
1088
1089 gpointer
1090 mono_arch_create_handler_block_trampoline (void)
1091 {
1092         guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD);
1093         guint8 *code, *buf;
1094         int tramp_size = 64;
1095         code = buf = mono_global_codeman_reserve (tramp_size);
1096
1097         /*
1098         This trampoline restore the call chain of the handler block then jumps into the code that deals with it.
1099         */
1100
1101         if (mono_get_jit_tls_offset () != -1) {
1102                 code = mono_x86_emit_tls_get (code, X86_EAX, mono_get_jit_tls_offset ());
1103                 x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoJitTlsData, handler_block_return_address), 4);
1104                 /*simulate a call*/
1105                 /*Fix stack alignment*/
1106                 x86_alu_reg_imm (code, X86_SUB, X86_ESP, 0x8);
1107                 x86_push_reg (code, X86_EAX);
1108                 x86_jump_code (code, tramp);
1109         } else {
1110                 /*Slow path uses a c helper*/
1111                 x86_alu_reg_imm (code, X86_SUB, X86_ESP, 0x8);
1112                 x86_push_reg (code, X86_ESP);
1113                 x86_push_imm (code, tramp);
1114                 x86_jump_code (code, handler_block_trampoline_helper);
1115         }
1116
1117         nacl_global_codeman_validate (&buf, tramp_size, &code);
1118
1119         mono_arch_flush_icache (buf, code - buf);
1120         g_assert (code - buf <= tramp_size);
1121
1122         if (mono_jit_map_is_enabled ())
1123                 mono_emit_jit_tramp (buf, code - buf, "handler_block_trampoline");
1124
1125         return buf;
1126 }
1127
1128 guint8*
1129 mono_arch_get_call_target (guint8 *code)
1130 {
1131         if (code [-5] == 0xe8) {
1132                 guint32 disp = *(guint32*)(code - 4);
1133                 guint8 *target = code + disp;
1134
1135                 return target;
1136         } else {
1137                 return NULL;
1138         }
1139 }
1140
1141 guint32
1142 mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
1143 {
1144         return *(guint32*)(plt_entry + NACL_SIZE (6, 12));
1145 }
1146
1147 /*
1148  * mono_arch_get_gsharedvt_arg_trampoline:
1149  *
1150  *   Return a trampoline which passes ARG to the gsharedvt in/out trampoline ADDR.
1151  */
1152 gpointer
1153 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
1154 {
1155         guint8 *code, *start;
1156         int buf_len;
1157
1158         buf_len = 10;
1159
1160         start = code = mono_domain_code_reserve (domain, buf_len);
1161
1162         x86_mov_reg_imm (code, X86_EAX, arg);
1163         x86_jump_code (code, addr);
1164         g_assert ((code - start) <= buf_len);
1165
1166         nacl_domain_code_validate (domain, &start, buf_len, &code);
1167         mono_arch_flush_icache (start, code - start);
1168
1169         return start;
1170 }
1171
1172 #if defined(MONOTOUCH) || defined(MONO_EXTENSIONS)
1173
1174 #include "../../../mono-extensions/mono/mini/tramp-x86-gsharedvt.c"
1175
1176 #else
1177
1178 gpointer
1179 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
1180 {
1181         if (info)
1182                 *info = NULL;
1183         return NULL;
1184 }
1185
1186 #endif /* !MONOTOUCH */