1 /**
2 * \file
3 * JIT trampoline code for amd64
4 *
5 * Authors:
6 * Dietmar Maurer (dietmar@ximian.com)
7 * Zoltan Varga (vargaz@gmail.com)
8 * Johan Lorensson (lateralusx.github@gmail.com)
9 *
10 * (C) 2001 Ximian, Inc.
11 * Copyright 2003-2011 Novell, Inc (http://www.novell.com)
12 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
13 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 */
15
16 #include <config.h>
17 #include <glib.h>
18
19 #include <mono/metadata/abi-details.h>
20 #include <mono/metadata/appdomain.h>
21 #include <mono/metadata/marshal.h>
22 #include <mono/metadata/tabledefs.h>
23 #include <mono/metadata/profiler-private.h>
24 #include <mono/metadata/gc-internals.h>
25 #include <mono/arch/amd64/amd64-codegen.h>
26
27 #include <mono/utils/memcheck.h>
28
29 #include "mini.h"
30 #include "mini-amd64.h"
31 #include "mini-runtime.h"
32 #include "debugger-agent.h"
33
34 #ifndef DISABLE_INTERPRETER
35 #include "interp/interp.h"
36 #endif
37
38 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
39
40 #define IS_REX(inst) (((inst) >= 0x40) && ((inst) <= 0x4f))
41
42 #ifndef DISABLE_JIT
43 /*
44 * mono_arch_get_unbox_trampoline:
45 * @m: method pointer
46 * @addr: pointer to native code for @m
47 *
48 * when value type methods are called through the vtable we need to unbox the
49 * this argument. This method returns a pointer to a trampoline which does
50 * unboxing before calling the method
51 */
52 gpointer
mono_arch_get_unbox_trampoline(MonoMethod * m,gpointer addr)53 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
54 {
55 guint8 *code, *start;
56 GSList *unwind_ops;
57 int this_reg, size = 20;
58
59 MonoDomain *domain = mono_domain_get ();
60
61 this_reg = mono_arch_get_this_arg_reg (NULL);
62
63 start = code = (guint8 *)mono_domain_code_reserve (domain, size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
64
65 unwind_ops = mono_arch_get_cie_program ();
66
67 amd64_alu_reg_imm (code, X86_ADD, this_reg, sizeof (MonoObject));
68 /* FIXME: Optimize this */
69 amd64_mov_reg_imm (code, AMD64_RAX, addr);
70 amd64_jump_reg (code, AMD64_RAX);
71 g_assert ((code - start) < size);
72 g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
73
74 mono_arch_flush_icache (start, code - start);
75 MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE, m));
76
77 mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), domain);
78
79 return start;
80 }
81
82 /*
83 * mono_arch_get_static_rgctx_trampoline:
84 *
85 * Create a trampoline which sets RGCTX_REG to ARG, then jumps to ADDR.
86 */
87 gpointer
mono_arch_get_static_rgctx_trampoline(gpointer arg,gpointer addr)88 mono_arch_get_static_rgctx_trampoline (gpointer arg, gpointer addr)
89 {
90 guint8 *code, *start;
91 GSList *unwind_ops;
92 int buf_len;
93
94 MonoDomain *domain = mono_domain_get ();
95
96 #ifdef MONO_ARCH_NOMAP32BIT
97 buf_len = 32;
98 #else
99 /* AOTed code could still have a non-32 bit address */
100 if ((((guint64)addr) >> 32) == 0)
101 buf_len = 16;
102 else
103 buf_len = 30;
104 #endif
105
106 start = code = (guint8 *)mono_domain_code_reserve (domain, buf_len + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
107
108 unwind_ops = mono_arch_get_cie_program ();
109
110 amd64_mov_reg_imm (code, MONO_ARCH_RGCTX_REG, arg);
111 amd64_jump_code (code, addr);
112 g_assert ((code - start) < buf_len);
113 g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
114
115 mono_arch_flush_icache (start, code - start);
116 MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL));
117
118 mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), domain);
119
120 return start;
121 }
122 #endif /* !DISABLE_JIT */
123
124 #ifdef _WIN64
125 // Workaround lack of Valgrind support for 64-bit Windows
126 #define VALGRIND_DISCARD_TRANSLATIONS(...)
127 #endif
128
129 /*
130 * mono_arch_patch_callsite:
131 *
132 * Patch the callsite whose address is given by ORIG_CODE so it calls ADDR. ORIG_CODE
133 * points to the pc right after the call.
134 */
135 void
mono_arch_patch_callsite(guint8 * method_start,guint8 * orig_code,guint8 * addr)136 mono_arch_patch_callsite (guint8 *method_start, guint8 *orig_code, guint8 *addr)
137 {
138 guint8 *code;
139 guint8 buf [16];
140 gboolean can_write = mono_breakpoint_clean_code (method_start, orig_code, 14, buf, sizeof (buf));
141
142 code = buf + 14;
143
144 /* mov 64-bit imm into r11 (followed by call reg?) or direct call*/
145 if (((code [-13] == 0x49) && (code [-12] == 0xbb)) || (code [-5] == 0xe8)) {
146 if (code [-5] != 0xe8) {
147 if (can_write) {
148 g_assert ((guint64)(orig_code - 11) % 8 == 0);
149 mono_atomic_xchg_ptr ((gpointer*)(orig_code - 11), addr);
150 VALGRIND_DISCARD_TRANSLATIONS (orig_code - 11, sizeof (gpointer));
151 }
152 } else {
153 gboolean disp_32bit = ((((gint64)addr - (gint64)orig_code)) < (1 << 30)) && ((((gint64)addr - (gint64)orig_code)) > -(1 << 30));
154
155 if ((((guint64)(addr)) >> 32) != 0 && !disp_32bit) {
156 /*
157 * This might happen with LLVM or when calling AOTed code. Create a thunk.
158 */
159 guint8 *thunk_start, *thunk_code;
160
161 thunk_start = thunk_code = (guint8 *)mono_domain_code_reserve (mono_domain_get (), 32);
162 amd64_jump_membase (thunk_code, AMD64_RIP, 0);
163 *(guint64*)thunk_code = (guint64)addr;
164 addr = thunk_start;
165 g_assert ((((guint64)(addr)) >> 32) == 0);
166 mono_arch_flush_icache (thunk_start, thunk_code - thunk_start);
167 MONO_PROFILER_RAISE (jit_code_buffer, (thunk_start, thunk_code - thunk_start, MONO_PROFILER_CODE_BUFFER_HELPER, NULL));
168 }
169 if (can_write) {
170 mono_atomic_xchg_i32 ((gint32*)(orig_code - 4), ((gint64)addr - (gint64)orig_code));
171 VALGRIND_DISCARD_TRANSLATIONS (orig_code - 5, 4);
172 }
173 }
174 }
175 else if ((code [-7] == 0x41) && (code [-6] == 0xff) && (code [-5] == 0x15)) {
176 /* call *<OFFSET>(%rip) */
177 gpointer *got_entry = (gpointer*)((guint8*)orig_code + (*(guint32*)(orig_code - 4)));
178 if (can_write) {
179 mono_atomic_xchg_ptr (got_entry, addr);
180 VALGRIND_DISCARD_TRANSLATIONS (orig_code - 5, sizeof (gpointer));
181 }
182 }
183 }
184
185 #ifndef DISABLE_JIT
186 guint8*
mono_arch_create_llvm_native_thunk(MonoDomain * domain,guint8 * addr)187 mono_arch_create_llvm_native_thunk (MonoDomain *domain, guint8 *addr)
188 {
189 /*
190 * The caller is LLVM code and the call displacement might exceed 32 bits. We can't determine the caller address, so
191 * we add a thunk every time.
192 * Since the caller is also allocated using the domain code manager, hopefully the displacement will fit into 32 bits.
193 * FIXME: Avoid this if possible if !MONO_ARCH_NOMAP32BIT and ADDR is 32 bits.
194 */
195 guint8 *thunk_start, *thunk_code;
196
197 thunk_start = thunk_code = (guint8 *)mono_domain_code_reserve (mono_domain_get (), 32);
198 amd64_jump_membase (thunk_code, AMD64_RIP, 0);
199 *(guint64*)thunk_code = (guint64)addr;
200 addr = thunk_start;
201 mono_arch_flush_icache (thunk_start, thunk_code - thunk_start);
202 MONO_PROFILER_RAISE (jit_code_buffer, (thunk_start, thunk_code - thunk_start, MONO_PROFILER_CODE_BUFFER_HELPER, NULL));
203 return addr;
204 }
205 #endif /* !DISABLE_JIT */
206
207 void
mono_arch_patch_plt_entry(guint8 * code,gpointer * got,mgreg_t * regs,guint8 * addr)208 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
209 {
210 gint32 disp;
211 gpointer *plt_jump_table_entry;
212
213 /* A PLT entry: jmp *<DISP>(%rip) */
214 g_assert (code [0] == 0xff);
215 g_assert (code [1] == 0x25);
216
217 disp = *(gint32*)(code + 2);
218
219 plt_jump_table_entry = (gpointer*)(code + 6 + disp);
220
221 mono_atomic_xchg_ptr (plt_jump_table_entry, addr);
222 }
223
224 #ifndef DISABLE_JIT
225 static void
stack_unaligned(MonoTrampolineType tramp_type)226 stack_unaligned (MonoTrampolineType tramp_type)
227 {
228 printf ("%d\n", tramp_type);
229 g_assert_not_reached ();
230 }
231
232 guchar*
mono_arch_create_generic_trampoline(MonoTrampolineType tramp_type,MonoTrampInfo ** info,gboolean aot)233 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
234 {
235 char *tramp_name;
236 guint8 *buf, *code, *tramp, *br [2], *r11_save_code, *after_r11_save_code, *br_ex_check;
237 int i, lmf_offset, offset, res_offset, arg_offset, rax_offset, ex_offset, tramp_offset, ctx_offset, saved_regs_offset;
238 int r11_save_offset, saved_fpregs_offset, rbp_offset, framesize, orig_rsp_to_rbp_offset, cfa_offset;
239 gboolean has_caller;
240 GSList *unwind_ops = NULL;
241 MonoJumpInfo *ji = NULL;
242 const guint kMaxCodeSize = 630;
243
244 if (tramp_type == MONO_TRAMPOLINE_JUMP)
245 has_caller = FALSE;
246 else
247 has_caller = TRUE;
248
249 code = buf = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize + MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE);
250
251 /* Compute stack frame size and offsets */
252 offset = 0;
253 rbp_offset = -offset;
254
255 offset += sizeof(mgreg_t);
256 rax_offset = -offset;
257
258 offset += sizeof(mgreg_t);
259 ex_offset = -offset;
260
261 offset += sizeof(mgreg_t);
262 r11_save_offset = -offset;
263
264 offset += sizeof(mgreg_t);
265 tramp_offset = -offset;
266
267 offset += sizeof(gpointer);
268 arg_offset = -offset;
269
270 offset += sizeof(mgreg_t);
271 res_offset = -offset;
272
273 offset += sizeof (MonoContext);
274 ctx_offset = -offset;
275 saved_regs_offset = ctx_offset + MONO_STRUCT_OFFSET (MonoContext, gregs);
276 saved_fpregs_offset = ctx_offset + MONO_STRUCT_OFFSET (MonoContext, fregs);
277
278 offset += sizeof (MonoLMFTramp);
279 lmf_offset = -offset;
280
281 #ifdef TARGET_WIN32
282 /* Reserve space where the callee can save the argument registers */
283 offset += 4 * sizeof (mgreg_t);
284 #endif
285
286 framesize = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT);
287
288 // CFA = sp + 16 (the trampoline address is on the stack)
289 cfa_offset = 16;
290 mono_add_unwind_op_def_cfa (unwind_ops, code, buf, AMD64_RSP, 16);
291 // IP saved at CFA - 8
292 mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RIP, -8);
293
294 orig_rsp_to_rbp_offset = 0;
295 r11_save_code = code;
296 /* Reserve space for the mov_membase_reg to save R11 */
297 code += 5;
298 after_r11_save_code = code;
299
300 /* Pop the return address off the stack */
301 amd64_pop_reg (code, AMD64_R11);
302 orig_rsp_to_rbp_offset += sizeof(mgreg_t);
303
304 cfa_offset -= sizeof(mgreg_t);
305 mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
306
307 /*
308 * Allocate a new stack frame
309 */
310 amd64_push_reg (code, AMD64_RBP);
311 cfa_offset += sizeof(mgreg_t);
312 mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
313 mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RBP, - cfa_offset);
314
315 orig_rsp_to_rbp_offset -= sizeof(mgreg_t);
316 amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));
317 mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, AMD64_RBP);
318 mono_add_unwind_op_fp_alloc (unwind_ops, code, buf, AMD64_RBP, 0);
319 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
320
321 /* Compute the trampoline address from the return address */
322 if (aot) {
323 /* 7 = length of call *<offset>(rip) */
324 amd64_alu_reg_imm (code, X86_SUB, AMD64_R11, 7);
325 } else {
326 /* 5 = length of amd64_call_membase () */
327 amd64_alu_reg_imm (code, X86_SUB, AMD64_R11, 5);
328 }
329 amd64_mov_membase_reg (code, AMD64_RBP, tramp_offset, AMD64_R11, sizeof(gpointer));
330
331 /* Save all registers */
332 for (i = 0; i < AMD64_NREG; ++i) {
333 if (i == AMD64_RBP) {
334 /* RAX is already saved */
335 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RBP, rbp_offset, sizeof(mgreg_t));
336 amd64_mov_membase_reg (code, AMD64_RBP, saved_regs_offset + (i * sizeof(mgreg_t)), AMD64_RAX, sizeof(mgreg_t));
337 } else if (i == AMD64_RIP) {
338 if (has_caller)
339 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, 8, sizeof(gpointer));
340 else
341 amd64_mov_reg_imm (code, AMD64_R11, 0);
342 amd64_mov_membase_reg (code, AMD64_RBP, saved_regs_offset + (i * sizeof(mgreg_t)), AMD64_R11, sizeof(mgreg_t));
343 } else if (i == AMD64_RSP) {
344 amd64_mov_reg_reg (code, AMD64_R11, AMD64_RSP, sizeof(mgreg_t));
345 amd64_alu_reg_imm (code, X86_ADD, AMD64_R11, framesize + 16);
346 amd64_mov_membase_reg (code, AMD64_RBP, saved_regs_offset + (i * sizeof(mgreg_t)), AMD64_R11, sizeof(mgreg_t));
347 } else if (i != AMD64_R11) {
348 amd64_mov_membase_reg (code, AMD64_RBP, saved_regs_offset + (i * sizeof(mgreg_t)), i, sizeof(mgreg_t));
349 } else {
350 /* We have to save R11 right at the start of
351 the trampoline code because it's used as a
352 scratch register */
353 /* This happens before the frame is set up, so it goes into the redzone */
354 amd64_mov_membase_reg (r11_save_code, AMD64_RSP, r11_save_offset + orig_rsp_to_rbp_offset, i, sizeof(mgreg_t));
355 g_assert (r11_save_code == after_r11_save_code);
356
357 /* Copy from the save slot into the register array slot */
358 amd64_mov_reg_membase (code, i, AMD64_RSP, r11_save_offset + orig_rsp_to_rbp_offset, sizeof(mgreg_t));
359 amd64_mov_membase_reg (code, AMD64_RBP, saved_regs_offset + (i * sizeof(mgreg_t)), i, sizeof(mgreg_t));
360 }
361 /* cfa = rbp + cfa_offset */
362 mono_add_unwind_op_offset (unwind_ops, code, buf, i, - cfa_offset + saved_regs_offset + (i * sizeof (mgreg_t)));
363 }
364 for (i = 0; i < AMD64_XMM_NREG; ++i)
365 if (AMD64_IS_ARGUMENT_XREG (i))
366 #if defined(MONO_HAVE_SIMD_REG)
367 amd64_movdqu_membase_reg (code, AMD64_RBP, saved_fpregs_offset + (i * sizeof(MonoContextSimdReg)), i);
368 #else
369 amd64_movsd_membase_reg (code, AMD64_RBP, saved_fpregs_offset + (i * sizeof(double)), i);
370 #endif
371
372 /* Check that the stack is aligned */
373 amd64_mov_reg_reg (code, AMD64_R11, AMD64_RSP, sizeof (mgreg_t));
374 amd64_alu_reg_imm (code, X86_AND, AMD64_R11, 15);
375 amd64_alu_reg_imm (code, X86_CMP, AMD64_R11, 0);
376 br [0] = code;
377 amd64_branch_disp (code, X86_CC_Z, 0, FALSE);
378 if (aot) {
379 amd64_mov_reg_imm (code, AMD64_R11, 0);
380 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, 8);
381 } else {
382 amd64_mov_reg_imm (code, MONO_AMD64_ARG_REG1, tramp_type);
383 amd64_mov_reg_imm (code, AMD64_R11, stack_unaligned);
384 amd64_call_reg (code, AMD64_R11);
385 }
386 mono_amd64_patch (br [0], code);
387 //amd64_breakpoint (code);
388
389 /* Obtain the trampoline argument which is encoded in the instruction stream */
390 if (aot) {
391 /* Load the GOT offset */
392 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, tramp_offset, sizeof(gpointer));
393 /*
394 * r11 points to a call *<offset>(%rip) instruction, load the
395 * pc-relative offset from the instruction itself.
396 */
397 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, 3, 4);
398 /* 7 is the length of the call, 8 is the offset to the next got slot */
399 amd64_alu_reg_imm_size (code, X86_ADD, AMD64_RAX, 7 + sizeof (gpointer), sizeof(gpointer));
400 /* Compute the address of the GOT slot */
401 amd64_alu_reg_reg_size (code, X86_ADD, AMD64_R11, AMD64_RAX, sizeof(gpointer));
402 /* Load the value */
403 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, sizeof(gpointer));
404 } else {
405 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, tramp_offset, sizeof(gpointer));
406 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, 5, 1);
407 amd64_widen_reg (code, AMD64_RAX, AMD64_RAX, TRUE, FALSE);
408 amd64_alu_reg_imm_size (code, X86_CMP, AMD64_RAX, 4, 1);
409 br [0] = code;
410 x86_branch8 (code, X86_CC_NE, 6, FALSE);
411 /* 32 bit immediate */
412 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 6, 4);
413 br [1] = code;
414 x86_jump8 (code, 10);
415 /* 64 bit immediate */
416 mono_amd64_patch (br [0], code);
417 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 6, 8);
418 mono_amd64_patch (br [1], code);
419 }
420 amd64_mov_membase_reg (code, AMD64_RBP, arg_offset, AMD64_R11, sizeof(gpointer));
421
422 /* Save LMF begin */
423
424 /* Save sp */
425 amd64_mov_reg_reg (code, AMD64_R11, AMD64_RSP, sizeof(mgreg_t));
426 amd64_alu_reg_imm (code, X86_ADD, AMD64_R11, framesize + 16);
427 amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + MONO_STRUCT_OFFSET (MonoLMF, rsp), AMD64_R11, sizeof(mgreg_t));
428 /* Save pointer to context */
429 amd64_lea_membase (code, AMD64_R11, AMD64_RBP, ctx_offset);
430 amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + MONO_STRUCT_OFFSET (MonoLMFTramp, ctx), AMD64_R11, sizeof(mgreg_t));
431
432 if (aot) {
433 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_get_lmf_addr");
434 } else {
435 amd64_mov_reg_imm (code, AMD64_R11, mono_get_lmf_addr);
436 }
437 amd64_call_reg (code, AMD64_R11);
438
439 /* Save lmf_addr */
440 amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + MONO_STRUCT_OFFSET (MonoLMFTramp, lmf_addr), AMD64_RAX, sizeof(gpointer));
441 /* Save previous_lmf */
442 /* Set the third lowest bit to signal that this is a MonoLMFTramp structure */
443 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RAX, 0, sizeof(gpointer));
444 amd64_alu_reg_imm_size (code, X86_ADD, AMD64_R11, 0x5, sizeof(gpointer));
445 amd64_mov_membase_reg (code, AMD64_RBP, lmf_offset + MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), AMD64_R11, sizeof(gpointer));
446 /* Set new lmf */
447 amd64_lea_membase (code, AMD64_R11, AMD64_RBP, lmf_offset);
448 amd64_mov_membase_reg (code, AMD64_RAX, 0, AMD64_R11, sizeof(gpointer));
449
450 /* Save LMF end */
451
452 /* Arg1 is the pointer to the saved registers */
453 amd64_lea_membase (code, AMD64_ARG_REG1, AMD64_RBP, saved_regs_offset);
454
455 /* Arg2 is the address of the calling code */
456 if (has_caller)
457 amd64_mov_reg_membase (code, AMD64_ARG_REG2, AMD64_RBP, 8, sizeof(gpointer));
458 else
459 amd64_mov_reg_imm (code, AMD64_ARG_REG2, 0);
460
461 /* Arg3 is the method/vtable ptr */
462 amd64_mov_reg_membase (code, AMD64_ARG_REG3, AMD64_RBP, arg_offset, sizeof(gpointer));
463
464 /* Arg4 is the trampoline address */
465 amd64_mov_reg_membase (code, AMD64_ARG_REG4, AMD64_RBP, tramp_offset, sizeof(gpointer));
466
467 if (aot) {
468 char *icall_name = g_strdup_printf ("trampoline_func_%d", tramp_type);
469 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
470 } else {
471 tramp = (guint8*)mono_get_trampoline_func (tramp_type);
472 amd64_mov_reg_imm (code, AMD64_R11, tramp);
473 }
474 amd64_call_reg (code, AMD64_R11);
475 amd64_mov_membase_reg (code, AMD64_RBP, res_offset, AMD64_RAX, sizeof(mgreg_t));
476
477 /* Restore LMF */
478 amd64_mov_reg_membase (code, AMD64_RCX, AMD64_RBP, lmf_offset + MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), sizeof(gpointer));
479 amd64_alu_reg_imm_size (code, X86_SUB, AMD64_RCX, 0x5, sizeof(gpointer));
480 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, lmf_offset + MONO_STRUCT_OFFSET (MonoLMFTramp, lmf_addr), sizeof(gpointer));
481 amd64_mov_membase_reg (code, AMD64_R11, 0, AMD64_RCX, sizeof(gpointer));
482
483 /*
484 * Save rax to the stack, after the leave instruction, this will become part of
485 * the red zone.
486 */
487 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RBP, res_offset, sizeof(mgreg_t));
488 amd64_mov_membase_reg (code, AMD64_RBP, rax_offset, AMD64_RAX, sizeof(mgreg_t));
489
490 /* Check for thread interruption */
491 /* This is not perf critical code so no need to check the interrupt flag */
492 /*
493 * Have to call the _force_ variant, since there could be a protected wrapper on the top of the stack.
494 */
495 if (aot) {
496 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_thread_force_interruption_checkpoint_noraise");
497 } else {
498 amd64_mov_reg_imm (code, AMD64_R11, (guint8*)mono_thread_force_interruption_checkpoint_noraise);
499 }
500 amd64_call_reg (code, AMD64_R11);
501
502 amd64_test_reg_reg (code, AMD64_RAX, AMD64_RAX);
503 br_ex_check = code;
504 amd64_branch8 (code, X86_CC_Z, -1, 1);
505
506 /*
507 * Exception case:
508 * We have an exception we want to throw in the caller's frame, so pop
509 * the trampoline frame and throw from the caller.
510 */
511 #if TARGET_WIN32
512 amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
513 amd64_pop_reg (code, AMD64_RBP);
514 mono_add_unwind_op_same_value (unwind_ops, code, buf, AMD64_RBP);
515 #else
516 amd64_leave (code);
517 #endif
518 /* We are in the parent frame, the exception is in rax */
519 /*
520 * EH is initialized after trampolines, so get the address of the variable
521 * which contains throw_exception, and load it from there.
522 */
523 if (aot) {
524 /* Not really a jit icall */
525 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "throw_exception_addr");
526 } else {
527 amd64_mov_reg_imm (code, AMD64_R11, (guint8*)mono_get_throw_exception_addr ());
528 }
529 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, sizeof(gpointer));
530 amd64_mov_reg_reg (code, AMD64_ARG_REG1, AMD64_RAX, sizeof(mgreg_t));
531 /*
532 * We still have the original return value on the top of the stack, so the
533 * throw trampoline will use that as the throw site.
534 */
535 amd64_jump_reg (code, AMD64_R11);
536
537 /* Normal case */
538 mono_amd64_patch (br_ex_check, code);
539
540 /* Restore argument registers, r10 (imt method/rgxtx)
541 and rax (needed for direct calls to C vararg functions). */
542 for (i = 0; i < AMD64_NREG; ++i)
543 if (AMD64_IS_ARGUMENT_REG (i) || i == AMD64_R10 || i == AMD64_RAX)
544 amd64_mov_reg_membase (code, i, AMD64_RBP, saved_regs_offset + (i * sizeof(mgreg_t)), sizeof(mgreg_t));
545 for (i = 0; i < AMD64_XMM_NREG; ++i)
546 if (AMD64_IS_ARGUMENT_XREG (i))
547 #if defined(MONO_HAVE_SIMD_REG)
548 amd64_movdqu_reg_membase (code, i, AMD64_RBP, saved_fpregs_offset + (i * sizeof(MonoContextSimdReg)));
549 #else
550 amd64_movsd_reg_membase (code, i, AMD64_RBP, saved_fpregs_offset + (i * sizeof(double)));
551 #endif
552
553 /* Restore stack */
554 #if TARGET_WIN32
555 amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
556 amd64_pop_reg (code, AMD64_RBP);
557 mono_add_unwind_op_same_value (unwind_ops, code, buf, AMD64_RBP);
558 #else
559 amd64_leave (code);
560 #endif
561 cfa_offset -= sizeof (mgreg_t);
562 mono_add_unwind_op_def_cfa (unwind_ops, code, buf, AMD64_RSP, cfa_offset);
563
564 if (MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type)) {
565 /* Load result */
566 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RSP, rax_offset - sizeof(mgreg_t), sizeof(mgreg_t));
567 amd64_ret (code);
568 } else {
569 /* call the compiled method using the saved rax */
570 amd64_jump_membase (code, AMD64_RSP, rax_offset - sizeof(mgreg_t));
571 }
572
573 g_assert ((code - buf) <= kMaxCodeSize);
574 g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE));
575
576 mono_arch_flush_icache (buf, code - buf);
577 MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL));
578
579 tramp_name = mono_get_generic_trampoline_name (tramp_type);
580 *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
581 g_free (tramp_name);
582
583 return buf;
584 }
585
586 gpointer
mono_arch_create_specific_trampoline(gpointer arg1,MonoTrampolineType tramp_type,MonoDomain * domain,guint32 * code_len)587 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
588 {
589 guint8 *code, *buf, *tramp;
590 int size;
591 gboolean far_addr = FALSE;
592
593 tramp = mono_get_trampoline_code (tramp_type);
594
595 if ((((guint64)arg1) >> 32) == 0)
596 size = 5 + 1 + 4;
597 else
598 size = 5 + 1 + 8;
599
600 code = buf = (guint8 *)mono_domain_code_reserve_align (domain, size, 1);
601
602 if (((gint64)tramp - (gint64)code) >> 31 != 0 && ((gint64)tramp - (gint64)code) >> 31 != -1) {
603 #ifndef MONO_ARCH_NOMAP32BIT
604 g_assert_not_reached ();
605 #endif
606 far_addr = TRUE;
607 size += 16;
608 code = buf = (guint8 *)mono_domain_code_reserve_align (domain, size, 1);
609 }
610
611 if (far_addr) {
612 amd64_mov_reg_imm (code, AMD64_R11, tramp);
613 amd64_call_reg (code, AMD64_R11);
614 } else {
615 amd64_call_code (code, tramp);
616 }
617 /* The trampoline code will obtain the argument from the instruction stream */
618 if ((((guint64)arg1) >> 32) == 0) {
619 *code = 0x4;
620 *(guint32*)(code + 1) = (gint64)arg1;
621 code += 5;
622 } else {
623 *code = 0x8;
624 *(guint64*)(code + 1) = (gint64)arg1;
625 code += 9;
626 }
627
628 g_assert ((code - buf) <= size);
629
630 if (code_len)
631 *code_len = size;
632
633 mono_arch_flush_icache (buf, size);
634 MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE, mono_get_generic_trampoline_simple_name (tramp_type)));
635
636 return buf;
637 }
638
639 gpointer
mono_arch_create_rgctx_lazy_fetch_trampoline(guint32 slot,MonoTrampInfo ** info,gboolean aot)640 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
641 {
642 guint8 *tramp;
643 guint8 *code, *buf;
644 guint8 **rgctx_null_jumps;
645 int tramp_size;
646 int depth, index;
647 int i;
648 gboolean mrgctx;
649 MonoJumpInfo *ji = NULL;
650 GSList *unwind_ops;
651
652 mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
653 index = MONO_RGCTX_SLOT_INDEX (slot);
654 if (mrgctx)
655 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
656 for (depth = 0; ; ++depth) {
657 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
658
659 if (index < size - 1)
660 break;
661 index -= size - 1;
662 }
663
664 tramp_size = 64 + 8 * depth;
665
666 code = buf = (guint8 *)mono_global_codeman_reserve (tramp_size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
667
668 unwind_ops = mono_arch_get_cie_program ();
669
670 rgctx_null_jumps = (guint8 **)g_malloc (sizeof (guint8*) * (depth + 2));
671
672 if (mrgctx) {
673 /* get mrgctx ptr */
674 amd64_mov_reg_reg (code, AMD64_RAX, AMD64_ARG_REG1, 8);
675 } else {
676 /* load rgctx ptr from vtable */
677 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_ARG_REG1, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context), sizeof(gpointer));
678 /* is the rgctx ptr null? */
679 amd64_test_reg_reg (code, AMD64_RAX, AMD64_RAX);
680 /* if yes, jump to actual trampoline */
681 rgctx_null_jumps [0] = code;
682 amd64_branch8 (code, X86_CC_Z, -1, 1);
683 }
684
685 for (i = 0; i < depth; ++i) {
686 /* load ptr to next array */
687 if (mrgctx && i == 0)
688 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RAX, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT, sizeof(gpointer));
689 else
690 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RAX, 0, sizeof(gpointer));
691 /* is the ptr null? */
692 amd64_test_reg_reg (code, AMD64_RAX, AMD64_RAX);
693 /* if yes, jump to actual trampoline */
694 rgctx_null_jumps [i + 1] = code;
695 amd64_branch8 (code, X86_CC_Z, -1, 1);
696 }
697
698 /* fetch slot */
699 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RAX, sizeof (gpointer) * (index + 1), sizeof(gpointer));
700 /* is the slot null? */
701 amd64_test_reg_reg (code, AMD64_RAX, AMD64_RAX);
702 /* if yes, jump to actual trampoline */
703 rgctx_null_jumps [depth + 1] = code;
704 amd64_branch8 (code, X86_CC_Z, -1, 1);
705 /* otherwise return */
706 amd64_ret (code);
707
708 for (i = mrgctx ? 1 : 0; i <= depth + 1; ++i)
709 mono_amd64_patch (rgctx_null_jumps [i], code);
710
711 g_free (rgctx_null_jumps);
712
713 if (MONO_ARCH_VTABLE_REG != AMD64_ARG_REG1) {
714 /* move the rgctx pointer to the VTABLE register */
715 amd64_mov_reg_reg (code, MONO_ARCH_VTABLE_REG, AMD64_ARG_REG1, sizeof(gpointer));
716 }
717
718 if (aot) {
719 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
720 amd64_jump_reg (code, AMD64_R11);
721 } else {
722 tramp = (guint8 *)mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), NULL);
723
724 /* jump to the actual trampoline */
725 amd64_jump_code (code, tramp);
726 }
727
728 mono_arch_flush_icache (buf, code - buf);
729 MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL));
730
731 g_assert (code - buf <= tramp_size);
732 g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
733
734 char *name = mono_get_rgctx_fetch_trampoline_name (slot);
735 *info = mono_tramp_info_create (name, buf, code - buf, ji, unwind_ops);
736 g_free (name);
737
738 return buf;
739 }
740
741 gpointer
mono_arch_create_general_rgctx_lazy_fetch_trampoline(MonoTrampInfo ** info,gboolean aot)742 mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboolean aot)
743 {
744 guint8 *code, *buf;
745 int tramp_size;
746 MonoJumpInfo *ji = NULL;
747 GSList *unwind_ops;
748
749 g_assert (aot);
750 tramp_size = 64;
751
752 code = buf = (guint8 *)mono_global_codeman_reserve (tramp_size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
753
754 unwind_ops = mono_arch_get_cie_program ();
755
756 // FIXME: Currently, we always go to the slow path.
757 /* This receives a <slot, trampoline> in the rgctx arg reg. */
758 /* Load trampoline addr */
759 amd64_mov_reg_membase (code, AMD64_R11, MONO_ARCH_RGCTX_REG, 8, 8);
760 /* move the rgctx pointer to the VTABLE register */
761 amd64_mov_reg_reg (code, MONO_ARCH_VTABLE_REG, AMD64_ARG_REG1, sizeof(gpointer));
762 /* Jump to the trampoline */
763 amd64_jump_reg (code, AMD64_R11);
764
765 mono_arch_flush_icache (buf, code - buf);
766 MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL));
767
768 g_assert (code - buf <= tramp_size);
769 g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
770
771 if (info)
772 *info = mono_tramp_info_create ("rgctx_fetch_trampoline_general", buf, code - buf, ji, unwind_ops);
773
774 return buf;
775 }
776
777 void
mono_arch_invalidate_method(MonoJitInfo * ji,void * func,gpointer func_arg)778 mono_arch_invalidate_method (MonoJitInfo *ji, void *func, gpointer func_arg)
779 {
780 /* FIXME: This is not thread safe */
781 guint8 *code = (guint8 *)ji->code_start;
782
783 amd64_mov_reg_imm (code, AMD64_ARG_REG1, func_arg);
784 amd64_mov_reg_imm (code, AMD64_R11, func);
785
786 x86_push_imm (code, (guint64)func_arg);
787 amd64_call_reg (code, AMD64_R11);
788 }
789 #endif /* !DISABLE_JIT */
790
791 /*
792 * mono_arch_get_call_target:
793 *
794 * Return the address called by the code before CODE if exists.
795 */
796 guint8*
mono_arch_get_call_target(guint8 * code)797 mono_arch_get_call_target (guint8 *code)
798 {
799 if (code [-5] == 0xe8) {
800 gint32 disp = *(gint32*)(code - 4);
801 guint8 *target = code + disp;
802
803 return target;
804 } else {
805 return NULL;
806 }
807 }
808
809 /*
810 * mono_arch_get_plt_info_offset:
811 *
812 * Return the PLT info offset belonging to the plt entry PLT_ENTRY.
813 */
814 guint32
mono_arch_get_plt_info_offset(guint8 * plt_entry,mgreg_t * regs,guint8 * code)815 mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
816 {
817 return *(guint32*)(plt_entry + 6);
818 }
819
820 #ifndef DISABLE_JIT
821 /*
822 * mono_arch_create_sdb_trampoline:
823 *
824 * Return a trampoline which captures the current context, passes it to
825 * debugger_agent_single_step_from_context ()/debugger_agent_breakpoint_from_context (),
826 * then restores the (potentially changed) context.
827 */
828 guint8*
mono_arch_create_sdb_trampoline(gboolean single_step,MonoTrampInfo ** info,gboolean aot)829 mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot)
830 {
831 int tramp_size = 512;
832 int i, framesize, ctx_offset, cfa_offset, gregs_offset;
833 guint8 *code, *buf;
834 GSList *unwind_ops = NULL;
835 MonoJumpInfo *ji = NULL;
836
837 code = buf = (guint8 *)mono_global_codeman_reserve (tramp_size + MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE);
838
839 framesize = 0;
840 #ifdef TARGET_WIN32
841 /* Reserve space where the callee can save the argument registers */
842 framesize += 4 * sizeof (mgreg_t);
843 #endif
844
845 ctx_offset = framesize;
846 framesize += sizeof (MonoContext);
847
848 framesize = ALIGN_TO (framesize, MONO_ARCH_FRAME_ALIGNMENT);
849
850 // CFA = sp + 8
851 cfa_offset = 8;
852 mono_add_unwind_op_def_cfa (unwind_ops, code, buf, AMD64_RSP, 8);
853 // IP saved at CFA - 8
854 mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RIP, -cfa_offset);
855
856 amd64_push_reg (code, AMD64_RBP);
857 cfa_offset += sizeof(mgreg_t);
858 mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
859 mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RBP, - cfa_offset);
860
861 amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));
862 mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, AMD64_RBP);
863 mono_add_unwind_op_fp_alloc (unwind_ops, code, buf, AMD64_RBP, 0);
864 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
865
866 gregs_offset = ctx_offset + MONO_STRUCT_OFFSET (MonoContext, gregs);
867
868 /* Initialize a MonoContext structure on the stack */
869 for (i = 0; i < AMD64_NREG; ++i) {
870 if (i != AMD64_RIP && i != AMD64_RSP && i != AMD64_RBP)
871 amd64_mov_membase_reg (code, AMD64_RSP, gregs_offset + (i * sizeof (mgreg_t)), i, sizeof (mgreg_t));
872 }
873 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, 0, sizeof (mgreg_t));
874 amd64_mov_membase_reg (code, AMD64_RSP, gregs_offset + (AMD64_RBP * sizeof (mgreg_t)), AMD64_R11, sizeof (mgreg_t));
875 amd64_lea_membase (code, AMD64_R11, AMD64_RBP, 2 * sizeof (mgreg_t));
876 amd64_mov_membase_reg (code, AMD64_RSP, gregs_offset + (AMD64_RSP * sizeof (mgreg_t)), AMD64_R11, sizeof (mgreg_t));
877 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, sizeof (mgreg_t), sizeof (mgreg_t));
878 amd64_mov_membase_reg (code, AMD64_RSP, gregs_offset + (AMD64_RIP * sizeof (mgreg_t)), AMD64_R11, sizeof (mgreg_t));
879
880 /* Call the single step/breakpoint function in sdb */
881 amd64_lea_membase (code, AMD64_ARG_REG1, AMD64_RSP, ctx_offset);
882
883 if (aot) {
884 if (single_step)
885 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "debugger_agent_single_step_from_context");
886 else
887 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "debugger_agent_breakpoint_from_context");
888 } else {
889 if (single_step)
890 amd64_mov_reg_imm (code, AMD64_R11, debugger_agent_single_step_from_context);
891 else
892 amd64_mov_reg_imm (code, AMD64_R11, debugger_agent_breakpoint_from_context);
893 }
894 amd64_call_reg (code, AMD64_R11);
895
896 /* Restore registers from ctx */
897 for (i = 0; i < AMD64_NREG; ++i) {
898 if (i != AMD64_RIP && i != AMD64_RSP && i != AMD64_RBP)
899 amd64_mov_reg_membase (code, i, AMD64_RSP, gregs_offset + (i * sizeof (mgreg_t)), sizeof (mgreg_t));
900 }
901 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RSP, gregs_offset + (AMD64_RBP * sizeof (mgreg_t)), sizeof (mgreg_t));
902 amd64_mov_membase_reg (code, AMD64_RBP, 0, AMD64_R11, sizeof (mgreg_t));
903 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RSP, gregs_offset + (AMD64_RIP * sizeof (mgreg_t)), sizeof (mgreg_t));
904 amd64_mov_membase_reg (code, AMD64_RBP, sizeof (mgreg_t), AMD64_R11, sizeof (mgreg_t));
905
906 #if TARGET_WIN32
907 amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
908 amd64_pop_reg (code, AMD64_RBP);
909 mono_add_unwind_op_same_value (unwind_ops, code, buf, AMD64_RBP);
910 #else
911 amd64_leave (code);
912 #endif
913 cfa_offset -= sizeof (mgreg_t);
914 mono_add_unwind_op_def_cfa (unwind_ops, code, buf, AMD64_RSP, cfa_offset);
915 amd64_ret (code);
916
917 mono_arch_flush_icache (code, code - buf);
918 MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL));
919 g_assert (code - buf <= tramp_size);
920 g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE));
921
922 const char *tramp_name = single_step ? "sdb_single_step_trampoline" : "sdb_breakpoint_trampoline";
923 *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
924
925 return buf;
926 }
927
928 /*
929 * mono_arch_get_enter_icall_trampoline:
930 *
931 * A trampoline that handles the transition from interpreter into native
932 * world. It requiers to set up a descriptor (InterpMethodArguments), so the
933 * trampoline can translate the arguments into the native calling convention.
934 *
935 * See also `build_args_from_sig ()` in interp.c.
936 */
937 gpointer
mono_arch_get_enter_icall_trampoline(MonoTrampInfo ** info)938 mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info)
939 {
940 #ifndef DISABLE_INTERPRETER
941 guint8 *start = NULL, *code, *label_gexits [INTERP_ICALL_TRAMP_IARGS], *label_fexits [INTERP_ICALL_TRAMP_FARGS], *label_leave_tramp [3], *label_is_float_ret;
942 MonoJumpInfo *ji = NULL;
943 GSList *unwind_ops = NULL;
944 static int farg_regs[] = {AMD64_XMM0, AMD64_XMM1, AMD64_XMM2, AMD64_XMM3};
945 int buf_len, i, framesize = 0, off_rbp, off_methodargs, off_targetaddr;
946
947 g_assert ((sizeof (farg_regs) / sizeof (farg_regs [0])) >= INTERP_ICALL_TRAMP_FARGS);
948 buf_len = 512 + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0);
949 start = code = (guint8 *) mono_global_codeman_reserve (buf_len);
950
951 off_rbp = -framesize;
952
953 framesize += sizeof (mgreg_t);
954 off_methodargs = -framesize;
955
956 framesize += sizeof (mgreg_t);
957 off_targetaddr = -framesize;
958
959 framesize += (INTERP_ICALL_TRAMP_IARGS - PARAM_REGS) * sizeof (mgreg_t);
960
961 amd64_push_reg (code, AMD64_RBP);
962 amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof (mgreg_t));
963 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, ALIGN_TO (framesize, MONO_ARCH_FRAME_ALIGNMENT));
964
965 /* save InterpMethodArguments* onto stack */
966 amd64_mov_membase_reg (code, AMD64_RBP, off_methodargs, AMD64_ARG_REG2, sizeof (mgreg_t));
967
968 /* save target address on stack */
969 amd64_mov_membase_reg (code, AMD64_RBP, off_targetaddr, AMD64_ARG_REG1, sizeof (mgreg_t));
970
971 /* load pointer to InterpMethodArguments* into R11 */
972 amd64_mov_reg_reg (code, AMD64_R11, AMD64_ARG_REG2, 8);
973
974 /* move flen into RAX */
975 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, MONO_STRUCT_OFFSET (InterpMethodArguments, flen), sizeof (mgreg_t));
976 /* load pointer to fargs into R11 */
977 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, MONO_STRUCT_OFFSET (InterpMethodArguments, fargs), sizeof (mgreg_t));
978
979 for (i = 0; i < INTERP_ICALL_TRAMP_FARGS; ++i) {
980 amd64_test_reg_reg (code, AMD64_RAX, AMD64_RAX);
981 label_fexits [i] = code;
982 x86_branch8 (code, X86_CC_Z, 0, FALSE);
983
984 amd64_sse_movsd_reg_membase (code, farg_regs [i], AMD64_R11, i * sizeof (double));
985 amd64_dec_reg_size (code, AMD64_RAX, 1);
986 }
987
988 for (i = 0; i < INTERP_ICALL_TRAMP_FARGS; i++)
989 x86_patch (label_fexits [i], code);
990
991 /* load pointer to InterpMethodArguments* into R11 */
992 amd64_mov_reg_reg (code, AMD64_R11, AMD64_ARG_REG2, sizeof (mgreg_t));
993 /* move ilen into RAX */
994 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, MONO_STRUCT_OFFSET (InterpMethodArguments, ilen), sizeof (mgreg_t));
995
996 int stack_offset = 0;
997 for (i = 0; i < INTERP_ICALL_TRAMP_IARGS; i++) {
998 amd64_test_reg_reg (code, AMD64_RAX, AMD64_RAX);
999 label_gexits [i] = code;
1000 x86_branch32 (code, X86_CC_Z, 0, FALSE);
1001
1002 /* load pointer to InterpMethodArguments* into R11 */
1003 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, off_methodargs, sizeof (mgreg_t));
1004 /* load pointer to iargs into R11 */
1005 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, MONO_STRUCT_OFFSET (InterpMethodArguments, iargs), sizeof (mgreg_t));
1006
1007 if (i < PARAM_REGS) {
1008 amd64_mov_reg_membase (code, param_regs [i], AMD64_R11, i * sizeof (mgreg_t), sizeof (mgreg_t));
1009 } else {
1010 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, i * sizeof (mgreg_t), sizeof (mgreg_t));
1011 amd64_mov_membase_reg (code, AMD64_RSP, stack_offset, AMD64_R11, sizeof (mgreg_t));
1012 stack_offset += sizeof (mgreg_t);
1013 }
1014 amd64_dec_reg_size (code, AMD64_RAX, 1);
1015 }
1016
1017 for (i = 0; i < INTERP_ICALL_TRAMP_IARGS; i++)
1018 x86_patch (label_gexits [i], code);
1019
1020 /* load target addr */
1021 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, off_targetaddr, sizeof (mgreg_t));
1022
1023 /* call into native function */
1024 amd64_call_reg (code, AMD64_R11);
1025
1026 /* load InterpMethodArguments */
1027 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, off_methodargs, sizeof (mgreg_t));
1028
1029 /* load is_float_ret */
1030 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, MONO_STRUCT_OFFSET (InterpMethodArguments, is_float_ret), sizeof (mgreg_t));
1031
1032 /* check if a float return value is expected */
1033 amd64_test_reg_reg (code, AMD64_R11, AMD64_R11);
1034
1035 label_is_float_ret = code;
1036 x86_branch8 (code, X86_CC_NZ, 0, FALSE);
1037
1038 /* greg return */
1039 /* load InterpMethodArguments */
1040 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, off_methodargs, sizeof (mgreg_t));
1041 /* load retval */
1042 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, MONO_STRUCT_OFFSET (InterpMethodArguments, retval), sizeof (mgreg_t));
1043
1044 amd64_test_reg_reg (code, AMD64_R11, AMD64_R11);
1045 label_leave_tramp [0] = code;
1046 x86_branch8 (code, X86_CC_Z, 0, FALSE);
1047
1048 amd64_mov_membase_reg (code, AMD64_R11, 0, AMD64_RAX, sizeof (mgreg_t));
1049
1050 label_leave_tramp [1] = code;
1051 x86_jump8 (code, 0);
1052
1053 /* freg return */
1054 x86_patch (label_is_float_ret, code);
1055 /* load InterpMethodArguments */
1056 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, off_methodargs, sizeof (mgreg_t));
1057 /* load retval */
1058 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, MONO_STRUCT_OFFSET (InterpMethodArguments, retval), sizeof (mgreg_t));
1059
1060 amd64_test_reg_reg (code, AMD64_R11, AMD64_R11);
1061 label_leave_tramp [2] = code;
1062 x86_branch8 (code, X86_CC_Z, 0, FALSE);
1063
1064 amd64_sse_movsd_membase_reg (code, AMD64_R11, 0, AMD64_XMM0);
1065
1066 for (i = 0; i < 3; i++)
1067 x86_patch (label_leave_tramp [i], code);
1068
1069 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, ALIGN_TO (framesize, MONO_ARCH_FRAME_ALIGNMENT));
1070 amd64_pop_reg (code, AMD64_RBP);
1071 amd64_ret (code);
1072
1073 g_assert (code - start < buf_len);
1074
1075 mono_arch_flush_icache (start, code - start);
1076 MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL));
1077
1078 if (info)
1079 *info = mono_tramp_info_create ("enter_icall_trampoline", start, code - start, ji, unwind_ops);
1080
1081 return start;
1082 #else
1083 g_assert_not_reached ();
1084 return NULL;
1085 #endif /* DISABLE_INTERPRETER */
1086 }
1087 #endif /* !DISABLE_JIT */
1088
1089 #ifdef DISABLE_JIT
1090 gpointer
mono_arch_get_unbox_trampoline(MonoMethod * m,gpointer addr)1091 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
1092 {
1093 g_assert_not_reached ();
1094 return NULL;
1095 }
1096
1097 gpointer
mono_arch_get_static_rgctx_trampoline(gpointer arg,gpointer addr)1098 mono_arch_get_static_rgctx_trampoline (gpointer arg, gpointer addr)
1099 {
1100 g_assert_not_reached ();
1101 return NULL;
1102 }
1103
1104 gpointer
mono_arch_create_rgctx_lazy_fetch_trampoline(guint32 slot,MonoTrampInfo ** info,gboolean aot)1105 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
1106 {
1107 g_assert_not_reached ();
1108 return NULL;
1109 }
1110
1111 guchar*
mono_arch_create_generic_trampoline(MonoTrampolineType tramp_type,MonoTrampInfo ** info,gboolean aot)1112 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
1113 {
1114 g_assert_not_reached ();
1115 return NULL;
1116 }
1117
1118 gpointer
mono_arch_create_specific_trampoline(gpointer arg1,MonoTrampolineType tramp_type,MonoDomain * domain,guint32 * code_len)1119 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
1120 {
1121 g_assert_not_reached ();
1122 return NULL;
1123 }
1124
1125 gpointer
mono_arch_create_general_rgctx_lazy_fetch_trampoline(MonoTrampInfo ** info,gboolean aot)1126 mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboolean aot)
1127 {
1128 g_assert_not_reached ();
1129 return NULL;
1130 }
1131
1132 void
mono_arch_invalidate_method(MonoJitInfo * ji,void * func,gpointer func_arg)1133 mono_arch_invalidate_method (MonoJitInfo *ji, void *func, gpointer func_arg)
1134 {
1135 g_assert_not_reached ();
1136 return;
1137 }
1138
1139 guint8*
mono_arch_create_sdb_trampoline(gboolean single_step,MonoTrampInfo ** info,gboolean aot)1140 mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot)
1141 {
1142 g_assert_not_reached ();
1143 return NULL;
1144 }
1145
1146 gpointer
mono_arch_get_enter_icall_trampoline(MonoTrampInfo ** info)1147 mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info)
1148 {
1149 g_assert_not_reached ();
1150 return NULL;
1151 }
1152 #endif /* DISABLE_JIT */
1153