Merge pull request #665 from andreas-auerswald/master
[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, size = NACL_SIZE(16, 32);
44         MonoDomain *domain = mono_domain_get ();
45
46         start = code = mono_domain_code_reserve (domain, size);
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) < size);
51
52         nacl_domain_code_validate (domain, &start, size, &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 = NACL_SIZE (10, 32);
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         char *tramp_name;
309         guint8 *buf, *code, *tramp;
310         int pushed_args, pushed_args_caller_saved;
311         GSList *unwind_ops = NULL;
312         MonoJumpInfo *ji = NULL;
313
314         unwind_ops = mono_arch_get_cie_program ();
315
316         code = buf = mono_global_codeman_reserve (256);
317
318         /* Note that there is a single argument to the trampoline
319          * and it is stored at: esp + pushed_args * sizeof (gpointer)
320          * the ret address is at: esp + (pushed_args + 1) * sizeof (gpointer)
321          */
322
323         /* Put all registers into an array on the stack
324          * If this code is changed, make sure to update the offset value in
325          * mono_arch_get_this_arg_from_call () in mini-x86.c.
326          */
327         x86_push_reg (code, X86_EDI);
328         x86_push_reg (code, X86_ESI);
329         x86_push_reg (code, X86_EBP);
330         x86_push_reg (code, X86_ESP);
331         x86_push_reg (code, X86_EBX);
332         x86_push_reg (code, X86_EDX);
333         x86_push_reg (code, X86_ECX);
334         x86_push_reg (code, X86_EAX);
335
336         pushed_args_caller_saved = pushed_args = 8;
337
338         /* Align stack on apple */
339         x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4);
340
341         pushed_args ++;
342
343         /* save LMF begin */
344
345         /* save the IP (caller ip) */
346         if (tramp_type == MONO_TRAMPOLINE_JUMP)
347                 x86_push_imm (code, 0);
348         else
349                 x86_push_membase (code, X86_ESP, (pushed_args + 1) * sizeof (gpointer));
350
351         pushed_args++;
352
353         x86_push_reg (code, X86_EBP);
354         x86_push_reg (code, X86_ESI);
355         x86_push_reg (code, X86_EDI);
356         x86_push_reg (code, X86_EBX);
357
358         pushed_args += 4;
359
360         /* save ESP */
361         x86_push_reg (code, X86_ESP);
362         /* Adjust ESP so it points to the previous frame */
363         x86_alu_membase_imm (code, X86_ADD, X86_ESP, 0, (pushed_args + 2) * 4);
364
365         pushed_args ++;
366
367         /* save method info */
368         if ((tramp_type == MONO_TRAMPOLINE_JIT) || (tramp_type == MONO_TRAMPOLINE_JUMP))
369                 x86_push_membase (code, X86_ESP, pushed_args * sizeof (gpointer));
370         else
371                 x86_push_imm (code, 0);
372
373         pushed_args++;
374
375         /* On apple, the stack is correctly aligned to 16 bytes because pushed_args is
376          * 16 and there is the extra trampoline arg + the return ip pushed by call
377          * FIXME: Note that if an exception happens while some args are pushed
378          * on the stack, the stack will be misaligned.
379          */
380         g_assert (pushed_args == 16);
381
382         /* get the address of lmf for the current thread */
383         if (aot) {
384                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_get_lmf_addr");
385                 x86_call_reg (code, X86_EAX);
386         } else {
387                 x86_call_code (code, mono_get_lmf_addr);
388         }
389         /* push lmf */
390         x86_push_reg (code, X86_EAX); 
391         /* push *lfm (previous_lmf) */
392         x86_push_membase (code, X86_EAX, 0);
393         /* Signal to mono_arch_find_jit_info () that this is a trampoline frame */
394         x86_alu_membase_imm (code, X86_ADD, X86_ESP, 0, 1);
395         /* *(lmf) = ESP */
396         x86_mov_membase_reg (code, X86_EAX, 0, X86_ESP, 4);
397         /* save LFM end */
398
399         pushed_args += 2;
400
401         /* starting the call sequence */
402
403         /* FIXME: Push the trampoline address */
404         x86_push_imm (code, 0);
405
406         pushed_args++;
407
408         /* push the method info */
409         x86_push_membase (code, X86_ESP, pushed_args * sizeof (gpointer));
410
411         pushed_args++;
412
413         /* push the return address onto the stack */
414         if (tramp_type == MONO_TRAMPOLINE_JUMP)
415                 x86_push_imm (code, 0);
416         else
417                 x86_push_membase (code, X86_ESP, (pushed_args + 1) * sizeof (gpointer));
418         pushed_args++;
419         /* push the address of the register array */
420         x86_lea_membase (code, X86_EAX, X86_ESP, (pushed_args - 8) * sizeof (gpointer));
421         x86_push_reg (code, X86_EAX);
422
423         pushed_args++;
424
425 #ifdef __APPLE__
426         /* check the stack is aligned after the ret ip is pushed */
427         /*x86_mov_reg_reg (buf, X86_EDX, X86_ESP, 4);
428         x86_alu_reg_imm (buf, X86_AND, X86_EDX, 15);
429         x86_alu_reg_imm (buf, X86_CMP, X86_EDX, 0);
430         x86_branch_disp (buf, X86_CC_Z, 3, FALSE);
431         x86_breakpoint (buf);*/
432 #endif
433
434         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, X86_ESP, ((pushed_args + 2) * 4));
435
436         if (aot) {
437                 char *icall_name = g_strdup_printf ("trampoline_func_%d", tramp_type);
438                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
439                 x86_call_reg (code, X86_EAX);
440         } else {
441                 tramp = (guint8*)mono_get_trampoline_func (tramp_type);
442                 x86_call_code (code, tramp);
443         }
444
445         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4*4);
446
447         pushed_args -= 4;
448
449         /* Check for thread interruption */
450         /* This is not perf critical code so no need to check the interrupt flag */
451         /* Align the stack on osx */
452         x86_alu_reg_imm (code, X86_SUB, X86_ESP, 3 * 4);
453         x86_push_reg (code, X86_EAX);
454         if (aot) {
455                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_thread_force_interruption_checkpoint");
456                 x86_call_reg (code, X86_EAX);
457         } else {
458                 x86_call_code (code, (guint8*)mono_thread_force_interruption_checkpoint);
459         }
460         x86_pop_reg (code, X86_EAX);
461         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 3 * 4);
462
463         /* Restore LMF */
464
465         /* ebx = previous_lmf */
466         x86_pop_reg (code, X86_EBX);
467         pushed_args--;
468         x86_alu_reg_imm (code, X86_SUB, X86_EBX, 1);
469
470         /* edi = lmf */
471         x86_pop_reg (code, X86_EDI);
472         pushed_args--;
473
474         /* *(lmf) = previous_lmf */
475         x86_mov_membase_reg (code, X86_EDI, 0, X86_EBX, 4);
476
477         /* discard method info */
478         x86_pop_reg (code, X86_ESI);
479         pushed_args--;
480
481         /* discard ESP */
482         x86_pop_reg (code, X86_ESI);
483         pushed_args--;
484
485         /* restore caller saved regs */
486         x86_pop_reg (code, X86_EBX);
487         x86_pop_reg (code, X86_EDI);
488         x86_pop_reg (code, X86_ESI);
489         x86_pop_reg (code, X86_EBP);
490
491         pushed_args -= 4;
492
493         /* discard save IP */
494         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
495         pushed_args--;
496
497         /* restore LMF end */
498
499         if (!MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type)) {
500                 /* 
501                  * Overwrite the method ptr with the address we need to jump to,
502                  * to free %eax.
503                  */
504                 x86_mov_membase_reg (code, X86_ESP, pushed_args * sizeof (gpointer), X86_EAX, 4);
505         }
506
507         /* Restore caller saved registers */
508         x86_mov_reg_membase (code, X86_ECX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_ECX) * 4, 4);
509         x86_mov_reg_membase (code, X86_EDX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_EDX) * 4, 4);
510         if ((tramp_type == MONO_TRAMPOLINE_RESTORE_STACK_PROT) || (tramp_type == MONO_TRAMPOLINE_AOT_PLT))
511                 x86_mov_reg_membase (code, X86_EAX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_EAX) * 4, 4);
512
513         if (!MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type)) {
514                 /* Pop saved reg array + stack align */
515                 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 9 * 4);
516                 pushed_args -= 9;
517                 g_assert (pushed_args == 0);
518         } else {
519                 /* Pop saved reg array + stack align + method ptr */
520                 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 10 * 4);
521                 pushed_args -= 10;
522
523                 /* We've popped one more stack item than we've pushed (the
524                    method ptr argument), so we must end up at -1. */
525                 g_assert (pushed_args == -1);
526         }
527
528         x86_ret (code);
529
530         nacl_global_codeman_validate (&buf, 256, &code);
531         g_assert ((code - buf) <= 256);
532
533         if (info) {
534                 tramp_name = mono_get_generic_trampoline_name (tramp_type);
535                 *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
536                 g_free (tramp_name);
537         }
538
539         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
540                 /* Initialize the nullified class init trampoline used in the AOT case */
541                 nullified_class_init_trampoline = mono_arch_get_nullified_class_init_trampoline (NULL);
542         }
543
544         return buf;
545 }
546
547 gpointer
548 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
549 {
550         guint8 *code, *buf;
551         int tramp_size = NACL_SIZE (16, kNaClAlignment);                
552
553         code = buf = mono_global_codeman_reserve (tramp_size);
554         x86_ret (code);
555
556         nacl_global_codeman_validate (&buf, tramp_size, &code);
557
558         mono_arch_flush_icache (buf, code - buf);
559
560         if (info)
561                 *info = mono_tramp_info_create ("nullified_class_init_trampoline", buf, code - buf, NULL, NULL);
562
563         if (mono_jit_map_is_enabled ())
564                 mono_emit_jit_tramp (buf, code - buf, "nullified_class_init_trampoline");
565
566         return buf;
567 }
568
569 #define TRAMPOLINE_SIZE 10
570
571 gpointer
572 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
573 {
574         guint8 *code, *buf, *tramp;
575         
576         tramp = mono_get_trampoline_code (tramp_type);
577
578         code = buf = mono_domain_code_reserve_align (domain, TRAMPOLINE_SIZE, NACL_SIZE (4, kNaClAlignment));
579
580         x86_push_imm (buf, arg1);
581         x86_jump_code (buf, tramp);
582         g_assert ((buf - code) <= TRAMPOLINE_SIZE);
583
584         nacl_domain_code_validate (domain, &code, NACL_SIZE (4, kNaClAlignment), &buf);
585
586         mono_arch_flush_icache (code, buf - code);
587
588         if (code_len)
589                 *code_len = buf - code;
590
591         return code;
592 }
593
594 gpointer
595 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
596 {
597         guint8 *tramp;
598         guint8 *code, *buf;
599         guint8 **rgctx_null_jumps;
600         int tramp_size;
601         int depth, index;
602         int i;
603         gboolean mrgctx;
604         MonoJumpInfo *ji = NULL;
605         GSList *unwind_ops = NULL;
606
607         unwind_ops = mono_arch_get_cie_program ();
608
609         mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
610         index = MONO_RGCTX_SLOT_INDEX (slot);
611         if (mrgctx)
612                 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
613         for (depth = 0; ; ++depth) {
614                 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
615
616                 if (index < size - 1)
617                         break;
618                 index -= size - 1;
619         }
620
621 #if defined(__default_codegen__)
622         tramp_size = (aot ? 64 : 36) + 6 * depth;
623 #elif defined(__native_client_codegen__)
624         tramp_size = (aot ? 64 : 36) + 2 * kNaClAlignment +
625           6 * (depth + kNaClAlignment);
626 #endif
627
628         code = buf = mono_global_codeman_reserve (tramp_size);
629
630         rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
631
632         /* load vtable/mrgctx ptr */
633         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
634         if (!mrgctx) {
635                 /* load rgctx ptr from vtable */
636                 x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context), 4);
637                 /* is the rgctx ptr null? */
638                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
639                 /* if yes, jump to actual trampoline */
640                 rgctx_null_jumps [0] = code;
641                 x86_branch8 (code, X86_CC_Z, -1, 1);
642         }
643
644         for (i = 0; i < depth; ++i) {
645                 /* load ptr to next array */
646                 if (mrgctx && i == 0)
647                         x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT, 4);
648                 else
649                         x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, 4);
650                 /* is the ptr null? */
651                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
652                 /* if yes, jump to actual trampoline */
653                 rgctx_null_jumps [i + 1] = code;
654                 x86_branch8 (code, X86_CC_Z, -1, 1);
655         }
656
657         /* fetch slot */
658         x86_mov_reg_membase (code, X86_EAX, X86_EAX, sizeof (gpointer) * (index + 1), 4);
659         /* is the slot null? */
660         x86_test_reg_reg (code, X86_EAX, X86_EAX);
661         /* if yes, jump to actual trampoline */
662         rgctx_null_jumps [depth + 1] = code;
663         x86_branch8 (code, X86_CC_Z, -1, 1);
664         /* otherwise return */
665         x86_ret (code);
666
667         for (i = mrgctx ? 1 : 0; i <= depth + 1; ++i)
668                 x86_patch (rgctx_null_jumps [i], code);
669
670         g_free (rgctx_null_jumps);
671
672         x86_mov_reg_membase (code, MONO_ARCH_VTABLE_REG, X86_ESP, 4, 4);
673
674         if (aot) {
675                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
676                 x86_jump_reg (code, X86_EAX);
677         } else {
678                 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), NULL);
679
680                 /* jump to the actual trampoline */
681                 x86_jump_code (code, tramp);
682         }
683
684         nacl_global_codeman_validate (&buf, tramp_size, &code);
685         mono_arch_flush_icache (buf, code - buf);
686
687         g_assert (code - buf <= tramp_size);
688
689         if (info) {
690                 char *name = mono_get_rgctx_fetch_trampoline_name (slot);
691                 *info = mono_tramp_info_create (name, buf, code - buf, ji, unwind_ops);
692                 g_free (name);
693         }
694
695         return buf;
696 }
697
698 /*
699  * mono_arch_create_general_rgctx_lazy_fetch_trampoline:
700  *
701  *   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
702  * the trampoline to call if the slot is not filled.
703  */
704 gpointer
705 mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboolean aot)
706 {
707         guint8 *code, *buf;
708         int tramp_size;
709         MonoJumpInfo *ji = NULL;
710         GSList *unwind_ops = NULL;
711
712         g_assert (aot);
713
714         unwind_ops = mono_arch_get_cie_program ();
715
716         tramp_size = 64;
717
718         code = buf = mono_global_codeman_reserve (tramp_size);
719
720         // FIXME: Currently, we always go to the slow path.
721         
722         /* Load trampoline addr */
723         x86_mov_reg_membase (code, X86_EAX, MONO_ARCH_RGCTX_REG, 4, 4);
724         /* Load mrgctx/vtable */
725         x86_mov_reg_membase (code, MONO_ARCH_VTABLE_REG, X86_ESP, 4, 4);
726
727         x86_jump_reg (code, X86_EAX);
728
729         nacl_global_codeman_validate (&buf, tramp_size, &code);
730         mono_arch_flush_icache (buf, code - buf);
731
732         g_assert (code - buf <= tramp_size);
733
734         if (info)
735                 *info = mono_tramp_info_create ("rgctx_fetch_trampoline_general", buf, code - buf, ji, unwind_ops);
736
737         return buf;
738 }
739
740 gpointer
741 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
742 {
743         guint8 *tramp;
744         guint8 *code, *buf;
745         static int byte_offset = -1;
746         static guint8 bitmask;
747         guint8 *jump;
748         int tramp_size;
749         GSList *unwind_ops = NULL;
750         MonoJumpInfo *ji = NULL;
751
752         tramp_size = 64;
753
754         code = buf = mono_global_codeman_reserve (tramp_size);
755
756         unwind_ops = mono_arch_get_cie_program ();
757
758         if (byte_offset < 0)
759                 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
760
761         x86_test_membase_imm (code, MONO_ARCH_VTABLE_REG, byte_offset, bitmask);
762         jump = code;
763         x86_branch8 (code, X86_CC_Z, -1, 1);
764
765         x86_ret (code);
766
767         x86_patch (jump, code);
768
769         /* Push the vtable so the stack is the same as in a specific trampoline */
770         x86_push_reg (code, MONO_ARCH_VTABLE_REG);
771
772         if (aot) {
773                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_generic_class_init");
774                 x86_jump_reg (code, X86_EAX);
775         } else {
776                 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC_CLASS_INIT);
777
778                 /* jump to the actual trampoline */
779                 x86_jump_code (code, tramp);
780         }
781
782         mono_arch_flush_icache (code, code - buf);
783
784         g_assert (code - buf <= tramp_size);
785 #ifdef __native_client_codegen__
786         g_assert (code - buf <= kNaClAlignment);
787 #endif
788
789         nacl_global_codeman_validate (&buf, tramp_size, &code);
790
791         if (info)
792                 *info = mono_tramp_info_create ("generic_class_init_trampoline", buf, code - buf, ji, unwind_ops);
793
794         return buf;
795 }
796
797 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
798 /*
799  * The code produced by this trampoline is equivalent to this:
800  *
801  * if (obj) {
802  *      if (obj->synchronisation) {
803  *              if (obj->synchronisation->owner == 0) {
804  *                      if (cmpxch (&obj->synchronisation->owner, TID, 0) == 0)
805  *                              return;
806  *              }
807  *              if (obj->synchronisation->owner == TID) {
808  *                      ++obj->synchronisation->nest;
809  *                      return;
810  *              }
811  *      }
812  * }
813  * return full_monitor_enter ();
814  *
815  */
816 gpointer
817 mono_arch_create_monitor_enter_trampoline (MonoTrampInfo **info, gboolean aot)
818 {
819         guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_MONITOR_ENTER);
820         guint8 *code, *buf;
821         guint8 *jump_obj_null, *jump_sync_null, *jump_other_owner, *jump_cmpxchg_failed, *jump_tid, *jump_sync_thin_hash = NULL;
822         int tramp_size;
823         int owner_offset, nest_offset, dummy;
824         MonoJumpInfo *ji = NULL;
825         GSList *unwind_ops = NULL;
826
827         g_assert (MONO_ARCH_MONITOR_OBJECT_REG == X86_EAX);
828
829         mono_monitor_threads_sync_members_offset (&owner_offset, &nest_offset, &dummy);
830         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset) == sizeof (gpointer));
831         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32));
832         owner_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset);
833         nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset);
834
835         tramp_size = NACL_SIZE (96, 128);
836
837         code = buf = mono_global_codeman_reserve (tramp_size);
838
839         if (mono_thread_get_tls_offset () != -1) {
840                 /* MonoObject* obj is in EAX */
841                 /* is obj null? */
842                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
843                 /* if yes, jump to actual trampoline */
844                 jump_obj_null = code;
845                 x86_branch8 (code, X86_CC_Z, -1, 1);
846
847                 /* load obj->synchronization to ECX */
848                 x86_mov_reg_membase (code, X86_ECX, X86_EAX, G_STRUCT_OFFSET (MonoObject, synchronisation), 4);
849
850                 if (mono_gc_is_moving ()) {
851                         /*if bit zero is set it's a thin hash*/
852                         /*FIXME use testb encoding*/
853                         x86_test_reg_imm (code, X86_ECX, 0x01);
854                         jump_sync_thin_hash = code;
855                         x86_branch8 (code, X86_CC_NE, -1, 1);
856
857                         /*clear bits used by the gc*/
858                         x86_alu_reg_imm (code, X86_AND, X86_ECX, ~0x3);
859                 }
860
861                 /* is synchronization null? */
862                 x86_test_reg_reg (code, X86_ECX, X86_ECX);
863
864                 /* if yes, jump to actual trampoline */
865                 jump_sync_null = code;
866                 x86_branch8 (code, X86_CC_Z, -1, 1);
867
868                 /* load MonoInternalThread* into EDX */
869                 code = mono_x86_emit_tls_get (code, X86_EDX, mono_thread_get_tls_offset ());
870                 /* load TID into EDX */
871                 x86_mov_reg_membase (code, X86_EDX, X86_EDX, G_STRUCT_OFFSET (MonoInternalThread, tid), 4);
872
873                 /* is synchronization->owner null? */
874                 x86_alu_membase_imm (code, X86_CMP, X86_ECX, owner_offset, 0);
875                 /* if not, jump to next case */
876                 jump_tid = code;
877                 x86_branch8 (code, X86_CC_NZ, -1, 1);
878
879                 /* if yes, try a compare-exchange with the TID */
880                 /* free up register EAX, needed for the zero */
881                 x86_push_reg (code, X86_EAX);
882                 /* zero EAX */
883                 x86_alu_reg_reg (code, X86_XOR, X86_EAX, X86_EAX);
884                 /* compare and exchange */
885                 x86_prefix (code, X86_LOCK_PREFIX);
886                 x86_cmpxchg_membase_reg (code, X86_ECX, owner_offset, X86_EDX);
887                 /* if not successful, jump to actual trampoline */
888                 jump_cmpxchg_failed = code;
889                 x86_branch8 (code, X86_CC_NZ, -1, 1);
890                 /* if successful, pop and return */
891                 x86_pop_reg (code, X86_EAX);
892                 x86_ret (code);
893
894                 /* next case: synchronization->owner is not null */
895                 x86_patch (jump_tid, code);
896                 /* is synchronization->owner == TID? */
897                 x86_alu_membase_reg (code, X86_CMP, X86_ECX, owner_offset, X86_EDX);
898                 /* if not, jump to actual trampoline */
899                 jump_other_owner = code;
900                 x86_branch8 (code, X86_CC_NZ, -1, 1);
901                 /* if yes, increment nest */
902                 x86_inc_membase (code, X86_ECX, nest_offset);
903                 /* return */
904                 x86_ret (code);
905
906                 /* push obj */
907                 x86_patch (jump_obj_null, code);
908                 if (jump_sync_thin_hash)
909                         x86_patch (jump_sync_thin_hash, code);
910                 x86_patch (jump_sync_null, code);
911                 x86_patch (jump_other_owner, code);
912                 x86_push_reg (code, X86_EAX);
913                 /* jump to the actual trampoline */
914                 x86_patch (jump_cmpxchg_failed, code);
915                 if (aot) {
916                         /* We are calling the generic trampoline directly, the argument is pushed
917                          * on the stack just like a specific trampoline.
918                          */
919                         code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_monitor_enter");
920                         x86_jump_reg (code, X86_EAX);
921                 } else {
922                         x86_jump_code (code, tramp);
923                 }
924         } else {
925                 /* push obj and jump to the actual trampoline */
926                 x86_push_reg (code, X86_EAX);
927                 if (aot) {
928                         code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_monitor_enter");
929                         x86_jump_reg (code, X86_EAX);
930                 } else {
931                         x86_jump_code (code, tramp);
932                 }
933         }
934
935         mono_arch_flush_icache (buf, code - buf);
936         g_assert (code - buf <= tramp_size);
937
938         nacl_global_codeman_validate (&buf, tramp_size, &code);
939
940         if (info)
941                 *info = mono_tramp_info_create ("monitor_enter_trampoline", buf, code - buf, ji, unwind_ops);
942
943         return buf;
944 }
945
946 gpointer
947 mono_arch_create_monitor_exit_trampoline (MonoTrampInfo **info, gboolean aot)
948 {
949         guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_MONITOR_EXIT);
950         guint8 *code, *buf;
951         guint8 *jump_obj_null, *jump_have_waiters, *jump_sync_null, *jump_not_owned, *jump_sync_thin_hash = NULL;
952         guint8 *jump_next;
953         int tramp_size;
954         int owner_offset, nest_offset, entry_count_offset;
955         MonoJumpInfo *ji = NULL;
956         GSList *unwind_ops = NULL;
957
958         g_assert (MONO_ARCH_MONITOR_OBJECT_REG == X86_EAX);
959
960         mono_monitor_threads_sync_members_offset (&owner_offset, &nest_offset, &entry_count_offset);
961         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset) == sizeof (gpointer));
962         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32));
963         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (entry_count_offset) == sizeof (gint32));
964         owner_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset);
965         nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset);
966         entry_count_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (entry_count_offset);
967
968         tramp_size = NACL_SIZE (96, 128);
969
970         code = buf = mono_global_codeman_reserve (tramp_size);
971
972         if (mono_thread_get_tls_offset () != -1) {
973                 /* MonoObject* obj is in EAX */
974                 /* is obj null? */
975                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
976                 /* if yes, jump to actual trampoline */
977                 jump_obj_null = code;
978                 x86_branch8 (code, X86_CC_Z, -1, 1);
979
980                 /* load obj->synchronization to ECX */
981                 x86_mov_reg_membase (code, X86_ECX, X86_EAX, G_STRUCT_OFFSET (MonoObject, synchronisation), 4);
982
983                 if (mono_gc_is_moving ()) {
984                         /*if bit zero is set it's a thin hash*/
985                         /*FIXME use testb encoding*/
986                         x86_test_reg_imm (code, X86_ECX, 0x01);
987                         jump_sync_thin_hash = code;
988                         x86_branch8 (code, X86_CC_NE, -1, 1);
989
990                         /*clear bits used by the gc*/
991                         x86_alu_reg_imm (code, X86_AND, X86_ECX, ~0x3);
992                 }
993
994                 /* is synchronization null? */
995                 x86_test_reg_reg (code, X86_ECX, X86_ECX);
996                 /* if yes, jump to actual trampoline */
997                 jump_sync_null = code;
998                 x86_branch8 (code, X86_CC_Z, -1, 1);
999
1000                 /* next case: synchronization is not null */
1001                 /* load MonoInternalThread* into EDX */
1002                 code = mono_x86_emit_tls_get (code, X86_EDX, mono_thread_get_tls_offset ());
1003                 /* load TID into EDX */
1004                 x86_mov_reg_membase (code, X86_EDX, X86_EDX, G_STRUCT_OFFSET (MonoInternalThread, tid), 4);
1005                 /* is synchronization->owner == TID */
1006                 x86_alu_membase_reg (code, X86_CMP, X86_ECX, owner_offset, X86_EDX);
1007                 /* if no, jump to actual trampoline */
1008                 jump_not_owned = code;
1009                 x86_branch8 (code, X86_CC_NZ, -1, 1);
1010
1011                 /* next case: synchronization->owner == TID */
1012                 /* is synchronization->nest == 1 */
1013                 x86_alu_membase_imm (code, X86_CMP, X86_ECX, nest_offset, 1);
1014                 /* if not, jump to next case */
1015                 jump_next = code;
1016                 x86_branch8 (code, X86_CC_NZ, -1, 1);
1017                 /* if yes, is synchronization->entry_count zero? */
1018                 x86_alu_membase_imm (code, X86_CMP, X86_ECX, entry_count_offset, 0);
1019                 /* if not, jump to actual trampoline */
1020                 jump_have_waiters = code;
1021                 x86_branch8 (code, X86_CC_NZ, -1 , 1);
1022                 /* if yes, set synchronization->owner to null and return */
1023                 x86_mov_membase_imm (code, X86_ECX, owner_offset, 0, 4);
1024                 x86_ret (code);
1025
1026                 /* next case: synchronization->nest is not 1 */
1027                 x86_patch (jump_next, code);
1028                 /* decrease synchronization->nest and return */
1029                 x86_dec_membase (code, X86_ECX, nest_offset);
1030                 x86_ret (code);
1031
1032                 /* push obj and jump to the actual trampoline */
1033                 x86_patch (jump_obj_null, code);
1034                 if (jump_sync_thin_hash)
1035                         x86_patch (jump_sync_thin_hash, code);
1036                 x86_patch (jump_have_waiters, code);
1037                 x86_patch (jump_not_owned, code);
1038                 x86_patch (jump_sync_null, code);
1039         }
1040
1041         /* push obj and jump to the actual trampoline */
1042         x86_push_reg (code, X86_EAX);
1043         if (aot) {
1044                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_monitor_exit");
1045                 x86_jump_reg (code, X86_EAX);
1046         } else {
1047                 x86_jump_code (code, tramp);
1048         }
1049
1050         nacl_global_codeman_validate (&buf, tramp_size, &code);
1051
1052         mono_arch_flush_icache (buf, code - buf);
1053         g_assert (code - buf <= tramp_size);
1054
1055         if (info)
1056                 *info = mono_tramp_info_create ("monitor_exit_trampoline", buf, code - buf, ji, unwind_ops);
1057
1058         return buf;
1059 }
1060
1061 #else
1062
1063 gpointer
1064 mono_arch_create_monitor_enter_trampoline (MonoTrampInfo **info, gboolean aot)
1065 {
1066         g_assert_not_reached ();
1067         return NULL;
1068 }
1069
1070 gpointer
1071 mono_arch_create_monitor_exit_trampoline (MonoTrampInfo **info, gboolean aot)
1072 {
1073         g_assert_not_reached ();
1074         return NULL;
1075 }
1076
1077 #endif
1078
1079 void
1080 mono_arch_invalidate_method (MonoJitInfo *ji, void *func, gpointer func_arg)
1081 {
1082         /* FIXME: This is not thread safe */
1083         guint8 *code = ji->code_start;
1084
1085         x86_push_imm (code, func_arg);
1086         x86_call_code (code, (guint8*)func);
1087 }
1088
1089 static void
1090 handler_block_trampoline_helper (gpointer *ptr)
1091 {
1092         MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id);
1093         *ptr = jit_tls->handler_block_return_address;
1094 }
1095
1096 gpointer
1097 mono_arch_create_handler_block_trampoline (void)
1098 {
1099         guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD);
1100         guint8 *code, *buf;
1101         int tramp_size = 64;
1102         code = buf = mono_global_codeman_reserve (tramp_size);
1103
1104         /*
1105         This trampoline restore the call chain of the handler block then jumps into the code that deals with it.
1106         */
1107
1108         if (mono_get_jit_tls_offset () != -1) {
1109                 code = mono_x86_emit_tls_get (code, X86_EAX, mono_get_jit_tls_offset ());
1110                 x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoJitTlsData, handler_block_return_address), 4);
1111                 /*simulate a call*/
1112                 /*Fix stack alignment*/
1113                 x86_alu_reg_imm (code, X86_SUB, X86_ESP, 0x8);
1114                 x86_push_reg (code, X86_EAX);
1115                 x86_jump_code (code, tramp);
1116         } else {
1117                 /*Slow path uses a c helper*/
1118                 x86_alu_reg_imm (code, X86_SUB, X86_ESP, 0x8);
1119                 x86_push_reg (code, X86_ESP);
1120                 x86_push_imm (code, tramp);
1121                 x86_jump_code (code, handler_block_trampoline_helper);
1122         }
1123
1124         nacl_global_codeman_validate (&buf, tramp_size, &code);
1125
1126         mono_arch_flush_icache (buf, code - buf);
1127         g_assert (code - buf <= tramp_size);
1128
1129         if (mono_jit_map_is_enabled ())
1130                 mono_emit_jit_tramp (buf, code - buf, "handler_block_trampoline");
1131
1132         return buf;
1133 }
1134
1135 guint8*
1136 mono_arch_get_call_target (guint8 *code)
1137 {
1138         if (code [-5] == 0xe8) {
1139                 guint32 disp = *(guint32*)(code - 4);
1140                 guint8 *target = code + disp;
1141
1142                 return target;
1143         } else {
1144                 return NULL;
1145         }
1146 }
1147
1148 guint32
1149 mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
1150 {
1151         return *(guint32*)(plt_entry + NACL_SIZE (6, 12));
1152 }
1153
1154 /*
1155  * mono_arch_get_gsharedvt_arg_trampoline:
1156  *
1157  *   Return a trampoline which passes ARG to the gsharedvt in/out trampoline ADDR.
1158  */
1159 gpointer
1160 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
1161 {
1162         guint8 *code, *start;
1163         int buf_len;
1164
1165         buf_len = 10;
1166
1167         start = code = mono_domain_code_reserve (domain, buf_len);
1168
1169         x86_mov_reg_imm (code, X86_EAX, arg);
1170         x86_jump_code (code, addr);
1171         g_assert ((code - start) <= buf_len);
1172
1173         nacl_domain_code_validate (domain, &start, buf_len, &code);
1174         mono_arch_flush_icache (start, code - start);
1175
1176         return start;
1177 }
1178
1179 #if defined(MONOTOUCH) || defined(MONO_EXTENSIONS)
1180
1181 #include "../../../mono-extensions/mono/mini/tramp-x86-gsharedvt.c"
1182
1183 #else
1184
1185 gpointer
1186 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
1187 {
1188         if (info)
1189                 *info = NULL;
1190         return NULL;
1191 }
1192
1193 #endif /* !MONOTOUCH */