New tests.
[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/arch/x86/x86-codegen.h>
21
22 #include <mono/utils/memcheck.h>
23
24 #include "mini.h"
25 #include "mini-x86.h"
26
27 static guint8* nullified_class_init_trampoline;
28
29 /*
30  * mono_arch_get_unbox_trampoline:
31  * @gsctx: the generic sharing context
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 (MonoGenericSharingContext *gsctx, MonoMethod *m, gpointer addr)
41 {
42         guint8 *code, *start;
43         int this_pos = 4;
44         MonoDomain *domain = mono_domain_get ();
45
46         if (MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
47                 this_pos = 8;
48             
49         start = code = mono_domain_code_reserve (domain, 16);
50
51         x86_alu_membase_imm (code, X86_ADD, X86_ESP, this_pos, sizeof (MonoObject));
52         x86_jump_code (code, addr);
53         g_assert ((code - start) < 16);
54
55         return start;
56 }
57
58 gpointer
59 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
60 {
61         guint8 *code, *start;
62         int buf_len;
63
64         MonoDomain *domain = mono_domain_get ();
65
66         buf_len = 10;
67
68         start = code = mono_domain_code_reserve (domain, buf_len);
69
70         x86_mov_reg_imm (code, MONO_ARCH_RGCTX_REG, mrgctx);
71         x86_jump_code (code, addr);
72         g_assert ((code - start) <= buf_len);
73
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         mono_arch_flush_icache (start, code - start);
103
104         return start;
105 }
106
107 void
108 mono_arch_patch_callsite (guint8 *method_start, guint8 *orig_code, guint8 *addr)
109 {
110         guint8 *code;
111         guint8 buf [8];
112         gboolean can_write = mono_breakpoint_clean_code (method_start, orig_code, 8, buf, sizeof (buf));
113
114         code = buf + 8;
115
116         /* go to the start of the call instruction
117          *
118          * address_byte = (m << 6) | (o << 3) | reg
119          * call opcode: 0xff address_byte displacement
120          * 0xff m=1,o=2 imm8
121          * 0xff m=2,o=2 imm32
122          */
123         code -= 6;
124         orig_code -= 6;
125         if ((code [1] == 0xe8)) {
126                 if (can_write) {
127                         InterlockedExchange ((gint32*)(orig_code + 2), (guint)addr - ((guint)orig_code + 1) - 5);
128
129                         /* Tell valgrind to recompile the patched code */
130                         VALGRIND_DISCARD_TRANSLATIONS (orig_code + 2, 4);
131                 }
132         } else if (code [1] == 0xe9) {
133                 /* A PLT entry: jmp <DISP> */
134                 if (can_write)
135                         InterlockedExchange ((gint32*)(orig_code + 2), (guint)addr - ((guint)orig_code + 1) - 5);
136         } else {
137                 printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
138                                 code [4], code [5], code [6]);
139                 g_assert_not_reached ();
140         }
141 }
142
143 void
144 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
145 {
146         guint32 offset;
147
148         /* Patch the jump table entry used by the plt entry */
149
150         /* A PLT entry: jmp *<DISP>(%ebx) */
151         g_assert (code [0] == 0xff);
152         g_assert (code [1] == 0xa3);
153
154         offset = *(guint32*)(code + 2);
155
156         if (!got)
157                 got = (gpointer*)(gsize) regs [MONO_ARCH_GOT_REG];
158         *(guint8**)((guint8*)got + offset) = addr;
159 }
160
161 void
162 mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs)
163 {
164         guint8 buf [16];
165         gboolean can_write = mono_breakpoint_clean_code (NULL, code, 6, buf, sizeof (buf));
166
167         if (!can_write)
168                 return;
169
170         code -= 5;
171         if (code [0] == 0xe8) {
172                 if (!mono_running_on_valgrind ()) {
173                         guint32 ops;
174                         /*
175                          * Thread safe code patching using the algorithm from the paper
176                          * 'Practicing JUDO: Java Under Dynamic Optimizations'
177                          */
178                         /* 
179                          * First atomically change the the first 2 bytes of the call to a
180                          * spinning jump.
181                          */
182                         ops = 0xfeeb;
183                         InterlockedExchange ((gint32*)code, ops);
184
185                         /* Then change the other bytes to a nop */
186                         code [2] = 0x90;
187                         code [3] = 0x90;
188                         code [4] = 0x90;
189
190                         /* Then atomically change the first 4 bytes to a nop as well */
191                         ops = 0x90909090;
192                         InterlockedExchange ((gint32*)code, ops);
193                         /* FIXME: the calltree skin trips on the self modifying code above */
194
195                         /* Tell valgrind to recompile the patched code */
196                         //VALGRIND_DISCARD_TRANSLATIONS (code, 8);
197                 }
198         } else if (code [0] == 0x90 || code [0] == 0xeb) {
199                 /* Already changed by another thread */
200                 ;
201         } else if ((code [-1] == 0xff) && (x86_modrm_reg (code [0]) == 0x2)) {
202                 /* call *<OFFSET>(<REG>) -> Call made from AOT code */
203                 gpointer *vtable_slot;
204
205                 vtable_slot = mono_get_vcall_slot_addr (code + 5, regs);
206                 g_assert (vtable_slot);
207
208                 *vtable_slot = nullified_class_init_trampoline;
209         } else {
210                         printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
211                                 code [4], code [5], code [6]);
212                         g_assert_not_reached ();
213                 }
214 }
215
216 void
217 mono_arch_nullify_plt_entry (guint8 *code, mgreg_t *regs)
218 {
219         if (mono_aot_only && !nullified_class_init_trampoline)
220                 nullified_class_init_trampoline = mono_aot_get_trampoline ("nullified_class_init_trampoline");
221
222         mono_arch_patch_plt_entry (code, NULL, regs, nullified_class_init_trampoline);
223 }
224
225 guchar*
226 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
227 {
228         guint8 *buf, *code, *tramp;
229         int pushed_args, pushed_args_caller_saved;
230         GSList *unwind_ops = NULL;
231         MonoJumpInfo *ji = NULL;
232
233         code = buf = mono_global_codeman_reserve (256);
234
235         /* Note that there is a single argument to the trampoline
236          * and it is stored at: esp + pushed_args * sizeof (gpointer)
237          * the ret address is at: esp + (pushed_args + 1) * sizeof (gpointer)
238          */
239
240         /* Put all registers into an array on the stack
241          * If this code is changed, make sure to update the offset value in
242          * mono_arch_get_this_arg_from_call () in mini-x86.c.
243          */
244         x86_push_reg (code, X86_EDI);
245         x86_push_reg (code, X86_ESI);
246         x86_push_reg (code, X86_EBP);
247         x86_push_reg (code, X86_ESP);
248         x86_push_reg (code, X86_EBX);
249         x86_push_reg (code, X86_EDX);
250         x86_push_reg (code, X86_ECX);
251         x86_push_reg (code, X86_EAX);
252
253         pushed_args_caller_saved = pushed_args = 8;
254
255         /* Align stack on apple */
256         x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4);
257
258         pushed_args ++;
259
260         /* save LMF begin */
261
262         /* save the IP (caller ip) */
263         if (tramp_type == MONO_TRAMPOLINE_JUMP)
264                 x86_push_imm (code, 0);
265         else
266                 x86_push_membase (code, X86_ESP, (pushed_args + 1) * sizeof (gpointer));
267
268         pushed_args++;
269
270         x86_push_reg (code, X86_EBP);
271         x86_push_reg (code, X86_ESI);
272         x86_push_reg (code, X86_EDI);
273         x86_push_reg (code, X86_EBX);
274
275         pushed_args += 4;
276
277         /* save ESP */
278         x86_push_reg (code, X86_ESP);
279         /* Adjust ESP so it points to the previous frame */
280         x86_alu_membase_imm (code, X86_ADD, X86_ESP, 0, (pushed_args + 2) * 4);
281
282         pushed_args ++;
283
284         /* save method info */
285         if ((tramp_type == MONO_TRAMPOLINE_JIT) || (tramp_type == MONO_TRAMPOLINE_JUMP))
286                 x86_push_membase (code, X86_ESP, pushed_args * sizeof (gpointer));
287         else
288                 x86_push_imm (code, 0);
289
290         pushed_args++;
291
292         /* On apple, the stack is correctly aligned to 16 bytes because pushed_args is
293          * 16 and there is the extra trampoline arg + the return ip pushed by call
294          * FIXME: Note that if an exception happens while some args are pushed
295          * on the stack, the stack will be misaligned.
296          */
297         g_assert (pushed_args == 16);
298
299         /* get the address of lmf for the current thread */
300         if (aot) {
301                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_get_lmf_addr");
302                 x86_call_reg (code, X86_EAX);
303         } else {
304                 x86_call_code (code, mono_get_lmf_addr);
305         }
306         /* push lmf */
307         x86_push_reg (code, X86_EAX); 
308         /* push *lfm (previous_lmf) */
309         x86_push_membase (code, X86_EAX, 0);
310         /* Signal to mono_arch_find_jit_info () that this is a trampoline frame */
311         x86_alu_membase_imm (code, X86_ADD, X86_ESP, 0, 1);
312         /* *(lmf) = ESP */
313         x86_mov_membase_reg (code, X86_EAX, 0, X86_ESP, 4);
314         /* save LFM end */
315
316         pushed_args += 2;
317
318         /* starting the call sequence */
319
320         /* FIXME: Push the trampoline address */
321         x86_push_imm (code, 0);
322
323         pushed_args++;
324
325         /* push the method info */
326         x86_push_membase (code, X86_ESP, pushed_args * sizeof (gpointer));
327
328         pushed_args++;
329
330         /* push the return address onto the stack */
331         if (tramp_type == MONO_TRAMPOLINE_JUMP)
332                 x86_push_imm (code, 0);
333         else
334                 x86_push_membase (code, X86_ESP, (pushed_args + 1) * sizeof (gpointer));
335         pushed_args++;
336         /* push the address of the register array */
337         x86_lea_membase (code, X86_EAX, X86_ESP, (pushed_args - 8) * sizeof (gpointer));
338         x86_push_reg (code, X86_EAX);
339
340         pushed_args++;
341
342 #ifdef __APPLE__
343         /* check the stack is aligned after the ret ip is pushed */
344         /*x86_mov_reg_reg (buf, X86_EDX, X86_ESP, 4);
345         x86_alu_reg_imm (buf, X86_AND, X86_EDX, 15);
346         x86_alu_reg_imm (buf, X86_CMP, X86_EDX, 0);
347         x86_branch_disp (buf, X86_CC_Z, 3, FALSE);
348         x86_breakpoint (buf);*/
349 #endif
350
351         if (aot) {
352                 char *icall_name = g_strdup_printf ("trampoline_func_%d", tramp_type);
353                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
354                 x86_call_reg (code, X86_EAX);
355         } else {
356                 tramp = (guint8*)mono_get_trampoline_func (tramp_type);
357                 x86_call_code (code, tramp);
358         }
359
360         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4*4);
361
362         pushed_args -= 4;
363
364         /* Check for thread interruption */
365         /* This is not perf critical code so no need to check the interrupt flag */
366         /* Align the stack on osx */
367         x86_alu_reg_imm (code, X86_SUB, X86_ESP, 3 * 4);
368         x86_push_reg (code, X86_EAX);
369         if (aot) {
370                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_thread_force_interruption_checkpoint");
371                 x86_call_reg (code, X86_EAX);
372         } else {
373                 x86_call_code (code, (guint8*)mono_thread_force_interruption_checkpoint);
374         }
375         x86_pop_reg (code, X86_EAX);
376         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 3 * 4);
377
378         /* Restore LMF */
379
380         /* ebx = previous_lmf */
381         x86_pop_reg (code, X86_EBX);
382         pushed_args--;
383         x86_alu_reg_imm (code, X86_SUB, X86_EBX, 1);
384
385         /* edi = lmf */
386         x86_pop_reg (code, X86_EDI);
387         pushed_args--;
388
389         /* *(lmf) = previous_lmf */
390         x86_mov_membase_reg (code, X86_EDI, 0, X86_EBX, 4);
391
392         /* discard method info */
393         x86_pop_reg (code, X86_ESI);
394         pushed_args--;
395
396         /* discard ESP */
397         x86_pop_reg (code, X86_ESI);
398         pushed_args--;
399
400         /* restore caller saved regs */
401         x86_pop_reg (code, X86_EBX);
402         x86_pop_reg (code, X86_EDI);
403         x86_pop_reg (code, X86_ESI);
404         x86_pop_reg (code, X86_EBP);
405
406         pushed_args -= 4;
407
408         /* discard save IP */
409         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
410         pushed_args--;
411
412         /* restore LMF end */
413
414         if (!MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type)) {
415                 /* 
416                  * Overwrite the method ptr with the address we need to jump to,
417                  * to free %eax.
418                  */
419                 x86_mov_membase_reg (code, X86_ESP, pushed_args * sizeof (gpointer), X86_EAX, 4);
420         }
421
422         /* Restore caller saved registers */
423         x86_mov_reg_membase (code, X86_ECX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_ECX) * 4, 4);
424         x86_mov_reg_membase (code, X86_EDX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_EDX) * 4, 4);
425         if ((tramp_type == MONO_TRAMPOLINE_RESTORE_STACK_PROT) || (tramp_type == MONO_TRAMPOLINE_AOT_PLT))
426                 x86_mov_reg_membase (code, X86_EAX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_EAX) * 4, 4);
427
428         if (!MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type)) {
429                 /* Pop saved reg array + stack align */
430                 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 9 * 4);
431                 pushed_args -= 9;
432                 g_assert (pushed_args == 0);
433         } else {
434                 /* Pop saved reg array + stack align + method ptr */
435                 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 10 * 4);
436                 pushed_args -= 10;
437
438                 /* We've popped one more stack item than we've pushed (the
439                    method ptr argument), so we must end up at -1. */
440                 g_assert (pushed_args == -1);
441         }
442
443         x86_ret (code);
444
445         g_assert ((code - buf) <= 256);
446
447         if (info)
448                 *info = mono_tramp_info_create (g_strdup_printf ("generic_trampoline_%d", tramp_type), buf, code - buf, ji, unwind_ops);
449
450         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
451                 /* Initialize the nullified class init trampoline used in the AOT case */
452                 nullified_class_init_trampoline = mono_arch_get_nullified_class_init_trampoline (NULL);
453         }
454
455         return buf;
456 }
457
458 gpointer
459 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
460 {
461         guint8 *code, *buf;
462
463         code = buf = mono_global_codeman_reserve (16);
464         x86_ret (code);
465
466         mono_arch_flush_icache (buf, code - buf);
467
468         if (info)
469                 *info = mono_tramp_info_create (g_strdup_printf ("nullified_class_init_trampoline"), buf, code - buf, NULL, NULL);
470
471         return buf;
472 }
473
474 #define TRAMPOLINE_SIZE 10
475
476 gpointer
477 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
478 {
479         guint8 *code, *buf, *tramp;
480         
481         tramp = mono_get_trampoline_code (tramp_type);
482
483         code = buf = mono_domain_code_reserve_align (domain, TRAMPOLINE_SIZE, 4);
484
485         x86_push_imm (buf, arg1);
486         x86_jump_code (buf, tramp);
487         g_assert ((buf - code) <= TRAMPOLINE_SIZE);
488
489         mono_arch_flush_icache (code, buf - code);
490
491         if (code_len)
492                 *code_len = buf - code;
493
494         return code;
495 }
496
497 gpointer
498 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
499 {
500         guint8 *tramp;
501         guint8 *code, *buf;
502         guint8 **rgctx_null_jumps;
503         int tramp_size;
504         int depth, index;
505         int i;
506         gboolean mrgctx;
507         MonoJumpInfo *ji = NULL;
508         GSList *unwind_ops = NULL;
509
510         mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
511         index = MONO_RGCTX_SLOT_INDEX (slot);
512         if (mrgctx)
513                 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
514         for (depth = 0; ; ++depth) {
515                 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
516
517                 if (index < size - 1)
518                         break;
519                 index -= size - 1;
520         }
521
522         tramp_size = (aot ? 64 : 36) + 6 * depth;
523
524         code = buf = mono_global_codeman_reserve (tramp_size);
525
526         rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
527
528         /* load vtable/mrgctx ptr */
529         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
530         if (!mrgctx) {
531                 /* load rgctx ptr from vtable */
532                 x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context), 4);
533                 /* is the rgctx ptr null? */
534                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
535                 /* if yes, jump to actual trampoline */
536                 rgctx_null_jumps [0] = code;
537                 x86_branch8 (code, X86_CC_Z, -1, 1);
538         }
539
540         for (i = 0; i < depth; ++i) {
541                 /* load ptr to next array */
542                 if (mrgctx && i == 0)
543                         x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT, 4);
544                 else
545                         x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, 4);
546                 /* is the ptr null? */
547                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
548                 /* if yes, jump to actual trampoline */
549                 rgctx_null_jumps [i + 1] = code;
550                 x86_branch8 (code, X86_CC_Z, -1, 1);
551         }
552
553         /* fetch slot */
554         x86_mov_reg_membase (code, X86_EAX, X86_EAX, sizeof (gpointer) * (index + 1), 4);
555         /* is the slot null? */
556         x86_test_reg_reg (code, X86_EAX, X86_EAX);
557         /* if yes, jump to actual trampoline */
558         rgctx_null_jumps [depth + 1] = code;
559         x86_branch8 (code, X86_CC_Z, -1, 1);
560         /* otherwise return */
561         x86_ret (code);
562
563         for (i = mrgctx ? 1 : 0; i <= depth + 1; ++i)
564                 x86_patch (rgctx_null_jumps [i], code);
565
566         g_free (rgctx_null_jumps);
567
568         x86_mov_reg_membase (code, MONO_ARCH_VTABLE_REG, X86_ESP, 4, 4);
569
570         if (aot) {
571                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
572                 x86_jump_reg (code, X86_EAX);
573         } else {
574                 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), NULL);
575
576                 /* jump to the actual trampoline */
577                 x86_jump_code (code, tramp);
578         }
579
580         mono_arch_flush_icache (buf, code - buf);
581
582         g_assert (code - buf <= tramp_size);
583
584         if (info)
585                 *info = mono_tramp_info_create (g_strdup_printf ("rgctx_fetch_trampoline_%u", slot), buf, code - buf, ji, unwind_ops);
586
587         return buf;
588 }
589
590 gpointer
591 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
592 {
593         guint8 *tramp;
594         guint8 *code, *buf;
595         static int byte_offset = -1;
596         static guint8 bitmask;
597         guint8 *jump;
598         int tramp_size;
599         GSList *unwind_ops = NULL;
600         MonoJumpInfo *ji = NULL;
601
602         tramp_size = 64;
603
604         code = buf = mono_global_codeman_reserve (tramp_size);
605
606         if (byte_offset < 0)
607                 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
608
609         x86_test_membase_imm (code, MONO_ARCH_VTABLE_REG, byte_offset, bitmask);
610         jump = code;
611         x86_branch8 (code, X86_CC_Z, -1, 1);
612
613         x86_ret (code);
614
615         x86_patch (jump, code);
616
617         /* Push the vtable so the stack is the same as in a specific trampoline */
618         x86_push_reg (code, MONO_ARCH_VTABLE_REG);
619
620         if (aot) {
621                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_generic_class_init");
622                 x86_jump_reg (code, X86_EAX);
623         } else {
624                 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC_CLASS_INIT);
625
626                 /* jump to the actual trampoline */
627                 x86_jump_code (code, tramp);
628         }
629
630         mono_arch_flush_icache (code, code - buf);
631
632         g_assert (code - buf <= tramp_size);
633
634         if (info)
635                 *info = mono_tramp_info_create (g_strdup_printf ("generic_class_init_trampoline"), buf, code - buf, ji, unwind_ops);
636
637         return buf;
638 }
639
640 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
641 /*
642  * The code produced by this trampoline is equivalent to this:
643  *
644  * if (obj) {
645  *      if (obj->synchronisation) {
646  *              if (obj->synchronisation->owner == 0) {
647  *                      if (cmpxch (&obj->synchronisation->owner, TID, 0) == 0)
648  *                              return;
649  *              }
650  *              if (obj->synchronisation->owner == TID) {
651  *                      ++obj->synchronisation->nest;
652  *                      return;
653  *              }
654  *      }
655  * }
656  * return full_monitor_enter ();
657  *
658  */
659 gpointer
660 mono_arch_create_monitor_enter_trampoline (MonoTrampInfo **info, gboolean aot)
661 {
662         guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_MONITOR_ENTER);
663         guint8 *code, *buf;
664         guint8 *jump_obj_null, *jump_sync_null, *jump_other_owner, *jump_cmpxchg_failed, *jump_tid;
665         int tramp_size;
666         int owner_offset, nest_offset, dummy;
667         MonoJumpInfo *ji = NULL;
668         GSList *unwind_ops = NULL;
669
670         g_assert (MONO_ARCH_MONITOR_OBJECT_REG == X86_EAX);
671
672         mono_monitor_threads_sync_members_offset (&owner_offset, &nest_offset, &dummy);
673         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset) == sizeof (gpointer));
674         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32));
675         owner_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset);
676         nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset);
677
678         tramp_size = 64;
679
680         code = buf = mono_global_codeman_reserve (tramp_size);
681
682         if (mono_thread_get_tls_offset () != -1) {
683                 /* MonoObject* obj is in EAX */
684                 /* is obj null? */
685                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
686                 /* if yes, jump to actual trampoline */
687                 jump_obj_null = code;
688                 x86_branch8 (code, X86_CC_Z, -1, 1);
689
690                 /* load obj->synchronization to ECX */
691                 x86_mov_reg_membase (code, X86_ECX, X86_EAX, G_STRUCT_OFFSET (MonoObject, synchronisation), 4);
692                 /* is synchronization null? */
693                 x86_test_reg_reg (code, X86_ECX, X86_ECX);
694                 /* if yes, jump to actual trampoline */
695                 jump_sync_null = code;
696                 x86_branch8 (code, X86_CC_Z, -1, 1);
697
698                 /* load MonoInternalThread* into EDX */
699                 code = mono_x86_emit_tls_get (code, X86_EDX, mono_thread_get_tls_offset ());
700                 /* load TID into EDX */
701                 x86_mov_reg_membase (code, X86_EDX, X86_EDX, G_STRUCT_OFFSET (MonoInternalThread, tid), 4);
702
703                 /* is synchronization->owner null? */
704                 x86_alu_membase_imm (code, X86_CMP, X86_ECX, owner_offset, 0);
705                 /* if not, jump to next case */
706                 jump_tid = code;
707                 x86_branch8 (code, X86_CC_NZ, -1, 1);
708
709                 /* if yes, try a compare-exchange with the TID */
710                 /* free up register EAX, needed for the zero */
711                 x86_push_reg (code, X86_EAX);
712                 /* zero EAX */
713                 x86_alu_reg_reg (code, X86_XOR, X86_EAX, X86_EAX);
714                 /* compare and exchange */
715                 x86_prefix (code, X86_LOCK_PREFIX);
716                 x86_cmpxchg_membase_reg (code, X86_ECX, owner_offset, X86_EDX);
717                 /* if not successful, jump to actual trampoline */
718                 jump_cmpxchg_failed = code;
719                 x86_branch8 (code, X86_CC_NZ, -1, 1);
720                 /* if successful, pop and return */
721                 x86_pop_reg (code, X86_EAX);
722                 x86_ret (code);
723
724                 /* next case: synchronization->owner is not null */
725                 x86_patch (jump_tid, code);
726                 /* is synchronization->owner == TID? */
727                 x86_alu_membase_reg (code, X86_CMP, X86_ECX, owner_offset, X86_EDX);
728                 /* if not, jump to actual trampoline */
729                 jump_other_owner = code;
730                 x86_branch8 (code, X86_CC_NZ, -1, 1);
731                 /* if yes, increment nest */
732                 x86_inc_membase (code, X86_ECX, nest_offset);
733                 /* return */
734                 x86_ret (code);
735
736                 /* push obj */
737                 x86_patch (jump_obj_null, code);
738                 x86_patch (jump_sync_null, code);
739                 x86_patch (jump_other_owner, code);
740                 x86_push_reg (code, X86_EAX);
741                 /* jump to the actual trampoline */
742                 x86_patch (jump_cmpxchg_failed, code);
743                 if (aot) {
744                         /* We are calling the generic trampoline directly, the argument is pushed
745                          * on the stack just like a specific trampoline.
746                          */
747                         code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_monitor_enter");
748                         x86_jump_reg (code, X86_EAX);
749                 } else {
750                         x86_jump_code (code, tramp);
751                 }
752         } else {
753                 /* push obj and jump to the actual trampoline */
754                 x86_push_reg (code, X86_EAX);
755                 if (aot) {
756                         code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_monitor_enter");
757                         x86_jump_reg (code, X86_EAX);
758                 } else {
759                         x86_jump_code (code, tramp);
760                 }
761         }
762
763         mono_arch_flush_icache (buf, code - buf);
764         g_assert (code - buf <= tramp_size);
765
766         if (info)
767                 *info = mono_tramp_info_create (g_strdup_printf ("monitor_enter_trampoline"), buf, code - buf, ji, unwind_ops);
768
769         return buf;
770 }
771
772 gpointer
773 mono_arch_create_monitor_exit_trampoline (MonoTrampInfo **info, gboolean aot)
774 {
775         guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_MONITOR_EXIT);
776         guint8 *code, *buf;
777         guint8 *jump_obj_null, *jump_have_waiters, *jump_sync_null, *jump_not_owned;
778         guint8 *jump_next;
779         int tramp_size;
780         int owner_offset, nest_offset, entry_count_offset;
781         MonoJumpInfo *ji = NULL;
782         GSList *unwind_ops = NULL;
783
784         g_assert (MONO_ARCH_MONITOR_OBJECT_REG == X86_EAX);
785
786         mono_monitor_threads_sync_members_offset (&owner_offset, &nest_offset, &entry_count_offset);
787         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset) == sizeof (gpointer));
788         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32));
789         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (entry_count_offset) == sizeof (gint32));
790         owner_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset);
791         nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset);
792         entry_count_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (entry_count_offset);
793
794         tramp_size = 64;
795
796         code = buf = mono_global_codeman_reserve (tramp_size);
797
798         if (mono_thread_get_tls_offset () != -1) {
799                 /* MonoObject* obj is in EAX */
800                 /* is obj null? */
801                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
802                 /* if yes, jump to actual trampoline */
803                 jump_obj_null = code;
804                 x86_branch8 (code, X86_CC_Z, -1, 1);
805
806                 /* load obj->synchronization to ECX */
807                 x86_mov_reg_membase (code, X86_ECX, X86_EAX, G_STRUCT_OFFSET (MonoObject, synchronisation), 4);
808                 /* is synchronization null? */
809                 x86_test_reg_reg (code, X86_ECX, X86_ECX);
810                 /* if yes, jump to actual trampoline */
811                 jump_sync_null = code;
812                 x86_branch8 (code, X86_CC_Z, -1, 1);
813
814                 /* next case: synchronization is not null */
815                 /* load MonoInternalThread* into EDX */
816                 code = mono_x86_emit_tls_get (code, X86_EDX, mono_thread_get_tls_offset ());
817                 /* load TID into EDX */
818                 x86_mov_reg_membase (code, X86_EDX, X86_EDX, G_STRUCT_OFFSET (MonoInternalThread, tid), 4);
819                 /* is synchronization->owner == TID */
820                 x86_alu_membase_reg (code, X86_CMP, X86_ECX, owner_offset, X86_EDX);
821                 /* if no, jump to actual trampoline */
822                 jump_not_owned = code;
823                 x86_branch8 (code, X86_CC_NZ, -1, 1);
824
825                 /* next case: synchronization->owner == TID */
826                 /* is synchronization->nest == 1 */
827                 x86_alu_membase_imm (code, X86_CMP, X86_ECX, nest_offset, 1);
828                 /* if not, jump to next case */
829                 jump_next = code;
830                 x86_branch8 (code, X86_CC_NZ, -1, 1);
831                 /* if yes, is synchronization->entry_count zero? */
832                 x86_alu_membase_imm (code, X86_CMP, X86_ECX, entry_count_offset, 0);
833                 /* if not, jump to actual trampoline */
834                 jump_have_waiters = code;
835                 x86_branch8 (code, X86_CC_NZ, -1 , 1);
836                 /* if yes, set synchronization->owner to null and return */
837                 x86_mov_membase_imm (code, X86_ECX, owner_offset, 0, 4);
838                 x86_ret (code);
839
840                 /* next case: synchronization->nest is not 1 */
841                 x86_patch (jump_next, code);
842                 /* decrease synchronization->nest and return */
843                 x86_dec_membase (code, X86_ECX, nest_offset);
844                 x86_ret (code);
845
846                 /* push obj and jump to the actual trampoline */
847                 x86_patch (jump_obj_null, code);
848                 x86_patch (jump_have_waiters, code);
849                 x86_patch (jump_not_owned, code);
850                 x86_patch (jump_sync_null, code);
851         }
852
853         /* push obj and jump to the actual trampoline */
854         x86_push_reg (code, X86_EAX);
855         if (aot) {
856                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_monitor_exit");
857                 x86_jump_reg (code, X86_EAX);
858         } else {
859                 x86_jump_code (code, tramp);
860         }
861
862         mono_arch_flush_icache (buf, code - buf);
863         g_assert (code - buf <= tramp_size);
864
865         if (info)
866                 *info = mono_tramp_info_create (g_strdup_printf ("monitor_exit_trampoline"), buf, code - buf, ji, unwind_ops);
867
868         return buf;
869 }
870
871 #else
872
873 gpointer
874 mono_arch_create_monitor_enter_trampoline (MonoTrampInfo **info, gboolean aot)
875 {
876         g_assert_not_reached ();
877         return NULL;
878 }
879
880 gpointer
881 mono_arch_create_monitor_exit_trampoline (MonoTrampInfo **info, gboolean aot)
882 {
883         g_assert_not_reached ();
884         return NULL;
885 }
886
887 #endif
888
889 void
890 mono_arch_invalidate_method (MonoJitInfo *ji, void *func, gpointer func_arg)
891 {
892         /* FIXME: This is not thread safe */
893         guint8 *code = ji->code_start;
894
895         x86_push_imm (code, func_arg);
896         x86_call_code (code, (guint8*)func);
897 }
898
899 static void
900 handler_block_trampoline_helper (gpointer *ptr)
901 {
902         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
903         *ptr = jit_tls->handler_block_return_address;
904 }
905
906 gpointer
907 mono_arch_create_handler_block_trampoline (void)
908 {
909         guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD);
910         guint8 *code, *buf;
911         int tramp_size = 64;
912         code = buf = mono_global_codeman_reserve (tramp_size);
913
914         /*
915         This trampoline restore the call chain of the handler block then jumps into the code that deals with it.
916         */
917
918         if (mono_get_jit_tls_offset () != -1) {
919                 code = mono_x86_emit_tls_get (code, X86_EAX, mono_get_jit_tls_offset ());
920                 x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoJitTlsData, handler_block_return_address), 4);
921                 /*simulate a call*/
922                 x86_push_reg (code, X86_EAX);
923                 x86_jump_code (code, tramp);
924         } else {
925                 /*Slow path uses a c helper*/
926                 x86_push_reg (code, X86_ESP);
927                 x86_push_imm (code, tramp);
928                 x86_jump_code (code, handler_block_trampoline_helper);
929         }
930
931         mono_arch_flush_icache (buf, code - buf);
932         g_assert (code - buf <= tramp_size);
933
934         return buf;
935 }
936
937 guint8*
938 mono_arch_get_call_target (guint8 *code)
939 {
940         if (code [-5] == 0xe8) {
941                 guint32 disp = *(guint32*)(code - 4);
942                 guint8 *target = code + disp;
943
944                 return target;
945         } else {
946                 return NULL;
947         }
948 }
949
950 guint32
951 mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
952 {
953         return *(guint32*)(plt_entry + 6);
954 }