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>
10 #include <mono/utils/mono-error-internals.h>
13 #include "debug-mini.h"
16 * Address of the trampoline code. This is used by the debugger to check
17 * whether a method is a trampoline.
19 guint8
* mono_trampoline_code
[MONO_TRAMPOLINE_NUM
];
21 static GHashTable
*class_init_hash_addr
= NULL
;
22 static GHashTable
*rgctx_lazy_fetch_trampoline_hash
= NULL
;
23 static GHashTable
*rgctx_lazy_fetch_trampoline_hash_addr
= NULL
;
24 static guint32 trampoline_calls
;
26 #define mono_trampolines_lock() EnterCriticalSection (&trampolines_mutex)
27 #define mono_trampolines_unlock() LeaveCriticalSection (&trampolines_mutex)
28 static CRITICAL_SECTION trampolines_mutex
;
31 get_unbox_trampoline (MonoGenericSharingContext
*gsctx
, MonoMethod
*m
, gpointer addr
, gboolean need_rgctx_tramp
)
36 * The unbox trampolines call the method directly, so need to add
37 * an rgctx tramp before them.
39 return mono_create_static_rgctx_trampoline (m
, mono_aot_get_unbox_trampoline (m
));
41 return mono_aot_get_unbox_trampoline (m
);
43 return mono_arch_get_unbox_trampoline (gsctx
, m
, addr
);
47 #ifdef MONO_ARCH_HAVE_STATIC_RGCTX_TRAMPOLINE
55 rgctx_tramp_info_equal (gconstpointer ka
, gconstpointer kb
)
57 const RgctxTrampInfo
*i1
= ka
;
58 const RgctxTrampInfo
*i2
= kb
;
60 if (i1
->m
== i2
->m
&& i1
->addr
== i2
->addr
)
67 rgctx_tramp_info_hash (gconstpointer data
)
69 const RgctxTrampInfo
*info
= data
;
71 return GPOINTER_TO_UINT (info
->m
) ^ GPOINTER_TO_UINT (info
->addr
);
75 * mono_create_static_rgctx_trampoline:
77 * Return a static rgctx trampoline for M which branches to ADDR which should
78 * point to the compiled code of M.
80 * Static rgctx trampolines are used when a shared generic method which doesn't
81 * have a this argument is called indirectly, ie. from code which can't pass in
82 * the rgctx argument. The trampoline sets the rgctx argument and jumps to the
83 * methods code. These trampolines are similar to the unbox trampolines, they
84 * perform the same task as the static rgctx wrappers, but they are smaller/faster,
85 * and can be made to work with full AOT.
86 * On PPC addr should be an ftnptr and the return value is an ftnptr too.
89 mono_create_static_rgctx_trampoline (MonoMethod
*m
, gpointer addr
)
94 RgctxTrampInfo tmp_info
;
97 #ifdef PPC_USES_FUNCTION_DESCRIPTOR
98 g_assert (((gpointer
*)addr
) [2] == 0);
101 if (mini_method_get_context (m
)->method_inst
)
102 ctx
= mono_method_lookup_rgctx (mono_class_vtable (mono_domain_get (), m
->klass
), mini_method_get_context (m
)->method_inst
);
104 ctx
= mono_class_vtable (mono_domain_get (), m
->klass
);
106 domain
= mono_domain_get ();
109 * In the AOT case, addr might point to either the method, or to an unbox trampoline,
110 * so make the hash keyed on the m+addr pair.
112 mono_domain_lock (domain
);
113 if (!domain_jit_info (domain
)->static_rgctx_trampoline_hash
)
114 domain_jit_info (domain
)->static_rgctx_trampoline_hash
= g_hash_table_new (rgctx_tramp_info_hash
, rgctx_tramp_info_equal
);
116 tmp_info
.addr
= addr
;
117 res
= g_hash_table_lookup (domain_jit_info (domain
)->static_rgctx_trampoline_hash
,
119 mono_domain_unlock (domain
);
124 res
= mono_aot_get_static_rgctx_trampoline (ctx
, addr
);
126 res
= mono_arch_get_static_rgctx_trampoline (m
, ctx
, addr
);
128 mono_domain_lock (domain
);
129 /* Duplicates inserted while we didn't hold the lock are OK */
130 info
= mono_domain_alloc (domain
, sizeof (RgctxTrampInfo
));
133 g_hash_table_insert (domain_jit_info (domain
)->static_rgctx_trampoline_hash
, info
, res
);
134 mono_domain_unlock (domain
);
140 mono_create_static_rgctx_trampoline (MonoMethod
*m
, gpointer addr
)
143 * This shouldn't happen as all arches which support generic sharing support
144 * static rgctx trampolines as well.
146 g_assert_not_reached ();
151 mono_get_vcall_slot_addr (guint8
* code
, mgreg_t
*regs
)
155 vt
= mono_arch_get_vcall_slot (code
, regs
, &displacement
);
158 return (gpointer
*)((char*)vt
+ displacement
);
161 #ifdef MONO_ARCH_HAVE_IMT
164 mono_convert_imt_slot_to_vtable_slot (gpointer
* slot
, mgreg_t
*regs
, guint8
*code
, MonoMethod
*method
, MonoMethod
**impl_method
, gboolean
*need_rgctx_tramp
, gboolean
*variance_used
)
166 MonoObject
*this_argument
= mono_arch_get_this_arg_from_call (NULL
, mono_method_signature (method
), regs
, code
);
167 MonoVTable
*vt
= this_argument
->vtable
;
168 int displacement
= slot
- ((gpointer
*)vt
);
170 if (displacement
> 0) {
171 /* slot is in the vtable, not in the IMT */
173 printf ("mono_convert_imt_slot_to_vtable_slot: slot %p is in the vtable, not in the IMT\n", slot
);
177 MonoMethod
*imt_method
= mono_arch_find_imt_method (regs
, code
);
178 int interface_offset
;
179 int imt_slot
= MONO_IMT_SIZE
+ displacement
;
181 /*This has to be variance aware since imt_method can be from an interface that vt->klass doesn't directly implement*/
182 interface_offset
= mono_class_interface_offset_with_variance (vt
->klass
, imt_method
->klass
, variance_used
);
184 if (interface_offset
< 0) {
185 g_error ("%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));
187 mono_vtable_build_imt_slot (vt
, mono_method_get_imt_slot (imt_method
));
192 if (imt_method
->is_inflated
&& ((MonoMethodInflated
*)imt_method
)->context
.method_inst
) {
193 MonoGenericContext context
= { NULL
, NULL
};
196 * Generic virtual method, imt_method contains the inflated interface
197 * method, need to get the inflated impl method.
199 /* imt_method->slot might not be set */
200 impl
= mono_class_get_vtable_entry (vt
->klass
, interface_offset
+ mono_method_get_declaring_generic_method (imt_method
)->slot
);
202 if (impl
->klass
->generic_class
)
203 context
.class_inst
= impl
->klass
->generic_class
->context
.class_inst
;
204 context
.method_inst
= ((MonoMethodInflated
*)imt_method
)->context
.method_inst
;
205 impl
= mono_class_inflate_generic_method (impl
, &context
);
207 impl
= mono_class_get_vtable_entry (vt
->klass
, interface_offset
+ mono_method_get_vtable_slot (imt_method
));
210 if (mono_method_needs_static_rgctx_invoke (impl
, FALSE
))
211 *need_rgctx_tramp
= TRUE
;
215 printf ("mono_convert_imt_slot_to_vtable_slot: method = %s.%s.%s, imt_method = %s.%s.%s\n",
216 method
->klass
->name_space
, method
->klass
->name
, method
->name
,
217 imt_method
->klass
->name_space
, imt_method
->klass
->name
, imt_method
->name
);
220 g_assert (imt_slot
< MONO_IMT_SIZE
);
221 if (vt
->imt_collisions_bitmap
& (1 << imt_slot
)) {
222 int slot
= mono_method_get_vtable_index (imt_method
);
224 gpointer
*vtable_slot
;
226 g_assert (slot
!= -1);
227 vtable_offset
= interface_offset
+ slot
;
228 vtable_slot
= & (vt
->vtable
[vtable_offset
]);
230 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
);
235 printf ("mono_convert_imt_slot_to_vtable_slot: slot %p[%d] is in the IMT, but not colliding\n", slot
, imt_slot
);
244 * common_call_trampoline:
246 * The code to handle normal, virtual, and interface method calls and jumps, both
247 * from JITted and LLVM compiled code.
250 common_call_trampoline (mgreg_t
*regs
, guint8
*code
, gpointer arg
, guint8
* tramp
, MonoVTable
*vt
, gpointer
*vtable_slot
, gboolean need_rgctx_tramp
)
252 gpointer addr
, compiled_method
;
253 gboolean generic_shared
= FALSE
;
255 MonoMethod
*declaring
= NULL
;
256 MonoMethod
*generic_virtual
= NULL
, *variant_iface
= NULL
;
258 gboolean proxy
= FALSE
, variance_used
= FALSE
;
259 gpointer
*orig_vtable_slot
;
260 MonoJitInfo
*ji
= NULL
;
264 orig_vtable_slot
= vtable_slot
;
266 if (m
== MONO_FAKE_VTABLE_METHOD
) {
272 ji
= mono_jit_info_table_find (mono_domain_get (), (char*)code
);
274 printf ("Caller: %s\n", mono_method_full_name (ji
->method
, TRUE
));
275 /* Print some debug info */
276 for (i
= 0; i
< 32; ++i
)
277 printf ("0x%x ", code
[-32 + i
]);
281 displacement
= (guint8
*)vtable_slot
- (guint8
*)vt
;
282 if (displacement
> 0) {
283 int slot
= (displacement
- G_STRUCT_OFFSET (MonoVTable
, vtable
)) / sizeof (gpointer
);
284 g_assert (slot
>= 0);
286 /* Avoid loading metadata or creating a generic vtable if possible */
287 addr
= mono_aot_get_method_from_vt_slot (mono_domain_get (), vt
, slot
);
289 addr
= mono_create_ftnptr (mono_domain_get (), addr
);
290 if (addr
&& !vt
->klass
->valuetype
) {
291 vtable_slot
= mono_get_vcall_slot_addr (code
, regs
);
292 if (mono_aot_is_got_entry (code
, (guint8
*)vtable_slot
) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot
)) {
293 *vtable_slot
= mono_get_addr_from_ftnptr (addr
);
299 m
= mono_class_get_vtable_entry (vt
->klass
, slot
);
300 if (mono_method_needs_static_rgctx_invoke (m
, FALSE
))
301 need_rgctx_tramp
= TRUE
;
303 /*g_print ("%s with disp %d: %s at %p\n", vt->klass->name, displacement, m->name, code);*/
305 /* We got here from an interface method: redirect to IMT handling */
306 m
= MONO_FAKE_IMT_METHOD
;
307 /*g_print ("vtable with disp %d at %p\n", displacement, code);*/
311 /* this is the IMT trampoline */
312 #ifdef MONO_ARCH_HAVE_IMT
313 if (m
== MONO_FAKE_IMT_METHOD
) {
314 MonoMethod
*impl_method
;
315 MonoObject
*this_arg
;
317 /* we get the interface method because mono_convert_imt_slot_to_vtable_slot ()
318 * needs the signature to be able to find the this argument
320 m
= mono_arch_find_imt_method (regs
, code
);
321 vtable_slot
= orig_vtable_slot
;
322 g_assert (vtable_slot
);
324 this_arg
= mono_arch_get_this_arg_from_call (NULL
, mono_method_signature (m
), regs
, code
);
326 if (this_arg
->vtable
->klass
== mono_defaults
.transparent_proxy_class
) {
327 /* Use the slow path for now */
329 m
= mono_object_get_virtual_method (this_arg
, m
);
331 vtable_slot
= mono_convert_imt_slot_to_vtable_slot (vtable_slot
, regs
, code
, m
, &impl_method
, &need_rgctx_tramp
, &variance_used
);
332 /* mono_convert_imt_slot_to_vtable_slot () also gives us the method that is supposed
333 * to be called, so we compile it and go ahead as usual.
335 /*g_print ("imt found method %p (%s) at %p\n", impl_method, impl_method->name, code);*/
336 if (m
->is_inflated
&& ((MonoMethodInflated
*)m
)->context
.method_inst
) {
337 /* Generic virtual method */
339 need_rgctx_tramp
= TRUE
;
340 } else if (variance_used
&& mono_class_has_variant_generic_params (m
->klass
)) {
348 #ifdef MONO_ARCH_LLVM_SUPPORTED
349 if (!vtable_slot
&& code
&& !need_rgctx_tramp
&& mono_method_needs_static_rgctx_invoke (m
, FALSE
)) {
351 * Call this only if the called method is shared, cause it is slow/loads a lot of
354 ji
= mini_jit_info_table_find (mono_domain_get (), (char*)code
, NULL
);
355 if (ji
&& ji
->from_llvm
) {
356 /* LLVM can't pass an rgctx arg */
357 need_rgctx_tramp
= TRUE
;
363 MonoGenericContext context
= { NULL
, NULL
};
364 MonoMethod
*declaring
;
367 declaring
= mono_method_get_declaring_generic_method (m
);
371 if (m
->klass
->generic_class
)
372 context
.class_inst
= m
->klass
->generic_class
->context
.class_inst
;
374 g_assert (!m
->klass
->generic_container
);
376 #ifdef MONO_ARCH_HAVE_IMT
377 generic_virtual
= mono_arch_find_imt_method (regs
, code
);
379 if (generic_virtual
) {
380 g_assert (generic_virtual
->is_inflated
);
381 context
.method_inst
= ((MonoMethodInflated
*)generic_virtual
)->context
.method_inst
;
384 m
= mono_class_inflate_generic_method (declaring
, &context
);
385 /* FIXME: only do this if the method is sharable */
386 need_rgctx_tramp
= TRUE
;
387 } else if ((context_used
= mono_method_check_context_used (m
))) {
388 MonoClass
*klass
= NULL
;
389 MonoMethod
*actual_method
= NULL
;
390 MonoVTable
*vt
= NULL
;
391 MonoGenericInst
*method_inst
= NULL
;
394 generic_shared
= TRUE
;
398 if (m
->is_inflated
&& mono_method_get_context (m
)->method_inst
) {
399 #ifdef MONO_ARCH_RGCTX_REG
400 MonoMethodRuntimeGenericContext
*mrgctx
= (MonoMethodRuntimeGenericContext
*)mono_arch_find_static_call_vtable (regs
, code
);
402 klass
= mrgctx
->class_vtable
->klass
;
403 method_inst
= mrgctx
->method_inst
;
405 g_assert_not_reached ();
407 } else if ((m
->flags
& METHOD_ATTRIBUTE_STATIC
) || m
->klass
->valuetype
) {
408 #ifdef MONO_ARCH_RGCTX_REG
409 MonoVTable
*vtable
= mono_arch_find_static_call_vtable (regs
, code
);
411 klass
= vtable
->klass
;
413 g_assert_not_reached ();
416 #ifdef MONO_ARCH_HAVE_IMT
417 MonoObject
*this_argument
= mono_arch_get_this_arg_from_call (NULL
, mono_method_signature (m
), regs
, code
);
419 vt
= this_argument
->vtable
;
420 vtable_slot
= orig_vtable_slot
;
422 g_assert (this_argument
->vtable
->klass
->inited
);
423 //mono_class_init (this_argument->vtable->klass);
426 klass
= this_argument
->vtable
->klass
->supertypes
[m
->klass
->idepth
- 1];
432 g_assert (vtable_slot
|| klass
);
435 int displacement
= vtable_slot
- ((gpointer
*)vt
);
437 g_assert_not_reached ();
439 g_assert (displacement
> 0);
441 actual_method
= vt
->klass
->vtable
[displacement
];
445 MonoGenericContext context
= { NULL
, NULL
};
448 declaring
= mono_method_get_declaring_generic_method (m
);
452 if (klass
->generic_class
)
453 context
.class_inst
= klass
->generic_class
->context
.class_inst
;
454 else if (klass
->generic_container
)
455 context
.class_inst
= klass
->generic_container
->context
.class_inst
;
456 context
.method_inst
= method_inst
;
458 actual_method
= mono_class_inflate_generic_method (declaring
, &context
);
460 actual_method
= mono_class_get_method_generic (klass
, m
);
464 g_assert (actual_method
);
465 g_assert (actual_method
->klass
== klass
);
467 if (actual_method
->is_inflated
)
468 declaring
= mono_method_get_declaring_generic_method (actual_method
);
475 if (m
->iflags
& METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED
) {
479 ji
= mini_jit_info_table_find (mono_domain_get (), (char*)code
, NULL
);
483 /* Avoid recursion */
484 if (!(ji
&& ji
->method
->wrapper_type
== MONO_WRAPPER_SYNCHRONIZED
))
485 m
= mono_marshal_get_synchronized_wrapper (m
);
488 /* Calls made through delegates on platforms without delegate trampolines */
489 if (!code
&& mono_method_needs_static_rgctx_invoke (m
, FALSE
))
490 need_rgctx_tramp
= TRUE
;
492 addr
= compiled_method
= mono_compile_method (m
);
495 mono_debugger_trampoline_compiled (code
, m
, addr
);
497 if (need_rgctx_tramp
)
498 addr
= mono_create_static_rgctx_trampoline (m
, addr
);
500 if (generic_virtual
|| variant_iface
) {
501 MonoMethod
*target
= generic_virtual
? generic_virtual
: variant_iface
;
503 vtable_slot
= orig_vtable_slot
;
504 g_assert (vtable_slot
);
506 if (vt
->klass
->valuetype
) /*FIXME is this required variant iface?*/
507 addr
= get_unbox_trampoline (mono_get_generic_context_from_code (code
), m
, addr
, need_rgctx_tramp
);
509 mono_method_add_generic_virtual_invocation (mono_domain_get (),
516 /* the method was jumped to */
518 MonoDomain
*domain
= mono_domain_get ();
520 /* Patch the got entries pointing to this method */
522 * We do this here instead of in mono_codegen () to cover the case when m
523 * was loaded from an aot image.
525 if (domain_jit_info (domain
)->jump_target_got_slot_hash
) {
528 mono_domain_lock (domain
);
529 list
= g_hash_table_lookup (domain_jit_info (domain
)->jump_target_got_slot_hash
, m
);
531 for (tmp
= list
; tmp
; tmp
= tmp
->next
) {
532 gpointer
*got_slot
= tmp
->data
;
535 g_hash_table_remove (domain_jit_info (domain
)->jump_target_got_slot_hash
, m
);
538 mono_domain_unlock (domain
);
544 vtable_slot
= orig_vtable_slot
;
547 gboolean variance_used
= FALSE
;
548 if (m
->klass
->valuetype
)
549 addr
= get_unbox_trampoline (mono_get_generic_context_from_code (code
), m
, addr
, need_rgctx_tramp
);
550 g_assert (*vtable_slot
);
552 if (!proxy
&& (mono_aot_is_got_entry (code
, (guint8
*)vtable_slot
) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot
))) {
553 #ifdef MONO_ARCH_HAVE_IMT
554 vtable_slot
= mono_convert_imt_slot_to_vtable_slot (vtable_slot
, regs
, code
, m
, NULL
, &need_rgctx_tramp
, &variance_used
);
556 *vtable_slot
= mono_get_addr_from_ftnptr (addr
);
560 guint8
*plt_entry
= mono_aot_get_plt_entry (code
);
563 mono_aot_patch_plt_entry (plt_entry
, NULL
, regs
, addr
);
565 if (generic_shared
) {
566 if (m
->wrapper_type
!= MONO_WRAPPER_NONE
)
567 m
= mono_marshal_method_from_wrapper (m
);
568 g_assert (mono_method_is_generic_sharable_impl (m
, FALSE
));
571 /* Patch calling code */
573 MonoJitInfo
*target_ji
=
574 mono_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (compiled_method
));
577 ji
= mono_jit_info_table_find (mono_domain_get (), (char*)code
);
579 if (mono_method_same_domain (ji
, target_ji
))
580 mono_arch_patch_callsite (ji
->code_start
, code
, addr
);
589 * mono_magic_trampoline:
591 * This trampoline handles calls from JITted code.
594 mono_magic_trampoline (mgreg_t
*regs
, guint8
*code
, gpointer arg
, guint8
* tramp
)
596 gpointer
*vtable_slot
;
602 if (code
&& !mono_use_llvm
)
603 vt
= mono_arch_get_vcall_slot (code
, regs
, &displacement
);
607 vtable_slot
= (gpointer
*)((char*)vt
+ displacement
);
611 return common_call_trampoline (regs
, code
, arg
, tramp
, vt
, vtable_slot
, FALSE
);
614 #ifdef MONO_ARCH_LLVM_SUPPORTED
616 * mono_llvm_vcall_trampoline:
618 * This trampoline handles virtual calls when using LLVM.
621 mono_llvm_vcall_trampoline (mgreg_t
*regs
, guint8
*code
, MonoMethod
*m
, guint8
*tramp
)
625 gpointer
*vtable_slot
;
631 * We have the method which is called, we need to obtain the vtable slot without
632 * disassembly which is impossible with LLVM.
633 * So we use the this argument.
635 this = mono_arch_get_this_arg_from_call (NULL
, mono_method_signature (m
), regs
, code
);
638 slot
= mono_method_get_vtable_slot (m
);
640 g_assert (slot
!= -1);
642 g_assert (this->vtable
->klass
->vtable
[slot
] == m
);
646 g_assert (!m
->is_generic
);
648 vtable_slot
= &(vt
->vtable
[slot
]);
650 return common_call_trampoline (regs
, code
, m
, tramp
, vt
, vtable_slot
, mono_method_needs_static_rgctx_invoke (m
, 0));
655 mono_generic_virtual_remoting_trampoline (mgreg_t
*regs
, guint8
*code
, MonoMethod
*m
, guint8
*tramp
)
657 MonoGenericContext context
= { NULL
, NULL
};
658 MonoMethod
*imt_method
, *declaring
;
663 g_assert (m
->is_generic
);
666 declaring
= mono_method_get_declaring_generic_method (m
);
670 if (m
->klass
->generic_class
)
671 context
.class_inst
= m
->klass
->generic_class
->context
.class_inst
;
673 g_assert (!m
->klass
->generic_container
);
675 #ifdef MONO_ARCH_HAVE_IMT
676 imt_method
= mono_arch_find_imt_method (regs
, code
);
677 if (imt_method
->is_inflated
)
678 context
.method_inst
= ((MonoMethodInflated
*)imt_method
)->context
.method_inst
;
680 m
= mono_class_inflate_generic_method (declaring
, &context
);
681 m
= mono_marshal_get_remoting_invoke_with_check (m
);
683 addr
= mono_compile_method (m
);
686 mono_debugger_trampoline_compiled (NULL
, m
, addr
);
692 * mono_aot_trampoline:
694 * This trampoline handles calls made from AOT code. We try to bypass the
695 * normal JIT compilation logic to avoid loading the metadata for the method.
697 #ifdef MONO_ARCH_AOT_SUPPORTED
699 mono_aot_trampoline (mgreg_t
*regs
, guint8
*code
, guint8
*token_info
,
704 MonoMethod
*method
= NULL
;
706 gpointer
*vtable_slot
;
711 image
= *(gpointer
*)(gpointer
)token_info
;
712 token_info
+= sizeof (gpointer
);
713 token
= *(guint32
*)(gpointer
)token_info
;
715 addr
= mono_aot_get_method_from_token (mono_domain_get (), image
, token
);
717 method
= mono_get_method (image
, token
, NULL
);
720 /* Use the generic code */
721 return mono_magic_trampoline (regs
, code
, method
, tramp
);
724 addr
= mono_create_ftnptr (mono_domain_get (), addr
);
726 vtable_slot
= mono_get_vcall_slot_addr (code
, regs
);
727 g_assert (!vtable_slot
);
729 /* This is a normal call through a PLT entry */
730 plt_entry
= mono_aot_get_plt_entry (code
);
731 g_assert (plt_entry
);
733 mono_aot_patch_plt_entry (plt_entry
, NULL
, regs
, addr
);
739 * mono_aot_plt_trampoline:
741 * This trampoline handles calls made from AOT code through the PLT table.
744 mono_aot_plt_trampoline (mgreg_t
*regs
, guint8
*code
, guint8
*aot_module
,
747 guint32 plt_info_offset
= mono_aot_get_plt_info_offset (regs
, code
);
752 res
= mono_aot_plt_resolve (aot_module
, plt_info_offset
, code
);
754 if (mono_loader_get_last_error ())
755 mono_raise_exception (mono_loader_error_prepare_exception (mono_loader_get_last_error ()));
756 // FIXME: Error handling (how ?)
765 * mono_class_init_trampoline:
767 * This method calls mono_runtime_class_init () to run the static constructor
768 * for the type, then patches the caller code so it is not called again.
771 mono_class_init_trampoline (mgreg_t
*regs
, guint8
*code
, MonoVTable
*vtable
, guint8
*tramp
)
773 guint8
*plt_entry
= mono_aot_get_plt_entry (code
);
777 mono_runtime_class_init (vtable
);
780 mono_arch_nullify_plt_entry (plt_entry
, regs
);
782 mono_arch_nullify_class_init_trampoline (code
, regs
);
787 * mono_generic_class_init_trampoline:
789 * This method calls mono_runtime_class_init () to run the static constructor
793 mono_generic_class_init_trampoline (mgreg_t
*regs
, guint8
*code
, MonoVTable
*vtable
, guint8
*tramp
)
797 mono_runtime_class_init (vtable
);
801 mono_rgctx_lazy_fetch_trampoline (mgreg_t
*regs
, guint8
*code
, gpointer data
, guint8
*tramp
)
803 #ifdef MONO_ARCH_VTABLE_REG
804 static gboolean inited
= FALSE
;
805 static int num_lookups
= 0;
806 guint32 slot
= GPOINTER_TO_UINT (data
);
807 mgreg_t
*r
= (mgreg_t
*)regs
;
808 gpointer arg
= (gpointer
)(gssize
)r
[MONO_ARCH_VTABLE_REG
];
809 guint32 index
= MONO_RGCTX_SLOT_INDEX (slot
);
810 gboolean mrgctx
= MONO_RGCTX_SLOT_IS_MRGCTX (slot
);
815 mono_counters_register ("RGCTX unmanaged lookups", MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &num_lookups
);
822 return mono_method_fill_runtime_generic_context (arg
, index
);
824 return mono_class_fill_runtime_generic_context (arg
, index
);
826 g_assert_not_reached ();
831 mono_monitor_enter_trampoline (mgreg_t
*regs
, guint8
*code
, MonoObject
*obj
, guint8
*tramp
)
833 mono_monitor_enter (obj
);
837 mono_monitor_exit_trampoline (mgreg_t
*regs
, guint8
*code
, MonoObject
*obj
, guint8
*tramp
)
839 mono_monitor_exit (obj
);
842 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
845 * mono_delegate_trampoline:
847 * This trampoline handles calls made to Delegate:Invoke ().
848 * This is called once the first time a delegate is invoked, so it must be fast.
851 mono_delegate_trampoline (mgreg_t
*regs
, guint8
*code
, gpointer
*tramp_data
, guint8
* tramp
)
853 MonoDomain
*domain
= mono_domain_get ();
854 MonoDelegate
*delegate
;
857 MonoMethod
*method
= NULL
;
858 gboolean multicast
, callvirt
;
859 gboolean need_rgctx_tramp
= FALSE
;
860 MonoMethod
*invoke
= tramp_data
[0];
861 guint8
*impl_this
= tramp_data
[1];
862 guint8
*impl_nothis
= tramp_data
[2];
864 MonoMethodSignature
*sig
;
868 /* Obtain the delegate object according to the calling convention */
871 * Avoid calling mono_get_generic_context_from_code () now since it is expensive,
872 * get_this_arg_from_call will call it if needed.
874 delegate
= mono_arch_get_this_arg_from_call (NULL
, mono_method_signature (invoke
), regs
, code
);
876 if (delegate
->method
) {
877 method
= delegate
->method
;
880 * delegate->method_ptr == NULL means the delegate was initialized by
881 * mini_delegate_ctor, while != NULL means it is initialized by
882 * mono_delegate_ctor_with_method (). In both cases, we need to add wrappers
883 * (ctor_with_method () does this, but it doesn't store the wrapper back into
886 if (delegate
->target
&& delegate
->target
->vtable
->klass
== mono_defaults
.transparent_proxy_class
) {
888 if (((MonoTransparentProxy
*)delegate
->target
)->remote_class
->proxy_class
!= mono_defaults
.com_object_class
&&
889 !((MonoTransparentProxy
*)delegate
->target
)->remote_class
->proxy_class
->is_com_object
)
891 method
= mono_marshal_get_remoting_invoke (method
);
894 mono_error_init (&err
);
895 sig
= mono_method_signature_checked (method
, &err
);
897 mono_error_raise_exception (&err
);
899 if (sig
->hasthis
&& method
->klass
->valuetype
)
900 method
= mono_marshal_get_unbox_wrapper (method
);
903 ji
= mono_jit_info_table_find (domain
, mono_get_addr_from_ftnptr (delegate
->method_ptr
));
909 mono_error_init (&err
);
910 sig
= mono_method_signature_checked (method
, &err
);
912 mono_error_raise_exception (&err
);
914 callvirt
= !delegate
->target
&& sig
->hasthis
;
917 if (method
&& method
->iflags
& METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED
)
918 method
= mono_marshal_get_synchronized_wrapper (method
);
920 if (method
&& mono_method_needs_static_rgctx_invoke (method
, FALSE
))
921 need_rgctx_tramp
= TRUE
;
924 * If the called address is a trampoline, replace it with the compiled method so
925 * further calls don't have to go through the trampoline.
927 if (method
&& !callvirt
) {
928 /* Avoid the overhead of looking up an already compiled method if possible */
929 if (delegate
->method_code
&& *delegate
->method_code
) {
930 delegate
->method_ptr
= *delegate
->method_code
;
932 delegate
->method_ptr
= mono_compile_method (method
);
933 if (need_rgctx_tramp
)
934 delegate
->method_ptr
= mono_create_static_rgctx_trampoline (method
, delegate
->method_ptr
);
935 if (delegate
->method_code
)
936 *delegate
->method_code
= delegate
->method_ptr
;
937 mono_debugger_trampoline_compiled (NULL
, method
, delegate
->method_ptr
);
940 if (need_rgctx_tramp
)
941 delegate
->method_ptr
= mono_create_static_rgctx_trampoline (method
, delegate
->method_ptr
);
944 multicast
= ((MonoMulticastDelegate
*)delegate
)->prev
!= NULL
;
945 if (!multicast
&& !callvirt
) {
946 if (method
&& (method
->flags
& METHOD_ATTRIBUTE_STATIC
) && mono_method_signature (method
)->param_count
== mono_method_signature (invoke
)->param_count
+ 1)
947 /* Closed static delegate */
950 code
= delegate
->target
? impl_this
: impl_nothis
;
953 delegate
->invoke_impl
= mono_get_addr_from_ftnptr (code
);
958 /* The general, unoptimized case */
959 m
= mono_marshal_get_delegate_invoke (invoke
, delegate
);
960 code
= mono_compile_method (m
);
961 delegate
->invoke_impl
= mono_get_addr_from_ftnptr (code
);
962 mono_debugger_trampoline_compiled (NULL
, m
, delegate
->invoke_impl
);
970 * mono_get_trampoline_func:
972 * Return the C function which needs to be called by the generic trampoline of type
976 mono_get_trampoline_func (MonoTrampolineType tramp_type
)
978 switch (tramp_type
) {
979 case MONO_TRAMPOLINE_JIT
:
980 case MONO_TRAMPOLINE_JUMP
:
981 return mono_magic_trampoline
;
982 case MONO_TRAMPOLINE_CLASS_INIT
:
983 return mono_class_init_trampoline
;
984 case MONO_TRAMPOLINE_GENERIC_CLASS_INIT
:
985 return mono_generic_class_init_trampoline
;
986 case MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
:
987 return mono_rgctx_lazy_fetch_trampoline
;
988 #ifdef MONO_ARCH_AOT_SUPPORTED
989 case MONO_TRAMPOLINE_AOT
:
990 return mono_aot_trampoline
;
991 case MONO_TRAMPOLINE_AOT_PLT
:
992 return mono_aot_plt_trampoline
;
994 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
995 case MONO_TRAMPOLINE_DELEGATE
:
996 return mono_delegate_trampoline
;
998 case MONO_TRAMPOLINE_RESTORE_STACK_PROT
:
999 return mono_altstack_restore_prot
;
1000 case MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING
:
1001 return mono_generic_virtual_remoting_trampoline
;
1002 case MONO_TRAMPOLINE_MONITOR_ENTER
:
1003 return mono_monitor_enter_trampoline
;
1004 case MONO_TRAMPOLINE_MONITOR_EXIT
:
1005 return mono_monitor_exit_trampoline
;
1006 #ifdef MONO_ARCH_LLVM_SUPPORTED
1007 case MONO_TRAMPOLINE_LLVM_VCALL
:
1008 return mono_llvm_vcall_trampoline
;
1011 g_assert_not_reached ();
1017 mono_trampolines_init (void)
1019 InitializeCriticalSection (&trampolines_mutex
);
1024 mono_trampoline_code
[MONO_TRAMPOLINE_JIT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_JIT
);
1025 mono_trampoline_code
[MONO_TRAMPOLINE_JUMP
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_JUMP
);
1026 mono_trampoline_code
[MONO_TRAMPOLINE_CLASS_INIT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT
);
1027 mono_trampoline_code
[MONO_TRAMPOLINE_GENERIC_CLASS_INIT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_GENERIC_CLASS_INIT
);
1028 mono_trampoline_code
[MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
);
1029 #ifdef MONO_ARCH_AOT_SUPPORTED
1030 mono_trampoline_code
[MONO_TRAMPOLINE_AOT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_AOT
);
1031 mono_trampoline_code
[MONO_TRAMPOLINE_AOT_PLT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_AOT_PLT
);
1033 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
1034 mono_trampoline_code
[MONO_TRAMPOLINE_DELEGATE
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_DELEGATE
);
1036 mono_trampoline_code
[MONO_TRAMPOLINE_RESTORE_STACK_PROT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_RESTORE_STACK_PROT
);
1037 mono_trampoline_code
[MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING
);
1038 mono_trampoline_code
[MONO_TRAMPOLINE_MONITOR_ENTER
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_MONITOR_ENTER
);
1039 mono_trampoline_code
[MONO_TRAMPOLINE_MONITOR_EXIT
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_MONITOR_EXIT
);
1040 #ifdef MONO_ARCH_LLVM_SUPPORTED
1041 mono_trampoline_code
[MONO_TRAMPOLINE_LLVM_VCALL
] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_LLVM_VCALL
);
1044 mono_counters_register ("Calls to trampolines", MONO_COUNTER_JIT
| MONO_COUNTER_INT
, &trampoline_calls
);
1048 mono_trampolines_cleanup (void)
1050 if (class_init_hash_addr
)
1051 g_hash_table_destroy (class_init_hash_addr
);
1053 DeleteCriticalSection (&trampolines_mutex
);
1057 mono_get_trampoline_code (MonoTrampolineType tramp_type
)
1059 g_assert (mono_trampoline_code
[tramp_type
]);
1061 return mono_trampoline_code
[tramp_type
];
1065 mono_create_specific_trampoline (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, guint32
*code_len
)
1068 return mono_aot_create_specific_trampoline (mono_defaults
.corlib
, arg1
, tramp_type
, domain
, code_len
);
1070 return mono_arch_create_specific_trampoline (arg1
, tramp_type
, domain
, code_len
);
1074 mono_create_class_init_trampoline (MonoVTable
*vtable
)
1077 MonoDomain
*domain
= vtable
->domain
;
1079 g_assert (!vtable
->klass
->generic_container
);
1081 /* previously created trampoline code */
1082 mono_domain_lock (domain
);
1084 g_hash_table_lookup (domain_jit_info (domain
)->class_init_trampoline_hash
,
1086 mono_domain_unlock (domain
);
1090 code
= mono_create_specific_trampoline (vtable
, MONO_TRAMPOLINE_CLASS_INIT
, domain
, NULL
);
1092 ptr
= mono_create_ftnptr (domain
, code
);
1094 /* store trampoline address */
1095 mono_domain_lock (domain
);
1096 g_hash_table_insert (domain_jit_info (domain
)->class_init_trampoline_hash
,
1098 mono_domain_unlock (domain
);
1100 mono_trampolines_lock ();
1101 if (!class_init_hash_addr
)
1102 class_init_hash_addr
= g_hash_table_new (NULL
, NULL
);
1103 g_hash_table_insert (class_init_hash_addr
, ptr
, vtable
);
1104 mono_trampolines_unlock ();
1110 mono_create_generic_class_init_trampoline (void)
1112 #ifdef MONO_ARCH_VTABLE_REG
1113 static gpointer code
;
1115 mono_trampolines_lock ();
1119 /* get_named_code () might return an ftnptr, but our caller expects a direct pointer */
1120 code
= mono_get_addr_from_ftnptr (mono_aot_get_named_code ("generic_class_init_trampoline"));
1122 code
= mono_arch_create_generic_class_init_trampoline ();
1125 mono_trampolines_unlock ();
1129 g_assert_not_reached ();
1134 mono_create_jump_trampoline (MonoDomain
*domain
, MonoMethod
*method
, gboolean add_sync_wrapper
)
1138 guint32 code_size
= 0;
1140 code
= mono_jit_find_compiled_method_with_jit_info (domain
, method
, &ji
);
1142 * We cannot recover the correct type of a shared generic
1143 * method from its native code address, so we use the
1144 * trampoline instead.
1146 if (code
&& !ji
->has_generic_jit_info
)
1149 mono_domain_lock (domain
);
1150 code
= g_hash_table_lookup (domain_jit_info (domain
)->jump_trampoline_hash
, method
);
1151 mono_domain_unlock (domain
);
1155 code
= mono_create_specific_trampoline (method
, MONO_TRAMPOLINE_JUMP
, mono_domain_get (), &code_size
);
1156 g_assert (code_size
);
1158 ji
= mono_domain_alloc0 (domain
, MONO_SIZEOF_JIT_INFO
);
1159 ji
->code_start
= code
;
1160 ji
->code_size
= code_size
;
1161 ji
->method
= method
;
1164 * mono_delegate_ctor needs to find the method metadata from the
1165 * trampoline address, so we save it here.
1168 mono_jit_info_table_add (domain
, ji
);
1170 mono_domain_lock (domain
);
1171 g_hash_table_insert (domain_jit_info (domain
)->jump_trampoline_hash
, method
, ji
->code_start
);
1172 mono_domain_unlock (domain
);
1174 return ji
->code_start
;
1178 mono_create_jit_trampoline_in_domain (MonoDomain
*domain
, MonoMethod
*method
)
1182 if (mono_aot_only
) {
1183 /* Avoid creating trampolines if possible */
1184 gpointer code
= mono_jit_find_compiled_method (domain
, method
);
1190 mono_domain_lock (domain
);
1191 tramp
= g_hash_table_lookup (domain_jit_info (domain
)->jit_trampoline_hash
, method
);
1192 mono_domain_unlock (domain
);
1196 tramp
= mono_create_specific_trampoline (method
, MONO_TRAMPOLINE_JIT
, domain
, NULL
);
1198 mono_domain_lock (domain
);
1199 g_hash_table_insert (domain_jit_info (domain
)->jit_trampoline_hash
, method
, tramp
);
1200 mono_domain_unlock (domain
);
1202 mono_jit_stats
.method_trampolines
++;
1208 mono_create_jit_trampoline (MonoMethod
*method
)
1210 return mono_create_jit_trampoline_in_domain (mono_domain_get (), method
);
1214 mono_create_jit_trampoline_from_token (MonoImage
*image
, guint32 token
)
1218 MonoDomain
*domain
= mono_domain_get ();
1219 guint8
*buf
, *start
;
1221 buf
= start
= mono_domain_code_reserve (domain
, 2 * sizeof (gpointer
));
1223 *(gpointer
*)(gpointer
)buf
= image
;
1224 buf
+= sizeof (gpointer
);
1225 *(guint32
*)(gpointer
)buf
= token
;
1227 tramp
= mono_create_specific_trampoline (start
, MONO_TRAMPOLINE_AOT
, domain
, NULL
);
1229 mono_jit_stats
.method_trampolines
++;
1235 mono_create_delegate_trampoline (MonoClass
*klass
)
1237 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
1238 MonoDomain
*domain
= mono_domain_get ();
1240 guint32 code_size
= 0;
1241 gpointer
*tramp_data
;
1244 mono_domain_lock (domain
);
1245 ptr
= g_hash_table_lookup (domain_jit_info (domain
)->delegate_trampoline_hash
, klass
);
1246 mono_domain_unlock (domain
);
1250 // Precompute the delegate invoke impl and pass it to the delegate trampoline
1251 invoke
= mono_get_delegate_invoke (klass
);
1254 tramp_data
= mono_domain_alloc (domain
, sizeof (gpointer
) * 3);
1255 tramp_data
[0] = invoke
;
1256 tramp_data
[1] = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke
), TRUE
);
1257 tramp_data
[2] = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke
), FALSE
);
1259 ptr
= mono_create_specific_trampoline (tramp_data
, MONO_TRAMPOLINE_DELEGATE
, mono_domain_get (), &code_size
);
1260 g_assert (code_size
);
1262 /* store trampoline address */
1263 mono_domain_lock (domain
);
1264 g_hash_table_insert (domain_jit_info (domain
)->delegate_trampoline_hash
,
1266 mono_domain_unlock (domain
);
1275 mono_create_rgctx_lazy_fetch_trampoline (guint32 offset
)
1277 static gboolean inited
= FALSE
;
1278 static int num_trampolines
= 0;
1280 gpointer tramp
, ptr
;
1283 return mono_aot_get_lazy_fetch_trampoline (offset
);
1285 mono_trampolines_lock ();
1286 if (rgctx_lazy_fetch_trampoline_hash
)
1287 tramp
= g_hash_table_lookup (rgctx_lazy_fetch_trampoline_hash
, GUINT_TO_POINTER (offset
));
1290 mono_trampolines_unlock ();
1294 tramp
= mono_arch_create_rgctx_lazy_fetch_trampoline (offset
);
1295 ptr
= mono_create_ftnptr (mono_get_root_domain (), tramp
);
1297 mono_trampolines_lock ();
1298 if (!rgctx_lazy_fetch_trampoline_hash
) {
1299 rgctx_lazy_fetch_trampoline_hash
= g_hash_table_new (NULL
, NULL
);
1300 rgctx_lazy_fetch_trampoline_hash_addr
= g_hash_table_new (NULL
, NULL
);
1302 g_hash_table_insert (rgctx_lazy_fetch_trampoline_hash
, GUINT_TO_POINTER (offset
), ptr
);
1303 g_assert (offset
!= -1);
1304 g_hash_table_insert (rgctx_lazy_fetch_trampoline_hash_addr
, ptr
, GUINT_TO_POINTER (offset
+ 1));
1305 mono_trampolines_unlock ();
1308 mono_counters_register ("RGCTX num lazy fetch trampolines",
1309 MONO_COUNTER_GENERICS
| MONO_COUNTER_INT
, &num_trampolines
);
1318 mono_create_monitor_enter_trampoline (void)
1320 static gpointer code
;
1322 if (mono_aot_only
) {
1324 code
= mono_aot_get_named_code ("monitor_enter_trampoline");
1328 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
1329 mono_trampolines_lock ();
1332 code
= mono_arch_create_monitor_enter_trampoline ();
1334 mono_trampolines_unlock ();
1337 g_assert_not_reached ();
1344 mono_create_monitor_exit_trampoline (void)
1346 static gpointer code
;
1348 if (mono_aot_only
) {
1350 code
= mono_aot_get_named_code ("monitor_exit_trampoline");
1354 #ifdef MONO_ARCH_MONITOR_OBJECT_REG
1355 mono_trampolines_lock ();
1358 code
= mono_arch_create_monitor_exit_trampoline ();
1360 mono_trampolines_unlock ();
1363 g_assert_not_reached ();
1368 #ifdef MONO_ARCH_LLVM_SUPPORTED
1370 * mono_create_llvm_vcall_trampoline:
1372 * LLVM emits code for virtual calls which mono_get_vcall_slot is unable to
1373 * decode, i.e. only the final branch address is available:
1374 * mov <offset>(%rax), %rax
1375 * <random code inserted by instruction scheduling>
1378 * To work around this problem, we don't use the common vtable trampoline when
1379 * llvm is enabled. Instead, we use one trampoline per method.
1382 mono_create_llvm_vcall_trampoline (MonoMethod
*method
)
1387 domain
= mono_domain_get ();
1389 mono_domain_lock (domain
);
1390 res
= g_hash_table_lookup (domain_jit_info (domain
)->llvm_vcall_trampoline_hash
, method
);
1391 mono_domain_unlock (domain
);
1395 res
= mono_create_specific_trampoline (method
, MONO_TRAMPOLINE_LLVM_VCALL
, domain
, NULL
);
1397 mono_domain_lock (domain
);
1398 g_hash_table_insert (domain_jit_info (domain
)->llvm_vcall_trampoline_hash
, method
, res
);
1399 mono_domain_unlock (domain
);
1405 * mono_create_llvm_imt_trampoline:
1407 * LLVM compiled code can't pass in the IMT argument, so we use this trampoline, which
1408 * sets the IMT argument, then branches to the contents of the vtable slot given by
1409 * vt_offset in the vtable which is obtained from the argument list.
1412 mono_create_llvm_imt_trampoline (MonoDomain
*domain
, MonoMethod
*m
, int vt_offset
)
1414 #ifdef MONO_ARCH_HAVE_LLVM_IMT_TRAMPOLINE
1415 return mono_arch_get_llvm_imt_trampoline (domain
, m
, vt_offset
);
1417 g_assert_not_reached ();
1424 mono_find_class_init_trampoline_by_addr (gconstpointer addr
)
1428 mono_trampolines_lock ();
1429 if (class_init_hash_addr
)
1430 res
= g_hash_table_lookup (class_init_hash_addr
, addr
);
1433 mono_trampolines_unlock ();
1438 mono_find_rgctx_lazy_fetch_trampoline_by_addr (gconstpointer addr
)
1442 mono_trampolines_lock ();
1443 if (rgctx_lazy_fetch_trampoline_hash_addr
) {
1444 /* We store the real offset + 1 so we can detect when the lookup fails */
1445 offset
= GPOINTER_TO_INT (g_hash_table_lookup (rgctx_lazy_fetch_trampoline_hash_addr
, addr
));
1453 mono_trampolines_unlock ();