Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / mini / tramp-arm.c
1 /**
2  * \file
3  * JIT trampoline code for ARM
4  *
5  * Authors:
6  *   Paolo Molaro (lupus@ximian.com)
7  *
8  * (C) 2001-2003 Ximian, Inc.
9  * Copyright 2003-2011 Novell Inc
10  * Copyright 2011 Xamarin Inc
11  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12  */
13
14 #include <config.h>
15 #include <glib.h>
16
17 #include <mono/metadata/abi-details.h>
18 #include <mono/metadata/appdomain.h>
19 #include <mono/metadata/marshal.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/profiler-private.h>
22 #include <mono/arch/arm/arm-codegen.h>
23 #include <mono/arch/arm/arm-vfp-codegen.h>
24
25 #include "mini.h"
26 #include "mini-arm.h"
27 #include "debugger-agent.h"
28 #include "jit-icalls.h"
29
30 #ifdef ENABLE_INTERPRETER
31 #include "interp/interp.h"
32 #endif
33
34 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
35
36 void
37 mono_arch_patch_callsite (guint8 *method_start, guint8 *code_ptr, guint8 *addr)
38 {
39         guint32 *code = (guint32*)code_ptr;
40
41         /* This is the 'bl' or the 'mov pc' instruction */
42         --code;
43         
44         /*
45          * Note that methods are called also with the bl opcode.
46          */
47         if ((((*code) >> 25)  & 7) == 5) {
48                 /*g_print ("direct patching\n");*/
49                 arm_patch ((guint8*)code, addr);
50                 mono_arch_flush_icache ((guint8*)code, 4);
51                 return;
52         }
53
54         if ((((*code) >> 20) & 0xFF) == 0x12) {
55                 /*g_print ("patching bx\n");*/
56                 arm_patch ((guint8*)code, addr);
57                 mono_arch_flush_icache ((guint8*)(code - 2), 4);
58                 return;
59         }
60
61         g_assert_not_reached ();
62 }
63
64 void
65 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
66 {
67         guint8 *jump_entry;
68
69         /* Patch the jump table entry used by the plt entry */
70         if (*(guint32*)code == 0xe59fc000) {
71                 /* ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0); */
72                 guint32 offset = ((guint32*)code)[2];
73                 
74                 jump_entry = code + offset + 12;
75         } else if (*(guint16*)(code - 4) == 0xf8df) {
76                 /* 
77                  * Thumb PLT entry, begins with ldr.w ip, [pc, #8], code points to entry + 4, see
78                  * mono_arm_get_thumb_plt_entry ().
79                  */
80                 guint32 offset;
81
82                 code -= 4;
83                 offset = *(guint32*)(code + 12);
84                 jump_entry = code + offset + 8;
85         } else {
86                 g_assert_not_reached ();
87         }
88
89         *(guint8**)jump_entry = addr;
90 }
91
92 #ifndef DISABLE_JIT
93
94 #define arm_is_imm12(v) ((int)(v) > -4096 && (int)(v) < 4096)
95
96 /*
97  * Return the instruction to jump from code to target, 0 if not
98  * reachable with a single instruction
99  */
100 static guint32
101 branch_for_target_reachable (guint8 *branch, guint8 *target)
102 {
103         gint diff = target - branch - 8;
104         g_assert ((diff & 3) == 0);
105         if (diff >= 0) {
106                 if (diff <= 33554431)
107                         return (ARMCOND_AL << ARMCOND_SHIFT) | (ARM_BR_TAG) | (diff >> 2);
108         } else {
109                 /* diff between 0 and -33554432 */
110                 if (diff >= -33554432)
111                         return (ARMCOND_AL << ARMCOND_SHIFT) | (ARM_BR_TAG) | ((diff >> 2) & ~0xff000000);
112         }
113         return 0;
114 }
115
116 static inline guint8*
117 emit_bx (guint8* code, int reg)
118 {
119         if (mono_arm_thumb_supported ())
120                 ARM_BX (code, reg);
121         else
122                 ARM_MOV_REG_REG (code, ARMREG_PC, reg);
123         return code;
124 }
125
126 /* Stack size for trampoline function
127  */
128 #define STACK ALIGN_TO (sizeof (MonoLMF), MONO_ARCH_FRAME_ALIGNMENT)
129
130 /* Method-specific trampoline code fragment size */
131 #define METHOD_TRAMPOLINE_SIZE 64
132
133 /* Jump-specific trampoline code fragment size */
134 #define JUMP_TRAMPOLINE_SIZE   64
135
136 guchar*
137 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
138 {
139         char *tramp_name;
140         guint8 *buf, *code = NULL;
141         guint8 *load_get_lmf_addr  = NULL, *load_trampoline  = NULL;
142         gpointer *constants;
143         int i, cfa_offset, regsave_size, lr_offset;
144         GSList *unwind_ops = NULL;
145         MonoJumpInfo *ji = NULL;
146         int buf_len;
147
148         /* Now we'll create in 'buf' the ARM trampoline code. This
149          is the trampoline code common to all methods  */
150
151         buf_len = 272;
152
153         /* Add space for saving/restoring VFP regs. */
154         if (mono_arm_is_hard_float ())
155                 buf_len += 8 * 2;
156
157         code = buf = mono_global_codeman_reserve (buf_len);
158
159         /*
160          * At this point lr points to the specific arg and sp points to the saved
161          * regs on the stack (all but PC and SP). The original LR value has been
162          * saved as sp + LR_OFFSET by the push in the specific trampoline
163          */
164
165         /* The size of the area already allocated by the push in the specific trampoline */
166         regsave_size = 14 * sizeof (mgreg_t);
167         /* The offset where lr was saved inside the regsave area */
168         lr_offset = 13 * sizeof (mgreg_t);
169
170         // CFA = SP + (num registers pushed) * 4
171         cfa_offset = 14 * sizeof (mgreg_t);
172         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, cfa_offset);
173         // PC saved at sp+LR_OFFSET
174         mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_LR, -4);
175         /* Callee saved regs */
176         for (i = 0; i < 8; ++i)
177                 mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_R4 + i, -regsave_size + ((4 + i) * 4));
178
179         if (aot) {
180                 /* 
181                  * For page trampolines the data is in r1, so just move it, otherwise use the got slot as below.
182                  * The trampoline contains a pc-relative offset to the got slot 
183                  * preceeding the got slot where the value is stored. The offset can be
184                  * found at [lr + 0].
185                  */
186                 /* See if emit_trampolines () in aot-compiler.c for the '2' */
187                 if (aot == 2) {
188                         ARM_MOV_REG_REG (code, ARMREG_V2, ARMREG_R1);
189                 } else {
190                         ARM_LDR_IMM (code, ARMREG_V2, ARMREG_LR, 0);
191                         ARM_ADD_REG_IMM (code, ARMREG_V2, ARMREG_V2, 4, 0);
192                         ARM_LDR_REG_REG (code, ARMREG_V2, ARMREG_V2, ARMREG_LR);
193                 }
194         } else {
195                 ARM_LDR_IMM (code, ARMREG_V2, ARMREG_LR, 0);
196         }
197         ARM_LDR_IMM (code, ARMREG_V3, ARMREG_SP, lr_offset);
198
199         /* we build the MonoLMF structure on the stack - see mini-arm.h
200          * The pointer to the struct is put in r1.
201          * the iregs array is already allocated on the stack by push.
202          */
203         code = mono_arm_emit_load_imm (code, ARMREG_R2, STACK - regsave_size);
204         ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_R2);
205         cfa_offset += STACK - regsave_size;
206         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
207         /* V1 == lmf */
208         code = mono_arm_emit_load_imm (code, ARMREG_R2, STACK - sizeof (MonoLMF));
209         ARM_ADD_REG_REG (code, ARMREG_V1, ARMREG_SP, ARMREG_R2);
210
211         /* ok, now we can continue with the MonoLMF setup, mostly untouched 
212          * from emit_prolog in mini-arm.c
213          * This is a synthetized call to mono_get_lmf_addr ()
214          */
215         if (aot) {
216                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_get_lmf_addr");
217                 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
218                 ARM_B (code, 0);
219                 *(gpointer*)code = NULL;
220                 code += 4;
221                 ARM_LDR_REG_REG (code, ARMREG_R0, ARMREG_PC, ARMREG_R0);
222         } else {
223                 load_get_lmf_addr = code;
224                 code += 4;
225         }
226         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
227         code = emit_bx (code, ARMREG_R0);
228
229         /*
230          * The stack now looks like:
231          *       <saved regs>
232          * v1 -> <rest of LMF>
233          * sp -> <alignment>
234          */
235
236         /* r0 is the result from mono_get_lmf_addr () */
237         ARM_STR_IMM (code, ARMREG_R0, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, lmf_addr));
238         /* new_lmf->previous_lmf = *lmf_addr */
239         ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
240         ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
241         /* *(lmf_addr) = r1 */
242         ARM_STR_IMM (code, ARMREG_V1, ARMREG_R0, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
243         /* save method info (it's in v2) */
244         if ((tramp_type == MONO_TRAMPOLINE_JIT) || (tramp_type == MONO_TRAMPOLINE_JUMP))
245                 ARM_STR_IMM (code, ARMREG_V2, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, method));
246         else {
247                 ARM_MOV_REG_IMM8 (code, ARMREG_R2, 0);
248                 ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, method));
249         }
250         /* save caller SP */
251         code = mono_arm_emit_load_imm (code, ARMREG_R2, cfa_offset);
252         ARM_ADD_REG_REG (code, ARMREG_R2, ARMREG_SP, ARMREG_R2);
253         ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, sp));
254         /* save caller FP */
255         ARM_LDR_IMM (code, ARMREG_R2, ARMREG_V1, (MONO_STRUCT_OFFSET (MonoLMF, iregs) + ARMREG_FP*4));
256         ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, fp));
257         /* save the IP (caller ip) */
258         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
259                 ARM_MOV_REG_IMM8 (code, ARMREG_R2, 0);
260         } else {
261                 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_V1, (MONO_STRUCT_OFFSET (MonoLMF, iregs) + 13*4));
262         }
263         ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, ip));
264
265         /* Save VFP registers. */
266         if (mono_arm_is_hard_float ()) {
267                 /*
268                  * Strictly speaking, we don't have to save d0-d7 in the LMF, but
269                  * it's easier than attempting to store them on the stack since
270                  * this trampoline code is pretty messy.
271                  */
272                 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, fregs));
273                 ARM_FSTMD (code, ARM_VFP_D0, 8, ARMREG_R0);
274         }
275
276         /*
277          * Now we're ready to call xxx_trampoline ().
278          */
279         /* Arg 1: the saved registers */
280         ARM_ADD_REG_IMM (code, ARMREG_R0, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, iregs), 0);
281
282         /* Arg 2: code (next address to the instruction that called us) */
283         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
284                 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0);
285         } else {
286                 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_V3);
287         }
288         
289         /* Arg 3: the specific argument, stored in v2
290          */
291         ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_V2);
292
293         if (aot) {
294                 char *icall_name = g_strdup_printf ("trampoline_func_%d", tramp_type);
295                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
296                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
297                 ARM_B (code, 0);
298                 *(gpointer*)code = NULL;
299                 code += 4;
300                 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
301         } else {
302                 load_trampoline = code;
303                 code += 4;
304         }
305
306         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
307         code = emit_bx (code, ARMREG_IP);
308
309         /* OK, code address is now on r0. Move it to the place on the stack
310          * where IP was saved (it is now no more useful to us and it can be
311          * clobbered). This way we can just restore all the regs in one inst
312          * and branch to IP.
313          */
314         ARM_STR_IMM (code, ARMREG_R0, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, iregs) + (ARMREG_R12 * sizeof (mgreg_t)));
315
316         /* Check for thread interruption */
317         /* This is not perf critical code so no need to check the interrupt flag */
318         /* 
319          * Have to call the _force_ variant, since there could be a protected wrapper on the top of the stack.
320          */
321         if (aot) {
322                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_interruption_checkpoint_from_trampoline");
323                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
324                 ARM_B (code, 0);
325                 *(gpointer*)code = NULL;
326                 code += 4;
327                 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
328         } else {
329                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
330                 ARM_B (code, 0);
331                 *(gpointer*)code = mono_interruption_checkpoint_from_trampoline;
332                 code += 4;
333         }
334         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
335         code = emit_bx (code, ARMREG_IP);
336
337         /*
338          * Now we restore the MonoLMF (see emit_epilogue in mini-arm.c)
339          * and the rest of the registers, so the method called will see
340          * the same state as before we executed.
341          */
342         /* ip = previous_lmf */
343         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
344         /* lr = lmf_addr */
345         ARM_LDR_IMM (code, ARMREG_LR, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, lmf_addr));
346         /* *(lmf_addr) = previous_lmf */
347         ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
348
349         /* Restore VFP registers. */
350         if (mono_arm_is_hard_float ()) {
351                 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, fregs));
352                 ARM_FLDMD (code, ARM_VFP_D0, 8, ARMREG_R0);
353         }
354
355         /* Non-standard function epilogue. Instead of doing a proper
356          * return, we just jump to the compiled code.
357          */
358         /* Restore the registers and jump to the code:
359          * Note that IP has been conveniently set to the method addr.
360          */
361         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, STACK - regsave_size);
362         cfa_offset -= STACK - regsave_size;
363         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
364         ARM_POP_NWB (code, 0x5fff);
365         mono_add_unwind_op_same_value (unwind_ops, code, buf, ARMREG_LR);
366         if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH)
367                 ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_IP);
368         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, regsave_size);
369         cfa_offset -= regsave_size;
370         g_assert (cfa_offset == 0);
371         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
372         if (MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type))
373                 code = emit_bx (code, ARMREG_LR);
374         else
375                 code = emit_bx (code, ARMREG_IP);
376
377         constants = (gpointer*)code;
378         constants [0] = mono_get_lmf_addr;
379         constants [1] = (gpointer)mono_get_trampoline_func (tramp_type);
380
381         if (!aot) {
382                 /* backpatch by emitting the missing instructions skipped above */
383                 ARM_LDR_IMM (load_get_lmf_addr, ARMREG_R0, ARMREG_PC, (code - load_get_lmf_addr - 8));
384                 ARM_LDR_IMM (load_trampoline, ARMREG_IP, ARMREG_PC, (code + 4 - load_trampoline - 8));
385         }
386
387         code += 8;
388
389         /* Flush instruction cache, since we've generated code */
390         mono_arch_flush_icache (buf, code - buf);
391         MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL));
392
393         /* Sanity check */
394         g_assert ((code - buf) <= buf_len);
395
396         g_assert (info);
397         tramp_name = mono_get_generic_trampoline_name (tramp_type);
398         *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
399         g_free (tramp_name);
400
401         return buf;
402 }
403
404 #define SPEC_TRAMP_SIZE 24
405
406 gpointer
407 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
408 {
409         guint8 *code, *buf, *tramp;
410         gpointer *constants;
411         guint32 short_branch = FALSE;
412         guint32 size = SPEC_TRAMP_SIZE;
413
414         tramp = mono_get_trampoline_code (tramp_type);
415
416         if (domain) {
417                 mono_domain_lock (domain);
418                 code = buf = mono_domain_code_reserve_align (domain, size, 4);
419                 if ((short_branch = branch_for_target_reachable (code + 4, tramp))) {
420                         size = 12;
421                         mono_domain_code_commit (domain, code, SPEC_TRAMP_SIZE, size);
422                 }
423                 mono_domain_unlock (domain);
424         } else {
425                 code = buf = mono_global_codeman_reserve (size);
426                 short_branch = FALSE;
427         }
428
429         /* we could reduce this to 12 bytes if tramp is within reach:
430          * ARM_PUSH ()
431          * ARM_BL ()
432          * method-literal
433          * The called code can access method using the lr register
434          * A 20 byte sequence could be:
435          * ARM_PUSH ()
436          * ARM_MOV_REG_REG (lr, pc)
437          * ARM_LDR_IMM (pc, pc, 0)
438          * method-literal
439          * tramp-literal
440          */
441         /* We save all the registers, except PC and SP */
442         ARM_PUSH (code, 0x5fff);
443         if (short_branch) {
444                 constants = (gpointer*)code;
445                 constants [0] = GUINT_TO_POINTER (short_branch | (1 << 24));
446                 constants [1] = arg1;
447                 code += 8;
448         } else {
449                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 8); /* temp reg */
450                 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
451                 code = emit_bx (code, ARMREG_R1);
452
453                 constants = (gpointer*)code;
454                 constants [0] = arg1;
455                 constants [1] = tramp;
456                 code += 8;
457         }
458
459         /* Flush instruction cache, since we've generated code */
460         mono_arch_flush_icache (buf, code - buf);
461         MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE, mono_get_generic_trampoline_simple_name (tramp_type)));
462
463         g_assert ((code - buf) <= size);
464
465         if (code_len)
466                 *code_len = code - buf;
467
468         return buf;
469 }
470
471 /*
472  * mono_arch_get_unbox_trampoline:
473  * @m: method pointer
474  * @addr: pointer to native code for @m
475  *
476  * when value type methods are called through the vtable we need to unbox the
477  * this argument. This method returns a pointer to a trampoline which does
478  * unboxing before calling the method
479  */
480 gpointer
481 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
482 {
483         guint8 *code, *start;
484         MonoDomain *domain = mono_domain_get ();
485         GSList *unwind_ops;
486         guint32 size = 16;
487
488         start = code = mono_domain_code_reserve (domain, size);
489
490         unwind_ops = mono_arch_get_cie_program ();
491
492         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 4);
493         ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, sizeof (MonoObject));
494         code = emit_bx (code, ARMREG_IP);
495         *(guint32*)code = (guint32)addr;
496         code += 4;
497         mono_arch_flush_icache (start, code - start);
498         MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE, m));
499         g_assert ((code - start) <= size);
500         /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
501         g_print ("unbox code is at %p for method at %p\n", start, addr);*/
502
503         mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), domain);
504
505         return start;
506 }
507
508 gpointer
509 mono_arch_get_static_rgctx_trampoline (gpointer arg, gpointer addr)
510 {
511         guint8 *code, *start;
512         GSList *unwind_ops;
513         int buf_len = 16;
514         MonoDomain *domain = mono_domain_get ();
515
516         start = code = mono_domain_code_reserve (domain, buf_len);
517
518         unwind_ops = mono_arch_get_cie_program ();
519
520         ARM_LDR_IMM (code, MONO_ARCH_RGCTX_REG, ARMREG_PC, 0);
521         ARM_LDR_IMM (code, ARMREG_PC, ARMREG_PC, 0);
522         *(guint32*)code = (guint32)arg;
523         code += 4;
524         *(guint32*)code = (guint32)addr;
525         code += 4;
526
527         g_assert ((code - start) <= buf_len);
528
529         mono_arch_flush_icache (start, code - start);
530         MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL));
531
532         mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), domain);
533
534         return start;
535 }
536
537 gpointer
538 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
539 {
540         guint8 *tramp;
541         guint8 *code, *buf;
542         int tramp_size;
543         guint32 code_len;
544         guint8 **rgctx_null_jumps;
545         int depth, index;
546         int i, njumps;
547         gboolean mrgctx;
548         MonoJumpInfo *ji = NULL;
549         GSList *unwind_ops = NULL;
550
551         mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
552         index = MONO_RGCTX_SLOT_INDEX (slot);
553         if (mrgctx)
554                 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
555         for (depth = 0; ; ++depth) {
556                 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
557
558                 if (index < size - 1)
559                         break;
560                 index -= size - 1;
561         }
562
563         tramp_size = 64 + 16 * depth;
564
565         code = buf = mono_global_codeman_reserve (tramp_size);
566
567         unwind_ops = mono_arch_get_cie_program ();
568
569         rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
570         njumps = 0;
571
572         /* The vtable/mrgctx is in R0 */
573         g_assert (MONO_ARCH_VTABLE_REG == ARMREG_R0);
574
575         if (mrgctx) {
576                 /* get mrgctx ptr */
577                 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
578         } else {
579                 /* load rgctx ptr from vtable */
580                 g_assert (arm_is_imm12 (MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context)));
581                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R0, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
582                 /* is the rgctx ptr null? */
583                 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
584                 /* if yes, jump to actual trampoline */
585                 rgctx_null_jumps [njumps ++] = code;
586                 ARM_B_COND (code, ARMCOND_EQ, 0);
587         }
588
589         for (i = 0; i < depth; ++i) {
590                 /* load ptr to next array */
591                 if (mrgctx && i == 0) {
592                         g_assert (arm_is_imm12 (MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT));
593                         ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R1, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
594                 } else {
595                         ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R1, 0);
596                 }
597                 /* is the ptr null? */
598                 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
599                 /* if yes, jump to actual trampoline */
600                 rgctx_null_jumps [njumps ++] = code;
601                 ARM_B_COND (code, ARMCOND_EQ, 0);
602         }
603
604         /* fetch slot */
605         code = mono_arm_emit_load_imm (code, ARMREG_R2, sizeof (gpointer) * (index + 1));
606         ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_R1, ARMREG_R2);
607         /* is the slot null? */
608         ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
609         /* if yes, jump to actual trampoline */
610         rgctx_null_jumps [njumps ++] = code;
611         ARM_B_COND (code, ARMCOND_EQ, 0);
612         /* otherwise return, result is in R1 */
613         ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_R1);
614         code = emit_bx (code, ARMREG_LR);
615
616         g_assert (njumps <= depth + 2);
617         for (i = 0; i < njumps; ++i)
618                 arm_patch (rgctx_null_jumps [i], code);
619
620         g_free (rgctx_null_jumps);
621
622         /* Slowpath */
623
624         /* The vtable/mrgctx is still in R0 */
625
626         if (aot) {
627                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
628                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
629                 ARM_B (code, 0);
630                 *(gpointer*)code = NULL;
631                 code += 4;
632                 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_R1);
633         } else {
634                 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), &code_len);
635
636                 /* Jump to the actual trampoline */
637                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0); /* temp reg */
638                 code = emit_bx (code, ARMREG_R1);
639                 *(gpointer*)code = tramp;
640                 code += 4;
641         }
642
643         mono_arch_flush_icache (buf, code - buf);
644         MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL));
645
646         g_assert (code - buf <= tramp_size);
647
648         char *name = mono_get_rgctx_fetch_trampoline_name (slot);
649         *info = mono_tramp_info_create (name, buf, code - buf, ji, unwind_ops);
650         g_free (name);
651
652         return buf;
653 }
654
655 gpointer
656 mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboolean aot)
657 {
658         guint8 *code, *buf;
659         int tramp_size;
660         MonoJumpInfo *ji = NULL;
661         GSList *unwind_ops = NULL;
662
663         g_assert (aot);
664
665         tramp_size = 32;
666
667         code = buf = mono_global_codeman_reserve (tramp_size);
668
669         unwind_ops = mono_arch_get_cie_program ();
670
671         // FIXME: Currently, we always go to the slow path.
672         /* Load trampoline addr */
673         ARM_LDR_IMM (code, ARMREG_R1, MONO_ARCH_RGCTX_REG, 4);
674         /* The vtable/mrgctx is in R0 */
675         g_assert (MONO_ARCH_VTABLE_REG == ARMREG_R0);
676         code = emit_bx (code, ARMREG_R1);
677
678         mono_arch_flush_icache (buf, code - buf);
679         MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL));
680
681         g_assert (code - buf <= tramp_size);
682
683         *info = mono_tramp_info_create ("rgctx_fetch_trampoline_general", buf, code - buf, ji, unwind_ops);
684
685         return buf;
686 }
687
688 guint8*
689 mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot)
690 {
691         guint8 *buf, *code;
692         GSList *unwind_ops = NULL;
693         MonoJumpInfo *ji = NULL;
694         int frame_size;
695
696         buf = code = mono_global_codeman_reserve (96);
697
698         /*
699          * Construct the MonoContext structure on the stack.
700          */
701
702         frame_size = sizeof (MonoContext);
703         frame_size = ALIGN_TO (frame_size, MONO_ARCH_FRAME_ALIGNMENT);
704         ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, frame_size);
705
706         /* save ip, lr and pc into their correspodings ctx.regs slots. */
707         ARM_STR_IMM (code, ARMREG_IP, ARMREG_SP, MONO_STRUCT_OFFSET (MonoContext, regs) + sizeof (mgreg_t) * ARMREG_IP);
708         ARM_STR_IMM (code, ARMREG_LR, ARMREG_SP, MONO_STRUCT_OFFSET (MonoContext, regs) + 4 * ARMREG_LR);
709         ARM_STR_IMM (code, ARMREG_LR, ARMREG_SP, MONO_STRUCT_OFFSET (MonoContext, regs) + 4 * ARMREG_PC);
710
711         /* save r0..r10 and fp */
712         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_SP, MONO_STRUCT_OFFSET (MonoContext, regs));
713         ARM_STM (code, ARMREG_IP, 0x0fff);
714
715         /* now we can update fp. */
716         ARM_MOV_REG_REG (code, ARMREG_FP, ARMREG_SP);
717
718         /* make ctx.esp hold the actual value of sp at the beginning of this method. */
719         ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_FP, frame_size);
720         ARM_STR_IMM (code, ARMREG_R0, ARMREG_IP, 4 * ARMREG_SP);
721         ARM_STR_IMM (code, ARMREG_R0, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, regs) + 4 * ARMREG_SP);
722
723         /* make ctx.eip hold the address of the call. */
724         //ARM_SUB_REG_IMM8 (code, ARMREG_LR, ARMREG_LR, 4);
725         ARM_STR_IMM (code, ARMREG_LR, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, pc));
726
727         /* r0 now points to the MonoContext */
728         ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_FP);
729
730         /* call */
731         if (aot) {
732                 if (single_step)
733                         ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "debugger_agent_single_step_from_context");
734                 else
735                         ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "debugger_agent_breakpoint_from_context");
736                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
737                 ARM_B (code, 0);
738                 *(gpointer*)code = NULL;
739                 code += 4;
740                 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
741                 ARM_BLX_REG (code, ARMREG_IP);
742         } else {
743                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
744                 ARM_B (code, 0);
745                 if (single_step)
746                         *(gpointer*)code = debugger_agent_single_step_from_context;
747                 else
748                         *(gpointer*)code = debugger_agent_breakpoint_from_context;
749                 code += 4;
750                 ARM_BLX_REG (code, ARMREG_IP);
751         }
752
753         /* we're back; save ctx.eip and ctx.esp into the corresponding regs slots. */
754         ARM_LDR_IMM (code, ARMREG_R0, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, pc));
755         ARM_STR_IMM (code, ARMREG_R0, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, regs) + 4 * ARMREG_LR);
756         ARM_STR_IMM (code, ARMREG_R0, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, regs) + 4 * ARMREG_PC);
757
758         /* make ip point to the regs array, then restore everything, including pc. */
759         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, regs));
760         ARM_LDM (code, ARMREG_IP, 0xffff);
761
762         mono_arch_flush_icache (buf, code - buf);
763         MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL));
764
765         const char *tramp_name = single_step ? "sdb_single_step_trampoline" : "sdb_breakpoint_trampoline";
766         *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
767
768         return buf;
769 }
770
771 /*
772  * mono_arch_get_enter_icall_trampoline:
773  *
774  *   See tramp-amd64.c for documentation.
775  */
776 gpointer
777 mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info)
778 {
779 #ifdef ENABLE_INTERPRETER
780         const int gregs_num = INTERP_ICALL_TRAMP_IARGS;
781         const int fregs_num = INTERP_ICALL_TRAMP_FARGS;
782
783         guint8 *start = NULL, *code, *label_gexits [gregs_num], *label_fexits [fregs_num], *label_leave_tramp [3], *label_is_float_ret;
784         MonoJumpInfo *ji = NULL;
785         GSList *unwind_ops = NULL;
786         int buf_len, i, framesize, off_methodargs, off_targetaddr;
787         const int fp_reg = ARMREG_R7;
788
789         buf_len = 512 + 1024;
790         start = code = (guint8 *) mono_global_codeman_reserve (buf_len);
791
792         framesize = 5 * sizeof (mgreg_t); /* lr, r4, r8, r6 and plus one */
793
794         off_methodargs = -framesize;
795         framesize += sizeof (mgreg_t);
796
797         off_targetaddr = -framesize;
798         framesize += sizeof (mgreg_t);
799
800         framesize = ALIGN_TO (framesize + 4 * sizeof (mgreg_t), MONO_ARCH_FRAME_ALIGNMENT);
801
802         /* allocate space on stack for argument passing */
803         const int stack_space = ALIGN_TO (((gregs_num - ARMREG_R3) * sizeof (mgreg_t)), MONO_ARCH_FRAME_ALIGNMENT);
804
805         /* iOS ABI */
806         ARM_PUSH (code, (1 << fp_reg) | (1 << ARMREG_LR));
807         ARM_MOV_REG_REG (code, fp_reg, ARMREG_SP);
808
809         /* use r4, r8 and r6 as scratch registers */
810         ARM_PUSH (code, (1 << ARMREG_R4) | (1 << ARMREG_R8) | (1 << ARMREG_R6));
811         ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, stack_space + framesize);
812
813         /* save InterpMethodArguments* onto stack */
814         ARM_STR_IMM (code, ARMREG_R1, fp_reg, off_methodargs);
815
816         /* save target address onto stack */
817         ARM_STR_IMM (code, ARMREG_R0, fp_reg, off_targetaddr);
818
819         /* load pointer to InterpMethodArguments* into r4 */
820         ARM_MOV_REG_REG (code, ARMREG_R4, ARMREG_R1);
821
822         /* move flen into r8 */
823         ARM_LDR_IMM (code, ARMREG_R8, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, flen));
824         /* load pointer to fargs into r6 */
825         ARM_LDR_IMM (code, ARMREG_R6, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, fargs));
826
827         for (i = 0; i < fregs_num; ++i) {
828                 ARM_CMP_REG_IMM (code, ARMREG_R8, 0, 0);
829                 label_fexits [i] = code;
830                 ARM_B_COND (code, ARMCOND_EQ, 0);
831
832                 g_assert (i <= ARM_VFP_D7); /* otherwise, need to pass args on stack */
833                 ARM_FLDD (code, i, ARMREG_R6, i * sizeof (double));
834                 ARM_SUB_REG_IMM8 (code, ARMREG_R8, ARMREG_R8, 1);
835         }
836
837         for (i = 0; i < fregs_num; i++)
838                 arm_patch (label_fexits [i], code);
839
840         /* move ilen into r8 */
841         ARM_LDR_IMM (code, ARMREG_R8, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, ilen));
842         /* load pointer to iargs into r6 */
843         ARM_LDR_IMM (code, ARMREG_R6, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, iargs));
844
845         int stack_offset = 0;
846         for (i = 0; i < gregs_num; i++) {
847                 ARM_CMP_REG_IMM (code, ARMREG_R8, 0, 0);
848                 label_gexits [i] = code;
849                 ARM_B_COND (code, ARMCOND_EQ, 0);
850
851                 if (i <= ARMREG_R3) {
852                         ARM_LDR_IMM (code, i, ARMREG_R6, i * sizeof (mgreg_t));
853                 } else {
854                         ARM_LDR_IMM (code, ARMREG_R4, ARMREG_R6, i * sizeof (mgreg_t));
855                         ARM_STR_IMM (code, ARMREG_R4, ARMREG_SP, stack_offset);
856                         stack_offset += sizeof (mgreg_t);
857                 }
858                 ARM_SUB_REG_IMM8 (code, ARMREG_R8, ARMREG_R8, 1);
859         }
860
861         for (i = 0; i < gregs_num; i++)
862                 arm_patch (label_gexits [i], code);
863
864         /* load target addr */
865         ARM_LDR_IMM (code, ARMREG_R4, fp_reg, off_targetaddr);
866
867         /* call into native function */
868         ARM_BLX_REG (code, ARMREG_R4);
869
870         /* load InterpMethodArguments */
871         ARM_LDR_IMM (code, ARMREG_R4, fp_reg, off_methodargs);
872
873         /* load is_float_ret */
874         ARM_LDR_IMM (code, ARMREG_R8, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, is_float_ret));
875
876         /* check if a float return value is expected */
877         ARM_CMP_REG_IMM (code, ARMREG_R8, 0, 0);
878         label_is_float_ret = code;
879         ARM_B_COND (code, ARMCOND_NE, 0);
880
881         /* greg return */
882         /* load retval */
883         ARM_LDR_IMM (code, ARMREG_R8, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, retval));
884
885         ARM_CMP_REG_IMM (code, ARMREG_R8, 0, 0);
886         label_leave_tramp [0] = code;
887         ARM_B_COND (code, ARMCOND_EQ, 0);
888
889         /* store greg result, always write back 64bit */
890         ARM_STR_IMM (code, ARMREG_R0, ARMREG_R8, 0);
891         ARM_STR_IMM (code, ARMREG_R1, ARMREG_R8, 4);
892
893         label_leave_tramp [1] = code;
894         ARM_B_COND (code, ARMCOND_AL, 0);
895
896         /* freg return */
897         arm_patch (label_is_float_ret, code);
898         /* load retval */
899         ARM_LDR_IMM (code, ARMREG_R8, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, retval));
900
901         ARM_CMP_REG_IMM (code, ARMREG_R8, 0, 0);
902         label_leave_tramp [2] = code;
903         ARM_B_COND (code, ARMCOND_EQ, 0);
904
905         /* store freg result */
906         ARM_FSTD (code, ARM_VFP_F0, ARMREG_R8, 0);
907
908         for (i = 0; i < 3; i++)
909                 arm_patch (label_leave_tramp [i], code);
910
911         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, stack_space + framesize);
912         ARM_POP (code, (1 << ARMREG_R4) | (1 << ARMREG_R8) | (1 << ARMREG_R6));
913         ARM_MOV_REG_REG (code, ARMREG_SP, fp_reg);
914         ARM_POP (code, (1 << fp_reg) | (1 << ARMREG_PC));
915
916         g_assert (code - start < buf_len);
917
918         mono_arch_flush_icache (start, code - start);
919         MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL));
920
921         if (info)
922                 *info = mono_tramp_info_create ("enter_icall_trampoline", start, code - start, ji, unwind_ops);
923
924         return start;
925 #else
926         g_assert_not_reached ();
927         return NULL;
928 #endif /* ENABLE_INTERPRETER */
929 }
930
931 #else
932
933 guchar*
934 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
935 {
936         g_assert_not_reached ();
937         return NULL;
938 }
939
940 gpointer
941 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
942 {
943         g_assert_not_reached ();
944         return NULL;
945 }
946
947 gpointer
948 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
949 {
950         g_assert_not_reached ();
951         return NULL;
952 }
953
954 gpointer
955 mono_arch_get_static_rgctx_trampoline (gpointer arg, gpointer addr)
956 {
957         g_assert_not_reached ();
958         return NULL;
959 }
960
961 gpointer
962 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
963 {
964         g_assert_not_reached ();
965         return NULL;
966 }
967
968 guint8*
969 mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot)
970 {
971         g_assert_not_reached ();
972         return NULL;
973 }
974
975 gpointer
976 mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info)
977 {
978         g_assert_not_reached ();
979         return NULL;
980 }
981 #endif /* DISABLE_JIT */
982
983 guint8*
984 mono_arch_get_call_target (guint8 *code)
985 {
986         guint32 ins = ((guint32*)(gpointer)code) [-1];
987
988         /* Should be a 'bl' or a 'b' */
989         if (((ins >> 25) & 0x7) == 0x5) {
990                 gint32 disp = ((((gint32)ins) & 0xffffff) << 8) >> 8;
991                 guint8 *target = code - 4 + 8 + (disp * 4);
992
993                 return target;
994         } else {
995                 return NULL;
996         }
997 }
998
999 guint32
1000 mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
1001 {
1002         /* The offset is stored as the 4th word of the plt entry */
1003         return ((guint32*)plt_entry) [3];
1004 }
1005
1006 /*
1007  * Return the address of the PLT entry called by the thumb code CODE.
1008  */
1009 guint8*
1010 mono_arm_get_thumb_plt_entry (guint8 *code)
1011 {
1012         int s, j1, j2, imm10, imm11, i1, i2, imm32;
1013         guint8 *bl, *base;
1014         guint16 t1, t2;
1015         guint8 *target;
1016
1017         /* code should be right after a BL */
1018         code = (guint8*)((mgreg_t)code & ~1);
1019         base = (guint8*)((mgreg_t)code & ~3);
1020         bl = code - 4;
1021         t1 = ((guint16*)bl) [0];
1022         t2 = ((guint16*)bl) [1];
1023
1024         g_assert ((t1 >> 11) == 0x1e);
1025
1026         s = (t1 >> 10) & 0x1;
1027         imm10 = (t1 >> 0) & 0x3ff;
1028         j1 = (t2 >> 13) & 0x1;
1029         j2 = (t2 >> 11) & 0x1;
1030         imm11 = t2 & 0x7ff;
1031
1032         i1 = (s ^ j1) ? 0 : 1;
1033         i2 = (s ^ j2) ? 0 : 1;
1034
1035         imm32 = (imm11 << 1) | (imm10 << 12) | (i2 << 22) | (i1 << 23);
1036         if (s)
1037                 /* Sign extend from 24 bits to 32 bits */
1038                 imm32 = ((gint32)imm32 << 8) >> 8;
1039
1040         target = code + imm32;
1041
1042         /* target now points to the thumb plt entry */
1043         /* ldr.w r12, [pc, #8] */
1044         g_assert (((guint16*)target) [0] == 0xf8df);
1045         g_assert (((guint16*)target) [1] == 0xc008);
1046
1047         /* 
1048          * The PLT info offset is at offset 16, but mono_arch_get_plt_entry_offset () returns
1049          * the 3rd word, so compensate by returning a different value.
1050          */
1051         target += 4;
1052
1053         return target;
1054 }
1055
1056 #ifndef DISABLE_JIT
1057
1058 /*
1059  * mono_arch_get_gsharedvt_arg_trampoline:
1060  *
1061  *   See tramp-x86.c for documentation.
1062  */
1063 gpointer
1064 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
1065 {
1066         guint8 *code, *buf;
1067         int buf_len;
1068         gpointer *constants;
1069
1070         buf_len = 24;
1071
1072         buf = code = mono_domain_code_reserve (domain, buf_len);
1073
1074         /* Similar to the specialized trampoline code */
1075         ARM_PUSH (code, (1 << ARMREG_R0) | (1 << ARMREG_R1) | (1 << ARMREG_R2) | (1 << ARMREG_R3) | (1 << ARMREG_LR));
1076         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 8);
1077         /* arg is passed in LR */
1078         ARM_LDR_IMM (code, ARMREG_LR, ARMREG_PC, 0);
1079         code = emit_bx (code, ARMREG_IP);
1080         constants = (gpointer*)code;
1081         constants [0] = arg;
1082         constants [1] = addr;
1083         code += 8;
1084
1085         g_assert ((code - buf) <= buf_len);
1086
1087         mono_arch_flush_icache (buf, code - buf);
1088         MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL));
1089
1090         mono_tramp_info_register (mono_tramp_info_create (NULL, buf, code - buf, NULL, NULL), domain);
1091
1092         return buf;
1093 }
1094
1095 #else
1096
1097 gpointer
1098 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
1099 {
1100         g_assert_not_reached ();
1101         return NULL;
1102 }
1103
1104 #endif