3 * libcorkscrew-based native unwinder
6 * Zoltan Varga <vargaz@gmail.com>
7 * Rodrigo Kumpera <kumpera@gmail.com>
8 * Andi McClure <andi.mcclure@xamarin.com>
9 * Johan Lorensson <johan.lorensson@xamarin.com>
11 * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
12 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
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/metadata/gc-internals.h>
23 #include <mono/arch/amd64/amd64-codegen.h>
25 #include <mono/utils/memcheck.h>
28 #include "mini-amd64.h"
29 #include "mini-amd64-gsharedvt.h"
30 #include "debugger-agent.h"
32 #if defined (MONO_ARCH_GSHAREDVT_SUPPORTED)
34 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
36 #define SRC_REG_SHIFT 0
37 #define SRC_REG_MASK 0xFFFF
39 #define SRC_DESCRIPTOR_MARSHAL_SHIFT 16
40 #define SRC_DESCRIPTOR_MARSHAL_MASK 0x0FF
42 #define SLOT_COUNT_SHIFT 24
43 #define SLOT_COUNT_MASK 0xFF
46 mono_amd64_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
50 #ifdef DEBUG_AMD64_GSHAREDVT
51 printf ("mono_amd64_start_gsharedvt_call info %p caller %p callee %p ctx %p\n", info, caller, callee, mrgctx_reg);
53 for (i = 0; i < PARAM_REGS; ++i)
54 printf ("\treg [%d] -> %p\n", i, caller [i]);
57 /* Set vtype ret arg */
58 if (info->vret_slot != -1) {
59 DEBUG_AMD64_GSHAREDVT_PRINT ("vret handling\n[%d] < &%d (%p)\n", info->vret_arg_reg, info->vret_slot, &callee [info->vret_slot]);
60 g_assert (info->vret_slot);
61 callee [info->vret_arg_reg] = &callee [info->vret_slot];
64 for (i = 0; i < info->map_count; ++i) {
65 int src = info->map [i * 2];
66 int dst = info->map [(i * 2) + 1];
67 int arg_marshal = (src >> SRC_DESCRIPTOR_MARSHAL_SHIFT) & SRC_DESCRIPTOR_MARSHAL_MASK;
69 int source_reg = src & SRC_REG_MASK;
70 int dest_reg = dst & SRC_REG_MASK;
72 DEBUG_AMD64_GSHAREDVT_PRINT ("source %x dest %x marshal %d: ", src, dst, arg_marshal);
73 switch (arg_marshal) {
74 case GSHAREDVT_ARG_NONE:
75 callee [dest_reg] = caller [source_reg];
76 DEBUG_AMD64_GSHAREDVT_PRINT ("[%d] <- %d (%p) <- (%p)\n", dest_reg, source_reg, &callee [dest_reg], caller [source_reg]);
78 case GSHAREDVT_ARG_BYVAL_TO_BYREF:
79 /* gsharedvt argument passed by addr in reg/stack slot */
80 callee [dest_reg] = &caller [source_reg];
81 DEBUG_AMD64_GSHAREDVT_PRINT ("[%d] <- &%d (%p) <- (%p)\n", dest_reg, source_reg, &callee [dest_reg], &caller [source_reg]);
83 case GSHAREDVT_ARG_BYREF_TO_BYVAL: {
84 int slot_count = (src >> SLOT_COUNT_SHIFT) & SLOT_COUNT_MASK;
86 gpointer *addr = caller [source_reg];
88 for (j = 0; j < slot_count; ++j)
89 callee [dest_reg + j] = addr [j];
90 DEBUG_AMD64_GSHAREDVT_PRINT ("[%d] <- [%d] (%d words) (%p) <- (%p)\n", dest_reg, source_reg, slot_count, &callee [dest_reg], &caller [source_reg]);
93 case GSHAREDVT_ARG_BYREF_TO_BYVAL_U1: {
94 guint8 *addr = caller [source_reg];
96 callee [dest_reg] = (gpointer)(mgreg_t)*addr;
97 DEBUG_AMD64_GSHAREDVT_PRINT ("[%d] <- (u1) [%d] (%p) <- (%p)\n", dest_reg, source_reg, &callee [dest_reg], &caller [source_reg]);
100 case GSHAREDVT_ARG_BYREF_TO_BYVAL_U2: {
101 guint16 *addr = caller [source_reg];
103 callee [dest_reg] = (gpointer)(mgreg_t)*addr;
104 DEBUG_AMD64_GSHAREDVT_PRINT ("[%d] <- (u2) [%d] (%p) <- (%p)\n", dest_reg, source_reg, &callee [dest_reg], &caller [source_reg]);
107 case GSHAREDVT_ARG_BYREF_TO_BYVAL_U4: {
108 guint32 *addr = caller [source_reg];
110 callee [dest_reg] = (gpointer)(mgreg_t)*addr;
111 DEBUG_AMD64_GSHAREDVT_PRINT ("[%d] <- (u4) [%d] (%p) <- (%p)\n", dest_reg, source_reg, &callee [dest_reg], &caller [source_reg]);
116 g_error ("cant handle arg marshal %d\n", arg_marshal);
120 //Can't handle for now
121 if (info->vcall_offset != -1){
122 MonoObject *this_obj = caller [0];
124 DEBUG_AMD64_GSHAREDVT_PRINT ("target is a vcall at offset %d\n", info->vcall_offset / 8);
125 if (G_UNLIKELY (!this_obj))
127 if (info->vcall_offset == MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET)
128 /* delegate invoke */
129 return ((MonoDelegate*)this_obj)->invoke_impl;
131 return *(gpointer*)((char*)this_obj->vtable + info->vcall_offset);
132 } else if (info->calli) {
133 /* The address to call is passed in the mrgctx reg */
136 DEBUG_AMD64_GSHAREDVT_PRINT ("target is %p\n", info->addr);
146 * mono_arch_get_gsharedvt_arg_trampoline:
148 * See tramp-x86.c for documentation.
151 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
153 guint8 *code, *start;
158 start = code = mono_domain_code_reserve (domain, buf_len);
160 amd64_mov_reg_imm (code, AMD64_RAX, arg);
161 amd64_jump_code (code, addr);
162 g_assert ((code - start) < buf_len);
164 mono_arch_flush_icache (start, code - start);
165 MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL));
167 mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), domain);
173 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
176 int buf_len, cfa_offset;
177 GSList *unwind_ops = NULL;
178 MonoJumpInfo *ji = NULL;
179 int n_arg_regs, n_arg_fregs, framesize, i;
180 int info_offset, offset, rgctx_arg_reg_offset;
181 int caller_reg_area_offset, callee_reg_area_offset, callee_stack_area_offset;
182 guint8 *br_out, *br [64], *br_ret [64];
187 buf = code = mono_global_codeman_reserve (buf_len + MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE);
190 * We are being called by an gsharedvt arg trampoline, the info argument is in AMD64_RAX.
192 n_arg_regs = PARAM_REGS;
193 n_arg_fregs = FLOAT_PARAM_REGS;
195 /* Compute stack frame size and offsets */
198 info_offset = offset;
202 rgctx_arg_reg_offset = offset;
205 /*callconv in regs */
206 caller_reg_area_offset = offset;
207 reg_area_size = ALIGN_TO ((n_arg_regs + n_arg_fregs) * 8, MONO_ARCH_FRAME_ALIGNMENT);
208 offset += reg_area_size;
212 g_assert (framesize % MONO_ARCH_FRAME_ALIGNMENT == 0);
213 g_assert (reg_area_size % MONO_ARCH_FRAME_ALIGNMENT == 0);
215 /* unwind markers 1/3 */
216 cfa_offset = sizeof (gpointer);
217 mono_add_unwind_op_def_cfa (unwind_ops, code, buf, AMD64_RSP, cfa_offset);
218 mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RIP, -cfa_offset);
220 /* save the old frame pointer */
221 amd64_push_reg (code, AMD64_RBP);
223 /* unwind markers 2/3 */
224 cfa_offset += sizeof (gpointer);
225 mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
226 mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RBP, - cfa_offset);
228 /* set it as the new frame pointer */
229 amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));
231 /* unwind markers 3/3 */
232 mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, AMD64_RBP);
233 mono_add_unwind_op_fp_alloc (unwind_ops, code, buf, AMD64_RBP, 0);
235 /* setup the frame */
236 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
241 amd64_mov_membase_reg (code, AMD64_RSP, info_offset, AMD64_RAX, sizeof (mgreg_t));
243 amd64_mov_membase_reg (code, AMD64_RSP, rgctx_arg_reg_offset, MONO_ARCH_RGCTX_REG, sizeof (mgreg_t));
245 for (i = 0; i < n_arg_regs; ++i)
246 amd64_mov_membase_reg (code, AMD64_RSP, caller_reg_area_offset + i * 8, param_regs [i], sizeof (mgreg_t));
248 for (i = 0; i < n_arg_fregs; ++i)
249 amd64_sse_movsd_membase_reg (code, AMD64_RSP, caller_reg_area_offset + (i + n_arg_regs) * 8, i);
251 /* TODO Allocate stack area used to pass arguments to the method */
254 /* Allocate callee register area just below the caller area so it can be accessed from start_gsharedvt_call using negative offsets */
255 /* XXX figure out alignment */
256 callee_reg_area_offset = reg_area_size - ((n_arg_regs + n_arg_fregs) * 8); /* Ensure alignment */
257 callee_stack_area_offset = callee_reg_area_offset + reg_area_size;
258 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, reg_area_size);
260 /* Allocate stack area used to pass arguments to the method */
261 amd64_mov_reg_membase (code, AMD64_R11, AMD64_RAX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage), 4);
262 amd64_alu_reg_reg (code, X86_SUB, AMD64_RSP, AMD64_R11);
264 /* The stack now looks like this:
266 <caller stack params area>
269 <caller registers area>
276 /* Call start_gsharedvt_call () */
278 amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG1, AMD64_RAX, sizeof(mgreg_t));
279 /* arg2 = caller stack area */
280 amd64_lea_membase (code, MONO_AMD64_ARG_REG2, AMD64_RBP, -(framesize - caller_reg_area_offset));
282 /* arg3 == callee stack area */
283 amd64_lea_membase (code, MONO_AMD64_ARG_REG3, AMD64_RSP, callee_reg_area_offset);
285 /* arg4 = mrgctx reg */
286 amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG4, MONO_ARCH_RGCTX_REG, sizeof(mgreg_t));
289 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_amd64_start_gsharedvt_call");
291 /* Since we are doing a call as part of setting up stackframe, the reserved shadow stack used by Windows platform is allocated up in
292 the callee stack area but currently the callee reg area is in between. Windows calling convention dictates that room is made on stack where
293 callee can save any parameters passed in registers. Since Windows x64 calling convention
294 uses 4 registers for the first 4 parameters, stack needs to be adjusted before making the call.
295 NOTE, Windows calling convention assumes that space for all registers have been reserved, regardless
296 of the number of function parameters actually used.
298 int shadow_reg_size = 0;
300 shadow_reg_size = ALIGN_TO (PARAM_REGS * sizeof(gpointer), MONO_ARCH_FRAME_ALIGNMENT);
301 amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, shadow_reg_size);
302 amd64_call_reg (code, AMD64_R11);
303 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, shadow_reg_size);
305 amd64_call_reg (code, AMD64_R11);
308 amd64_call_code (code, mono_amd64_start_gsharedvt_call);
311 /* Method to call is now on RAX. Restore regs and jump */
312 amd64_mov_reg_reg (code, AMD64_R11, AMD64_RAX, sizeof(mgreg_t));
314 for (i = 0; i < n_arg_regs; ++i)
315 amd64_mov_reg_membase (code, param_regs [i], AMD64_RSP, callee_reg_area_offset + i * 8, sizeof (mgreg_t));
317 for (i = 0; i < n_arg_fregs; ++i)
318 amd64_sse_movsd_reg_membase (code, i, AMD64_RSP, callee_reg_area_offset + (i + n_arg_regs) * 8);
321 amd64_mov_reg_membase (code, MONO_ARCH_RGCTX_REG, AMD64_RBP, -(framesize - rgctx_arg_reg_offset), sizeof (mgreg_t));
323 /* Clear callee reg area */
324 amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, reg_area_size);
327 amd64_call_reg (code, AMD64_R11);
329 /* Marshal return value. Available registers: R10 and R11 */
330 /* Load info struct */
331 amd64_mov_reg_membase (code, AMD64_R10, AMD64_RBP, -(framesize - info_offset), sizeof (mgreg_t));
333 /* Branch to the in/out handling code */
334 amd64_alu_membase_imm_size (code, X86_CMP, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in), 1, 4);
338 x86_branch32 (code, X86_CC_NE, 0, TRUE);
345 /* Use first input parameter register as scratch since it is volatile on all platforms */
346 amd64_mov_reg_membase (code, MONO_AMD64_ARG_REG1, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot), 4);
347 amd64_alu_reg_imm (code, X86_SUB, MONO_AMD64_ARG_REG1, n_arg_regs + n_arg_fregs);
348 amd64_shift_reg_imm (code, X86_SHL, MONO_AMD64_ARG_REG1, 3);
350 /* vret address is RBP - (framesize - caller_reg_area_offset) */
351 amd64_mov_reg_reg (code, AMD64_R11, AMD64_RSP, sizeof(mgreg_t));
352 amd64_alu_reg_reg (code, X86_ADD, AMD64_R11, MONO_AMD64_ARG_REG1);
354 /* Load ret marshal type */
355 /* Load vret address in R11 */
356 amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
358 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
359 amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
361 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
363 x86_breakpoint (code); /* unhandled case */
365 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
366 mono_amd64_patch (br [i], code);
368 case GSHAREDVT_RET_NONE:
370 case GSHAREDVT_RET_I1:
371 amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, FALSE);
373 case GSHAREDVT_RET_U1:
374 amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, FALSE);
376 case GSHAREDVT_RET_I2:
377 amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, TRUE);
379 case GSHAREDVT_RET_U2:
380 amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, TRUE);
382 case GSHAREDVT_RET_I4: // CORRECT
383 case GSHAREDVT_RET_U4: // THIS IS INCORRECT. WHY IS IT NOT FAILING?
384 amd64_movsxd_reg_membase (code, AMD64_RAX, AMD64_R11, 0);
386 case GSHAREDVT_RET_I8:
387 amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, 0, 8);
389 case GSHAREDVT_RET_IREGS_1:
390 amd64_mov_reg_membase (code, return_regs [i - GSHAREDVT_RET_IREGS_1], AMD64_R11, 0, 8);
392 case GSHAREDVT_RET_R8:
393 amd64_sse_movsd_reg_membase (code, AMD64_XMM0, AMD64_R11, 0);
396 x86_breakpoint (code); /* can't handle specific case */
399 br_ret [b_ret_index ++] = code;
400 x86_jump32 (code, 0);
406 mono_amd64_patch (br_out, code);
409 Address to write return to is in the original value of the register specified by vret_arg_reg.
410 This will be either RSI, RDI (System V) or RCX, RDX (Windows) depending on whether this is a static call.
412 We alloc 'framesize' bytes below RBP to save regs, info and rgctx. RSP = RBP - framesize
413 We store RDI (System V), RCX (Windows) at RSP + caller_reg_area_offset + slot_index_of (register) * 8.
415 address: RBP - framesize + caller_reg_area_offset + 8*slot
418 int caller_vret_offset = caller_reg_area_offset - framesize;
420 /* Load vret address in R11 */
421 /* Position to return to is passed as a hidden argument. Load 'vret_arg_slot' to find it */
422 amd64_movsxd_reg_membase (code, AMD64_R11, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
424 // In the GSHAREDVT_RET_NONE case, vret_arg_slot is -1. In this case, skip marshalling.
425 amd64_alu_reg_imm (code, X86_CMP, AMD64_R11, 0);
426 br_ret [b_ret_index ++] = code;
427 amd64_branch32 (code, X86_CC_LT, 0, TRUE);
429 /* Compute ret area address in the caller frame, *( ((gpointer *)RBP) [R11+2] ) */
430 amd64_shift_reg_imm (code, X86_SHL, AMD64_R11, 3);
431 amd64_alu_reg_imm (code, X86_ADD, AMD64_R11, caller_vret_offset);
432 amd64_alu_reg_reg (code, X86_ADD, AMD64_R11, AMD64_RBP);
433 amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, sizeof (gpointer));
435 /* Load ret marshal type in R10 */
436 amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
438 // Switch table for ret_marshal value
439 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
440 amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
442 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
444 x86_breakpoint (code); /* unhandled case */
446 for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
447 mono_amd64_patch (br [i], code);
449 case GSHAREDVT_RET_NONE:
451 case GSHAREDVT_RET_IREGS_1:
452 amd64_mov_membase_reg (code, AMD64_R11, 0, return_regs [i - GSHAREDVT_RET_IREGS_1], 8);
454 case GSHAREDVT_RET_R8:
455 amd64_sse_movsd_membase_reg (code, AMD64_R11, 0, AMD64_XMM0);
458 x86_breakpoint (code); /* can't handle specific case */
461 br_ret [b_ret_index ++] = code;
462 x86_jump32 (code, 0);
466 for (i = 0; i < b_ret_index; ++i)
467 mono_amd64_patch (br_ret [i], code);
471 amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
472 amd64_pop_reg (code, AMD64_RBP);
473 mono_add_unwind_op_same_value (unwind_ops, code, buf, AMD64_RBP);
479 g_assert ((code - buf) < buf_len);
480 g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE));
483 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
485 mono_arch_flush_icache (buf, code - buf);
492 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
494 g_assert_not_reached ();
499 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
501 g_assert_not_reached ();
510 mono_amd64_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
512 g_assert_not_reached ();