Merge pull request #4444 from lateralusX/jlorenss/windows-unwind-info
[mono.git] / mono / mini / tramp-amd64-gsharedvt.c
1 /*
2  * tramp-amd64-gsharedvt.c: libcorkscrew-based native unwinder
3  *
4  * Authors:
5  *   Zoltan Varga <vargaz@gmail.com>
6  *   Rodrigo Kumpera <kumpera@gmail.com>
7  *   Andi McClure <andi.mcclure@xamarin.com>
8  *   Johan Lorensson <johan.lorensson@xamarin.com>
9  *
10  * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
11  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12  */
13 #include <config.h>
14 #include <glib.h>
15
16 #include <mono/metadata/abi-details.h>
17 #include <mono/metadata/appdomain.h>
18 #include <mono/metadata/marshal.h>
19 #include <mono/metadata/tabledefs.h>
20 #include <mono/metadata/profiler-private.h>
21 #include <mono/metadata/gc-internals.h>
22 #include <mono/arch/amd64/amd64-codegen.h>
23
24 #include <mono/utils/memcheck.h>
25
26 #include "mini.h"
27 #include "mini-amd64.h"
28 #include "mini-amd64-gsharedvt.h"
29 #include "debugger-agent.h"
30
31 #if defined (MONO_ARCH_GSHAREDVT_SUPPORTED)
32
33 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
34
35 #define SRC_REG_SHIFT 0
36 #define SRC_REG_MASK 0xFFFF
37
38 #define SRC_DESCRIPTOR_MARSHAL_SHIFT 16
39 #define SRC_DESCRIPTOR_MARSHAL_MASK 0x0FF
40
41 #define SLOT_COUNT_SHIFT 24
42 #define SLOT_COUNT_MASK 0xFF
43
44 gpointer
45 mono_amd64_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
46 {
47         int i;
48
49 #ifdef DEBUG_AMD64_GSHAREDVT
50         printf ("mono_amd64_start_gsharedvt_call info %p caller %p callee %p ctx %p\n", info, caller, callee, mrgctx_reg);
51
52         for (i = 0; i < PARAM_REGS; ++i)
53                 printf ("\treg [%d] -> %p\n", i, caller [i]);
54 #endif
55
56         /* Set vtype ret arg */
57         if (info->vret_slot != -1) {
58                 DEBUG_AMD64_GSHAREDVT_PRINT ("vret handling\n[%d] < &%d (%p)\n", info->vret_arg_reg, info->vret_slot, &callee [info->vret_slot]);
59                 g_assert (info->vret_slot);
60                 callee [info->vret_arg_reg] = &callee [info->vret_slot];
61         }
62
63         for (i = 0; i < info->map_count; ++i) {
64                 int src = info->map [i * 2];
65                 int dst = info->map [(i * 2) + 1];
66                 int arg_marshal = (src >> SRC_DESCRIPTOR_MARSHAL_SHIFT) & SRC_DESCRIPTOR_MARSHAL_MASK;
67
68                 int source_reg = src & SRC_REG_MASK;
69                 int dest_reg = dst & SRC_REG_MASK;
70
71                 DEBUG_AMD64_GSHAREDVT_PRINT ("source %x dest %x marshal %d: ", src, dst, arg_marshal);
72                 switch (arg_marshal) {
73                 case GSHAREDVT_ARG_NONE:
74                         callee [dest_reg] = caller [source_reg];
75                         DEBUG_AMD64_GSHAREDVT_PRINT ("[%d] <- %d (%p) <- (%p)\n", dest_reg, source_reg, &callee [dest_reg], caller [source_reg]);
76                         break;
77                 case GSHAREDVT_ARG_BYVAL_TO_BYREF:
78                         /* gsharedvt argument passed by addr in reg/stack slot */
79                         callee [dest_reg] = &caller [source_reg];
80                         DEBUG_AMD64_GSHAREDVT_PRINT ("[%d] <- &%d (%p) <- (%p)\n", dest_reg, source_reg, &callee [dest_reg], &caller [source_reg]);
81                         break;
82                 case GSHAREDVT_ARG_BYREF_TO_BYVAL: {
83                         int slot_count = (src >> SLOT_COUNT_SHIFT) & SLOT_COUNT_MASK;
84                         int j;
85                         gpointer *addr = caller [source_reg];
86
87                         for (j = 0; j < slot_count; ++j)
88                                 callee [dest_reg + j] = addr [j];
89                         DEBUG_AMD64_GSHAREDVT_PRINT ("[%d] <- [%d] (%d words) (%p) <- (%p)\n", dest_reg, source_reg, slot_count, &callee [dest_reg], &caller [source_reg]);
90                         break;
91                 }
92                 default:
93                         g_error ("cant handle arg marshal %d\n", arg_marshal);
94                 }
95         }
96
97         //Can't handle for now
98         if (info->vcall_offset != -1){
99                 MonoObject *this_obj = caller [0];
100
101                 DEBUG_AMD64_GSHAREDVT_PRINT ("target is a vcall at offset %d\n", info->vcall_offset / 8);
102                 if (G_UNLIKELY (!this_obj))
103                         return NULL;
104                 if (info->vcall_offset == MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET)
105                         /* delegate invoke */
106                         return ((MonoDelegate*)this_obj)->invoke_impl;
107                 else
108                         return *(gpointer*)((char*)this_obj->vtable + info->vcall_offset);
109         } else if (info->calli) {
110                 /* The address to call is passed in the mrgctx reg */
111                 return mrgctx_reg;
112         } else {
113                 DEBUG_AMD64_GSHAREDVT_PRINT ("target is %p\n", info->addr);
114                 return info->addr;
115         }
116 }
117
118 #ifndef DISABLE_JIT
119
120 // Compiler support
121
122 /*
123  * mono_arch_get_gsharedvt_arg_trampoline:
124  *
125  *   See tramp-x86.c for documentation.
126  */
127 gpointer
128 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
129 {
130         guint8 *code, *start;
131         int buf_len;
132
133         buf_len = 32;
134
135         start = code = mono_domain_code_reserve (domain, buf_len);
136
137         amd64_mov_reg_imm (code, AMD64_RAX, arg);
138         amd64_jump_code (code, addr);
139         g_assert ((code - start) < buf_len);
140
141         mono_arch_flush_icache (start, code - start);
142         mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL);
143
144         mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), domain);
145
146         return start;
147 }
148
149 gpointer
150 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
151 {
152         guint8 *code, *buf;
153         int buf_len, cfa_offset;
154         GSList *unwind_ops = NULL;
155         MonoJumpInfo *ji = NULL;
156         int n_arg_regs, n_arg_fregs, framesize, i;
157         int info_offset, offset, rgctx_arg_reg_offset;
158         int caller_reg_area_offset, callee_reg_area_offset, callee_stack_area_offset;
159         guint8 *br_out, *br [64], *br_ret [64];
160         int b_ret_index;
161         int reg_area_size;
162
163         buf_len = 2048;
164         buf = code = mono_global_codeman_reserve (buf_len + MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE);
165
166         /*
167          * We are being called by an gsharedvt arg trampoline, the info argument is in AMD64_RAX.
168          */
169         n_arg_regs = PARAM_REGS;
170         n_arg_fregs = FLOAT_PARAM_REGS;
171
172         /* Compute stack frame size and offsets */
173         offset = 0;
174         /* info reg */
175         info_offset = offset;
176         offset += 8;
177
178         /* rgctx reg */
179         rgctx_arg_reg_offset = offset;
180         offset += 8;
181
182         /*callconv in regs */
183         caller_reg_area_offset = offset;
184         reg_area_size = ALIGN_TO ((n_arg_regs + n_arg_fregs) * 8, MONO_ARCH_FRAME_ALIGNMENT);
185         offset += reg_area_size;
186
187         framesize = offset;
188
189         g_assert (framesize % MONO_ARCH_FRAME_ALIGNMENT == 0);
190         g_assert (reg_area_size % MONO_ARCH_FRAME_ALIGNMENT == 0);
191
192         /* unwind markers 1/3 */
193         cfa_offset = sizeof (gpointer);
194         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, AMD64_RSP, cfa_offset);
195         mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RIP, -cfa_offset);
196
197         /* save the old frame pointer */
198         amd64_push_reg (code, AMD64_RBP);
199
200         /* unwind markers 2/3 */
201         cfa_offset += sizeof (gpointer);
202         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
203         mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RBP, - cfa_offset);
204
205         /* set it as the new frame pointer */
206         amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));
207
208         /* unwind markers 3/3 */
209         mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, AMD64_RBP);
210         mono_add_unwind_op_fp_alloc (unwind_ops, code, buf, AMD64_RBP, 0);
211
212         /* setup the frame */
213         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
214         
215         /* save stuff */
216
217         /* save info */
218         amd64_mov_membase_reg (code, AMD64_RSP, info_offset, AMD64_RAX, sizeof (mgreg_t));
219         /* save rgctx */
220         amd64_mov_membase_reg (code, AMD64_RSP, rgctx_arg_reg_offset, MONO_ARCH_RGCTX_REG, sizeof (mgreg_t));
221
222         for (i = 0; i < n_arg_regs; ++i)
223                 amd64_mov_membase_reg (code, AMD64_RSP, caller_reg_area_offset + i * 8, param_regs [i], sizeof (mgreg_t));
224
225         for (i = 0; i < n_arg_fregs; ++i)
226                 amd64_sse_movsd_membase_reg (code, AMD64_RSP, caller_reg_area_offset + (i + n_arg_regs) * 8, i);
227
228         /* TODO Allocate stack area used to pass arguments to the method */
229
230
231         /* Allocate callee register area just below the caller area so it can be accessed from start_gsharedvt_call using negative offsets */
232         /* XXX figure out alignment */
233         callee_reg_area_offset = reg_area_size - ((n_arg_regs + n_arg_fregs) * 8); /* Ensure alignment */
234         callee_stack_area_offset = callee_reg_area_offset + reg_area_size;
235         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, reg_area_size);
236
237         /* Allocate stack area used to pass arguments to the method */
238         amd64_mov_reg_membase (code, AMD64_R11, AMD64_RAX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage), 4);
239         amd64_alu_reg_reg (code, X86_SUB, AMD64_RSP, AMD64_R11);
240
241         /* The stack now looks like this:
242
243         <caller stack params area>
244         <return address>
245         <old frame pointer>
246         <caller registers area>
247         <rgctx>
248         <gsharedvt info>
249         <callee stack area>
250         <callee reg area>
251          */
252
253         /* Call start_gsharedvt_call () */
254         /* arg1 == info */
255         amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG1, AMD64_RAX, sizeof(mgreg_t));
256         /* arg2 = caller stack area */
257         amd64_lea_membase (code, MONO_AMD64_ARG_REG2, AMD64_RBP, -(framesize - caller_reg_area_offset)); 
258
259         /* arg3 == callee stack area */
260         amd64_lea_membase (code, MONO_AMD64_ARG_REG3, AMD64_RSP, callee_reg_area_offset);
261
262         /* arg4 = mrgctx reg */
263         amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG4, MONO_ARCH_RGCTX_REG, sizeof(mgreg_t));
264
265         if (aot) {
266                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_amd64_start_gsharedvt_call");
267                 #ifdef TARGET_WIN32
268                         /* Since we are doing a call as part of setting up stackframe, the reserved shadow stack used by Windows platform is allocated up in
269                         the callee stack area but currently the callee reg area is in between. Windows calling convention dictates that room is made on stack where
270                         callee can save any parameters passed in registers. Since Windows x64 calling convention
271                         uses 4 registers for the first 4 parameters, stack needs to be adjusted before making the call.
272                         NOTE, Windows calling convention assumes that space for all registers have been reserved, regardless
273                         of the number of function parameters actually used.
274                         */
275                         int shadow_reg_size = 0;
276
277                         shadow_reg_size = ALIGN_TO (PARAM_REGS * sizeof(gpointer), MONO_ARCH_FRAME_ALIGNMENT);
278                         amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, shadow_reg_size);
279                         amd64_call_reg (code, AMD64_R11);
280                         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, shadow_reg_size);
281                 #else
282                         amd64_call_reg (code, AMD64_R11);
283                 #endif
284         } else {
285                 amd64_call_code (code, mono_amd64_start_gsharedvt_call);
286         }
287
288         /* Method to call is now on RAX. Restore regs and jump */
289         amd64_mov_reg_reg (code, AMD64_R11, AMD64_RAX, sizeof(mgreg_t));
290
291         for (i = 0; i < n_arg_regs; ++i)
292                 amd64_mov_reg_membase (code, param_regs [i], AMD64_RSP, callee_reg_area_offset + i * 8, sizeof (mgreg_t));
293
294         for (i = 0; i < n_arg_fregs; ++i)
295                 amd64_sse_movsd_reg_membase (code, i, AMD64_RSP, callee_reg_area_offset + (i + n_arg_regs) * 8);
296
297         //load rgctx
298         amd64_mov_reg_membase (code, MONO_ARCH_RGCTX_REG, AMD64_RBP, -(framesize - rgctx_arg_reg_offset), sizeof (mgreg_t));
299
300         /* Clear callee reg area */
301         amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, reg_area_size);
302
303         /* Call the thing */
304         amd64_call_reg (code, AMD64_R11);
305
306         /* Marshal return value. Available registers: R10 and R11 */
307         /* Load info struct */
308         amd64_mov_reg_membase (code, AMD64_R10, AMD64_RBP, -(framesize - info_offset), sizeof (mgreg_t));
309
310         /* Branch to the in/out handling code */
311         amd64_alu_membase_imm_size (code, X86_CMP, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in), 1, 4);
312
313         b_ret_index = 0;
314         br_out = code;
315         x86_branch32 (code, X86_CC_NE, 0, TRUE);
316
317         /*
318          * IN CASE
319          */
320
321         /* Load vret_slot */
322         /* Use first input parameter register as scratch since it is volatile on all platforms */
323         amd64_mov_reg_membase (code, MONO_AMD64_ARG_REG1, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot), 4);
324         amd64_alu_reg_imm (code, X86_SUB, MONO_AMD64_ARG_REG1, n_arg_regs + n_arg_fregs);
325         amd64_shift_reg_imm (code, X86_SHL, MONO_AMD64_ARG_REG1, 3);
326
327         /* vret address is RBP - (framesize - caller_reg_area_offset) */
328         amd64_mov_reg_reg (code, AMD64_R11, AMD64_RSP, sizeof(mgreg_t));
329         amd64_alu_reg_reg (code, X86_ADD, AMD64_R11, MONO_AMD64_ARG_REG1);
330
331         /* Load ret marshal type */
332         /* Load vret address in R11 */
333         amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
334
335         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
336                 amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
337                 br [i] = code;
338                 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
339         }
340         x86_breakpoint (code); /* unhandled case */
341
342         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
343                 mono_amd64_patch (br [i], code);
344                 switch (i) {
345                 case GSHAREDVT_RET_NONE:
346                         break;
347                 case GSHAREDVT_RET_I1:
348                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, FALSE);
349                         break;
350                 case GSHAREDVT_RET_U1:
351                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, FALSE);
352                         break;
353                 case GSHAREDVT_RET_I2:
354                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, TRUE);
355                         break;
356                 case GSHAREDVT_RET_U2:
357                         amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, TRUE);
358                         break;
359                 case GSHAREDVT_RET_I4: // CORRECT
360                 case GSHAREDVT_RET_U4: // THIS IS INCORRECT. WHY IS IT NOT FAILING?
361                         amd64_movsxd_reg_membase (code, AMD64_RAX, AMD64_R11, 0);
362                         break;
363                 case GSHAREDVT_RET_I8:
364                         amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, 0, 8);
365                         break;
366                 case GSHAREDVT_RET_IREGS_1:
367                         amd64_mov_reg_membase (code, return_regs [i - GSHAREDVT_RET_IREGS_1], AMD64_R11, 0, 8);
368                         break;
369                 case GSHAREDVT_RET_R8:
370                         amd64_sse_movsd_reg_membase (code, AMD64_XMM0, AMD64_R11, 0);
371                         break;
372                 default:
373                         x86_breakpoint (code); /* can't handle specific case */
374                 }
375
376                 br_ret [b_ret_index ++] = code;
377                 x86_jump32 (code, 0);
378         }
379
380         /*
381          * OUT CASE
382          */
383         mono_amd64_patch (br_out, code);
384
385         /*
386                 Address to write return to is in the original value of the register specified by vret_arg_reg.
387                 This will be either RSI, RDI (System V) or RCX, RDX (Windows) depending on whether this is a static call.
388                 Its location:
389                 We alloc 'framesize' bytes below RBP to save regs, info and rgctx. RSP = RBP - framesize
390                 We store RDI (System V), RCX (Windows) at RSP + caller_reg_area_offset + slot_index_of (register) * 8.
391
392                 address: RBP - framesize + caller_reg_area_offset + 8*slot
393         */
394
395         int caller_vret_offset = caller_reg_area_offset - framesize;
396
397         /* Load vret address in R11 */
398         /* Position to return to is passed as a hidden argument. Load 'vret_arg_slot' to find it */
399         amd64_movsxd_reg_membase (code, AMD64_R11, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
400
401         // In the GSHAREDVT_RET_NONE case, vret_arg_slot is -1. In this case, skip marshalling.
402         amd64_alu_reg_imm (code, X86_CMP, AMD64_R11, 0);
403         br_ret [b_ret_index ++] = code;
404         amd64_branch32 (code, X86_CC_LT, 0, TRUE);
405
406         /* Compute ret area address in the caller frame, *( ((gpointer *)RBP) [R11+2] ) */
407         amd64_shift_reg_imm (code, X86_SHL, AMD64_R11, 3);
408         amd64_alu_reg_imm (code, X86_ADD, AMD64_R11, caller_vret_offset);
409         amd64_alu_reg_reg (code, X86_ADD, AMD64_R11, AMD64_RBP);
410         amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, sizeof (gpointer));
411
412         /* Load ret marshal type in R10 */
413         amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
414
415         // Switch table for ret_marshal value
416         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
417                 amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
418                 br [i] = code;
419                 amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
420         }
421         x86_breakpoint (code); /* unhandled case */
422
423         for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
424                 mono_amd64_patch (br [i], code);
425                 switch (i) {
426                 case GSHAREDVT_RET_NONE:
427                         break;
428                 case GSHAREDVT_RET_IREGS_1:
429                         amd64_mov_membase_reg (code, AMD64_R11, 0, return_regs [i - GSHAREDVT_RET_IREGS_1], 8);
430                         break;
431                 case GSHAREDVT_RET_R8:
432                         amd64_sse_movsd_membase_reg (code, AMD64_R11, 0, AMD64_XMM0);
433                         break;
434                 default:
435                         x86_breakpoint (code); /* can't handle specific case */
436                 }
437
438                 br_ret [b_ret_index ++] = code;
439                 x86_jump32 (code, 0);
440         }
441
442         /* exit path */
443         for (i = 0; i < b_ret_index; ++i)
444                 mono_amd64_patch (br_ret [i], code);
445
446         /* Exit code path */
447 #if TARGET_WIN32
448         amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
449         amd64_pop_reg (code, AMD64_RBP);
450         mono_add_unwind_op_same_value (unwind_ops, code, buf, AMD64_RBP);
451 #else
452         amd64_leave (code);
453 #endif
454         amd64_ret (code);
455
456         g_assert ((code - buf) < buf_len);
457         g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE));
458
459         if (info)
460                 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
461
462         mono_arch_flush_icache (buf, code - buf);
463         return buf;
464 }
465
466 #else
467
468 gpointer
469 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
470 {
471         g_assert_not_reached ();
472         return NULL;
473 }
474
475 gpointer
476 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
477 {
478         g_assert_not_reached ();
479         return NULL;
480 }
481
482 #endif
483
484 #else
485
486 gpointer
487 mono_amd64_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
488 {
489         g_assert_not_reached ();
490         return NULL;
491 }
492
493 #endif