2010-06-15 Zoltan Varga <vargaz@gmail.com>
[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         unwind_ops = mono_arch_get_cie_program ();
234
235         code = buf = mono_global_codeman_reserve (256);
236
237         /* Note that there is a single argument to the trampoline
238          * and it is stored at: esp + pushed_args * sizeof (gpointer)
239          * the ret address is at: esp + (pushed_args + 1) * sizeof (gpointer)
240          */
241
242         /* Put all registers into an array on the stack
243          * If this code is changed, make sure to update the offset value in
244          * mono_arch_get_this_arg_from_call () in mini-x86.c.
245          */
246         x86_push_reg (code, X86_EDI);
247         x86_push_reg (code, X86_ESI);
248         x86_push_reg (code, X86_EBP);
249         x86_push_reg (code, X86_ESP);
250         x86_push_reg (code, X86_EBX);
251         x86_push_reg (code, X86_EDX);
252         x86_push_reg (code, X86_ECX);
253         x86_push_reg (code, X86_EAX);
254
255         pushed_args_caller_saved = pushed_args = 8;
256
257         /* Align stack on apple */
258         x86_alu_reg_imm (code, X86_SUB, X86_ESP, 4);
259
260         pushed_args ++;
261
262         /* save LMF begin */
263
264         /* save the IP (caller ip) */
265         if (tramp_type == MONO_TRAMPOLINE_JUMP)
266                 x86_push_imm (code, 0);
267         else
268                 x86_push_membase (code, X86_ESP, (pushed_args + 1) * sizeof (gpointer));
269
270         pushed_args++;
271
272         x86_push_reg (code, X86_EBP);
273         x86_push_reg (code, X86_ESI);
274         x86_push_reg (code, X86_EDI);
275         x86_push_reg (code, X86_EBX);
276
277         pushed_args += 4;
278
279         /* save ESP */
280         x86_push_reg (code, X86_ESP);
281         /* Adjust ESP so it points to the previous frame */
282         x86_alu_membase_imm (code, X86_ADD, X86_ESP, 0, (pushed_args + 2) * 4);
283
284         pushed_args ++;
285
286         /* save method info */
287         if ((tramp_type == MONO_TRAMPOLINE_JIT) || (tramp_type == MONO_TRAMPOLINE_JUMP))
288                 x86_push_membase (code, X86_ESP, pushed_args * sizeof (gpointer));
289         else
290                 x86_push_imm (code, 0);
291
292         pushed_args++;
293
294         /* On apple, the stack is correctly aligned to 16 bytes because pushed_args is
295          * 16 and there is the extra trampoline arg + the return ip pushed by call
296          * FIXME: Note that if an exception happens while some args are pushed
297          * on the stack, the stack will be misaligned.
298          */
299         g_assert (pushed_args == 16);
300
301         /* get the address of lmf for the current thread */
302         if (aot) {
303                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_get_lmf_addr");
304                 x86_call_reg (code, X86_EAX);
305         } else {
306                 x86_call_code (code, mono_get_lmf_addr);
307         }
308         /* push lmf */
309         x86_push_reg (code, X86_EAX); 
310         /* push *lfm (previous_lmf) */
311         x86_push_membase (code, X86_EAX, 0);
312         /* Signal to mono_arch_find_jit_info () that this is a trampoline frame */
313         x86_alu_membase_imm (code, X86_ADD, X86_ESP, 0, 1);
314         /* *(lmf) = ESP */
315         x86_mov_membase_reg (code, X86_EAX, 0, X86_ESP, 4);
316         /* save LFM end */
317
318         pushed_args += 2;
319
320         /* starting the call sequence */
321
322         /* FIXME: Push the trampoline address */
323         x86_push_imm (code, 0);
324
325         pushed_args++;
326
327         /* push the method info */
328         x86_push_membase (code, X86_ESP, pushed_args * sizeof (gpointer));
329
330         pushed_args++;
331
332         /* push the return address onto the stack */
333         if (tramp_type == MONO_TRAMPOLINE_JUMP)
334                 x86_push_imm (code, 0);
335         else
336                 x86_push_membase (code, X86_ESP, (pushed_args + 1) * sizeof (gpointer));
337         pushed_args++;
338         /* push the address of the register array */
339         x86_lea_membase (code, X86_EAX, X86_ESP, (pushed_args - 8) * sizeof (gpointer));
340         x86_push_reg (code, X86_EAX);
341
342         pushed_args++;
343
344 #ifdef __APPLE__
345         /* check the stack is aligned after the ret ip is pushed */
346         /*x86_mov_reg_reg (buf, X86_EDX, X86_ESP, 4);
347         x86_alu_reg_imm (buf, X86_AND, X86_EDX, 15);
348         x86_alu_reg_imm (buf, X86_CMP, X86_EDX, 0);
349         x86_branch_disp (buf, X86_CC_Z, 3, FALSE);
350         x86_breakpoint (buf);*/
351 #endif
352
353         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, X86_ESP, ((pushed_args + 2) * 4));
354
355         if (aot) {
356                 char *icall_name = g_strdup_printf ("trampoline_func_%d", tramp_type);
357                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
358                 x86_call_reg (code, X86_EAX);
359         } else {
360                 tramp = (guint8*)mono_get_trampoline_func (tramp_type);
361                 x86_call_code (code, tramp);
362         }
363
364         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4*4);
365
366         pushed_args -= 4;
367
368         /* Check for thread interruption */
369         /* This is not perf critical code so no need to check the interrupt flag */
370         /* Align the stack on osx */
371         x86_alu_reg_imm (code, X86_SUB, X86_ESP, 3 * 4);
372         x86_push_reg (code, X86_EAX);
373         if (aot) {
374                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_thread_force_interruption_checkpoint");
375                 x86_call_reg (code, X86_EAX);
376         } else {
377                 x86_call_code (code, (guint8*)mono_thread_force_interruption_checkpoint);
378         }
379         x86_pop_reg (code, X86_EAX);
380         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 3 * 4);
381
382         /* Restore LMF */
383
384         /* ebx = previous_lmf */
385         x86_pop_reg (code, X86_EBX);
386         pushed_args--;
387         x86_alu_reg_imm (code, X86_SUB, X86_EBX, 1);
388
389         /* edi = lmf */
390         x86_pop_reg (code, X86_EDI);
391         pushed_args--;
392
393         /* *(lmf) = previous_lmf */
394         x86_mov_membase_reg (code, X86_EDI, 0, X86_EBX, 4);
395
396         /* discard method info */
397         x86_pop_reg (code, X86_ESI);
398         pushed_args--;
399
400         /* discard ESP */
401         x86_pop_reg (code, X86_ESI);
402         pushed_args--;
403
404         /* restore caller saved regs */
405         x86_pop_reg (code, X86_EBX);
406         x86_pop_reg (code, X86_EDI);
407         x86_pop_reg (code, X86_ESI);
408         x86_pop_reg (code, X86_EBP);
409
410         pushed_args -= 4;
411
412         /* discard save IP */
413         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
414         pushed_args--;
415
416         /* restore LMF end */
417
418         if (!MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type)) {
419                 /* 
420                  * Overwrite the method ptr with the address we need to jump to,
421                  * to free %eax.
422                  */
423                 x86_mov_membase_reg (code, X86_ESP, pushed_args * sizeof (gpointer), X86_EAX, 4);
424         }
425
426         /* Restore caller saved registers */
427         x86_mov_reg_membase (code, X86_ECX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_ECX) * 4, 4);
428         x86_mov_reg_membase (code, X86_EDX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_EDX) * 4, 4);
429         if ((tramp_type == MONO_TRAMPOLINE_RESTORE_STACK_PROT) || (tramp_type == MONO_TRAMPOLINE_AOT_PLT))
430                 x86_mov_reg_membase (code, X86_EAX, X86_ESP, (pushed_args - pushed_args_caller_saved + X86_EAX) * 4, 4);
431
432         if (!MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type)) {
433                 /* Pop saved reg array + stack align */
434                 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 9 * 4);
435                 pushed_args -= 9;
436                 g_assert (pushed_args == 0);
437         } else {
438                 /* Pop saved reg array + stack align + method ptr */
439                 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 10 * 4);
440                 pushed_args -= 10;
441
442                 /* We've popped one more stack item than we've pushed (the
443                    method ptr argument), so we must end up at -1. */
444                 g_assert (pushed_args == -1);
445         }
446
447         x86_ret (code);
448
449         g_assert ((code - buf) <= 256);
450
451         if (info)
452                 *info = mono_tramp_info_create (g_strdup_printf ("generic_trampoline_%d", tramp_type), buf, code - buf, ji, unwind_ops);
453
454         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
455                 /* Initialize the nullified class init trampoline used in the AOT case */
456                 nullified_class_init_trampoline = mono_arch_get_nullified_class_init_trampoline (NULL);
457         }
458
459         return buf;
460 }
461
462 gpointer
463 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
464 {
465         guint8 *code, *buf;
466
467         code = buf = mono_global_codeman_reserve (16);
468         x86_ret (code);
469
470         mono_arch_flush_icache (buf, code - buf);
471
472         if (info)
473                 *info = mono_tramp_info_create (g_strdup_printf ("nullified_class_init_trampoline"), buf, code - buf, NULL, NULL);
474
475         return buf;
476 }
477
478 #define TRAMPOLINE_SIZE 10
479
480 gpointer
481 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
482 {
483         guint8 *code, *buf, *tramp;
484         
485         tramp = mono_get_trampoline_code (tramp_type);
486
487         code = buf = mono_domain_code_reserve_align (domain, TRAMPOLINE_SIZE, 4);
488
489         x86_push_imm (buf, arg1);
490         x86_jump_code (buf, tramp);
491         g_assert ((buf - code) <= TRAMPOLINE_SIZE);
492
493         mono_arch_flush_icache (code, buf - code);
494
495         if (code_len)
496                 *code_len = buf - code;
497
498         return code;
499 }
500
501 gpointer
502 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
503 {
504         guint8 *tramp;
505         guint8 *code, *buf;
506         guint8 **rgctx_null_jumps;
507         int tramp_size;
508         int depth, index;
509         int i;
510         gboolean mrgctx;
511         MonoJumpInfo *ji = NULL;
512         GSList *unwind_ops = NULL;
513
514         mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
515         index = MONO_RGCTX_SLOT_INDEX (slot);
516         if (mrgctx)
517                 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
518         for (depth = 0; ; ++depth) {
519                 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
520
521                 if (index < size - 1)
522                         break;
523                 index -= size - 1;
524         }
525
526         tramp_size = (aot ? 64 : 36) + 6 * depth;
527
528         code = buf = mono_global_codeman_reserve (tramp_size);
529
530         rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
531
532         /* load vtable/mrgctx ptr */
533         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
534         if (!mrgctx) {
535                 /* load rgctx ptr from vtable */
536                 x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context), 4);
537                 /* is the rgctx ptr null? */
538                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
539                 /* if yes, jump to actual trampoline */
540                 rgctx_null_jumps [0] = code;
541                 x86_branch8 (code, X86_CC_Z, -1, 1);
542         }
543
544         for (i = 0; i < depth; ++i) {
545                 /* load ptr to next array */
546                 if (mrgctx && i == 0)
547                         x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT, 4);
548                 else
549                         x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, 4);
550                 /* is the ptr null? */
551                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
552                 /* if yes, jump to actual trampoline */
553                 rgctx_null_jumps [i + 1] = code;
554                 x86_branch8 (code, X86_CC_Z, -1, 1);
555         }
556
557         /* fetch slot */
558         x86_mov_reg_membase (code, X86_EAX, X86_EAX, sizeof (gpointer) * (index + 1), 4);
559         /* is the slot null? */
560         x86_test_reg_reg (code, X86_EAX, X86_EAX);
561         /* if yes, jump to actual trampoline */
562         rgctx_null_jumps [depth + 1] = code;
563         x86_branch8 (code, X86_CC_Z, -1, 1);
564         /* otherwise return */
565         x86_ret (code);
566
567         for (i = mrgctx ? 1 : 0; i <= depth + 1; ++i)
568                 x86_patch (rgctx_null_jumps [i], code);
569
570         g_free (rgctx_null_jumps);
571
572         x86_mov_reg_membase (code, MONO_ARCH_VTABLE_REG, X86_ESP, 4, 4);
573
574         if (aot) {
575                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
576                 x86_jump_reg (code, X86_EAX);
577         } else {
578                 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), NULL);
579
580                 /* jump to the actual trampoline */
581                 x86_jump_code (code, tramp);
582         }
583
584         mono_arch_flush_icache (buf, code - buf);
585
586         g_assert (code - buf <= tramp_size);
587
588         if (info)
589                 *info = mono_tramp_info_create (g_strdup_printf ("rgctx_fetch_trampoline_%u", slot), buf, code - buf, ji, unwind_ops);
590
591         return buf;
592 }
593
594 gpointer
595 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
596 {
597         guint8 *tramp;
598         guint8 *code, *buf;
599         static int byte_offset = -1;
600         static guint8 bitmask;
601         guint8 *jump;
602         int tramp_size;
603         GSList *unwind_ops = NULL;
604         MonoJumpInfo *ji = NULL;
605
606         tramp_size = 64;
607
608         code = buf = mono_global_codeman_reserve (tramp_size);
609
610         if (byte_offset < 0)
611                 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
612
613         x86_test_membase_imm (code, MONO_ARCH_VTABLE_REG, byte_offset, bitmask);
614         jump = code;
615         x86_branch8 (code, X86_CC_Z, -1, 1);
616
617         x86_ret (code);
618
619         x86_patch (jump, code);
620
621         /* Push the vtable so the stack is the same as in a specific trampoline */
622         x86_push_reg (code, MONO_ARCH_VTABLE_REG);
623
624         if (aot) {
625                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_generic_class_init");
626                 x86_jump_reg (code, X86_EAX);
627         } else {
628                 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC_CLASS_INIT);
629
630                 /* jump to the actual trampoline */
631                 x86_jump_code (code, tramp);
632         }
633
634         mono_arch_flush_icache (code, code - buf);
635
636         g_assert (code - buf <= tramp_size);
637
638         if (info)
639                 *info = mono_tramp_info_create (g_strdup_printf ("generic_class_init_trampoline"), buf, code - buf, ji, unwind_ops);
640
641         return buf;
642 }
643
644 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
645 /*
646  * The code produced by this trampoline is equivalent to this:
647  *
648  * if (obj) {
649  *      if (obj->synchronisation) {
650  *              if (obj->synchronisation->owner == 0) {
651  *                      if (cmpxch (&obj->synchronisation->owner, TID, 0) == 0)
652  *                              return;
653  *              }
654  *              if (obj->synchronisation->owner == TID) {
655  *                      ++obj->synchronisation->nest;
656  *                      return;
657  *              }
658  *      }
659  * }
660  * return full_monitor_enter ();
661  *
662  */
663 gpointer
664 mono_arch_create_monitor_enter_trampoline (MonoTrampInfo **info, gboolean aot)
665 {
666         guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_MONITOR_ENTER);
667         guint8 *code, *buf;
668         guint8 *jump_obj_null, *jump_sync_null, *jump_other_owner, *jump_cmpxchg_failed, *jump_tid;
669         int tramp_size;
670         int owner_offset, nest_offset, dummy;
671         MonoJumpInfo *ji = NULL;
672         GSList *unwind_ops = NULL;
673
674         g_assert (MONO_ARCH_MONITOR_OBJECT_REG == X86_EAX);
675
676         mono_monitor_threads_sync_members_offset (&owner_offset, &nest_offset, &dummy);
677         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset) == sizeof (gpointer));
678         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32));
679         owner_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset);
680         nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset);
681
682         tramp_size = 64;
683
684         code = buf = mono_global_codeman_reserve (tramp_size);
685
686         if (mono_thread_get_tls_offset () != -1) {
687                 /* MonoObject* obj is in EAX */
688                 /* is obj null? */
689                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
690                 /* if yes, jump to actual trampoline */
691                 jump_obj_null = code;
692                 x86_branch8 (code, X86_CC_Z, -1, 1);
693
694                 /* load obj->synchronization to ECX */
695                 x86_mov_reg_membase (code, X86_ECX, X86_EAX, G_STRUCT_OFFSET (MonoObject, synchronisation), 4);
696                 /* is synchronization null? */
697                 x86_test_reg_reg (code, X86_ECX, X86_ECX);
698                 /* if yes, jump to actual trampoline */
699                 jump_sync_null = code;
700                 x86_branch8 (code, X86_CC_Z, -1, 1);
701
702                 /* load MonoInternalThread* into EDX */
703                 code = mono_x86_emit_tls_get (code, X86_EDX, mono_thread_get_tls_offset ());
704                 /* load TID into EDX */
705                 x86_mov_reg_membase (code, X86_EDX, X86_EDX, G_STRUCT_OFFSET (MonoInternalThread, tid), 4);
706
707                 /* is synchronization->owner null? */
708                 x86_alu_membase_imm (code, X86_CMP, X86_ECX, owner_offset, 0);
709                 /* if not, jump to next case */
710                 jump_tid = code;
711                 x86_branch8 (code, X86_CC_NZ, -1, 1);
712
713                 /* if yes, try a compare-exchange with the TID */
714                 /* free up register EAX, needed for the zero */
715                 x86_push_reg (code, X86_EAX);
716                 /* zero EAX */
717                 x86_alu_reg_reg (code, X86_XOR, X86_EAX, X86_EAX);
718                 /* compare and exchange */
719                 x86_prefix (code, X86_LOCK_PREFIX);
720                 x86_cmpxchg_membase_reg (code, X86_ECX, owner_offset, X86_EDX);
721                 /* if not successful, jump to actual trampoline */
722                 jump_cmpxchg_failed = code;
723                 x86_branch8 (code, X86_CC_NZ, -1, 1);
724                 /* if successful, pop and return */
725                 x86_pop_reg (code, X86_EAX);
726                 x86_ret (code);
727
728                 /* next case: synchronization->owner is not null */
729                 x86_patch (jump_tid, code);
730                 /* is synchronization->owner == TID? */
731                 x86_alu_membase_reg (code, X86_CMP, X86_ECX, owner_offset, X86_EDX);
732                 /* if not, jump to actual trampoline */
733                 jump_other_owner = code;
734                 x86_branch8 (code, X86_CC_NZ, -1, 1);
735                 /* if yes, increment nest */
736                 x86_inc_membase (code, X86_ECX, nest_offset);
737                 /* return */
738                 x86_ret (code);
739
740                 /* push obj */
741                 x86_patch (jump_obj_null, code);
742                 x86_patch (jump_sync_null, code);
743                 x86_patch (jump_other_owner, code);
744                 x86_push_reg (code, X86_EAX);
745                 /* jump to the actual trampoline */
746                 x86_patch (jump_cmpxchg_failed, code);
747                 if (aot) {
748                         /* We are calling the generic trampoline directly, the argument is pushed
749                          * on the stack just like a specific trampoline.
750                          */
751                         code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_monitor_enter");
752                         x86_jump_reg (code, X86_EAX);
753                 } else {
754                         x86_jump_code (code, tramp);
755                 }
756         } else {
757                 /* push obj and jump to the actual trampoline */
758                 x86_push_reg (code, X86_EAX);
759                 if (aot) {
760                         code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_monitor_enter");
761                         x86_jump_reg (code, X86_EAX);
762                 } else {
763                         x86_jump_code (code, tramp);
764                 }
765         }
766
767         mono_arch_flush_icache (buf, code - buf);
768         g_assert (code - buf <= tramp_size);
769
770         if (info)
771                 *info = mono_tramp_info_create (g_strdup_printf ("monitor_enter_trampoline"), buf, code - buf, ji, unwind_ops);
772
773         return buf;
774 }
775
776 gpointer
777 mono_arch_create_monitor_exit_trampoline (MonoTrampInfo **info, gboolean aot)
778 {
779         guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_MONITOR_EXIT);
780         guint8 *code, *buf;
781         guint8 *jump_obj_null, *jump_have_waiters, *jump_sync_null, *jump_not_owned;
782         guint8 *jump_next;
783         int tramp_size;
784         int owner_offset, nest_offset, entry_count_offset;
785         MonoJumpInfo *ji = NULL;
786         GSList *unwind_ops = NULL;
787
788         g_assert (MONO_ARCH_MONITOR_OBJECT_REG == X86_EAX);
789
790         mono_monitor_threads_sync_members_offset (&owner_offset, &nest_offset, &entry_count_offset);
791         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (owner_offset) == sizeof (gpointer));
792         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32));
793         g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (entry_count_offset) == sizeof (gint32));
794         owner_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (owner_offset);
795         nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset);
796         entry_count_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (entry_count_offset);
797
798         tramp_size = 64;
799
800         code = buf = mono_global_codeman_reserve (tramp_size);
801
802         if (mono_thread_get_tls_offset () != -1) {
803                 /* MonoObject* obj is in EAX */
804                 /* is obj null? */
805                 x86_test_reg_reg (code, X86_EAX, X86_EAX);
806                 /* if yes, jump to actual trampoline */
807                 jump_obj_null = code;
808                 x86_branch8 (code, X86_CC_Z, -1, 1);
809
810                 /* load obj->synchronization to ECX */
811                 x86_mov_reg_membase (code, X86_ECX, X86_EAX, G_STRUCT_OFFSET (MonoObject, synchronisation), 4);
812                 /* is synchronization null? */
813                 x86_test_reg_reg (code, X86_ECX, X86_ECX);
814                 /* if yes, jump to actual trampoline */
815                 jump_sync_null = code;
816                 x86_branch8 (code, X86_CC_Z, -1, 1);
817
818                 /* next case: synchronization is not null */
819                 /* load MonoInternalThread* into EDX */
820                 code = mono_x86_emit_tls_get (code, X86_EDX, mono_thread_get_tls_offset ());
821                 /* load TID into EDX */
822                 x86_mov_reg_membase (code, X86_EDX, X86_EDX, G_STRUCT_OFFSET (MonoInternalThread, tid), 4);
823                 /* is synchronization->owner == TID */
824                 x86_alu_membase_reg (code, X86_CMP, X86_ECX, owner_offset, X86_EDX);
825                 /* if no, jump to actual trampoline */
826                 jump_not_owned = code;
827                 x86_branch8 (code, X86_CC_NZ, -1, 1);
828
829                 /* next case: synchronization->owner == TID */
830                 /* is synchronization->nest == 1 */
831                 x86_alu_membase_imm (code, X86_CMP, X86_ECX, nest_offset, 1);
832                 /* if not, jump to next case */
833                 jump_next = code;
834                 x86_branch8 (code, X86_CC_NZ, -1, 1);
835                 /* if yes, is synchronization->entry_count zero? */
836                 x86_alu_membase_imm (code, X86_CMP, X86_ECX, entry_count_offset, 0);
837                 /* if not, jump to actual trampoline */
838                 jump_have_waiters = code;
839                 x86_branch8 (code, X86_CC_NZ, -1 , 1);
840                 /* if yes, set synchronization->owner to null and return */
841                 x86_mov_membase_imm (code, X86_ECX, owner_offset, 0, 4);
842                 x86_ret (code);
843
844                 /* next case: synchronization->nest is not 1 */
845                 x86_patch (jump_next, code);
846                 /* decrease synchronization->nest and return */
847                 x86_dec_membase (code, X86_ECX, nest_offset);
848                 x86_ret (code);
849
850                 /* push obj and jump to the actual trampoline */
851                 x86_patch (jump_obj_null, code);
852                 x86_patch (jump_have_waiters, code);
853                 x86_patch (jump_not_owned, code);
854                 x86_patch (jump_sync_null, code);
855         }
856
857         /* push obj and jump to the actual trampoline */
858         x86_push_reg (code, X86_EAX);
859         if (aot) {
860                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "generic_trampoline_monitor_exit");
861                 x86_jump_reg (code, X86_EAX);
862         } else {
863                 x86_jump_code (code, tramp);
864         }
865
866         mono_arch_flush_icache (buf, code - buf);
867         g_assert (code - buf <= tramp_size);
868
869         if (info)
870                 *info = mono_tramp_info_create (g_strdup_printf ("monitor_exit_trampoline"), buf, code - buf, ji, unwind_ops);
871
872         return buf;
873 }
874
875 #else
876
877 gpointer
878 mono_arch_create_monitor_enter_trampoline (MonoTrampInfo **info, gboolean aot)
879 {
880         g_assert_not_reached ();
881         return NULL;
882 }
883
884 gpointer
885 mono_arch_create_monitor_exit_trampoline (MonoTrampInfo **info, gboolean aot)
886 {
887         g_assert_not_reached ();
888         return NULL;
889 }
890
891 #endif
892
893 void
894 mono_arch_invalidate_method (MonoJitInfo *ji, void *func, gpointer func_arg)
895 {
896         /* FIXME: This is not thread safe */
897         guint8 *code = ji->code_start;
898
899         x86_push_imm (code, func_arg);
900         x86_call_code (code, (guint8*)func);
901 }
902
903 static void
904 handler_block_trampoline_helper (gpointer *ptr)
905 {
906         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
907         *ptr = jit_tls->handler_block_return_address;
908 }
909
910 gpointer
911 mono_arch_create_handler_block_trampoline (void)
912 {
913         guint8 *tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD);
914         guint8 *code, *buf;
915         int tramp_size = 64;
916         code = buf = mono_global_codeman_reserve (tramp_size);
917
918         /*
919         This trampoline restore the call chain of the handler block then jumps into the code that deals with it.
920         */
921
922         if (mono_get_jit_tls_offset () != -1) {
923                 code = mono_x86_emit_tls_get (code, X86_EAX, mono_get_jit_tls_offset ());
924                 x86_mov_reg_membase (code, X86_EAX, X86_EAX, G_STRUCT_OFFSET (MonoJitTlsData, handler_block_return_address), 4);
925                 /*simulate a call*/
926                 x86_push_reg (code, X86_EAX);
927                 x86_jump_code (code, tramp);
928         } else {
929                 /*Slow path uses a c helper*/
930                 x86_push_reg (code, X86_ESP);
931                 x86_push_imm (code, tramp);
932                 x86_jump_code (code, handler_block_trampoline_helper);
933         }
934
935         mono_arch_flush_icache (buf, code - buf);
936         g_assert (code - buf <= tramp_size);
937
938         return buf;
939 }
940
941 guint8*
942 mono_arch_get_call_target (guint8 *code)
943 {
944         if (code [-5] == 0xe8) {
945                 guint32 disp = *(guint32*)(code - 4);
946                 guint8 *target = code + disp;
947
948                 return target;
949         } else {
950                 return NULL;
951         }
952 }
953
954 guint32
955 mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
956 {
957         return *(guint32*)(plt_entry + 6);
958 }