5 #include <mono/metadata/appdomain.h>
6 #include <mono/metadata/metadata-internals.h>
7 #include <mono/metadata/marshal.h>
8 #include <mono/metadata/tabledefs.h>
9 #include <mono/utils/mono-counters.h>
12 #include "debug-mini.h"
15 * Address of the trampoline code. This is used by the debugger to check
16 * whether a method is a trampoline.
18 guint8
* mono_trampoline_code
[MONO_TRAMPOLINE_NUM
];
20 static GHashTable
*class_init_hash_addr
= NULL
;
21 static GHashTable
*rgctx_lazy_fetch_trampoline_hash
= NULL
;
22 static GHashTable
*rgctx_lazy_fetch_trampoline_hash_addr
= NULL
;
24 #define mono_trampolines_lock() EnterCriticalSection (&trampolines_mutex)
25 #define mono_trampolines_unlock() LeaveCriticalSection (&trampolines_mutex)
26 static CRITICAL_SECTION trampolines_mutex
;
29 get_unbox_trampoline (MonoGenericSharingContext
*gsctx
, MonoMethod
*m
, gpointer addr
, gboolean need_rgctx_tramp
)
34 * The unbox trampolines call the method directly, so need to add
35 * an rgctx tramp before them.
37 return mono_create_static_rgctx_trampoline (m
, mono_aot_get_unbox_trampoline (m
));
39 return mono_aot_get_unbox_trampoline (m
);
41 return mono_arch_get_unbox_trampoline (gsctx
, m
, addr
);
45 #ifdef MONO_ARCH_HAVE_STATIC_RGCTX_TRAMPOLINE
53 rgctx_tramp_info_equal (gconstpointer ka
, gconstpointer kb
)
55 const RgctxTrampInfo
*i1
= ka
;
56 const RgctxTrampInfo
*i2
= kb
;
58 if (i1
->m
== i2
->m
&& i1
->addr
== i2
->addr
)
65 rgctx_tramp_info_hash (gconstpointer data
)
67 const RgctxTrampInfo
*info
= data
;
69 return GPOINTER_TO_UINT (info
->m
) ^ GPOINTER_TO_UINT (info
->addr
);
73 * mono_create_static_rgctx_trampoline:
75 * Return a static rgctx trampoline for M which branches to ADDR which should
76 * point to the compiled code of M.
78 * Static rgctx trampolines are used when a shared generic method which doesn't
79 * have a this argument is called indirectly, ie. from code which can't pass in
80 * the rgctx argument. The trampoline sets the rgctx argument and jumps to the
81 * methods code. These trampolines are similar to the unbox trampolines, they
82 * perform the same task as the static rgctx wrappers, but they are smaller/faster,
83 * and can be made to work with full AOT.
86 mono_create_static_rgctx_trampoline (MonoMethod
*m
, gpointer addr
)
91 RgctxTrampInfo tmp_info
;
94 if (mini_method_get_context (m
)->method_inst
)
95 ctx
= mono_method_lookup_rgctx (mono_class_vtable (mono_domain_get (), m
->klass
), mini_method_get_context (m
)->method_inst
);
97 ctx
= mono_class_vtable (mono_domain_get (), m
->klass
);
99 domain
= mono_domain_get ();
102 * In the AOT case, addr might point to either the method, or to an unbox trampoline,
103 * so make the hash keyed on the m+addr pair.
105 mono_domain_lock (domain
);
106 if (!domain_jit_info (domain
)->static_rgctx_trampoline_hash
)
107 domain_jit_info (domain
)->static_rgctx_trampoline_hash
= g_hash_table_new (rgctx_tramp_info_hash
, rgctx_tramp_info_equal
);
109 tmp_info
.addr
= addr
;
110 res
= g_hash_table_lookup (domain_jit_info (domain
)->static_rgctx_trampoline_hash
,
112 mono_domain_unlock (domain
);
117 res
= mono_aot_get_static_rgctx_trampoline (ctx
, addr
);
119 res
= mono_arch_get_static_rgctx_trampoline (m
, ctx
, addr
);
121 mono_domain_lock (domain
);
122 /* Duplicates inserted while we didn't hold the lock are OK */
123 info
= mono_domain_alloc (domain
, sizeof (RgctxTrampInfo
));
126 g_hash_table_insert (domain_jit_info (domain
)->static_rgctx_trampoline_hash
, info
, res
);
127 mono_domain_unlock (domain
);
133 mono_create_static_rgctx_trampoline (MonoMethod
*m
, gpointer addr
)
136 * This shouldn't happen as all arches which support generic sharing support
137 * static rgctx trampolines as well.
139 g_assert_not_reached ();
144 mono_get_vcall_slot_addr (guint8
* code
, mgreg_t
*regs
)
148 vt
= mono_arch_get_vcall_slot (code
, regs
, &displacement
);
151 return (gpointer
*)((char*)vt
+ displacement
);
154 #ifdef MONO_ARCH_HAVE_IMT
157 mono_convert_imt_slot_to_vtable_slot (gpointer
* slot
, mgreg_t
*regs
, guint8
*code
, MonoMethod
*method
, MonoMethod
**impl_method
, gboolean
*need_rgctx_tramp
)
159 MonoObject
*this_argument
= mono_arch_get_this_arg_from_call (NULL
, mono_method_signature (method
), regs
, code
);
160 MonoVTable
*vt
= this_argument
->vtable
;
161 int displacement
= slot
- ((gpointer
*)vt
);
163 if (displacement
> 0) {
164 /* slot is in the vtable, not in the IMT */
166 printf ("mono_convert_imt_slot_to_vtable_slot: slot %p is in the vtable, not in the IMT\n", slot
);
170 MonoMethod
*imt_method
= mono_arch_find_imt_method (regs
, code
);
171 int interface_offset
;
172 int imt_slot
= MONO_IMT_SIZE
+ displacement
;
174 /*This has to be variance aware since imt_method can be from an interface that vt->klass doesn't directly implement*/
175 interface_offset
= mono_class_interface_offset_with_variance (vt
->klass
, imt_method
->klass
);
177 if (interface_offset
< 0) {
178 g_print ("%s doesn't implement interface %s\n", mono_type_get_name_full (&vt
->klass
->byval_arg
, 0), mono_type_get_name_full (&imt_method
->klass
->byval_arg
, 0));
179 g_assert_not_reached ();
181 mono_vtable_build_imt_slot (vt
, mono_method_get_imt_slot (imt_method
));
186 if (imt_method
->is_inflated
&& ((MonoMethodInflated
*)imt_method
)->context
.method_inst
) {
187 MonoGenericContext context
= { NULL
, NULL
};
190 * Generic virtual method, imt_method contains the inflated interface
191 * method, need to get the inflated impl method.
193 /* imt_method->slot might not be set */
194 impl
= mono_class_get_vtable_entry (vt
->klass
, interface_offset
+ mono_method_get_declaring_generic_method (imt_method
)->slot
);
196 if (impl
->klass
->generic_class
)
197 context
.class_inst
= impl
->klass
->generic_class
->context
.class_inst
;
198 context
.method_inst
= ((MonoMethodInflated
*)imt_method
)->context
.method_inst
;
199 impl
= mono_class_inflate_generic_method (impl
, &context
);
201 impl
= mono_class_get_vtable_entry (vt
->klass
, interface_offset
+ mono_method_get_vtable_slot (imt_method
));
204 if (mono_method_needs_static_rgctx_invoke (impl
, FALSE
))
205 *need_rgctx_tramp
= TRUE
;
209 printf ("mono_convert_imt_slot_to_vtable_slot: method = %s.%s.%s, imt_method = %s.%s.%s\n",
210 method
->klass
->name_space
, method
->klass
->name
, method
->name
,
211 imt_method
->klass
->name_space
, imt_method
->klass
->name
, imt_method
->name
);
214 g_assert (imt_slot
< MONO_IMT_SIZE
);
215 if (vt
->imt_collisions_bitmap
& (1 << imt_slot
)) {
216 int slot
= mono_method_get_vtable_index (imt_method
);
218 gpointer
*vtable_slot
;
220 g_assert (slot
!= -1);
221 vtable_offset
= interface_offset
+ slot
;
222 vtable_slot
= & (vt
->vtable
[vtable_offset
]);
224 printf ("mono_convert_imt_slot_to_vtable_slot: slot %p[%d] is in the IMT, and colliding becomes %p[%d] (interface_offset = %d, method->slot = %d)\n", slot
, imt_slot
, vtable_slot
, vtable_offset
, interface_offset
, imt_method
->slot
);
229 printf ("mono_convert_imt_slot_to_vtable_slot: slot %p[%d] is in the IMT, but not colliding\n", slot
, imt_slot
);
238 * common_call_trampoline:
240 * The code to handle normal, virtual, and interface method calls and jumps, both
241 * from JITted and LLVM compiled code.
244 common_call_trampoline (mgreg_t
*regs
, guint8
*code
, gpointer arg
, guint8
* tramp
, MonoVTable
*vt
, gpointer
*vtable_slot
, gboolean need_rgctx_tramp
)
247 gboolean generic_shared
= FALSE
;
249 MonoMethod
*declaring
= NULL
;
250 MonoMethod
*generic_virtual
= NULL
;
252 gboolean proxy
= FALSE
;
253 gpointer
*orig_vtable_slot
;
254 MonoJitInfo
*ji
= NULL
;
258 orig_vtable_slot
= vtable_slot
;
260 if (m
== MONO_FAKE_VTABLE_METHOD
) {
266 ji
= mono_jit_info_table_find (mono_domain_get (), (char*)code
);
268 printf ("Caller: %s\n", mono_method_full_name (ji
->method
, TRUE
));
269 /* Print some debug info */
270 for (i
= 0; i
< 32; ++i
)
271 printf ("0x%x ", code
[-32 + i
]);
275 displacement
= (guint8
*)vtable_slot
- (guint8
*)vt
;
276 if (displacement
> 0) {
277 int slot
= (displacement
- G_STRUCT_OFFSET (MonoVTable
, vtable
)) / sizeof (gpointer
);
278 g_assert (slot
>= 0);
280 /* Avoid loading metadata or creating a generic vtable if possible */
281 addr
= mono_aot_get_method_from_vt_slot (mono_domain_get (), vt
, slot
);
283 addr
= mono_create_ftnptr (mono_domain_get (), addr
);
284 if (addr
&& !vt
->klass
->valuetype
) {
285 vtable_slot
= mono_get_vcall_slot_addr (code
, regs
);
286 if (mono_aot_is_got_entry (code
, (guint8
*)vtable_slot
) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot
)) {
287 *vtable_slot
= mono_get_addr_from_ftnptr (addr
);
293 m
= mono_class_get_vtable_entry (vt
->klass
, slot
);
294 if (mono_method_needs_static_rgctx_invoke (m
, FALSE
))
295 need_rgctx_tramp
= TRUE
;
297 /*g_print ("%s with disp %d: %s at %p\n", vt->klass->name, displacement, m->name, code);*/
299 /* We got here from an interface method: redirect to IMT handling */
300 m
= MONO_FAKE_IMT_METHOD
;
301 /*g_print ("vtable with disp %d at %p\n", displacement, code);*/
305 /* this is the IMT trampoline */
306 #ifdef MONO_ARCH_HAVE_IMT
307 if (m
== MONO_FAKE_IMT_METHOD
) {
308 MonoMethod
*impl_method
;
309 MonoObject
*this_arg
;
311 /* we get the interface method because mono_convert_imt_slot_to_vtable_slot ()
312 * needs the signature to be able to find the this argument
314 m
= mono_arch_find_imt_method (regs
, code
);
315 vtable_slot
= orig_vtable_slot
;
316 g_assert (vtable_slot
);
318 this_arg
= mono_arch_get_this_arg_from_call (NULL
, mono_method_signature (m
), regs
, code
);
320 if (this_arg
->vtable
->klass
== mono_defaults
.transparent_proxy_class
) {
321 /* Use the slow path for now */
323 m
= mono_object_get_virtual_method (this_arg
, m
);
325 vtable_slot
= mono_convert_imt_slot_to_vtable_slot (vtable_slot
, regs
, code
, m
, &impl_method
, &need_rgctx_tramp
);
326 /* mono_convert_imt_slot_to_vtable_slot () also gives us the method that is supposed
327 * to be called, so we compile it and go ahead as usual.
329 /*g_print ("imt found method %p (%s) at %p\n", impl_method, impl_method->name, code);*/
330 if (m
->is_inflated
&& ((MonoMethodInflated
*)m
)->context
.method_inst
) {
331 /* Generic virtual method */
334 need_rgctx_tramp
= TRUE
;
342 #ifdef MONO_ARCH_LLVM_SUPPORTED
343 if (!vtable_slot
&& code
&& !need_rgctx_tramp
&& mono_method_needs_static_rgctx_invoke (m
, FALSE
)) {
345 * Call this only if the called method is shared, cause it is slow/loads a lot of
348 ji
= mono_jit_info_table_find (mono_domain_get (), (char*)code
);
349 if (ji
&& ji
->from_llvm
) {
350 /* LLVM can't pass an rgctx arg */
351 need_rgctx_tramp
= TRUE
;
357 MonoGenericContext context
= { NULL
, NULL
};
358 MonoMethod
*declaring
;
361 declaring
= mono_method_get_declaring_generic_method (m
);
365 if (m
->klass
->generic_class
)
366 context
.class_inst
= m
->klass
->generic_class
->context
.class_inst
;
368 g_assert (!m
->klass
->generic_container
);
370 #ifdef MONO_ARCH_HAVE_IMT
371 generic_virtual
= mono_arch_find_imt_method (regs
, code
);
373 if (generic_virtual
) {
374 g_assert (generic_virtual
->is_inflated
);
375 context
.method_inst
= ((MonoMethodInflated
*)generic_virtual
)->context
.method_inst
;
378 m
= mono_class_inflate_generic_method (declaring
, &context
);
379 /* FIXME: only do this if the method is sharable */
380 need_rgctx_tramp
= TRUE
;
381 } else if ((context_used
= mono_method_check_context_used (m
))) {
382 MonoClass
*klass
= NULL
;
383 MonoMethod
*actual_method
= NULL
;
384 MonoVTable
*vt
= NULL
;
385 MonoGenericInst
*method_inst
= NULL
;
388 generic_shared
= TRUE
;
392 if (m
->is_inflated
&& mono_method_get_context (m
)->method_inst
) {
393 #ifdef MONO_ARCH_RGCTX_REG
394 MonoMethodRuntimeGenericContext
*mrgctx
= (MonoMethodRuntimeGenericContext
*)mono_arch_find_static_call_vtable (regs
, code
);
396 klass
= mrgctx
->class_vtable
->klass
;
397 method_inst
= mrgctx
->method_inst
;
399 g_assert_not_reached ();
401 } else if ((m
->flags
& METHOD_ATTRIBUTE_STATIC
) || m
->klass
->valuetype
) {
402 #ifdef MONO_ARCH_RGCTX_REG
403 MonoVTable
*vtable
= mono_arch_find_static_call_vtable (regs
, code
);
405 klass
= vtable
->klass
;
407 g_assert_not_reached ();
410 #ifdef MONO_ARCH_HAVE_IMT
411 MonoObject
*this_argument
= mono_arch_get_this_arg_from_call (NULL
, mono_method_signature (m
), regs
, code
);
413 vt
= this_argument
->vtable
;
414 vtable_slot
= orig_vtable_slot
;
416 g_assert (this_argument
->vtable
->klass
->inited
);
417 //mono_class_init (this_argument->vtable->klass);
420 klass
= this_argument
->vtable
->klass
->supertypes
[m
->klass
->idepth
- 1];
426 g_assert (vtable_slot
|| klass
);
429 int displacement
= vtable_slot
- ((gpointer
*)vt
);
431 g_assert_not_reached ();
433 g_assert (displacement
> 0);
435 actual_method
= vt
->klass
->vtable
[displacement
];
439 MonoGenericContext context
= { NULL
, NULL
};
442 declaring
= mono_method_get_declaring_generic_method (m
);
446 if (klass
->generic_class
)
447 context
.class_inst
= klass
->generic_class
->context
.class_inst
;
448 else if (klass
->generic_container
)
449 context
.class_inst
= klass
->generic_container
->context
.class_inst
;
450 context
.method_inst
= method_inst
;
452 actual_method
= mono_class_inflate_generic_method (declaring
, &context
);
454 actual_method
= mono_class_get_method_generic (klass
, m
);
458 g_assert (actual_method
);
459 g_assert (actual_method
->klass
== klass
);
461 if (actual_method
->is_inflated
)
462 declaring
= mono_method_get_declaring_generic_method (actual_method
);
469 if (m
->iflags
& METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED
) {
473 ji
= mono_jit_info_table_find (mono_domain_get (), (char*)code
);
477 /* Avoid recursion */
478 if (!(ji
&& ji
->method
->wrapper_type
== MONO_WRAPPER_SYNCHRONIZED
))
479 m
= mono_marshal_get_synchronized_wrapper (m
);
482 /* Calls made through delegates on platforms without delegate trampolines */
483 if (!code
&& mono_method_needs_static_rgctx_invoke (m
, FALSE
))
484 need_rgctx_tramp
= TRUE
;
486 addr
= mono_compile_method (m
);
489 mono_debugger_trampoline_compiled (code
, m
, addr
);
491 if (need_rgctx_tramp
)
492 addr
= mono_create_static_rgctx_trampoline (m
, addr
);
494 if (generic_virtual
) {
495 vtable_slot
= orig_vtable_slot
;
496 g_assert (vtable_slot
);
498 if (vt
->klass
->valuetype
)
499 addr
= get_unbox_trampoline (mono_get_generic_context_from_code (code
), m
, addr
, need_rgctx_tramp
);
501 mono_method_add_generic_virtual_invocation (mono_domain_get (),
503 generic_virtual
, addr
);
508 /* the method was jumped to */
510 MonoDomain
*domain
= mono_domain_get ();
512 /* Patch the got entries pointing to this method */
514 * We do this here instead of in mono_codegen () to cover the case when m
515 * was loaded from an aot image.
517 if (domain_jit_info (domain
)->jump_target_got_slot_hash
) {
520 mono_domain_lock (domain
);
521 list
= g_hash_table_lookup (domain_jit_info (domain
)->jump_target_got_slot_hash
, m
);
523 for (tmp
= list
; tmp
; tmp
= tmp
->next
) {
524 gpointer
*got_slot
= tmp
->data
;
527 g_hash_table_remove (domain_jit_info (domain
)->jump_target_got_slot_hash
, m
);
530 mono_domain_unlock (domain
);
536 vtable_slot
= orig_vtable_slot
;
539 if (m
->klass
->valuetype
)
540 addr
= get_unbox_trampoline (mono_get_generic_context_from_code (code
), m
, addr
, need_rgctx_tramp
);
541 g_assert (*vtable_slot
);
543 if (!proxy
&& (mono_aot_is_got_entry (code
, (guint8
*)vtable_slot
) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot
))) {
544 #ifdef MONO_ARCH_HAVE_IMT
545 vtable_slot
= mono_convert_imt_slot_to_vtable_slot (vtable_slot
, regs
, code
, m
, NULL
, &need_rgctx_tramp
);
547 *vtable_slot
= mono_get_addr_from_ftnptr (addr
);
551 guint8
*plt_entry
= mono_aot_get_plt_entry (code
);
554 mono_arch_patch_plt_entry (plt_entry
, NULL
, regs
, addr
);
555 } else if (!generic_shared
|| (m
->iflags
& METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
) ||
556 mono_domain_lookup_shared_generic (mono_domain_get (), declaring
)) {
557 if (generic_shared
) {
558 if (m
->wrapper_type
!= MONO_WRAPPER_NONE
)
559 m
= mono_marshal_method_from_wrapper (m
);
560 g_assert (mono_method_is_generic_sharable_impl (m
, FALSE
));
563 /* Patch calling code */
567 MonoJitInfo
*target_ji
=
568 mono_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (addr
));
571 ji
= mono_jit_info_table_find (mono_domain_get (), (char*)code
);
573 if (mono_method_same_domain (ji
, target_ji
))
574 mono_arch_patch_callsite (ji
->code_start
, code
, addr
);
583 * mono_magic_trampoline:
585 * This trampoline handles calls from JITted code.
588 mono_magic_trampoline (mgreg_t
*regs
, guint8
*code
, gpointer arg
, guint8
* tramp
)
590 gpointer
*vtable_slot
;
594 if (code
&& !mono_use_llvm
)
595 vt
= mono_arch_get_vcall_slot (code
, regs
, &displacement
);
599 vtable_slot
= (gpointer
*)((char*)vt
+ displacement
);
603 return common_call_trampoline (regs
, code
, arg
, tramp
, vt
, vtable_slot
, FALSE
);
606 #ifdef MONO_ARCH_LLVM_SUPPORTED
608 * mono_llvm_vcall_trampoline:
610 * This trampoline handles virtual calls when using LLVM.
613 mono_llvm_vcall_trampoline (mgreg_t
*regs
, guint8
*code
, MonoMethod
*m
, guint8
*tramp
)
617 gpointer
*vtable_slot
;
621 * We have the method which is called, we need to obtain the vtable slot without
622 * disassembly which is impossible with LLVM.
623 * So we use the this argument.
625 this = mono_arch_get_this_arg_from_call (NULL
, mono_method_signature (m
), regs
, code
);
628 slot
= mono_method_get_vtable_slot (m
);
630 g_assert (slot
!= -1);
632 g_assert (this->vtable
->klass
->vtable
[slot
] == m
);
636 g_assert (!m
->is_generic
);
638 vtable_slot
= &(vt
->vtable
[slot
]);
640 return common_call_trampoline (regs
, code
, m
, tramp
, vt
, vtable_slot
, mono_method_needs_static_rgctx_invoke (m
, 0));
645 mono_generic_virtual_remoting_trampoline (mgreg_t
*regs
, guint8
*code
, MonoMethod
*m
, guint8
*tramp
)
647 MonoGenericContext context
= { NULL
, NULL
};
648 MonoMethod
*imt_method
, *declaring
;
651 g_assert (m
->is_generic
);
654 declaring
= mono_method_get_declaring_generic_method (m
);
658 if (m
->klass
->generic_class
)
659 context
.class_inst
= m
->klass
->generic_class
->context
.class_inst
;
661 g_assert (!m
->klass
->generic_container
);
663 #ifdef MONO_ARCH_HAVE_IMT
664 imt_method
= mono_arch_find_imt_method (regs
, code
);
665 if (imt_method
->is_inflated
)
666 context
.method_inst
= ((MonoMethodInflated
*)imt_method
)->context
.method_inst
;
668 m
= mono_class_inflate_generic_method (declaring
, &context
);
669 m
= mono_marshal_get_remoting_invoke_with_check (m
);
671 addr
= mono_compile_method (m
);
674 mono_debugger_trampoline_compiled (NULL
, m
, addr
);
680 * mono_aot_trampoline:
682 * This trampoline handles calls made from AOT code. We try to bypass the
683 * normal JIT compilation logic to avoid loading the metadata for the method.
685 #ifdef MONO_ARCH_AOT_SUPPORTED
687 mono_aot_trampoline (mgreg_t
*regs
, guint8
*code
, guint8
*token_info
,
692 MonoMethod
*method
= NULL
;
694 gpointer
*vtable_slot
;
695 gboolean is_got_entry
;
697 gboolean need_rgctx_tramp
= FALSE
;
699 image
= *(gpointer
*)(gpointer
)token_info
;
700 token_info
+= sizeof (gpointer
);
701 token
= *(guint32
*)(gpointer
)token_info
;
703 addr
= mono_aot_get_method_from_token (mono_domain_get (), image
, token
);
705 method
= mono_get_method (image
, token
, NULL
);
708 /* Use the generic code */
709 return mono_magic_trampoline (regs
, code
, method
, tramp
);
712 addr
= mono_create_ftnptr (mono_domain_get (), addr
);
714 vtable_slot
= mono_get_vcall_slot_addr (code
, regs
);
715 g_assert (!vtable_slot
);
717 /* This is a normal call through a PLT entry */
718 plt_entry
= mono_aot_get_plt_entry (code
);
719 g_assert (plt_entry
);
721 mono_arch_patch_plt_entry (plt_entry
, NULL
, regs
, addr
);
723 is_got_entry
= FALSE
;
726 * Since AOT code is only used in the root domain,
727 * mono_domain_get () != mono_get_root_domain () means the calling method
728 * is AppDomain:InvokeInDomain, so this is the same check as in
729 * mono_method_same_domain () but without loading the metadata for the method.
731 if ((is_got_entry
&& (mono_domain_get () == mono_get_root_domain ())) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot
)) {
732 #ifdef MONO_ARCH_HAVE_IMT
734 method
= mono_get_method (image
, token
, NULL
);
735 vtable_slot
= mono_convert_imt_slot_to_vtable_slot (vtable_slot
, regs
, code
, method
, NULL
, &need_rgctx_tramp
);
744 * mono_aot_plt_trampoline:
746 * This trampoline handles calls made from AOT code through the PLT table.
749 mono_aot_plt_trampoline (mgreg_t
*regs
, guint8
*code
, guint8
*aot_module
,
752 guint32 plt_info_offset
= mono_aot_get_plt_info_offset (regs
, code
);
754 return mono_aot_plt_resolve (aot_module
, plt_info_offset
, code
);
759 * mono_class_init_trampoline:
761 * This method calls mono_runtime_class_init () to run the static constructor
762 * for the type, then patches the caller code so it is not called again.
765 mono_class_init_trampoline (mgreg_t
*regs
, guint8
*code
, MonoVTable
*vtable
, guint8
*tramp
)
767 guint8
*plt_entry
= mono_aot_get_plt_entry (code
);
769 mono_runtime_class_init (vtable
);
772 mono_arch_nullify_plt_entry (plt_entry
, regs
);
774 mono_arch_nullify_class_init_trampoline (code
, regs
);
779 * mono_generic_class_init_trampoline:
781 * This method calls mono_runtime_class_init () to run the static constructor
785 mono_generic_class_init_trampoline (mgreg_t
*regs
, guint8
*code
, MonoVTable
*vtable
, guint8
*tramp
)
787 mono_runtime_class_init (vtable
);
791 mono_rgctx_lazy_fetch_trampoline (mgreg_t
*regs
, guint8
*code
, gpointer data
, guint8
*tramp
)
793 #ifdef MONO_ARCH_VTABLE_REG
794 static gboolean inited
= FALSE
;
795 static int num_lookups
= 0;
796 guint32 slot
= GPOINTER_TO_UINT (data
);
797 mgreg_t
*r
= (mgreg_t
*)regs
;
798 gpointer arg
= (gpointer
)(gssize
)r
[MONO_ARCH_VTABLE_REG
];
799 guint32 index
= MONO_RGCTX_SLOT_INDEX (slot
);
800 gboolean mrgctx
= MONO_RGCTX_SLOT_IS_MRGCTX (slot
);
803 mono_counters_register ("RGCTX unmanaged lookups", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &num_lookups
);
810 return mono_method_fill_runtime_generic_context (arg
, index
);
812 return mono_class_fill_runtime_generic_context (arg
, index
);
814 g_assert_not_reached ();
819 mono_monitor_enter_trampoline (mgreg_t
*regs
, guint8
*code
, MonoObject
*obj
, guint8
*tramp
)
821 mono_monitor_enter (obj
);
825 mono_monitor_exit_trampoline (mgreg_t
*regs
, guint8
*code
, MonoObject
*obj
, guint8
*tramp
)
827 mono_monitor_exit (obj
);
830 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
833 * mono_delegate_trampoline:
835 * This trampoline handles calls made to Delegate:Invoke ().
836 * This is called once the first time a delegate is invoked, so it must be fast.
839 mono_delegate_trampoline (mgreg_t
*regs
, guint8
*code
, gpointer
*tramp_data
, guint8
* tramp
)
841 MonoDomain
*domain
= mono_domain_get ();
842 MonoDelegate
*delegate
;
845 MonoMethod
*method
= NULL
;
846 gboolean multicast
, callvirt
;
847 gboolean need_rgctx_tramp
= FALSE
;
848 MonoMethod
*invoke
= tramp_data
[0];
849 guint8
*impl_this
= tramp_data
[1];
850 guint8
*impl_nothis
= tramp_data
[2];
852 /* Obtain the delegate object according to the calling convention */
855 * Avoid calling mono_get_generic_context_from_code () now since it is expensive,
856 * get_this_arg_from_call will call it if needed.
858 delegate
= mono_arch_get_this_arg_from_call (NULL
, mono_method_signature (invoke
), regs
, code
);
860 if (delegate
->method
) {
861 method
= delegate
->method
;
864 * delegate->method_ptr == NULL means the delegate was initialized by
865 * mini_delegate_ctor, while != NULL means it is initialized by
866 * mono_delegate_ctor_with_method (). In both cases, we need to add wrappers
867 * (ctor_with_method () does this, but it doesn't store the wrapper back into
870 if (delegate
->target
&& delegate
->target
->vtable
->klass
== mono_defaults
.transparent_proxy_class
) {
872 if (((MonoTransparentProxy
*)delegate
->target
)->remote_class
->proxy_class
!= mono_defaults
.com_object_class
&&
873 !((MonoTransparentProxy
*)delegate
->target
)->remote_class
->proxy_class
->is_com_object
)
875 method
= mono_marshal_get_remoting_invoke (method
);
877 else if (mono_method_signature (method
)->hasthis
&& method
->klass
->valuetype
)
878 method
= mono_marshal_get_unbox_wrapper (method
);
880 ji
= mono_jit_info_table_find (domain
, mono_get_addr_from_ftnptr (delegate
->method_ptr
));
884 callvirt
= !delegate
->target
&& method
&& mono_method_signature (method
)->hasthis
;
886 if (method
&& method
->iflags
& METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED
)
887 method
= mono_marshal_get_synchronized_wrapper (method
);
889 if (method
&& mono_method_needs_static_rgctx_invoke (method
, FALSE
))
890 need_rgctx_tramp
= TRUE
;
893 * If the called address is a trampoline, replace it with the compiled method so
894 * further calls don't have to go through the trampoline.
896 if (method
&& !callvirt
) {
897 /* Avoid the overhead of looking up an already compiled method if possible */
898 if (delegate
->method_code
&& *delegate
->method_code
) {
899 delegate
->method_ptr
= *delegate
->method_code
;
901 delegate
->method_ptr
= mono_compile_method (method
);
902 if (need_rgctx_tramp
)
903 delegate
->method_ptr
= mono_create_static_rgctx_trampoline (method
, delegate
->method_ptr
);
904 if (delegate
->method_code
)
905 *delegate
->method_code
= delegate
->method_ptr
;
906 mono_debugger_trampoline_compiled (NULL
, method
, delegate
->method_ptr
);
909 if (need_rgctx_tramp
)
910 delegate
->method_ptr
= mono_create_static_rgctx_trampoline (method
, delegate
->method_ptr
);
913 multicast
= ((MonoMulticastDelegate
*)delegate
)->prev
!= NULL
;
914 if (!multicast
&& !callvirt
) {
915 if (method
&& (method
->flags
& METHOD_ATTRIBUTE_STATIC
) && mono_method_signature (method
)->param_count
== mono_method_signature (invoke
)->param_count
+ 1)
916 /* Closed static delegate */
919 code
= delegate
->target
? impl_this
: impl_nothis
;
922 delegate
->invoke_impl
= mono_get_addr_from_ftnptr (code
);
927 /* The general, unoptimized case */
928 m
= mono_marshal_get_delegate_invoke (invoke
, delegate
);
929 code
= mono_compile_method (m
);
930 delegate
->invoke_impl
= mono_get_addr_from_ftnptr (code
);
931 mono_debugger_trampoline_compiled (NULL
, m
, delegate
->invoke_impl
);
939 * mono_get_trampoline_func:
941 * Return the C function which needs to be called by the generic trampoline of type
945 mono_get_trampoline_func (MonoTrampolineType tramp_type
)
947 switch (tramp_type
) {
948 case MONO_TRAMPOLINE_JIT
:
949 case MONO_TRAMPOLINE_JUMP
:
950 return mono_magic_trampoline
;
951 case MONO_TRAMPOLINE_CLASS_INIT
:
952 return mono_class_init_trampoline
;
953 case MONO_TRAMPOLINE_GENERIC_CLASS_INIT
:
954 return mono_generic_class_init_trampoline
;
955 case MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
:
956 return mono_rgctx_lazy_fetch_trampoline
;
957 #ifdef MONO_ARCH_AOT_SUPPORTED
958 case MONO_TRAMPOLINE_AOT
:
959 return mono_aot_trampoline
;
960 case MONO_TRAMPOLINE_AOT_PLT
:
961 return mono_aot_plt_trampoline
;
963 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
964 case MONO_TRAMPOLINE_DELEGATE
:
965 return mono_delegate_trampoline
;
967 case MONO_TRAMPOLINE_RESTORE_STACK_PROT
:
968 return mono_altstack_restore_prot
;
969 case MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING
:
970 return mono_generic_virtual_remoting_trampoline
;
971 case MONO_TRAMPOLINE_MONITOR_ENTER
:
972 return mono_monitor_enter_trampoline
;
973 case MONO_TRAMPOLINE_MONITOR_EXIT
:
974 return mono_monitor_exit_trampoline
;
975 #ifdef MONO_ARCH_LLVM_SUPPORTED
976 case MONO_TRAMPOLINE_LLVM_VCALL
:
977 return mono_llvm_vcall_trampoline
;
980 g_assert_not_reached ();
986 mono_trampolines_init (void)
988 InitializeCriticalSection (&trampolines_mutex
);
993 mono_trampoline_code
[MONO_TRAMPOLINE_JIT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_JIT
);
994 mono_trampoline_code
[MONO_TRAMPOLINE_JUMP
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_JUMP
);
995 mono_trampoline_code
[MONO_TRAMPOLINE_CLASS_INIT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT
);
996 mono_trampoline_code
[MONO_TRAMPOLINE_GENERIC_CLASS_INIT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_GENERIC_CLASS_INIT
);
997 mono_trampoline_code
[MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
);
998 #ifdef MONO_ARCH_AOT_SUPPORTED
999 mono_trampoline_code
[MONO_TRAMPOLINE_AOT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_AOT
);
1000 mono_trampoline_code
[MONO_TRAMPOLINE_AOT_PLT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_AOT_PLT
);
1002 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
1003 mono_trampoline_code
[MONO_TRAMPOLINE_DELEGATE
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_DELEGATE
);
1005 mono_trampoline_code
[MONO_TRAMPOLINE_RESTORE_STACK_PROT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_RESTORE_STACK_PROT
);
1006 mono_trampoline_code
[MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING
);
1007 mono_trampoline_code
[MONO_TRAMPOLINE_MONITOR_ENTER
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_MONITOR_ENTER
);
1008 mono_trampoline_code
[MONO_TRAMPOLINE_MONITOR_EXIT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_MONITOR_EXIT
);
1009 #ifdef MONO_ARCH_LLVM_SUPPORTED
1010 mono_trampoline_code
[MONO_TRAMPOLINE_LLVM_VCALL
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_LLVM_VCALL
);
1015 mono_trampolines_cleanup (void)
1017 if (class_init_hash_addr
)
1018 g_hash_table_destroy (class_init_hash_addr
);
1020 DeleteCriticalSection (&trampolines_mutex
);
1024 mono_get_trampoline_code (MonoTrampolineType tramp_type
)
1026 g_assert (mono_trampoline_code
[tramp_type
]);
1028 return mono_trampoline_code
[tramp_type
];
1032 mono_create_specific_trampoline (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, guint32
*code_len
)
1035 return mono_aot_create_specific_trampoline (mono_defaults
.corlib
, arg1
, tramp_type
, domain
, code_len
);
1037 return mono_arch_create_specific_trampoline (arg1
, tramp_type
, domain
, code_len
);
1041 mono_create_class_init_trampoline (MonoVTable
*vtable
)
1044 MonoDomain
*domain
= vtable
->domain
;
1046 g_assert (!vtable
->klass
->generic_container
);
1048 /* previously created trampoline code */
1049 mono_domain_lock (domain
);
1051 g_hash_table_lookup (domain_jit_info (domain
)->class_init_trampoline_hash
,
1053 mono_domain_unlock (domain
);
1057 code
= mono_create_specific_trampoline (vtable
, MONO_TRAMPOLINE_CLASS_INIT
, domain
, NULL
);
1059 ptr
= mono_create_ftnptr (domain
, code
);
1061 /* store trampoline address */
1062 mono_domain_lock (domain
);
1063 g_hash_table_insert (domain_jit_info (domain
)->class_init_trampoline_hash
,
1065 mono_domain_unlock (domain
);
1067 mono_trampolines_lock ();
1068 if (!class_init_hash_addr
)
1069 class_init_hash_addr
= g_hash_table_new (NULL
, NULL
);
1070 g_hash_table_insert (class_init_hash_addr
, ptr
, vtable
);
1071 mono_trampolines_unlock ();
1077 mono_create_generic_class_init_trampoline (void)
1079 #ifdef MONO_ARCH_VTABLE_REG
1080 static gpointer code
;
1082 mono_trampolines_lock ();
1086 code
= mono_aot_get_named_code ("generic_class_init_trampoline");
1088 code
= mono_arch_create_generic_class_init_trampoline ();
1091 mono_trampolines_unlock ();
1095 g_assert_not_reached ();
1100 mono_create_jump_trampoline (MonoDomain
*domain
, MonoMethod
*method
, gboolean add_sync_wrapper
)
1104 guint32 code_size
= 0;
1106 code
= mono_jit_find_compiled_method_with_jit_info (domain
, method
, &ji
);
1108 * We cannot recover the correct type of a shared generic
1109 * method from its native code address, so we use the
1110 * trampoline instead.
1112 if (code
&& !ji
->has_generic_jit_info
)
1115 mono_domain_lock (domain
);
1116 code
= g_hash_table_lookup (domain_jit_info (domain
)->jump_trampoline_hash
, method
);
1117 mono_domain_unlock (domain
);
1121 code
= mono_create_specific_trampoline (method
, MONO_TRAMPOLINE_JUMP
, mono_domain_get (), &code_size
);
1122 g_assert (code_size
);
1124 ji
= mono_domain_alloc0 (domain
, MONO_SIZEOF_JIT_INFO
);
1125 ji
->code_start
= code
;
1126 ji
->code_size
= code_size
;
1127 ji
->method
= method
;
1130 * mono_delegate_ctor needs to find the method metadata from the
1131 * trampoline address, so we save it here.
1134 mono_jit_info_table_add (domain
, ji
);
1136 mono_domain_lock (domain
);
1137 g_hash_table_insert (domain_jit_info (domain
)->jump_trampoline_hash
, method
, ji
->code_start
);
1138 mono_domain_unlock (domain
);
1140 return ji
->code_start
;
1144 mono_create_jit_trampoline_in_domain (MonoDomain
*domain
, MonoMethod
*method
)
1148 if (mono_aot_only
) {
1149 /* Avoid creating trampolines if possible */
1150 gpointer code
= mono_jit_find_compiled_method (domain
, method
);
1156 mono_domain_lock (domain
);
1157 tramp
= g_hash_table_lookup (domain_jit_info (domain
)->jit_trampoline_hash
, method
);
1158 mono_domain_unlock (domain
);
1162 tramp
= mono_create_specific_trampoline (method
, MONO_TRAMPOLINE_JIT
, domain
, NULL
);
1164 mono_domain_lock (domain
);
1165 g_hash_table_insert (domain_jit_info (domain
)->jit_trampoline_hash
, method
, tramp
);
1166 mono_domain_unlock (domain
);
1168 mono_jit_stats
.method_trampolines
++;
1174 mono_create_jit_trampoline (MonoMethod
*method
)
1176 return mono_create_jit_trampoline_in_domain (mono_domain_get (), method
);
1180 mono_create_jit_trampoline_from_token (MonoImage
*image
, guint32 token
)
1184 MonoDomain
*domain
= mono_domain_get ();
1185 guint8
*buf
, *start
;
1187 buf
= start
= mono_domain_code_reserve (domain
, 2 * sizeof (gpointer
));
1189 *(gpointer
*)(gpointer
)buf
= image
;
1190 buf
+= sizeof (gpointer
);
1191 *(guint32
*)(gpointer
)buf
= token
;
1193 tramp
= mono_create_specific_trampoline (start
, MONO_TRAMPOLINE_AOT
, domain
, NULL
);
1195 mono_jit_stats
.method_trampolines
++;
1201 mono_create_delegate_trampoline (MonoClass
*klass
)
1203 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
1204 MonoDomain
*domain
= mono_domain_get ();
1206 guint32 code_size
= 0;
1207 gpointer
*tramp_data
;
1210 mono_domain_lock (domain
);
1211 ptr
= g_hash_table_lookup (domain_jit_info (domain
)->delegate_trampoline_hash
, klass
);
1212 mono_domain_unlock (domain
);
1216 // Precompute the delegate invoke impl and pass it to the delegate trampoline
1217 invoke
= mono_get_delegate_invoke (klass
);
1220 tramp_data
= mono_domain_alloc (domain
, sizeof (gpointer
) * 3);
1221 tramp_data
[0] = invoke
;
1222 tramp_data
[1] = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke
), TRUE
);
1223 tramp_data
[2] = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke
), FALSE
);
1225 ptr
= mono_create_specific_trampoline (tramp_data
, MONO_TRAMPOLINE_DELEGATE
, mono_domain_get (), &code_size
);
1226 g_assert (code_size
);
1228 /* store trampoline address */
1229 mono_domain_lock (domain
);
1230 g_hash_table_insert (domain_jit_info (domain
)->delegate_trampoline_hash
,
1232 mono_domain_unlock (domain
);
1241 mono_create_rgctx_lazy_fetch_trampoline (guint32 offset
)
1243 static gboolean inited
= FALSE
;
1244 static int num_trampolines
= 0;
1246 gpointer tramp
, ptr
;
1249 return mono_aot_get_lazy_fetch_trampoline (offset
);
1251 mono_trampolines_lock ();
1252 if (rgctx_lazy_fetch_trampoline_hash
)
1253 tramp
= g_hash_table_lookup (rgctx_lazy_fetch_trampoline_hash
, GUINT_TO_POINTER (offset
));
1256 mono_trampolines_unlock ();
1260 tramp
= mono_arch_create_rgctx_lazy_fetch_trampoline (offset
);
1261 ptr
= mono_create_ftnptr (mono_get_root_domain (), tramp
);
1263 mono_trampolines_lock ();
1264 if (!rgctx_lazy_fetch_trampoline_hash
) {
1265 rgctx_lazy_fetch_trampoline_hash
= g_hash_table_new (NULL
, NULL
);
1266 rgctx_lazy_fetch_trampoline_hash_addr
= g_hash_table_new (NULL
, NULL
);
1268 g_hash_table_insert (rgctx_lazy_fetch_trampoline_hash
, GUINT_TO_POINTER (offset
), ptr
);
1269 g_assert (offset
!= -1);
1270 g_hash_table_insert (rgctx_lazy_fetch_trampoline_hash_addr
, ptr
, GUINT_TO_POINTER (offset
+ 1));
1271 mono_trampolines_unlock ();
1274 mono_counters_register ("RGCTX num lazy fetch trampolines",
1275 MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &num_trampolines
);
1284 mono_create_monitor_enter_trampoline (void)
1286 static gpointer code
;
1288 if (mono_aot_only
) {
1290 code
= mono_aot_get_named_code ("monitor_enter_trampoline");
1294 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
1295 mono_trampolines_lock ();
1298 code
= mono_arch_create_monitor_enter_trampoline ();
1300 mono_trampolines_unlock ();
1303 g_assert_not_reached ();
1310 mono_create_monitor_exit_trampoline (void)
1312 static gpointer code
;
1314 if (mono_aot_only
) {
1316 code
= mono_aot_get_named_code ("monitor_exit_trampoline");
1320 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
1321 mono_trampolines_lock ();
1324 code
= mono_arch_create_monitor_exit_trampoline ();
1326 mono_trampolines_unlock ();
1329 g_assert_not_reached ();
1334 #ifdef MONO_ARCH_LLVM_SUPPORTED
1336 * mono_create_llvm_vcall_trampoline:
1338 * LLVM emits code for virtual calls which mono_get_vcall_slot is unable to
1339 * decode, i.e. only the final branch address is available:
1340 * mov <offset>(%rax), %rax
1341 * <random code inserted by instruction scheduling>
1344 * To work around this problem, we don't use the common vtable trampoline when
1345 * llvm is enabled. Instead, we use one trampoline per method.
1348 mono_create_llvm_vcall_trampoline (MonoMethod
*method
)
1353 domain
= mono_domain_get ();
1355 mono_domain_lock (domain
);
1356 res
= g_hash_table_lookup (domain_jit_info (domain
)->llvm_vcall_trampoline_hash
, method
);
1357 mono_domain_unlock (domain
);
1361 res
= mono_create_specific_trampoline (method
, MONO_TRAMPOLINE_LLVM_VCALL
, domain
, NULL
);
1363 mono_domain_lock (domain
);
1364 g_hash_table_insert (domain_jit_info (domain
)->llvm_vcall_trampoline_hash
, method
, res
);
1365 mono_domain_unlock (domain
);
1371 * mono_create_llvm_imt_trampoline:
1373 * LLVM compiled code can't pass in the IMT argument, so we use this trampoline, which
1374 * sets the IMT argument, then branches to the contents of the vtable slot given by
1375 * vt_offset in the vtable which is obtained from the argument list.
1378 mono_create_llvm_imt_trampoline (MonoDomain
*domain
, MonoMethod
*m
, int vt_offset
)
1380 #ifdef MONO_ARCH_HAVE_LLVM_IMT_TRAMPOLINE
1381 return mono_arch_get_llvm_imt_trampoline (domain
, m
, vt_offset
);
1383 g_assert_not_reached ();
1390 mono_find_class_init_trampoline_by_addr (gconstpointer addr
)
1394 mono_trampolines_lock ();
1395 if (class_init_hash_addr
)
1396 res
= g_hash_table_lookup (class_init_hash_addr
, addr
);
1399 mono_trampolines_unlock ();
1404 mono_find_rgctx_lazy_fetch_trampoline_by_addr (gconstpointer addr
)
1408 mono_trampolines_lock ();
1409 if (rgctx_lazy_fetch_trampoline_hash_addr
) {
1410 /* We store the real offset + 1 so we can detect when the lookup fails */
1411 offset
= GPOINTER_TO_INT (g_hash_table_lookup (rgctx_lazy_fetch_trampoline_hash_addr
, addr
));
1419 mono_trampolines_unlock ();