From 31036fea05263d5fecb9740cdd5375b4b04afac6 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Tue, 13 Apr 2021 15:44:44 -0700 Subject: [PATCH 01/11] Update lpFirst and lpTop to reflect accurate compacted blocks (#51033) * include first and top in compacting * Convert to assert * Revert "Convert to assert" This reverts commit 7da1a8c7d910ad00bbc7b40314997f70960729a5. --- src/coreclr/jit/fgopt.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 9b0eafce39cef9..3fb3c37b8e68bd 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -1996,6 +1996,20 @@ void Compiler::fgUpdateLoopsAfterCompacting(BasicBlock* block, BasicBlock* bNext { optLoopTable[loopNum].lpEntry = block; } + + /* Check the loop's first block */ + + if (optLoopTable[loopNum].lpFirst == bNext) + { + optLoopTable[loopNum].lpFirst = block; + } + + /* Check the loop top */ + + if (optLoopTable[loopNum].lpTop == bNext) + { + optLoopTable[loopNum].lpTop = block; + } } } From 4b7d96a9f384928eeb5c7ffa5c7ea024bcbb5339 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 14 Apr 2021 01:07:41 +0200 Subject: [PATCH 02/11] Fix native stack size alignment on macOS arm64 (#51179) We were setting an unaligned stack size to the MethodDesc of ILStub. The lowest two bits actually overlap with two flags in the extended flags, so the size is expected to be aligned at least to 4 to not to overwrite the flags. On macOS arm64, the computed native stack size can be misaligned, so this change adds explicit alignment before storing the stack size to the MethodDesc. --- src/coreclr/vm/dllimport.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index 76bccc36ed5551..e3dd139540783d 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -3660,6 +3660,8 @@ static void CreateNDirectStubWorker(StubState* pss, } #endif // TARGET_X86 + nativeStackSize = ALIGN_UP(nativeStackSize, TARGET_POINTER_SIZE); + if (!FitsInU2(nativeStackSize)) COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX); From 53d3bddc936e16b34fbe71cfd33964c83709600c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rylek?= Date: Tue, 13 Apr 2021 16:40:53 -0700 Subject: [PATCH 03/11] Fix Crossgen2 compilation failures in several type generator tests (#51090) As the PR https://github.com/dotnet/runtime/pull/44041 effectively decoupled OwningType from Method in MethodWithToken, I believe we now need to include OwningType in the Equals check, otherwise we may (and sometimes do) crash in the assertion failure checking that OwningType matches between instances. Thanks Tomas --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 56a3cce130244e..60fe8683e22d1b 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -221,8 +221,9 @@ public override int GetHashCode() public bool Equals(MethodWithToken methodWithToken) { - bool equals = Method == methodWithToken.Method && Token.Equals(methodWithToken.Token) && ConstrainedType == methodWithToken.ConstrainedType && - Unboxing == methodWithToken.Unboxing; + bool equals = Method == methodWithToken.Method && Token.Equals(methodWithToken.Token) + && OwningType == methodWithToken.OwningType && ConstrainedType == methodWithToken.ConstrainedType + && Unboxing == methodWithToken.Unboxing; if (equals) { Debug.Assert(OwningTypeNotDerivedFromToken == methodWithToken.OwningTypeNotDerivedFromToken); From 5a7ecc051274230db3c87662fd248e61975dddd0 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Tue, 13 Apr 2021 20:27:13 -0400 Subject: [PATCH 04/11] [mono] Transition memory allocations from image sets to memory managers. (#50299) * [mono] Transition memory allocations from image sets to memory managers. * Move hashes from MonoImageSet to MonoGenericMemoryManager. * Add a mono_mem_manager_get_generic () function which returns a memory manager for a set images. Same as the get_image_set () function. * Transition users of image sets to use MonoGenericMemoryManager. * [mono] Unify MonoMemoryManager subclasses. * Remove unused image set code. --- .../mono/metadata/assembly-load-context.c | 4 +- src/mono/mono/metadata/class-init.c | 118 ++-- src/mono/mono/metadata/class-internals.h | 21 +- src/mono/mono/metadata/class.c | 51 +- src/mono/mono/metadata/image.c | 2 - src/mono/mono/metadata/loader-internals.h | 94 ++- src/mono/mono/metadata/memory-manager.c | 245 +++++++- src/mono/mono/metadata/metadata-internals.h | 109 ---- src/mono/mono/metadata/metadata.c | 581 ++---------------- src/mono/mono/mini/mini-generic-sharing.c | 32 +- src/mono/mono/utils/mono-mmap.c | 2 +- src/mono/mono/utils/mono-mmap.h | 2 +- 12 files changed, 492 insertions(+), 769 deletions(-) diff --git a/src/mono/mono/metadata/assembly-load-context.c b/src/mono/mono/metadata/assembly-load-context.c index 1dbd387ab117f0..808789d280bdc7 100644 --- a/src/mono/mono/metadata/assembly-load-context.c +++ b/src/mono/mono/metadata/assembly-load-context.c @@ -41,7 +41,7 @@ mono_alc_init (MonoAssemblyLoadContext *alc, gboolean collectible) mono_loaded_images_init (li, alc); alc->loaded_images = li; alc->loaded_assemblies = NULL; - alc->memory_manager = mono_mem_manager_create_singleton (alc, collectible); + alc->memory_manager = mono_mem_manager_new (&alc, 1, collectible); alc->generic_memory_managers = g_ptr_array_new (); mono_coop_mutex_init (&alc->memory_managers_lock); alc->unloading = FALSE; @@ -172,7 +172,7 @@ mono_alc_cleanup (MonoAssemblyLoadContext *alc) mono_alc_cleanup_assemblies (alc); - mono_mem_manager_free_singleton (alc->memory_manager, FALSE); + mono_mem_manager_free (alc->memory_manager, FALSE); alc->memory_manager = NULL; /*for (int i = 0; i < alc->generic_memory_managers->len; i++) { diff --git a/src/mono/mono/metadata/class-init.c b/src/mono/mono/metadata/class-init.c index a009c68b41c066..89f14dcd844e73 100644 --- a/src/mono/mono/metadata/class-init.c +++ b/src/mono/mono/metadata/class-init.c @@ -824,7 +824,7 @@ mono_class_create_generic_inst (MonoGenericClass *gclass) if (gclass->cached_class) return gclass->cached_class; - klass = (MonoClass *)mono_image_set_alloc0 (gclass->owner, sizeof (MonoClassGenericInst)); + klass = (MonoClass *)mono_mem_manager_alloc0 ((MonoMemoryManager*)gclass->owner, sizeof (MonoClassGenericInst)); gklass = gclass->container_class; @@ -850,7 +850,6 @@ mono_class_create_generic_inst (MonoGenericClass *gclass) klass->enumtype = gklass->enumtype; klass->valuetype = gklass->valuetype; - if (gklass->image->assembly_name && !strcmp (gklass->image->assembly_name, "System.Numerics.Vectors") && !strcmp (gklass->name_space, "System.Numerics") && !strcmp (gklass->name, "Vector`1")) { g_assert (gclass->context.class_inst); g_assert (gclass->context.class_inst->type_argc > 0); @@ -1004,22 +1003,25 @@ mono_class_create_bounded_array (MonoClass *eclass, guint32 rank, gboolean bound GSList *list, *rootlist = NULL; int nsize; char *name; - MonoImageSet* image_set; + MonoMemoryManager *mm; if (rank > 1) /* bounded only matters for one-dimensional arrays */ bounded = FALSE; image = eclass->image; - image_set = class_kind_may_contain_generic_instances ((MonoTypeKind)eclass->class_kind) ? mono_metadata_get_image_set_for_class (eclass) : NULL; + // FIXME: Optimize this + mm = class_kind_may_contain_generic_instances ((MonoTypeKind)eclass->class_kind) ? mono_metadata_get_mem_manager_for_class (eclass) : NULL; /* Check cache */ cached = NULL; if (rank == 1 && !bounded) { - if (image_set) { - mono_image_set_lock (image_set); - cached = (MonoClass *)g_hash_table_lookup (image_set->szarray_cache, eclass); - mono_image_set_unlock (image_set); + if (mm) { + mono_mem_manager_lock (mm); + if (!mm->szarray_cache) + mm->szarray_cache = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, NULL); + cached = (MonoClass *)g_hash_table_lookup (mm->szarray_cache, eclass); + mono_mem_manager_unlock (mm); } else { /* * This case is very frequent not just during compilation because of calls @@ -1033,9 +1035,11 @@ mono_class_create_bounded_array (MonoClass *eclass, guint32 rank, gboolean bound mono_os_mutex_unlock (&image->szarray_cache_lock); } } else { - if (image_set) { - mono_image_set_lock (image_set); - rootlist = (GSList *)g_hash_table_lookup (image_set->array_cache, eclass); + if (mm) { + mono_mem_manager_lock (mm); + if (!mm->array_cache) + mm->array_cache = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, NULL); + rootlist = (GSList *)g_hash_table_lookup (mm->array_cache, eclass); for (list = rootlist; list; list = list->next) { k = (MonoClass *)list->data; if ((m_class_get_rank (k) == rank) && (m_class_get_byval_arg (k)->type == (((rank > 1) || bounded) ? MONO_TYPE_ARRAY : MONO_TYPE_SZARRAY))) { @@ -1043,7 +1047,7 @@ mono_class_create_bounded_array (MonoClass *eclass, guint32 rank, gboolean bound break; } } - mono_image_set_unlock (image_set); + mono_mem_manager_unlock (mm); } else { mono_loader_lock (); if (!image->array_cache) @@ -1066,7 +1070,7 @@ mono_class_create_bounded_array (MonoClass *eclass, guint32 rank, gboolean bound if (!parent->inited) mono_class_init_internal (parent); - klass = image_set ? (MonoClass *)mono_image_set_alloc0 (image_set, sizeof (MonoClassArray)) : (MonoClass *)mono_image_alloc0 (image, sizeof (MonoClassArray)); + klass = mm ? (MonoClass *)mono_mem_manager_alloc0 (mm, sizeof (MonoClassArray)) : (MonoClass *)mono_image_alloc0 (image, sizeof (MonoClassArray)); klass->image = image; klass->name_space = eclass->name_space; @@ -1083,7 +1087,7 @@ mono_class_create_bounded_array (MonoClass *eclass, guint32 rank, gboolean bound name [nsize + maxrank] = '*'; name [nsize + maxrank + bounded] = ']'; name [nsize + maxrank + bounded + 1] = 0; - klass->name = image_set ? mono_image_set_strdup (image_set, name) : mono_image_strdup (image, name); + klass->name = mm ? mono_mem_manager_strdup (mm, name) : mono_image_strdup (image, name); g_free (name); klass->type_token = 0; @@ -1135,7 +1139,7 @@ mono_class_create_bounded_array (MonoClass *eclass, guint32 rank, gboolean bound klass->element_class = eclass; if ((rank > 1) || bounded) { - MonoArrayType *at = image_set ? (MonoArrayType *)mono_image_set_alloc0 (image_set, sizeof (MonoArrayType)) : (MonoArrayType *)mono_image_alloc0 (image, sizeof (MonoArrayType)); + MonoArrayType *at = mm ? (MonoArrayType *)mono_mem_manager_alloc0 (mm, sizeof (MonoArrayType)) : (MonoArrayType *)mono_image_alloc0 (image, sizeof (MonoArrayType)); klass->_byval_arg.type = MONO_TYPE_ARRAY; klass->_byval_arg.data.array = at; at->eklass = eclass; @@ -1162,19 +1166,19 @@ mono_class_create_bounded_array (MonoClass *eclass, guint32 rank, gboolean bound /* Check cache again */ cached = NULL; if (rank == 1 && !bounded) { - if (image_set) { - mono_image_set_lock (image_set); - cached = (MonoClass *)g_hash_table_lookup (image_set->szarray_cache, eclass); - mono_image_set_unlock (image_set); + if (mm) { + mono_mem_manager_lock (mm); + cached = (MonoClass *)g_hash_table_lookup (mm->szarray_cache, eclass); + mono_mem_manager_unlock (mm); } else { mono_os_mutex_lock (&image->szarray_cache_lock); cached = (MonoClass *)g_hash_table_lookup (image->szarray_cache, eclass); mono_os_mutex_unlock (&image->szarray_cache_lock); } } else { - if (image_set) { - mono_image_set_lock (image_set); - rootlist = (GSList *)g_hash_table_lookup (image_set->array_cache, eclass); + if (mm) { + mono_mem_manager_lock (mm); + rootlist = (GSList *)g_hash_table_lookup (mm->array_cache, eclass); for (list = rootlist; list; list = list->next) { k = (MonoClass *)list->data; if ((m_class_get_rank (k) == rank) && (m_class_get_byval_arg (k)->type == (((rank > 1) || bounded) ? MONO_TYPE_ARRAY : MONO_TYPE_SZARRAY))) { @@ -1182,7 +1186,7 @@ mono_class_create_bounded_array (MonoClass *eclass, guint32 rank, gboolean bound break; } } - mono_image_set_unlock (image_set); + mono_mem_manager_unlock (mm); } else { rootlist = (GSList *)g_hash_table_lookup (image->array_cache, eclass); for (list = rootlist; list; list = list->next) { @@ -1205,21 +1209,21 @@ mono_class_create_bounded_array (MonoClass *eclass, guint32 rank, gboolean bound ++class_array_count; if (rank == 1 && !bounded) { - if (image_set) { - mono_image_set_lock (image_set); - g_hash_table_insert (image_set->szarray_cache, eclass, klass); - mono_image_set_unlock (image_set); + if (mm) { + mono_mem_manager_lock (mm); + g_hash_table_insert (mm->szarray_cache, eclass, klass); + mono_mem_manager_unlock (mm); } else { mono_os_mutex_lock (&image->szarray_cache_lock); g_hash_table_insert (image->szarray_cache, eclass, klass); mono_os_mutex_unlock (&image->szarray_cache_lock); } } else { - if (image_set) { - mono_image_set_lock (image_set); + if (mm) { + mono_mem_manager_lock (mm); list = g_slist_append (rootlist, klass); - g_hash_table_insert (image_set->array_cache, eclass, list); - mono_image_set_unlock (image_set); + g_hash_table_insert (mm->array_cache, eclass, list); + mono_mem_manager_unlock (mm); } else { list = g_slist_append (rootlist, klass); g_hash_table_insert (image->array_cache, eclass, list); @@ -1407,21 +1411,21 @@ mono_class_create_ptr (MonoType *type) MonoClass *el_class; MonoImage *image; char *name; - MonoImageSet* image_set; + MonoMemoryManager *mm; el_class = mono_class_from_mono_type_internal (type); image = el_class->image; - image_set = class_kind_may_contain_generic_instances ((MonoTypeKind)el_class->class_kind) ? mono_metadata_get_image_set_for_class (el_class) : NULL; - - if (image_set) { - mono_image_set_lock (image_set); - if (image_set->ptr_cache) { - if ((result = (MonoClass *)g_hash_table_lookup (image_set->ptr_cache, el_class))) { - mono_image_set_unlock (image_set); - return result; - } - } - mono_image_set_unlock (image_set); + // FIXME: Optimize this + mm = class_kind_may_contain_generic_instances ((MonoTypeKind)el_class->class_kind) ? mono_metadata_get_mem_manager_for_class (el_class) : NULL; + + if (mm) { + mono_mem_manager_lock (mm); + if (!mm->ptr_cache) + mm->ptr_cache = g_hash_table_new (mono_aligned_addr_hash, NULL); + result = (MonoClass *)g_hash_table_lookup (mm->ptr_cache, el_class); + mono_mem_manager_unlock (mm); + if (result) + return result; } else { mono_image_lock (image); if (image->ptr_cache) { @@ -1433,7 +1437,7 @@ mono_class_create_ptr (MonoType *type) mono_image_unlock (image); } - result = image_set ? (MonoClass *)mono_image_set_alloc0 (image_set, sizeof (MonoClassPointer)) : (MonoClass *)mono_image_alloc0 (image, sizeof (MonoClassPointer)); + result = mm ? (MonoClass *)mono_mem_manager_alloc0 (mm, sizeof (MonoClassPointer)) : (MonoClass *)mono_image_alloc0 (image, sizeof (MonoClassPointer)); UnlockedAdd (&classes_size, sizeof (MonoClassPointer)); ++class_pointer_count; @@ -1441,7 +1445,7 @@ mono_class_create_ptr (MonoType *type) result->parent = NULL; /* no parent for PTR types */ result->name_space = el_class->name_space; name = g_strdup_printf ("%s*", el_class->name); - result->name = image_set ? mono_image_set_strdup (image_set, name) : mono_image_strdup (image, name); + result->name = mm ? mono_mem_manager_strdup (mm, name) : mono_image_strdup (image, name); result->class_kind = MONO_CLASS_POINTER; g_free (name); @@ -1466,21 +1470,17 @@ mono_class_create_ptr (MonoType *type) mono_class_setup_supertypes (result); - if (image_set) { - mono_image_set_lock (image_set); - if (image_set->ptr_cache) { - MonoClass *result2; - if ((result2 = (MonoClass *)g_hash_table_lookup (image_set->ptr_cache, el_class))) { - mono_image_set_unlock (image_set); - MONO_PROFILER_RAISE (class_failed, (result)); - return result2; - } - } - else { - image_set->ptr_cache = g_hash_table_new (mono_aligned_addr_hash, NULL); + if (mm) { + mono_mem_manager_lock (mm); + MonoClass *result2; + result2 = (MonoClass *)g_hash_table_lookup (mm->ptr_cache, el_class); + if (!result2) + g_hash_table_insert (mm->ptr_cache, el_class, result); + mono_mem_manager_unlock (mm); + if (result2) { + MONO_PROFILER_RAISE (class_failed, (result)); + return result2; } - g_hash_table_insert (image_set->ptr_cache, el_class, result); - mono_image_set_unlock (image_set); } else { mono_image_lock (image); if (image->ptr_cache) { diff --git a/src/mono/mono/metadata/class-internals.h b/src/mono/mono/metadata/class-internals.h index f6130da1b81c61..233e4ecc9b655e 100644 --- a/src/mono/mono/metadata/class-internals.h +++ b/src/mono/mono/metadata/class-internals.h @@ -405,7 +405,7 @@ struct _MonoMethodInflated { } method; MonoMethod *declaring; /* the generic method definition. */ MonoGenericContext context; /* The current instantiation */ - MonoImageSet *owner; /* The image set that the inflated method belongs to. */ + MonoMemoryManager *owner; /* The mem manager that the inflated method belongs to. */ }; /* @@ -419,12 +419,8 @@ struct _MonoGenericClass { guint need_sync : 1; /* Only if dynamic. Need to be synchronized with its container class after its finished. */ MonoClass *cached_class; /* if present, the MonoClass corresponding to the instantiation. */ - /* - * The image set which owns this generic class. Memory owned by the generic class - * including cached_class should be allocated from the mempool of the image set, - * so it is easy to free. - */ - MonoImageSet *owner; + /* The mem manager which owns this generic class. */ + MonoMemoryManager *owner; }; /* Additional details about a MonoGenericParam */ @@ -901,11 +897,14 @@ mono_class_inflate_generic_method_full_checked (MonoMethod *method, MonoClass *k MonoMethod * mono_class_inflate_generic_method_checked (MonoMethod *method, MonoGenericContext *context, MonoError *error); -MonoImageSet * -mono_metadata_get_image_set_for_class (MonoClass *klass); +MonoMemoryManager * +mono_metadata_get_mem_manager_for_type (MonoType *type); + +MonoMemoryManager * +mono_metadata_get_mem_manager_for_class (MonoClass *klass); -MonoImageSet * -mono_metadata_get_image_set_for_method (MonoMethodInflated *method); +MonoMemoryManager* +mono_metadata_get_mem_manager_for_method (MonoMethodInflated *method); MONO_API MonoMethodSignature * mono_metadata_get_inflated_signature (MonoMethodSignature *sig, MonoGenericContext *context); diff --git a/src/mono/mono/metadata/class.c b/src/mono/mono/metadata/class.c index 2d472f7b4c8eee..1bf5438e9f4268 100644 --- a/src/mono/mono/metadata/class.c +++ b/src/mono/mono/metadata/class.c @@ -1116,6 +1116,37 @@ mono_class_inflate_generic_method_checked (MonoMethod *method, MonoGenericContex return mono_class_inflate_generic_method_full_checked (method, NULL, context, error); } +static gboolean +inflated_method_equal (gconstpointer a, gconstpointer b) +{ + const MonoMethodInflated *ma = (const MonoMethodInflated *)a; + const MonoMethodInflated *mb = (const MonoMethodInflated *)b; + if (ma->declaring != mb->declaring) + return FALSE; + return mono_metadata_generic_context_equal (&ma->context, &mb->context); +} + +static guint +inflated_method_hash (gconstpointer a) +{ + const MonoMethodInflated *ma = (const MonoMethodInflated *)a; + return (mono_metadata_generic_context_hash (&ma->context) ^ mono_aligned_addr_hash (ma->declaring)); +} + +static void +free_inflated_method (MonoMethodInflated *imethod) +{ + MonoMethod *method = (MonoMethod*)imethod; + + if (method->signature) + mono_metadata_free_inflated_signature (method->signature); + + if (method->wrapper_type) + g_free (((MonoMethodWrapper*)method)->method_data); + + g_free (method); +} + /** * mono_class_inflate_generic_method_full_checked: * Instantiate method \p method with the generic context \p context. @@ -1178,12 +1209,14 @@ mono_class_inflate_generic_method_full_checked (MonoMethod *method, MonoClass *k if (!mono_class_is_gtd (iresult->declaring->klass) && !mono_class_is_ginst (iresult->declaring->klass)) iresult->context.class_inst = NULL; - MonoImageSet *set = mono_metadata_get_image_set_for_method (iresult); + MonoMemoryManager *mm = mono_metadata_get_mem_manager_for_method (iresult); // check cache - mono_image_set_lock (set); - cached = (MonoMethodInflated *)g_hash_table_lookup (set->gmethod_cache, iresult); - mono_image_set_unlock (set); + mono_mem_manager_lock (mm); + if (!mm->gmethod_cache) + mm->gmethod_cache = g_hash_table_new_full (inflated_method_hash, inflated_method_equal, NULL, (GDestroyNotify)free_inflated_method); + cached = (MonoMethodInflated *)g_hash_table_lookup (mm->gmethod_cache, iresult); + mono_mem_manager_unlock (mm); if (cached) { g_free (iresult); @@ -1276,14 +1309,14 @@ mono_class_inflate_generic_method_full_checked (MonoMethod *method, MonoClass *k */ // check cache - mono_image_set_lock (set); - cached = (MonoMethodInflated *)g_hash_table_lookup (set->gmethod_cache, iresult); + mono_mem_manager_lock (mm); + cached = (MonoMethodInflated *)g_hash_table_lookup (mm->gmethod_cache, iresult); if (!cached) { - g_hash_table_insert (set->gmethod_cache, iresult, iresult); - iresult->owner = set; + g_hash_table_insert (mm->gmethod_cache, iresult, iresult); + iresult->owner = mm; cached = iresult; } - mono_image_set_unlock (set); + mono_mem_manager_unlock (mm); return (MonoMethod*)cached; diff --git a/src/mono/mono/metadata/image.c b/src/mono/mono/metadata/image.c index b2171fe6ed8858..182f35cd96f8a4 100644 --- a/src/mono/mono/metadata/image.c +++ b/src/mono/mono/metadata/image.c @@ -2165,8 +2165,6 @@ mono_image_close_except_pools (MonoImage *image) mono_image_invoke_unload_hook (image); - mono_metadata_clean_for_image (image); - #ifdef ENABLE_METADATA_UPDATE mono_metadata_update_cleanup_on_close (image); #endif diff --git a/src/mono/mono/metadata/loader-internals.h b/src/mono/mono/metadata/loader-internals.h index 743e808eb729f3..ec6eb8f80cdf3b 100644 --- a/src/mono/mono/metadata/loader-internals.h +++ b/src/mono/mono/metadata/loader-internals.h @@ -16,6 +16,7 @@ #include #include #include +#include #if defined(TARGET_OSX) #define MONO_LOADER_LIBRARY_NAME "libcoreclr.dylib" @@ -30,8 +31,6 @@ G_BEGIN_DECLS typedef struct _MonoLoadedImages MonoLoadedImages; typedef struct _MonoAssemblyLoadContext MonoAssemblyLoadContext; typedef struct _MonoMemoryManager MonoMemoryManager; -typedef struct _MonoSingletonMemoryManager MonoSingletonMemoryManager; -typedef struct _MonoGenericMemoryManager MonoGenericMemoryManager; struct _MonoBundledSatelliteAssembly { const char *name; @@ -51,6 +50,44 @@ struct _MonoDllMap { }; #endif +typedef struct { + /* + * indexed by MonoMethodSignature + * Protected by the marshal lock + */ + GHashTable *delegate_invoke_cache; + GHashTable *delegate_begin_invoke_cache; + GHashTable *delegate_end_invoke_cache; + GHashTable *runtime_invoke_signature_cache; + GHashTable *runtime_invoke_sig_cache; + + /* + * indexed by SignaturePointerPair + */ + GHashTable *delegate_abstract_invoke_cache; + GHashTable *delegate_bound_static_invoke_cache; + + /* + * indexed by MonoMethod pointers + * Protected by the marshal lock + */ + GHashTable *runtime_invoke_method_cache; + GHashTable *managed_wrapper_cache; + + GHashTable *native_wrapper_cache; + GHashTable *native_wrapper_aot_cache; + GHashTable *native_wrapper_check_cache; + GHashTable *native_wrapper_aot_check_cache; + + GHashTable *native_func_wrapper_aot_cache; + GHashTable *native_func_wrapper_indirect_cache; /* Indexed by MonoMethodSignature. Protected by the marshal lock */ + GHashTable *synchronized_cache; + GHashTable *unbox_wrapper_cache; + GHashTable *cominterop_invoke_cache; + GHashTable *cominterop_wrapper_cache; /* LOCKING: marshal lock */ + GHashTable *thunk_invoke_cache; +} MonoWrapperCaches; + /* Lock-free allocator */ typedef struct { guint8 *mem; @@ -68,7 +105,7 @@ struct _MonoAssemblyLoadContext { // If taking this with the domain assemblies_lock, always take this second MonoCoopMutex assemblies_lock; // Holds ALC-specific memory - MonoSingletonMemoryManager *memory_manager; + MonoMemoryManager *memory_manager; GPtrArray *generic_memory_managers; // Protects generic_memory_managers; if taking this with the domain alcs_lock, always take this second MonoCoopMutex memory_managers_lock; @@ -117,7 +154,6 @@ struct _MonoMemoryManager { /* Information maintained by the execution engine */ gpointer runtime_info; - // !!! REGISTERED AS GC ROOTS !!! // Hashtables for Reflection handles MonoGHashTable *type_hash; MonoConcGHashTable *refobject_hash; @@ -125,22 +161,34 @@ struct _MonoMemoryManager { MonoGHashTable *type_init_exception_hash; // Maps delegate trampoline addr -> delegate object //MonoGHashTable *delegate_hash_table; - // End of GC roots -}; - -struct _MonoSingletonMemoryManager { - MonoMemoryManager memory_manager; - - // Parent ALC, NULL on framework - MonoAssemblyLoadContext *alc; -}; - -struct _MonoGenericMemoryManager { - MonoMemoryManager memory_manager; + /* + * Generic instances and aggregated custom modifiers depend on many alcs, and they need to be deleted if one + * of the alcs they depend on is unloaded. For example, + * List depends on both List's alc and Foo's alc. + * A MemoryManager is the owner of all generic instances depending on the same set of + * alcs. + */ // Parent ALCs int n_alcs; + // Allocated from the mempool MonoAssemblyLoadContext **alcs; + + // Generic-specific caches + GHashTable *ginst_cache, *gmethod_cache, *gsignature_cache; + MonoConcurrentHashTable *gclass_cache; + + /* mirror caches of ones already on MonoImage. These ones contain generics */ + GHashTable *szarray_cache, *array_cache, *ptr_cache; + + MonoWrapperCaches wrapper_caches; + + GHashTable *aggregate_modifiers_cache; + + /* Indexed by MonoGenericParam pointers */ + GHashTable **gshared_types; + /* The length of the above array */ + int gshared_types_len; }; void @@ -236,14 +284,14 @@ mono_alc_get_all_loaded_assemblies (void); MONO_API void mono_loader_save_bundled_library (int fd, uint64_t offset, uint64_t size, const char *destfname); -MonoSingletonMemoryManager * -mono_mem_manager_create_singleton (MonoAssemblyLoadContext *alc, gboolean collectible); +MonoMemoryManager * +mono_mem_manager_new (MonoAssemblyLoadContext **alcs, int nalcs, gboolean collectible); void -mono_mem_manager_free_singleton (MonoSingletonMemoryManager *memory_manager, gboolean debug_unload); +mono_mem_manager_free (MonoMemoryManager *memory_manager, gboolean debug_unload); void -mono_mem_manager_free_objects_singleton (MonoSingletonMemoryManager *memory_manager); +mono_mem_manager_free_objects (MonoMemoryManager *memory_manager); void mono_mem_manager_lock (MonoMemoryManager *memory_manager); @@ -285,6 +333,12 @@ mono_mem_manager_free_debug_info (MonoMemoryManager *memory_manager); gboolean mono_mem_manager_mp_contains_addr (MonoMemoryManager *memory_manager, gpointer addr); +MonoMemoryManager * +mono_mem_manager_get_generic (MonoImage **images, int nimages); + +MonoMemoryManager* +mono_mem_manager_merge (MonoMemoryManager *mm1, MonoMemoryManager *mm2); + G_END_DECLS #endif diff --git a/src/mono/mono/metadata/memory-manager.c b/src/mono/mono/metadata/memory-manager.c index 84fb080e295b39..a1d235af6abbbe 100644 --- a/src/mono/mono/metadata/memory-manager.c +++ b/src/mono/mono/metadata/memory-manager.c @@ -3,6 +3,7 @@ #include #include #include +#include static LockFreeMempool* lock_free_mempool_new (void) @@ -18,7 +19,7 @@ lock_free_mempool_free (LockFreeMempool *mp) chunk = mp->chunks; while (chunk) { next = (LockFreeMempoolChunk *)chunk->prev; - mono_vfree (chunk, mono_pagesize (), MONO_MEM_ACCOUNT_DOMAIN); + mono_vfree (chunk, mono_pagesize (), MONO_MEM_ACCOUNT_MEM_MANAGER); chunk = next; } g_free (mp); @@ -36,7 +37,7 @@ lock_free_mempool_chunk_new (LockFreeMempool *mp, int len) size = mono_pagesize (); while (size - sizeof (LockFreeMempoolChunk) < len) size += mono_pagesize (); - chunk = (LockFreeMempoolChunk *)mono_valloc (0, size, MONO_MMAP_READ|MONO_MMAP_WRITE, MONO_MEM_ACCOUNT_DOMAIN); + chunk = (LockFreeMempoolChunk *)mono_valloc (0, size, MONO_MMAP_READ|MONO_MMAP_WRITE, MONO_MEM_ACCOUNT_MEM_MANAGER); g_assert (chunk); chunk->mem = (guint8 *)ALIGN_PTR_TO ((char*)chunk + sizeof (LockFreeMempoolChunk), 16); chunk->size = ((char*)chunk + size) - (char*)chunk->mem; @@ -90,12 +91,15 @@ lock_free_mempool_alloc0 (LockFreeMempool *mp, guint size) return res; } -static void -memory_manager_init (MonoMemoryManager *memory_manager, gboolean collectible) +MonoMemoryManager* +mono_mem_manager_new (MonoAssemblyLoadContext **alcs, int nalcs, gboolean collectible) { MonoDomain *domain = mono_get_root_domain (); + MonoMemoryManager *memory_manager; - memory_manager->freeing = FALSE; + memory_manager = g_new0 (MonoMemoryManager, 1); + memory_manager->collectible = collectible; + memory_manager->n_alcs = nalcs; mono_coop_mutex_init_recursive (&memory_manager->lock); mono_os_mutex_init (&memory_manager->mp_mutex); @@ -104,6 +108,9 @@ memory_manager_init (MonoMemoryManager *memory_manager, gboolean collectible) memory_manager->code_mp = mono_code_manager_new (); memory_manager->lock_free_mp = lock_free_mempool_new (); + memory_manager->alcs = mono_mempool_alloc0 (memory_manager->_mp, sizeof (MonoAssemblyLoadContext *) * nalcs); + memcpy (memory_manager->alcs, alcs, sizeof (MonoAssemblyLoadContext *) * nalcs); + memory_manager->class_vtable_array = g_ptr_array_new (); // TODO: make these not linked to the domain for debugging @@ -113,18 +120,8 @@ memory_manager_init (MonoMemoryManager *memory_manager, gboolean collectible) if (mono_get_runtime_callbacks ()->init_mem_manager) mono_get_runtime_callbacks ()->init_mem_manager (memory_manager); -} - -MonoSingletonMemoryManager * -mono_mem_manager_create_singleton (MonoAssemblyLoadContext *alc, gboolean collectible) -{ - MonoSingletonMemoryManager *mem_manager = g_new0 (MonoSingletonMemoryManager, 1); - memory_manager_init ((MonoMemoryManager *)mem_manager, collectible); - mem_manager->memory_manager.is_generic = FALSE; - mem_manager->alc = alc; - - return mem_manager; + return memory_manager; } static void @@ -198,19 +195,19 @@ memory_manager_delete (MonoMemoryManager *memory_manager, gboolean debug_unload) } void -mono_mem_manager_free_objects_singleton (MonoSingletonMemoryManager *memory_manager) +mono_mem_manager_free_objects (MonoMemoryManager *memory_manager) { - g_assert (!memory_manager->memory_manager.freeing); + g_assert (!memory_manager->freeing); - memory_manager_delete_objects (&memory_manager->memory_manager); + memory_manager_delete_objects (memory_manager); } void -mono_mem_manager_free_singleton (MonoSingletonMemoryManager *memory_manager, gboolean debug_unload) +mono_mem_manager_free (MonoMemoryManager *memory_manager, gboolean debug_unload) { - g_assert (!memory_manager->memory_manager.is_generic); + g_assert (!memory_manager->is_generic); - memory_manager_delete (&memory_manager->memory_manager, debug_unload); + memory_manager_delete (memory_manager, debug_unload); g_free (memory_manager); } @@ -344,3 +341,207 @@ gpointer { return lock_free_mempool_alloc0 (memory_manager->lock_free_mp, size); } + +//107, 131, 163 +#define HASH_TABLE_SIZE 163 +static MonoMemoryManager *mem_manager_cache [HASH_TABLE_SIZE]; +static gint32 mem_manager_cache_hit, mem_manager_cache_miss; + +static guint32 +mix_hash (uintptr_t source) +{ + unsigned int hash = source; + + // Actual hash + hash = (((hash * 215497) >> 16) ^ ((hash * 1823231) + hash)); + + // Mix in highest bits on 64-bit systems only + if (sizeof (source) > 4) + hash = hash ^ ((source >> 31) >> 1); + + return hash; +} + +static guint32 +hash_alcs (MonoAssemblyLoadContext **alcs, int nalcs) +{ + guint32 res = 0; + int i; + for (i = 0; i < nalcs; ++i) + res += mix_hash ((size_t)alcs [i]); + + return res; +} + +static gboolean +match_mem_manager (MonoMemoryManager *mm, MonoAssemblyLoadContext **alcs, int nalcs) +{ + int j, k; + + if (mm->n_alcs != nalcs) + return FALSE; + /* The order might differ so check all pairs */ + for (j = 0; j < nalcs; ++j) { + for (k = 0; k < nalcs; ++k) + if (mm->alcs [k] == alcs [j]) + break; + if (k == nalcs) + /* Not found */ + break; + } + + return j == nalcs; +} + +static MonoMemoryManager* +mem_manager_cache_get (MonoAssemblyLoadContext **alcs, int nalcs) +{ + guint32 hash_code = hash_alcs (alcs, nalcs); + int index = hash_code % HASH_TABLE_SIZE; + MonoMemoryManager *mm = mem_manager_cache [index]; + if (!mm || !match_mem_manager (mm, alcs, nalcs)) { + UnlockedIncrement (&mem_manager_cache_miss); + return NULL; + } + UnlockedIncrement (&mem_manager_cache_hit); + return mm; +} + +static void +mem_manager_cache_add (MonoMemoryManager *mem_manager) +{ + guint32 hash_code = hash_alcs (mem_manager->alcs, mem_manager->n_alcs); + int index = hash_code % HASH_TABLE_SIZE; + mem_manager_cache [index] = mem_manager; +} + +static MonoMemoryManager* +get_mem_manager_for_alcs (MonoAssemblyLoadContext **alcs, int nalcs) +{ + MonoAssemblyLoadContext *alc; + GPtrArray *mem_managers; + MonoMemoryManager *res; + gboolean collectible; + + /* Can happen for dynamic images */ + if (nalcs == 0) + return mono_alc_get_default ()->memory_manager; + + /* Common case */ + if (nalcs == 1) + return alcs [0]->memory_manager; + + collectible = FALSE; + for (int i = 0; i < nalcs; ++i) + collectible |= alcs [i]->collectible; + if (!collectible) + /* Can use the default alc */ + return mono_alc_get_default ()->memory_manager; + + // Check in a lock free cache + res = mem_manager_cache_get (alcs, nalcs); + if (res) + return res; + + /* + * Find an existing mem manager for these ALCs. + * This can exist even if the cache lookup fails since the cache is very simple. + */ + + /* We can search any ALC in the list, use the first one for now */ + alc = alcs [0]; + + mono_alc_memory_managers_lock (alc); + + mem_managers = alc->generic_memory_managers; + + res = NULL; + for (int mindex = 0; mindex < mem_managers->len; ++mindex) { + MonoMemoryManager *mm = (MonoMemoryManager*)g_ptr_array_index (mem_managers, mindex); + + if (match_mem_manager (mm, alcs, nalcs)) { + res = mm; + break; + } + } + + mono_alc_memory_managers_unlock (alc); + + if (res) + return res; + + /* Create new mem manager */ + res = mono_mem_manager_new (alcs, nalcs, collectible); + res->is_generic = TRUE; + + /* The hashes are lazily inited in metadata.c */ + + /* Register it into its ALCs */ + for (int i = 0; i < nalcs; ++i) { + mono_alc_memory_managers_lock (alcs [i]); + g_ptr_array_add (alcs [i]->generic_memory_managers, res); + mono_alc_memory_managers_unlock (alcs [i]); + } + + mono_memory_barrier (); + + mem_manager_cache_add (res); + + return res; +} + +/* + * mono_mem_manager_get_generic: + * + * Return a memory manager for allocating memory owned by the set of IMAGES. + */ +MonoMemoryManager* +mono_mem_manager_get_generic (MonoImage **images, int nimages) +{ + MonoAssemblyLoadContext **alcs = g_newa (MonoAssemblyLoadContext*, nimages); + int nalcs, j; + + /* Collect the set of ALCs owning the images */ + nalcs = 0; + for (int i = 0; i < nimages; ++i) { + MonoAssemblyLoadContext *alc = mono_image_get_alc (images [i]); + + if (!alc) + continue; + + /* O(n^2), but shouldn't be a problem in practice */ + for (j = 0; j < nalcs; ++j) + if (alcs [j] == alc) + break; + if (j == nalcs) + alcs [nalcs ++] = alc; + } + + return get_mem_manager_for_alcs (alcs, nalcs); +} + +/* + * mono_mem_manager_merge: + * + * Return a mem manager which depends on the ALCs of MM1/MM2. + */ +MonoMemoryManager* +mono_mem_manager_merge (MonoMemoryManager *mm1, MonoMemoryManager *mm2) +{ + MonoAssemblyLoadContext **alcs = g_newa (MonoAssemblyLoadContext*, mm1->n_alcs + mm2->n_alcs); + + memcpy (alcs, mm1->alcs, sizeof (MonoAssemblyLoadContext*) * mm1->n_alcs); + + int nalcs = mm1->n_alcs; + /* O(n^2), but shouldn't be a problem in practice */ + for (int i = 0; i < mm2->n_alcs; ++i) { + int j; + for (j = 0; j < mm1->n_alcs; ++j) { + if (mm2->alcs [i] == mm1->alcs [j]) + break; + } + if (j == mm1->n_alcs) + alcs [nalcs ++] = mm2->alcs [i]; + } + return get_mem_manager_for_alcs (alcs, nalcs); +} diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index fed59f11db4782..919abc40b92fb8 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -231,44 +231,6 @@ struct _MonoAssembly { guint32 skipverification:2; /* Has SecurityPermissionFlag.SkipVerification permission */ }; -typedef struct { - /* - * indexed by MonoMethodSignature - * Protected by the marshal lock - */ - GHashTable *delegate_invoke_cache; - GHashTable *delegate_begin_invoke_cache; - GHashTable *delegate_end_invoke_cache; - GHashTable *runtime_invoke_signature_cache; - GHashTable *runtime_invoke_sig_cache; - - /* - * indexed by SignaturePointerPair - */ - GHashTable *delegate_abstract_invoke_cache; - GHashTable *delegate_bound_static_invoke_cache; - - /* - * indexed by MonoMethod pointers - * Protected by the marshal lock - */ - GHashTable *runtime_invoke_method_cache; - GHashTable *managed_wrapper_cache; - - GHashTable *native_wrapper_cache; - GHashTable *native_wrapper_aot_cache; - GHashTable *native_wrapper_check_cache; - GHashTable *native_wrapper_aot_check_cache; - - GHashTable *native_func_wrapper_aot_cache; - GHashTable *native_func_wrapper_indirect_cache; /* Indexed by MonoMethodSignature. Protected by the marshal lock */ - GHashTable *synchronized_cache; - GHashTable *unbox_wrapper_cache; - GHashTable *cominterop_invoke_cache; - GHashTable *cominterop_wrapper_cache; /* LOCKING: marshal lock */ - GHashTable *thunk_invoke_cache; -} MonoWrapperCaches; - typedef struct { const char* data; guint32 size; @@ -584,42 +546,6 @@ struct _MonoImage { mono_mutex_t lock; }; -/* - * Generic instances and aggregated custom modifiers depend on many images, and they need to be deleted if one - * of the images they depend on is unloaded. For example, - * List depends on both List's image and Foo's image. - * A MonoImageSet is the owner of all generic instances depending on the same set of - * images. - */ -typedef struct { - int nimages; - MonoImage **images; - - // Generic-specific caches - GHashTable *ginst_cache, *gmethod_cache, *gsignature_cache; - MonoConcurrentHashTable *gclass_cache; - - /* mirror caches of ones already on MonoImage. These ones contain generics */ - GHashTable *szarray_cache, *array_cache, *ptr_cache; - - MonoWrapperCaches wrapper_caches; - - GHashTable *aggregate_modifiers_cache; - - /* Indexed by MonoGenericParam pointers */ - GHashTable **gshared_types; - /* The length of the above array */ - int gshared_types_len; - - mono_mutex_t lock; - - /* - * Memory for generic instances owned by this image set should be allocated from - * this mempool, using the mono_image_set_alloc family of functions. - */ - MonoMemPool *mempool; -} MonoImageSet; - enum { MONO_SECTION_TEXT, MONO_SECTION_RSRC, @@ -912,32 +838,6 @@ void mono_image_load_enc_delta (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error); #endif /* ENABLE_METADATA_UPDATE */ -gpointer -mono_image_set_alloc (MonoImageSet *set, guint size); - -gpointer -mono_image_set_alloc0 (MonoImageSet *set, guint size); - -void -mono_image_set_lock (MonoImageSet *set); - -void -mono_image_set_unlock (MonoImageSet *set); - -char* -mono_image_set_strdup (MonoImageSet *set, const char *s); - -MonoImageSet * -mono_metadata_get_image_set_for_aggregate_modifiers (MonoAggregateModContainer *amods); - -MonoImageSet * -mono_metadata_get_image_set_for_type (MonoType *type); - -MonoImageSet * -mono_metadata_merge_image_sets (MonoImageSet *set1, MonoImageSet *set2); - -#define mono_image_set_new0(image,type,size) ((type *) mono_image_set_alloc0 (image, sizeof (type)* (size))) - gboolean mono_image_load_cli_header (MonoImage *image, MonoCLIImageInfo *iinfo); @@ -959,9 +859,6 @@ mono_metadata_decode_row_dynamic_checked (const MonoDynamicImage *image, const M MonoType* mono_metadata_get_shared_type (MonoType *type); -void -mono_metadata_clean_for_image (MonoImage *image); - void mono_metadata_clean_generic_classes_for_image (MonoImage *image); @@ -1193,12 +1090,6 @@ mono_metadata_parse_type_checked (MonoImage *m, MonoGenericContainer *container, MonoGenericContainer * mono_get_anonymous_container_for_image (MonoImage *image, gboolean is_mvar); -char * -mono_image_set_description (MonoImageSet *); - -MonoImageSet * -mono_find_image_set_owner (void *ptr); - void mono_loader_register_module (const char *name, MonoDl *module); diff --git a/src/mono/mono/metadata/metadata.c b/src/mono/mono/metadata/metadata.c index e5ec41db296f25..e18a6f4ec8b8cc 100644 --- a/src/mono/mono/metadata/metadata.c +++ b/src/mono/mono/metadata/metadata.c @@ -39,9 +39,6 @@ #include #include -static gint32 img_set_cache_hit, img_set_cache_miss, img_set_count; - - /* Auxiliary structure used for caching inflated signatures */ typedef struct { MonoMethodSignature *sig; @@ -1903,12 +1900,6 @@ builtin_types[] = { static GHashTable *type_cache = NULL; static gint32 next_generic_inst_id = 0; -/* Protected by image_sets_mutex */ -static MonoImageSet *mscorlib_image_set; -/* Protected by image_sets_mutex */ -static GPtrArray *image_sets; -static mono_mutex_t image_sets_mutex; - static guint mono_generic_class_hash (gconstpointer data); /* @@ -2038,12 +2029,6 @@ mono_metadata_init (void) for (i = 0; i < NBUILTIN_TYPES (); ++i) g_hash_table_insert (type_cache, (gpointer) &builtin_types [i], (gpointer) &builtin_types [i]); - mono_os_mutex_init_recursive (&image_sets_mutex); - - mono_counters_register ("ImgSet Cache Hit", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_cache_hit); - mono_counters_register ("ImgSet Cache Miss", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_cache_miss); - mono_counters_register ("ImgSet Count", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_count); - #ifdef ENABLE_METADATA_UPDATE mono_metadata_update_init (); #endif @@ -2930,319 +2915,6 @@ aggregate_modifiers_in_image (MonoAggregateModContainer *amods, MonoImage *image return FALSE; } -static void -image_sets_lock (void) -{ - mono_os_mutex_lock (&image_sets_mutex); -} - -static void -image_sets_unlock (void) -{ - mono_os_mutex_unlock (&image_sets_mutex); -} - -//1103, 1327, 1597 -#define HASH_TABLE_SIZE 1103 -static MonoImageSet *img_set_cache [HASH_TABLE_SIZE]; - -static guint32 -mix_hash (uintptr_t source) -{ - unsigned int hash = source; - - // Actual hash - hash = (((hash * 215497) >> 16) ^ ((hash * 1823231) + hash)); - - // Mix in highest bits on 64-bit systems only - if (sizeof (source) > 4) - hash = hash ^ ((source >> 31) >> 1); - - return hash; -} - -static guint32 -hash_images (MonoImage **images, int nimages) -{ - guint32 res = 0; - int i; - for (i = 0; i < nimages; ++i) - res += mix_hash ((size_t)images [i]); - - return res; -} - -static gboolean -compare_img_set (MonoImageSet *set, MonoImage **images, int nimages) -{ - int j, k; - - if (set->nimages != nimages) - return FALSE; - - for (j = 0; j < nimages; ++j) { - for (k = 0; k < nimages; ++k) - if (set->images [k] == images [j]) - break; // Break on match - - // If we iterated all the way through set->images, images[j] was *not* found. - if (k == nimages) - break; // Break on "image not found" - } - - // If we iterated all the way through images without breaking, all items in images were found in set->images - return j == nimages; -} - - -static MonoImageSet* -img_set_cache_get (MonoImage **images, int nimages) -{ - guint32 hash_code = hash_images (images, nimages); - int index = hash_code % HASH_TABLE_SIZE; - MonoImageSet *img = img_set_cache [index]; - if (!img || !compare_img_set (img, images, nimages)) { - UnlockedIncrement (&img_set_cache_miss); - return NULL; - } - UnlockedIncrement (&img_set_cache_hit); - return img; -} - -static void -img_set_cache_add (MonoImageSet *set) -{ - guint32 hash_code = hash_images (set->images, set->nimages); - int index = hash_code % HASH_TABLE_SIZE; - img_set_cache [index] = set; -} - -static void -img_set_cache_remove (MonoImageSet *is) -{ - guint32 hash_code = hash_images (is->images, is->nimages); - int index = hash_code % HASH_TABLE_SIZE; - if (img_set_cache [index] == is) - img_set_cache [index] = NULL; -} -/* - * get_image_set: - * - * Return a MonoImageSet representing the set of images in IMAGES. - */ -static MonoImageSet* -get_image_set (MonoImage **images, int nimages) -{ - int i, j, k; - MonoImageSet *set; - GSList *l; - - /* Common case: Image set contains corlib only. If we've seen that case before, we cached the set. */ - if (nimages == 1 && images [0] == mono_defaults.corlib && mscorlib_image_set) - return mscorlib_image_set; - - /* Happens with empty generic instances */ - // FIXME: Is corlib the correct thing to return here? If so, why? This may be an artifact of generic instances previously defaulting to allocating from corlib. - if (nimages == 0) - return mscorlib_image_set; - - set = img_set_cache_get (images, nimages); - if (set) - return set; - - image_sets_lock (); - - if (!image_sets) - image_sets = g_ptr_array_new (); - - // Before we go on, we should check to see whether a MonoImageSet with these images already exists. - // We can search the referred-by imagesets of any one of our images to do this. Arbitrarily pick one here: - if (images [0] == mono_defaults.corlib && nimages > 1) - l = images [1]->image_sets; // Prefer not to search the imagesets of corlib-- that will be a long list. - else - l = images [0]->image_sets; - - set = NULL; - while (l) // Iterate over selected list, looking for an imageset with members equal to our target one - { - set = (MonoImageSet *)l->data; - - if (set->nimages == nimages) { // Member count differs, this can't be it - // Compare all members to all members-- order might be different - for (j = 0; j < nimages; ++j) { - for (k = 0; k < nimages; ++k) - if (set->images [k] == images [j]) - break; // Break on match - - // If we iterated all the way through set->images, images[j] was *not* found. - if (k == nimages) - break; // Break on "image not found" - } - - // If we iterated all the way through images without breaking, all items in images were found in set->images - if (j == nimages) { - // Break on "found a set with equal members". - // This happens in case of a hash collision with a previously cached set. - break; - } - } - - l = l->next; - } - - // If we iterated all the way through l without breaking, the imageset does not already exist and we should create it - if (!l) { - set = g_new0 (MonoImageSet, 1); - set->nimages = nimages; - set->images = g_new0 (MonoImage*, nimages); - mono_os_mutex_init_recursive (&set->lock); - for (i = 0; i < nimages; ++i) - set->images [i] = images [i]; - set->gclass_cache = mono_conc_hashtable_new_full (mono_generic_class_hash, mono_generic_class_equal, NULL, (GDestroyNotify)free_generic_class); - set->ginst_cache = g_hash_table_new_full (mono_metadata_generic_inst_hash, mono_metadata_generic_inst_equal, NULL, (GDestroyNotify)free_generic_inst); - set->gmethod_cache = g_hash_table_new_full (inflated_method_hash, inflated_method_equal, NULL, (GDestroyNotify)free_inflated_method); - set->gsignature_cache = g_hash_table_new_full (inflated_signature_hash, inflated_signature_equal, NULL, (GDestroyNotify)free_inflated_signature); - - set->szarray_cache = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, NULL); - set->array_cache = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, NULL); - - set->aggregate_modifiers_cache = g_hash_table_new_full (aggregate_modifiers_hash, aggregate_modifiers_equal, NULL, (GDestroyNotify)free_aggregate_modifiers); - - for (i = 0; i < nimages; ++i) - set->images [i]->image_sets = g_slist_prepend (set->images [i]->image_sets, set); - - g_ptr_array_add (image_sets, set); - UnlockedIncrement (&img_set_count); /* locked by image_sets_lock () */ - } - - /* Cache the set. If there was a cache collision, the previously cached value will be replaced. */ - img_set_cache_add (set); - - if (nimages == 1 && images [0] == mono_defaults.corlib) { - mono_memory_barrier (); - mscorlib_image_set = set; - } - - image_sets_unlock (); - - return set; -} - -static void -delete_image_set (MonoImageSet *set) -{ - int i; - - mono_conc_hashtable_destroy (set->gclass_cache); - g_hash_table_destroy (set->ginst_cache); - g_hash_table_destroy (set->gmethod_cache); - g_hash_table_destroy (set->gsignature_cache); - - g_hash_table_destroy (set->szarray_cache); - g_hash_table_destroy (set->array_cache); - if (set->ptr_cache) - g_hash_table_destroy (set->ptr_cache); - - g_hash_table_destroy (set->aggregate_modifiers_cache); - - for (i = 0; i < set->gshared_types_len; ++i) { - if (set->gshared_types [i]) - g_hash_table_destroy (set->gshared_types [i]); - } - g_free (set->gshared_types); - - mono_wrapper_caches_free (&set->wrapper_caches); - - image_sets_lock (); - - for (i = 0; i < set->nimages; ++i) - set->images [i]->image_sets = g_slist_remove (set->images [i]->image_sets, set); - - g_ptr_array_remove (image_sets, set); - - image_sets_unlock (); - - img_set_cache_remove (set); - - if (set->mempool) - mono_mempool_destroy (set->mempool); - g_free (set->images); - mono_os_mutex_destroy (&set->lock); - g_free (set); -} - -void -mono_image_set_lock (MonoImageSet *set) -{ - mono_os_mutex_lock (&set->lock); -} - -void -mono_image_set_unlock (MonoImageSet *set) -{ - mono_os_mutex_unlock (&set->lock); -} - -gpointer -mono_image_set_alloc (MonoImageSet *set, guint size) -{ - gpointer res; - - mono_image_set_lock (set); - if (!set->mempool) - set->mempool = mono_mempool_new_size (INITIAL_IMAGE_SET_SIZE); - res = mono_mempool_alloc (set->mempool, size); - mono_image_set_unlock (set); - - return res; -} - -gpointer -mono_image_set_alloc0 (MonoImageSet *set, guint size) -{ - gpointer res; - - mono_image_set_lock (set); - if (!set->mempool) - set->mempool = mono_mempool_new_size (INITIAL_IMAGE_SET_SIZE); - res = mono_mempool_alloc0 (set->mempool, size); - mono_image_set_unlock (set); - - return res; -} - -char* -mono_image_set_strdup (MonoImageSet *set, const char *s) -{ - char *res; - - mono_image_set_lock (set); - if (!set->mempool) - set->mempool = mono_mempool_new_size (INITIAL_IMAGE_SET_SIZE); - res = mono_mempool_strdup (set->mempool, s); - mono_image_set_unlock (set); - - return res; -} - -// Get a descriptive string for a MonoImageSet -// Callers are obligated to free buffer with g_free after use -char * -mono_image_set_description (MonoImageSet *set) -{ - GString *result = g_string_new (NULL); - int img; - g_string_append (result, "["); - for (img = 0; img < set->nimages; img++) - { - if (img > 0) - g_string_append (result, ", "); - g_string_append (result, set->images[img]->name); - } - g_string_append (result, "]"); - return g_string_free (result, FALSE); -} - /* * Structure used by the collect_..._images functions to store the image list. */ @@ -3500,86 +3172,6 @@ check_gmethod (gpointer key, gpointer value, gpointer data) g_assert (!signature_in_image (mono_method_signature_internal ((MonoMethod*)method), image)); } -/* - * check_image_sets: - * - * Run a consistency check on the image set data structures. - */ -static G_GNUC_UNUSED void -check_image_sets (MonoImage *image) -{ - int i; - GSList *l = image->image_sets; - - if (!image_sets) - return; - - for (i = 0; i < image_sets->len; ++i) { - MonoImageSet *set = (MonoImageSet *)g_ptr_array_index (image_sets, i); - - if (!g_slist_find (l, set)) { - g_hash_table_foreach (set->gmethod_cache, check_gmethod, image); - } - } -} - -void -mono_metadata_clean_for_image (MonoImage *image) -{ - CleanForImageUserData ginst_data, gclass_data, amods_data; - GSList *l, *set_list; - - //check_image_sets (image); - - /* - * The data structures could reference each other so we delete them in two phases. - * This is required because of the hashing functions in gclass/ginst_cache. - */ - ginst_data.image = gclass_data.image = image; - ginst_data.list = gclass_data.list = NULL; - amods_data.image = image; - amods_data.list = NULL; - - /* Collect the items to delete */ - /* delete_image_set () modifies the lists so make a copy */ - for (l = image->image_sets; l; l = l->next) { - MonoImageSet *set = (MonoImageSet *)l->data; - - mono_image_set_lock (set); - mono_conc_hashtable_foreach_steal (set->gclass_cache, steal_gclass_in_image, &gclass_data); - g_hash_table_foreach_steal (set->ginst_cache, steal_ginst_in_image, &ginst_data); - g_hash_table_foreach_remove (set->gmethod_cache, inflated_method_in_image, image); - g_hash_table_foreach_remove (set->gsignature_cache, inflated_signature_in_image, image); - - g_hash_table_foreach_steal (set->szarray_cache, class_in_image, image); - g_hash_table_foreach_steal (set->array_cache, class_in_image, image); - if (set->ptr_cache) - g_hash_table_foreach_steal (set->ptr_cache, class_in_image, image); - - g_hash_table_foreach_steal (set->aggregate_modifiers_cache, steal_aggregate_modifiers_in_image, &amods_data); - - mono_image_set_unlock (set); - } - - /* Delete the removed items */ - for (l = ginst_data.list; l; l = l->next) - free_generic_inst ((MonoGenericInst *)l->data); - for (l = gclass_data.list; l; l = l->next) - free_generic_class ((MonoGenericClass *)l->data); - for (l = amods_data.list; l; l = l->next) - free_aggregate_modifiers ((MonoAggregateModContainer *)l->data); - g_slist_free (ginst_data.list); - g_slist_free (gclass_data.list); - /* delete_image_set () modifies the lists so make a copy */ - set_list = g_slist_copy (image->image_sets); - for (l = set_list; l; l = l->next) { - MonoImageSet *set = (MonoImageSet *)l->data; - - delete_image_set (set); - } - g_slist_free (set_list); -} - static void free_inflated_method (MonoMethodInflated *imethod) { @@ -3616,7 +3208,6 @@ static void free_inflated_signature (MonoInflatedMethodSignature *sig) { mono_metadata_free_inflated_signature (sig->sig); - g_free (sig); } static void @@ -3639,104 +3230,79 @@ mono_metadata_get_inflated_signature (MonoMethodSignature *sig, MonoGenericConte MonoInflatedMethodSignature helper; MonoInflatedMethodSignature *res; CollectData data; - MonoImageSet *set; helper.sig = sig; helper.context.class_inst = context->class_inst; helper.context.method_inst = context->method_inst; collect_data_init (&data); - collect_inflated_signature_images (&helper, &data); - - set = get_image_set (data.images, data.nimages); - + MonoMemoryManager *mm = mono_mem_manager_get_generic (data.images, data.nimages); collect_data_free (&data); - mono_image_set_lock (set); + mono_mem_manager_lock (mm); - res = (MonoInflatedMethodSignature *)g_hash_table_lookup (set->gsignature_cache, &helper); + if (!mm->gsignature_cache) + mm->gsignature_cache = g_hash_table_new_full (inflated_signature_hash, inflated_signature_equal, NULL, (GDestroyNotify)free_inflated_signature); + res = (MonoInflatedMethodSignature *)g_hash_table_lookup (mm->gsignature_cache, &helper); if (!res) { - res = g_new0 (MonoInflatedMethodSignature, 1); + res = mono_mem_manager_alloc0 (mm, sizeof (MonoInflatedMethodSignature)); res->sig = sig; res->context.class_inst = context->class_inst; res->context.method_inst = context->method_inst; - g_hash_table_insert (set->gsignature_cache, res, res); + g_hash_table_insert (mm->gsignature_cache, res, res); } - mono_image_set_unlock (set); + mono_mem_manager_unlock (mm); return res->sig; } -MonoImageSet * -mono_metadata_get_image_set_for_type (MonoType *type) +MonoMemoryManager * +mono_metadata_get_mem_manager_for_type (MonoType *type) { - MonoImageSet *set; + MonoMemoryManager *mm; CollectData image_set_data; collect_data_init (&image_set_data); collect_type_images (type, &image_set_data); - set = get_image_set (image_set_data.images, image_set_data.nimages); + mm = mono_mem_manager_get_generic (image_set_data.images, image_set_data.nimages); collect_data_free (&image_set_data); - return set; + return mm; } -MonoImageSet * -mono_metadata_get_image_set_for_class (MonoClass *klass) +MonoMemoryManager * +mono_metadata_get_mem_manager_for_class (MonoClass *klass) { - return mono_metadata_get_image_set_for_type (m_class_get_byval_arg (klass)); + return mono_metadata_get_mem_manager_for_type (m_class_get_byval_arg (klass)); } -MonoImageSet * -mono_metadata_get_image_set_for_method (MonoMethodInflated *method) +MonoMemoryManager * +mono_metadata_get_mem_manager_for_method (MonoMethodInflated *method) { - MonoImageSet *set; + MonoMemoryManager *mm; CollectData image_set_data; collect_data_init (&image_set_data); collect_method_images (method, &image_set_data); - set = get_image_set (image_set_data.images, image_set_data.nimages); + mm = mono_mem_manager_get_generic (image_set_data.images, image_set_data.nimages); collect_data_free (&image_set_data); - return set; + return mm; } -MonoImageSet * -mono_metadata_get_image_set_for_aggregate_modifiers (MonoAggregateModContainer *amods) +static MonoMemoryManager * +mono_metadata_get_mem_manager_for_aggregate_modifiers (MonoAggregateModContainer *amods) { - MonoImageSet *set; + MonoMemoryManager *mm; CollectData image_set_data; collect_data_init (&image_set_data); collect_aggregate_modifiers_images (amods, &image_set_data); - set = get_image_set (image_set_data.images, image_set_data.nimages); + mm = mono_mem_manager_get_generic (image_set_data.images, image_set_data.nimages); collect_data_free (&image_set_data); - return set; -} - -MonoImageSet * -mono_metadata_merge_image_sets (MonoImageSet *set1, MonoImageSet *set2) -{ - MonoImage **images = g_newa (MonoImage*, set1->nimages + set2->nimages); - - /* Add images from set1 */ - memcpy (images, set1->images, sizeof (MonoImage*) * set1->nimages); - - int nimages = set1->nimages; - // FIXME: Quaratic - /* Add images from set2 */ - for (int i = 0; i < set2->nimages; ++i) { - int j; - for (j = 0; j < set1->nimages; ++j) { - if (set2->images [i] == set1->images [j]) - break; - } - if (j == set1->nimages) - images [nimages ++] = set2->images [i]; - } - return get_image_set (images, nimages); + return mm; } static gboolean @@ -3807,35 +3373,36 @@ mono_metadata_get_canonical_generic_inst (MonoGenericInst *candidate) CollectData data; int type_argc = candidate->type_argc; gboolean is_open = candidate->is_open; - MonoImageSet *set; collect_data_init (&data); - collect_ginst_images (candidate, &data); - - set = get_image_set (data.images, data.nimages); - + MonoMemoryManager *mm = mono_mem_manager_get_generic (data.images, data.nimages); collect_data_free (&data); - mono_image_set_lock (set); + mono_mem_manager_lock (mm); + + if (!mm->ginst_cache) + mm->ginst_cache = g_hash_table_new_full (mono_metadata_generic_inst_hash, mono_metadata_generic_inst_equal, NULL, (GDestroyNotify)free_generic_inst); - MonoGenericInst *ginst = (MonoGenericInst *)g_hash_table_lookup (set->ginst_cache, candidate); + MonoGenericInst *ginst = (MonoGenericInst *)g_hash_table_lookup (mm->ginst_cache, candidate); if (!ginst) { int size = MONO_SIZEOF_GENERIC_INST + type_argc * sizeof (MonoType *); - ginst = (MonoGenericInst *)mono_image_set_alloc0 (set, size); + ginst = (MonoGenericInst *)mono_mem_manager_alloc0 (mm, size); #ifndef MONO_SMALL_CONFIG ginst->id = mono_atomic_inc_i32 (&next_generic_inst_id); #endif ginst->is_open = is_open; ginst->type_argc = type_argc; + // FIXME: Dup into the mem manager for (int i = 0; i < type_argc; ++i) ginst->type_argv [i] = mono_metadata_type_dup (NULL, candidate->type_argv [i]); - g_hash_table_insert (set->ginst_cache, ginst, ginst); + g_hash_table_insert (mm->ginst_cache, ginst, ginst); } - mono_image_set_unlock (set); + mono_mem_manager_unlock (mm); + return ginst; } @@ -3843,23 +3410,26 @@ MonoAggregateModContainer * mono_metadata_get_canonical_aggregate_modifiers (MonoAggregateModContainer *candidate) { g_assert (candidate->count > 0); - MonoImageSet *set = mono_metadata_get_image_set_for_aggregate_modifiers (candidate); + MonoMemoryManager *mm = mono_metadata_get_mem_manager_for_aggregate_modifiers (candidate); - mono_image_set_lock (set); + mono_mem_manager_lock (mm); + + if (!mm->aggregate_modifiers_cache) + mm->aggregate_modifiers_cache = g_hash_table_new_full (aggregate_modifiers_hash, aggregate_modifiers_equal, NULL, (GDestroyNotify)free_aggregate_modifiers); - MonoAggregateModContainer *amods = (MonoAggregateModContainer *)g_hash_table_lookup (set->aggregate_modifiers_cache, candidate); + MonoAggregateModContainer *amods = (MonoAggregateModContainer *)g_hash_table_lookup (mm->aggregate_modifiers_cache, candidate); if (!amods) { size_t size = mono_sizeof_aggregate_modifiers (candidate->count); - amods = (MonoAggregateModContainer *)mono_image_set_alloc0 (set, size); + amods = (MonoAggregateModContainer *)mono_mem_manager_alloc0 (mm, size); amods->count = candidate->count; for (int i = 0; i < candidate->count; ++i) { amods->modifiers [i].required = candidate->modifiers [i].required; amods->modifiers [i].type = mono_metadata_type_dup (NULL, candidate->modifiers [i].type); } - g_hash_table_insert (set->aggregate_modifiers_cache, amods, amods); + g_hash_table_insert (mm->aggregate_modifiers_cache, amods, amods); } - mono_image_set_unlock (set); + mono_mem_manager_unlock (mm); return amods; } @@ -3885,7 +3455,6 @@ mono_metadata_lookup_generic_class (MonoClass *container_class, MonoGenericInst MonoGenericClass *gclass; MonoGenericClass helper; gboolean is_tb_open = mono_metadata_is_type_builder_generic_type_definition (container_class, inst, is_dynamic); - MonoImageSet *set; CollectData data; g_assert (mono_class_get_generic_container (container_class)->type_argc == inst->type_argc); @@ -3897,14 +3466,21 @@ mono_metadata_lookup_generic_class (MonoClass *container_class, MonoGenericInst helper.is_tb_open = is_tb_open; collect_data_init (&data); - collect_gclass_images (&helper, &data); - - set = get_image_set (data.images, data.nimages); - + MonoMemoryManager *mm = mono_mem_manager_get_generic (data.images, data.nimages); collect_data_free (&data); - gclass = (MonoGenericClass *)mono_conc_hashtable_lookup (set->gclass_cache, &helper); + if (!mm->gclass_cache) { + mono_mem_manager_lock (mm); + if (!mm->gclass_cache) { + MonoConcurrentHashTable *cache = mono_conc_hashtable_new_full (mono_generic_class_hash, mono_generic_class_equal, NULL, (GDestroyNotify)free_generic_class); + mono_memory_barrier (); + mm->gclass_cache = cache; + } + mono_mem_manager_unlock (mm); + } + + gclass = (MonoGenericClass *)mono_conc_hashtable_lookup (mm->gclass_cache, &helper); /* A tripwire just to keep us honest */ g_assert (!helper.cached_class); @@ -3912,7 +3488,9 @@ mono_metadata_lookup_generic_class (MonoClass *container_class, MonoGenericInst if (gclass) return gclass; - gclass = mono_image_set_new0 (set, MonoGenericClass, 1); + mono_mem_manager_lock (mm); + + gclass = mono_mem_manager_alloc0 (mm, sizeof (MonoGenericClass)); if (is_dynamic) gclass->is_dynamic = 1; @@ -3920,19 +3498,17 @@ mono_metadata_lookup_generic_class (MonoClass *container_class, MonoGenericInst gclass->container_class = container_class; gclass->context.class_inst = inst; gclass->context.method_inst = NULL; - gclass->owner = set; + gclass->owner = mm; if (inst == mono_class_get_generic_container (container_class)->context.class_inst && !is_tb_open) gclass->cached_class = container_class; - mono_image_set_lock (set); - - MonoGenericClass *gclass2 = (MonoGenericClass*)mono_conc_hashtable_insert (set->gclass_cache, gclass, gclass); + MonoGenericClass *gclass2 = (MonoGenericClass*)mono_conc_hashtable_insert (mm->gclass_cache, gclass, gclass); if (!gclass2) gclass2 = gclass; // g_hash_table_insert (set->gclass_cache, gclass, gclass); - mono_image_set_unlock (set); + mono_mem_manager_unlock (mm); return gclass2; } @@ -8006,35 +7582,6 @@ mono_method_get_wrapper_cache (MonoMethod *method) } } -// This is support for the mempool reference tracking feature in checked-build, but lives in metadata.c due to use of static variables of this file. - -/** - * mono_find_image_set_owner: - * - * Find the imageset, if any, which a given pointer is located in the memory of. - */ -MonoImageSet * -mono_find_image_set_owner (void *ptr) -{ - MonoImageSet *owner = NULL; - int i; - - image_sets_lock (); - - if (image_sets) - { - for (i = 0; !owner && i < image_sets->len; ++i) { - MonoImageSet *set = (MonoImageSet *)g_ptr_array_index (image_sets, i); - if (mono_mempool_contains_addr (set->mempool, ptr)) - owner = set; - } - } - - image_sets_unlock (); - - return owner; -} - void mono_loader_set_strict_assembly_name_check (gboolean enabled) { diff --git a/src/mono/mono/mini/mini-generic-sharing.c b/src/mono/mono/mini/mini-generic-sharing.c index 6661dd6576d348..549d827c38d83e 100644 --- a/src/mono/mono/mini/mini-generic-sharing.c +++ b/src/mono/mono/mini/mini-generic-sharing.c @@ -3964,14 +3964,14 @@ shared_gparam_equal (gconstpointer ka, gconstpointer kb) MonoType* mini_get_shared_gparam (MonoType *t, MonoType *constraint) { - MonoImageSet *set; + MonoMemoryManager *mm; MonoGenericParam *par = t->data.generic_param; MonoGSharedGenericParam *copy, key; MonoType *res; MonoImage *image = NULL; char *name; - set = mono_metadata_merge_image_sets (mono_metadata_get_image_set_for_type (t), mono_metadata_get_image_set_for_type (constraint)); + mm = mono_mem_manager_merge (mono_metadata_get_mem_manager_for_type (t), mono_metadata_get_mem_manager_for_type (constraint)); memset (&key, 0, sizeof (key)); key.parent = par; @@ -3984,24 +3984,24 @@ mini_get_shared_gparam (MonoType *t, MonoType *constraint) * Need a cache to ensure the newly created gparam * is unique wrt T/CONSTRAINT. */ - mono_image_set_lock (set); - if (!set->gshared_types) { - set->gshared_types_len = MONO_TYPE_INTERNAL; - set->gshared_types = g_new0 (GHashTable*, set->gshared_types_len); - } - if (!set->gshared_types [constraint->type]) - set->gshared_types [constraint->type] = g_hash_table_new (shared_gparam_hash, shared_gparam_equal); - res = (MonoType *)g_hash_table_lookup (set->gshared_types [constraint->type], &key); - mono_image_set_unlock (set); + mono_mem_manager_lock (mm); + if (!mm->gshared_types) { + mm->gshared_types_len = MONO_TYPE_INTERNAL; + mm->gshared_types = g_new0 (GHashTable*, mm->gshared_types_len); + } + if (!mm->gshared_types [constraint->type]) + mm->gshared_types [constraint->type] = g_hash_table_new (shared_gparam_hash, shared_gparam_equal); + res = (MonoType *)g_hash_table_lookup (mm->gshared_types [constraint->type], &key); + mono_mem_manager_unlock (mm); if (res) return res; - copy = (MonoGSharedGenericParam *)mono_image_set_alloc0 (set, sizeof (MonoGSharedGenericParam)); + copy = (MonoGSharedGenericParam *)mono_mem_manager_alloc0 (mm, sizeof (MonoGSharedGenericParam)); memcpy (©->param, par, sizeof (MonoGenericParamFull)); copy->param.info.pklass = NULL; // FIXME: constraint = mono_metadata_type_dup (NULL, constraint); name = get_shared_gparam_name (constraint->type, ((MonoGenericParamFull*)copy)->info.name); - copy->param.info.name = mono_image_set_strdup (set, name); + copy->param.info.name = mono_mem_manager_strdup (mm, name); g_free (name); copy->param.owner = par->owner; @@ -4012,10 +4012,10 @@ mini_get_shared_gparam (MonoType *t, MonoType *constraint) res = mono_metadata_type_dup (NULL, t); res->data.generic_param = (MonoGenericParam*)copy; - mono_image_set_lock (set); + mono_mem_manager_lock (mm); /* Duplicates are ok */ - g_hash_table_insert (set->gshared_types [constraint->type], copy, res); - mono_image_set_unlock (set); + g_hash_table_insert (mm->gshared_types [constraint->type], copy, res); + mono_mem_manager_unlock (mm); return res; } diff --git a/src/mono/mono/utils/mono-mmap.c b/src/mono/mono/utils/mono-mmap.c index 2484163e9cf196..e362b698dc454c 100644 --- a/src/mono/mono/utils/mono-mmap.c +++ b/src/mono/mono/utils/mono-mmap.c @@ -115,7 +115,7 @@ mono_mem_account_type_name (MonoMemAccountType type) static const char *names[] = { "code", "hazard pointers", - "domain", + "mem manager", "SGen internal", "SGen nursery", "SGen LOS", diff --git a/src/mono/mono/utils/mono-mmap.h b/src/mono/mono/utils/mono-mmap.h index 14e6a096567e5b..9d4849fc432dc0 100644 --- a/src/mono/mono/utils/mono-mmap.h +++ b/src/mono/mono/utils/mono-mmap.h @@ -28,7 +28,7 @@ enum { typedef enum { MONO_MEM_ACCOUNT_CODE, MONO_MEM_ACCOUNT_HAZARD_POINTERS, - MONO_MEM_ACCOUNT_DOMAIN, + MONO_MEM_ACCOUNT_MEM_MANAGER, MONO_MEM_ACCOUNT_SGEN_INTERNAL, MONO_MEM_ACCOUNT_SGEN_NURSERY, MONO_MEM_ACCOUNT_SGEN_LOS, From 9ca2a70d6eb941d5482ea0205654beab6267a471 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Tue, 13 Apr 2021 20:27:32 -0400 Subject: [PATCH 05/11] [mono] Use the proper memory manager in the JIT code. (#51175) --- src/mono/mono/mini/method-to-ir.c | 4 +- src/mono/mono/mini/mini-amd64.c | 4 +- src/mono/mono/mini/mini-arm.c | 4 +- src/mono/mono/mini/mini-generic-sharing.c | 3 +- src/mono/mono/mini/mini-ppc.c | 4 +- src/mono/mono/mini/mini-runtime.c | 52 +++++++++++++---------- src/mono/mono/mini/mini-runtime.h | 14 +++--- src/mono/mono/mini/mini-s390x.c | 4 +- src/mono/mono/mini/mini-x86.c | 4 +- src/mono/mono/mini/mini.h | 4 +- src/mono/mono/mini/tramp-amd64.c | 4 +- src/mono/mono/mini/tramp-arm.c | 8 ++-- src/mono/mono/mini/tramp-ppc.c | 4 +- src/mono/mono/mini/tramp-s390x.c | 4 +- src/mono/mono/mini/tramp-x86.c | 6 +-- 15 files changed, 65 insertions(+), 58 deletions(-) diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index 88148513cdd573..d8477389eb9935 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -1690,7 +1690,7 @@ MONO_RESTORE_WARNING ji.type = patch_type; ji.data.target = data; - target = mono_resolve_patch_target (NULL, NULL, &ji, FALSE, error); + target = mono_resolve_patch_target_ext (cfg->mem_manager, NULL, NULL, &ji, FALSE, error); mono_error_assert_ok (error); EMIT_NEW_PCONST (cfg, ins, target); @@ -3603,7 +3603,7 @@ handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, Mono jit_mm->method_code_hash = g_hash_table_new (NULL, NULL); code_slot = (guint8 **)g_hash_table_lookup (jit_mm->method_code_hash, method); if (!code_slot) { - code_slot = (guint8 **)m_method_alloc0 (method, sizeof (gpointer)); + code_slot = (guint8 **)mono_mem_manager_alloc0 (jit_mm->mem_manager, sizeof (gpointer)); g_hash_table_insert (jit_mm->method_code_hash, method, code_slot); } jit_mm_unlock (jit_mm); diff --git a/src/mono/mono/mini/mini-amd64.c b/src/mono/mono/mini/mini-amd64.c index 94c009d334ba03..7861797eeb91f7 100644 --- a/src/mono/mono/mini/mini-amd64.c +++ b/src/mono/mono/mini/mini-amd64.c @@ -8618,6 +8618,7 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entri guint8 *code, *start; gboolean vtable_is_32bit = ((gsize)(vtable) == (gsize)(int)(gsize)(vtable)); GSList *unwind_ops; + MonoMemoryManager *mem_manager = m_class_get_mem_manager (vtable->klass); for (i = 0; i < count; ++i) { MonoIMTCheckItem *item = imt_entries [i]; @@ -8666,7 +8667,6 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entri if (fail_tramp) { code = (guint8 *)mini_alloc_generic_virtual_trampoline (vtable, size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)); } else { - MonoMemoryManager *mem_manager = m_class_get_mem_manager (vtable->klass); code = (guint8 *)mono_mem_manager_code_reserve (mem_manager, size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)); } start = code; @@ -8763,7 +8763,7 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entri MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE, NULL)); - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), mem_manager); return start; } diff --git a/src/mono/mono/mini/mini-arm.c b/src/mono/mono/mini/mini-arm.c index c58ab2707e9302..b08a5bcb649d28 100644 --- a/src/mono/mono/mini/mini-arm.c +++ b/src/mono/mono/mini/mini-arm.c @@ -6935,6 +6935,7 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entri char * cond; #endif GSList *unwind_ops; + MonoMemoryManager *mem_manager = m_class_get_mem_manager (vtable->klass); size = BASE_SIZE; constant_pool_starts = g_new0 (guint32*, count); @@ -6976,7 +6977,6 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entri if (fail_tramp) { code = (arminstr_t *)mini_alloc_generic_virtual_trampoline (vtable, size); } else { - MonoMemoryManager *mem_manager = m_class_get_mem_manager (vtable->klass); code = mono_mem_manager_code_reserve (mem_manager, size); } start = code; @@ -7154,7 +7154,7 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entri g_assert (DISTANCE (start, code) <= size); - mono_tramp_info_register (mono_tramp_info_create (NULL, (guint8*)start, DISTANCE (start, code), NULL, unwind_ops), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, (guint8*)start, DISTANCE (start, code), NULL, unwind_ops), mem_manager); return start; } diff --git a/src/mono/mono/mini/mini-generic-sharing.c b/src/mono/mono/mini/mini-generic-sharing.c index 549d827c38d83e..d1cc5e0eaacb77 100644 --- a/src/mono/mono/mini/mini-generic-sharing.c +++ b/src/mono/mono/mini/mini-generic-sharing.c @@ -3048,8 +3048,7 @@ mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot, { MonoRuntimeGenericContext *rgctx, *new_rgctx; gpointer info; - // FIXME: - MonoJitMemoryManager *jit_mm = get_default_jit_mm (); + MonoJitMemoryManager *jit_mm = jit_mm_for_class (class_vtable->klass); error_init (error); diff --git a/src/mono/mono/mini/mini-ppc.c b/src/mono/mono/mini/mini-ppc.c index 88f8e2fcd75502..19097c9df89d91 100644 --- a/src/mono/mono/mini/mini-ppc.c +++ b/src/mono/mono/mini/mini-ppc.c @@ -5474,6 +5474,7 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entri int i; int size = 0; guint8 *code, *start; + MonoMemoryManager *mem_manager = m_class_get_mem_manager (vtable->klass); for (i = 0; i < count; ++i) { MonoIMTCheckItem *item = imt_entries [i]; @@ -5508,7 +5509,6 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entri if (fail_tramp) { code = (guint8 *)mini_alloc_generic_virtual_trampoline (vtable, size); } else { - MonoMemoryManager *mem_manager = m_class_get_mem_manager (vtable->klass); code = mono_mem_manager_code_reserve (mem_manager, size); } start = code; @@ -5605,7 +5605,7 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entri mono_arch_flush_icache (start, size); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE, NULL)); - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), mem_manager); return start; } diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index bb25304af2dfa6..f1c43bcabaea95 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -472,11 +472,11 @@ mono_tramp_info_free (MonoTrampInfo *info) } static void -register_trampoline_jit_info (MonoDomain *domain, MonoTrampInfo *info) +register_trampoline_jit_info (MonoMemoryManager *mem_manager, MonoTrampInfo *info) { MonoJitInfo *ji; - ji = (MonoJitInfo *)mono_mem_manager_alloc0 (get_default_mem_manager (), mono_jit_info_size ((MonoJitInfoFlags)0, 0, 0)); + ji = (MonoJitInfo *)mono_mem_manager_alloc0 (mem_manager, mono_jit_info_size ((MonoJitInfoFlags)0, 0, 0)); mono_jit_info_init (ji, NULL, (guint8*)MINI_FTNPTR_TO_ADDR (info->code), info->code_size, (MonoJitInfoFlags)0, 0, 0); ji->d.tramp_info = info; ji->is_trampoline = TRUE; @@ -494,18 +494,15 @@ register_trampoline_jit_info (MonoDomain *domain, MonoTrampInfo *info) * Frees INFO. */ static void -mono_tramp_info_register_internal (MonoTrampInfo *info, MonoDomain *domain, gboolean aot) +mono_tramp_info_register_internal (MonoTrampInfo *info, MonoMemoryManager *mem_manager, gboolean aot) { MonoTrampInfo *copy; + MonoDomain *domain = mono_get_root_domain (); if (!info) return; - if (!domain) - domain = mono_get_root_domain (); - - // domain might be unset during startup - if (domain) { + if (mem_manager) { copy = mono_mem_manager_alloc0 (get_default_mem_manager (), sizeof (MonoTrampInfo)); } else { copy = g_new0 (MonoTrampInfo, 1); @@ -519,10 +516,9 @@ mono_tramp_info_register_internal (MonoTrampInfo *info, MonoDomain *domain, gboo if (info->unwind_ops) { copy->uw_info = mono_unwind_ops_encode (info->unwind_ops, ©->uw_info_len); copy->owns_uw_info = TRUE; - if (domain) { - /* Move unwind info into the domain's memory pool so that it is removed once the domain is released. */ + if (mem_manager) { guint8 *temp = copy->uw_info; - copy->uw_info = mono_mem_manager_alloc (get_default_mem_manager (), copy->uw_info_len); + copy->uw_info = mono_mem_manager_alloc (mem_manager, copy->uw_info_len); memcpy (copy->uw_info, temp, copy->uw_info_len); g_free (temp); } @@ -541,13 +537,13 @@ mono_tramp_info_register_internal (MonoTrampInfo *info, MonoDomain *domain, gboo #endif if (!domain) { - /* If no root domain has been created yet, postpone the registration. */ + /* If no domain has been created yet, postpone the registration. */ mono_jit_lock (); tramp_infos = g_slist_prepend (tramp_infos, copy); mono_jit_unlock (); } else if (copy->uw_info || info->method) { /* Only register trampolines that have unwind info */ - register_trampoline_jit_info (domain, copy); + register_trampoline_jit_info (mem_manager ? mem_manager : get_default_mem_manager (), copy); } if (mono_jit_map_is_enabled ()) @@ -557,15 +553,15 @@ mono_tramp_info_register_internal (MonoTrampInfo *info, MonoDomain *domain, gboo } void -mono_tramp_info_register (MonoTrampInfo *info, MonoDomain *domain) +mono_tramp_info_register (MonoTrampInfo *info, MonoMemoryManager *mem_manager) { - mono_tramp_info_register_internal (info, domain, FALSE); + mono_tramp_info_register_internal (info, mem_manager, FALSE); } void -mono_aot_tramp_info_register (MonoTrampInfo *info, MonoDomain *domain) +mono_aot_tramp_info_register (MonoTrampInfo *info, MonoMemoryManager *mem_manager) { - mono_tramp_info_register_internal (info, domain, TRUE); + mono_tramp_info_register_internal (info, mem_manager, TRUE); } /* Register trampolines created before the root domain was created in the jit info tables */ @@ -577,7 +573,7 @@ register_trampolines (MonoDomain *domain) for (l = tramp_infos; l; l = l->next) { MonoTrampInfo *info = (MonoTrampInfo *)l->data; - register_trampoline_jit_info (domain, info); + register_trampoline_jit_info (get_default_mem_manager (), info); } } @@ -1348,7 +1344,7 @@ mono_patch_info_equal (gconstpointer ka, gconstpointer kb) } gpointer -mono_resolve_patch_target (MonoMethod *method, guint8 *code, MonoJumpInfo *patch_info, gboolean run_cctors, MonoError *error) +mono_resolve_patch_target_ext (MonoMemoryManager *mem_manager, MonoMethod *method, guint8 *code, MonoJumpInfo *patch_info, gboolean run_cctors, MonoError *error) { unsigned char *ip = patch_info->ip.i + code; gconstpointer target = NULL; @@ -1424,7 +1420,7 @@ mono_resolve_patch_target (MonoMethod *method, guint8 *code, MonoJumpInfo *patch break; } case MONO_PATCH_INFO_METHOD_PINVOKE_ADDR_CACHE: { - target = mono_mem_manager_alloc0 (get_default_mem_manager (), sizeof (gpointer)); + target = mono_mem_manager_alloc0 (mem_manager, sizeof (gpointer)); break; } case MONO_PATCH_INFO_GC_SAFE_POINT_FLAG: @@ -1642,7 +1638,7 @@ mono_resolve_patch_target (MonoMethod *method, guint8 *code, MonoJumpInfo *patch break; } case MONO_PATCH_INFO_CASTCLASS_CACHE: { - target = mono_mem_manager_alloc0 (get_default_mem_manager (), sizeof (gpointer)); + target = mono_mem_manager_alloc0 (mem_manager, sizeof (gpointer)); break; } case MONO_PATCH_INFO_OBJC_SELECTOR_REF: { @@ -1654,7 +1650,7 @@ mono_resolve_patch_target (MonoMethod *method, guint8 *code, MonoJumpInfo *patch char *s; len = strlen ((const char *)patch_info->data.target); - s = (char *)mono_mem_manager_alloc0 (get_default_mem_manager (), len + 1); + s = (char *)mono_mem_manager_alloc0 (mem_manager, len + 1); memcpy (s, patch_info->data.target, len); target = s; @@ -1685,6 +1681,12 @@ mono_resolve_patch_target (MonoMethod *method, guint8 *code, MonoJumpInfo *patch return (gpointer)target; } +gpointer +mono_resolve_patch_target (MonoMethod *method, guint8 *code, MonoJumpInfo *patch_info, gboolean run_cctors, MonoError *error) +{ + return mono_resolve_patch_target_ext (get_default_mem_manager (), method, code, patch_info, run_cctors, error); +} + /* * mini_register_jump_site: * @@ -1823,7 +1825,11 @@ mini_lookup_method (MonoMethod *method, MonoMethod *shared) jit_code_hash_lock (jit_mm); ji = (MonoJitInfo *)mono_internal_hash_table_lookup (&jit_mm->jit_code_hash, method); + jit_code_hash_unlock (jit_mm); if (!ji && shared) { + jit_mm = jit_mm_for_method (shared); + + jit_code_hash_lock (jit_mm); /* Try generic sharing */ ji = (MonoJitInfo *)mono_internal_hash_table_lookup (&jit_mm->jit_code_hash, shared); if (ji && !ji->has_generic_jit_info) @@ -1837,8 +1843,8 @@ mini_lookup_method (MonoMethod *method, MonoMethod *shared) ++lookups; if (!ji) ++failed_lookups; + jit_code_hash_unlock (jit_mm); } - jit_code_hash_unlock (jit_mm); return ji; } diff --git a/src/mono/mono/mini/mini-runtime.h b/src/mono/mono/mini/mini-runtime.h index cfc7dd12f6706f..67d441495bcc84 100644 --- a/src/mono/mono/mini/mini-runtime.h +++ b/src/mono/mono/mini/mini-runtime.h @@ -78,12 +78,13 @@ get_default_mem_manager (void) static inline MonoJitMemoryManager* jit_mm_for_method (MonoMethod *method) { - /* - * Some places might not look up the correct memory manager because of generic instances/generic sharing etc. - * So use the same memory manager everywhere, this is not a problem since we don't support unloading yet. - */ - //return (MonoJitMemoryManager*)m_method_get_mem_manager (method)->runtime_info; - return get_default_jit_mm (); + return (MonoJitMemoryManager*)m_method_get_mem_manager (method)->runtime_info; +} + +static inline MonoJitMemoryManager* +jit_mm_for_class (MonoClass *klass) +{ + return (MonoJitMemoryManager*)m_class_get_mem_manager (klass)->runtime_info; } static inline void @@ -550,6 +551,7 @@ MonoJumpInfo *mono_patch_info_list_prepend (MonoJumpInfo *list, int ip, MonoJum MonoJumpInfoToken* mono_jump_info_token_new (MonoMemPool *mp, MonoImage *image, guint32 token); MonoJumpInfoToken* mono_jump_info_token_new2 (MonoMemPool *mp, MonoImage *image, guint32 token, MonoGenericContext *context); gpointer mono_resolve_patch_target (MonoMethod *method, guint8 *code, MonoJumpInfo *patch_info, gboolean run_cctors, MonoError *error); +gpointer mono_resolve_patch_target_ext (MonoMemoryManager *mem_manager, MonoMethod *method, guint8 *code, MonoJumpInfo *patch_info, gboolean run_cctors, MonoError *error); void mini_register_jump_site (MonoMethod *method, gpointer ip); void mini_patch_jump_sites (MonoMethod *method, gpointer addr); void mini_patch_llvm_jit_callees (MonoMethod *method, gpointer addr); diff --git a/src/mono/mono/mini/mini-s390x.c b/src/mono/mono/mini/mini-s390x.c index 1cc8f83e2b72c5..e16cae80658af3 100644 --- a/src/mono/mono/mini/mini-s390x.c +++ b/src/mono/mono/mini/mini-s390x.c @@ -6484,6 +6484,7 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, int i; int size = 0; guchar *code, *start; + MonoMemoryManager *mem_manager = m_class_get_mem_manager (vtable->klass); for (i = 0; i < count; ++i) { MonoIMTCheckItem *item = imt_entries [i]; @@ -6519,7 +6520,6 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, if (fail_tramp) { code = (guint8 *)mini_alloc_generic_virtual_trampoline (vtable, size); } else { - MonoMemoryManager *mem_manager = m_class_get_mem_manager (vtable->klass); code = mono_mem_manager_code_reserve (mem_manager, size); } @@ -6606,7 +6606,7 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, g_assert (code - start <= size); - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), mem_manager); return (start); } diff --git a/src/mono/mono/mini/mini-x86.c b/src/mono/mono/mini/mini-x86.c index d07ba7687ac6da..5cc9c3bfa6d3ce 100644 --- a/src/mono/mono/mini/mini-x86.c +++ b/src/mono/mono/mini/mini-x86.c @@ -5525,6 +5525,7 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entri int size = 0; guint8 *code, *start; GSList *unwind_ops; + MonoMemoryManager *mem_manager = m_class_get_mem_manager (vtable->klass); for (i = 0; i < count; ++i) { MonoIMTCheckItem *item = imt_entries [i]; @@ -5552,7 +5553,6 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entri if (fail_tramp) { code = (guint8 *)mini_alloc_generic_virtual_trampoline (vtable, size); } else { - MonoMemoryManager *mem_manager = m_class_get_mem_manager (vtable->klass); code = mono_mem_manager_code_reserve (mem_manager, size); } start = code; @@ -5644,7 +5644,7 @@ mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoIMTCheckItem **imt_entri MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE, NULL)); - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), mem_manager); return start; } diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index ac00a72237556a..6446d666efad90 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -2298,8 +2298,8 @@ void mono_emit_unwind_op (MonoCompile *cfg, int when, int val); MonoTrampInfo* mono_tramp_info_create (const char *name, guint8 *code, guint32 code_size, MonoJumpInfo *ji, GSList *unwind_ops); void mono_tramp_info_free (MonoTrampInfo *info); -void mono_aot_tramp_info_register (MonoTrampInfo *info, MonoDomain *domain); -void mono_tramp_info_register (MonoTrampInfo *info, MonoDomain *domain); +void mono_aot_tramp_info_register (MonoTrampInfo *info, MonoMemoryManager *mem_manager); +void mono_tramp_info_register (MonoTrampInfo *info, MonoMemoryManager *mem_manager); int mini_exception_id_by_name (const char *name); gboolean mini_type_is_hfa (MonoType *t, int *out_nfields, int *out_esize); diff --git a/src/mono/mono/mini/tramp-amd64.c b/src/mono/mono/mini/tramp-amd64.c index 805ae1694535bc..086796f51daa8a 100644 --- a/src/mono/mono/mini/tramp-amd64.c +++ b/src/mono/mono/mini/tramp-amd64.c @@ -78,7 +78,7 @@ mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr) mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE, m)); - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), mem_manager); return start; } @@ -117,7 +117,7 @@ mono_arch_get_static_rgctx_trampoline (MonoMemoryManager *mem_manager, gpointer mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL)); - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), mem_manager); return start; } diff --git a/src/mono/mono/mini/tramp-arm.c b/src/mono/mono/mini/tramp-arm.c index 1ad45caf2ffee3..3a9f87e02ae3ca 100644 --- a/src/mono/mono/mini/tramp-arm.c +++ b/src/mono/mono/mini/tramp-arm.c @@ -538,7 +538,7 @@ mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr) /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name); g_print ("unbox code is at %p for method at %p\n", start, addr);*/ - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), mem_manager); return start; } @@ -566,7 +566,7 @@ mono_arch_get_static_rgctx_trampoline (MonoMemoryManager *mem_manager, gpointer mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL)); - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), mem_manager); return start; } @@ -595,7 +595,7 @@ mono_arch_get_ftnptr_arg_trampoline (MonoMemoryManager *mem_manager, gpointer ar mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL)); - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), mem_manager); return start; } @@ -1189,7 +1189,7 @@ mono_arch_get_gsharedvt_arg_trampoline (gpointer arg, gpointer addr) mono_arch_flush_icache (buf, code - buf); MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL)); - mono_tramp_info_register (mono_tramp_info_create (NULL, buf, code - buf, NULL, NULL), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, buf, code - buf, NULL, NULL), mem_manager); return buf; } diff --git a/src/mono/mono/mini/tramp-ppc.c b/src/mono/mono/mini/tramp-ppc.c index 850753e7b9600e..67eb7c24db3bab 100644 --- a/src/mono/mono/mini/tramp-ppc.c +++ b/src/mono/mono/mini/tramp-ppc.c @@ -107,7 +107,7 @@ mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr) /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name); g_print ("unbox code is at %p for method at %p\n", start, addr);*/ - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), mem_manager); return start; } @@ -152,7 +152,7 @@ mono_arch_get_static_rgctx_trampoline (MonoMemoryManager *mem_manager, gpointer MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL)); g_assert ((code - start) <= size); - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), mem_manager); return start; } diff --git a/src/mono/mono/mini/tramp-s390x.c b/src/mono/mono/mini/tramp-s390x.c index d5d69093444de9..963416900435bb 100644 --- a/src/mono/mono/mini/tramp-s390x.c +++ b/src/mono/mono/mini/tramp-s390x.c @@ -117,7 +117,7 @@ mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr) snprintf(trampName, sizeof(trampName), "%s_unbox_trampoline", m->name); - mono_tramp_info_register (mono_tramp_info_create (trampName, start, code - start, NULL, NULL), NULL); + mono_tramp_info_register (mono_tramp_info_create (trampName, start, code - start, NULL, NULL), mem_manager); return start; } @@ -747,7 +747,7 @@ mono_arch_get_static_rgctx_trampoline (MonoMemoryManager *mem_manager, gpointer mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL)); - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, NULL), mem_manager); return(start); } diff --git a/src/mono/mono/mini/tramp-x86.c b/src/mono/mono/mini/tramp-x86.c index 395ae092f5af73..218e961dc16483 100644 --- a/src/mono/mono/mini/tramp-x86.c +++ b/src/mono/mono/mini/tramp-x86.c @@ -55,7 +55,7 @@ mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr) MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE, m)); - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), mem_manager); return start; } @@ -79,7 +79,7 @@ mono_arch_get_static_rgctx_trampoline (MonoMemoryManager *mem_manager, gpointer mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL)); - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), mem_manager); return start; } @@ -609,7 +609,7 @@ mono_arch_get_gsharedvt_arg_trampoline (gpointer arg, gpointer addr) mono_arch_flush_icache (start, code - start); MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL)); - mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), NULL); + mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), mem_manager); return start; } From fccdca068be7ac1fd44cdd62ef1aec3e6c752e77 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 13 Apr 2021 17:35:31 -0700 Subject: [PATCH 06/11] Simplify mibc usage in the build (#50536) - Produce a merged mibc with all scenarios squished together - Properly attach the mibc data to the incremental build for System.Private.CoreLib - This can't be done for the framework here. It will require mibc integration in the SDK - Enable pgo optimization in checked builds - Enable pgo optimization in framework compile for outerloop runs --- Build.proj | 2 +- Directory.Build.props | 2 +- src/coreclr/crossgen-corelib.proj | 31 ++++++++++++++----- src/coreclr/tools/r2rtest/BuildOptions.cs | 1 + .../tools/r2rtest/CommandLineOptions.cs | 9 ++++++ src/coreclr/tools/r2rtest/Crossgen2Runner.cs | 9 ++++++ .../Microsoft.NETCore.App/ReadyToRun.targets | 6 ++-- src/tests/build.cmd | 2 +- src/tests/build.sh | 2 +- 9 files changed, 49 insertions(+), 15 deletions(-) diff --git a/Build.proj b/Build.proj index 6dcc3b9542037b..44da6d66f9942c 100644 --- a/Build.proj +++ b/Build.proj @@ -18,7 +18,7 @@ - + diff --git a/Directory.Build.props b/Directory.Build.props index 4ee7e1db360f89..3917b5b3f7b51e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -200,7 +200,7 @@ - true + true true diff --git a/src/coreclr/crossgen-corelib.proj b/src/coreclr/crossgen-corelib.proj index 2399255b78d0f2..53c4e57c72eb58 100644 --- a/src/coreclr/crossgen-corelib.proj +++ b/src/coreclr/crossgen-corelib.proj @@ -45,6 +45,10 @@ + + + + System.Private.CoreLib $([MSBuild]::NormalizePath('$(BinDir)', 'IL', '$(CoreLibAssemblyName).dll')) @@ -53,12 +57,28 @@ $([MSBuild]::NormalizePath('$(BinDir)', 'PDB', '$(CoreLibAssemblyName).ni.pdb')) $([MSBuild]::NormalizePath('$(BinDir)', '$(CoreLibAssemblyName).perf.map')) + $([MSBuild]::NormalizePath('$(BinDir)', 'StandardOptimizationData.mibc')) - + + + $(DotNetCli) $([MSBuild]::NormalizePath('$(BinDir)', 'dotnet-pgo', 'dotnet-pgo.dll')) merge + $(DotNetPgoCmd) -o:$(MergedMibcPath) + $(DotNetPgoCmd) @(OptimizationMibcFiles->'-i:%(Identity)', ' ') + + + + + + + @@ -75,17 +95,12 @@ - - - - $(DotNetCli) $([MSBuild]::NormalizePath('$(BinDir)', '$(CrossDir)', 'crossgen2', 'crossgen2.dll')) $(CrossGenDllCmd) -o:$(CoreLibOutputPath) $(CrossGenDllCmd) -r:$([MSBuild]::NormalizePath('$(BinDir)', 'IL', '*.dll')) $(CrossGenDllCmd) --targetarch:$(TargetArchitecture) - @(OptimizationMibcFiles->'-m:%(Identity)', ' ') - $(CrossGenDllCmd) $(MibcArgs) --embed-pgo-data + $(CrossGenDllCmd) -m:$(MergedMibcPath) --embed-pgo-data $(CrossGenDllCmd) -O $(CrossGenDllCmd) $(CoreLibInputPath) diff --git a/src/coreclr/tools/r2rtest/BuildOptions.cs b/src/coreclr/tools/r2rtest/BuildOptions.cs index 0a2a8fe0eb7f3d..3465aa18598d7b 100644 --- a/src/coreclr/tools/r2rtest/BuildOptions.cs +++ b/src/coreclr/tools/r2rtest/BuildOptions.cs @@ -49,6 +49,7 @@ public class BuildOptions public string InputFileSearchString { get; set; } public string ConfigurationSuffix => (Release ? "-ret.out" : "-chk.out"); public string GCStress { get; set; } + public FileInfo[] MibcPath { get; set; } public string DotNetCli { get diff --git a/src/coreclr/tools/r2rtest/CommandLineOptions.cs b/src/coreclr/tools/r2rtest/CommandLineOptions.cs index ebadfd8fcdb450..d99e5af1c0c0a9 100644 --- a/src/coreclr/tools/r2rtest/CommandLineOptions.cs +++ b/src/coreclr/tools/r2rtest/CommandLineOptions.cs @@ -70,6 +70,7 @@ Command CompileFolder() => R2RDumpPath(), MeasurePerf(), InputFileSearchString(), + MibcPath(), }, CompileDirectoryCommand.CompileDirectory); @@ -107,6 +108,7 @@ Command CompileSubtree() => ExecutionTimeoutMinutes(), R2RDumpPath(), GCStress(), + MibcPath(), }, CompileSubtreeCommand.CompileSubtree); @@ -137,6 +139,7 @@ Command CompileFramework() => MeasurePerf(), InputFileSearchString(), OutputDirectory(), + MibcPath(), }, CompileFrameworkCommand.CompileFramework); @@ -155,6 +158,7 @@ Command CompileNugetPackages() => DegreeOfParallelism(), CompilationTimeoutMinutes(), ExecutionTimeoutMinutes(), + MibcPath(), }, CompileNugetCommand.CompileNuget); @@ -170,6 +174,7 @@ Command CompileSerp() => Pdb(), CompilationTimeoutMinutes(), Crossgen2Path(), + MibcPath(), }, options => { @@ -195,6 +200,10 @@ Option ReferencePath() => new Option(new[] { "--reference-path", "-r" }, "Folder containing assemblies to reference during compilation") { Argument = new Argument() { Arity = ArgumentArity.ZeroOrMore }.ExistingOnly() }; + Option MibcPath() => + new Option(new[] { "--mibc-path", "-m" }, "Mibc files to use in compilation") + { Argument = new Argument() { Arity = ArgumentArity.ZeroOrMore }.ExistingOnly() }; + Option Crossgen() => new Option(new[] { "--crossgen" }, "Compile the apps using Crossgen in the CORE_ROOT folder"); diff --git a/src/coreclr/tools/r2rtest/Crossgen2Runner.cs b/src/coreclr/tools/r2rtest/Crossgen2Runner.cs index 19d13dc2fc6073..2aa1dee6b6f6d0 100644 --- a/src/coreclr/tools/r2rtest/Crossgen2Runner.cs +++ b/src/coreclr/tools/r2rtest/Crossgen2Runner.cs @@ -122,6 +122,15 @@ protected override IEnumerable BuildCommandLineArguments(IEnumerable 0) + { + yield return "--embed-pgo-data"; + foreach (FileInfo mibc in _options.MibcPath) + { + yield return $"-m:{mibc.FullName}"; + } + } + if (!string.IsNullOrEmpty(Crossgen2RunnerOptions.CompositeRoot)) { yield return $"--compositerootpath={Crossgen2RunnerOptions.CompositeRoot}"; diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/ReadyToRun.targets b/src/installer/pkg/sfx/Microsoft.NETCore.App/ReadyToRun.targets index 5d3492a286ed67..5b5f23f28b7d08 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/ReadyToRun.targets +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/ReadyToRun.targets @@ -1,15 +1,15 @@ - + - - + + diff --git a/src/tests/build.cmd b/src/tests/build.cmd index b4f9a7b0702a41..18a85d6ae62214 100644 --- a/src/tests/build.cmd +++ b/src/tests/build.cmd @@ -578,7 +578,7 @@ exit /b 1 set "__CrossgenOutputDir=%__TestIntermediatesDir%\crossgen.out" -set __CrossgenCmd="%__RepoRootDir%\dotnet.cmd" "%CORE_ROOT%\R2RTest\R2RTest.dll" compile-framework -cr "%CORE_ROOT%" --output-directory "%__CrossgenOutputDir%" --release --nocleanup --target-arch %__BuildArch% -dop %NUMBER_OF_PROCESSORS% +set __CrossgenCmd="%__RepoRootDir%\dotnet.cmd" "%CORE_ROOT%\R2RTest\R2RTest.dll" compile-framework -cr "%CORE_ROOT%" --output-directory "%__CrossgenOutputDir%" --release --nocleanup --target-arch %__BuildArch% -dop %NUMBER_OF_PROCESSORS% -m "%CORE_ROOT%\StandardOptimizationData.mibc" if defined __CreatePdb ( set __CrossgenCmd=!__CrossgenCmd! --pdb diff --git a/src/tests/build.sh b/src/tests/build.sh index 4531a07e3d77fc..0d15d53b53910e 100755 --- a/src/tests/build.sh +++ b/src/tests/build.sh @@ -158,7 +158,7 @@ precompile_coreroot_fx() fi local outputDir="$__TestIntermediatesDir/crossgen.out" - local crossgenCmd="\"$__DotNetCli\" \"$CORE_ROOT/R2RTest/R2RTest.dll\" compile-framework -cr \"$CORE_ROOT\" --output-directory \"$outputDir\" --release --nocleanup --target-arch $__BuildArch -dop $__NumProc" + local crossgenCmd="\"$__DotNetCli\" \"$CORE_ROOT/R2RTest/R2RTest.dll\" compile-framework -cr \"$CORE_ROOT\" --output-directory \"$outputDir\" --release --nocleanup --target-arch $__BuildArch -dop $__NumProc -m \"$CORE_ROOT/StandardOptimizationData.mibc\"" if [[ "$__CompositeBuildMode" != 0 ]]; then crossgenCmd="$crossgenCmd --composite" From b886e4db6b78548cd97371009b8e278274c44449 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Tue, 13 Apr 2021 19:53:30 -0700 Subject: [PATCH 07/11] Fix issues with version resilient hash (#51191) I noticed that some methods under dynamic PGO did not have PGO data. Tracked this down to the Tier0 jit being unable to allocate a PGO schema. This in turn was caused by the IL hash computation failing. So, this PR fixes issues in the IL hash: * pass in the right pointer to the IL * advance the pointer as we scan the IL * don't bail out early for the InlineNone case --- src/coreclr/vm/versionresilienthashcode.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/versionresilienthashcode.cpp b/src/coreclr/vm/versionresilienthashcode.cpp index cdf11c23536011..28eecdfea05d5b 100644 --- a/src/coreclr/vm/versionresilienthashcode.cpp +++ b/src/coreclr/vm/versionresilienthashcode.cpp @@ -164,6 +164,7 @@ class ILInstructionParser { *data = *_pCode; _cbCode--; + _pCode++; return true; } return false; @@ -175,6 +176,7 @@ class ILInstructionParser { *data = *(uint16_t UNALIGNED*)_pCode; _cbCode -= 2; + _pCode += 2; return true; } return false; @@ -186,6 +188,7 @@ class ILInstructionParser { *data = *(uint32_t UNALIGNED*)_pCode; _cbCode -= 4; + _pCode += 4; return true; } return false; @@ -237,7 +240,7 @@ bool AddVersionResilientHashCodeForInstruction(ILInstructionParser *parser, xxHa switch (opcodeFormat) { case InlineNone: // no inline args - return opcodeValue; + break; case ShortInlineI: case ShortInlineBrTarget: @@ -355,7 +358,7 @@ bool GetVersionResilientILCodeHashCode(MethodDesc *pMD, int* hashCode, unsigned* { COR_ILMETHOD_DECODER header(pMD->GetILHeader(TRUE), pMD->GetMDImport(), NULL); - pILCode = header.GetCode(); + pILCode = header.Code; cbILCode = header.GetCodeSize(); maxStack = header.GetMaxStack(); EHCount = header.EHCount(); From f24879fb522ae31abd515e71ba2b24ce146c5dbc Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Tue, 13 Apr 2021 20:03:45 -0700 Subject: [PATCH 08/11] Expose an eeIsJitIntrinsic method for quickly determining if a method handle represents an intrinsic method (#51124) * Updating the JIT/EE interface to expose `isJitIntrinsic` * Update JITEEVersionIdentifier * Update fgFindJumpTargets to use the much cheaper `eeIsJitIntrinsic` * Ensure IsJitIntrinsic exists in the LightWeightMap and fix a copy/paste error * Run ThunkGenerator * Don't resolve tokens for CEE_CONSTRAINED when making inlining observations --- .../superpmi/superpmi-shared/lwmlist.h | 1 + .../superpmi-shared/methodcontext.cpp | 22 ++ .../superpmi/superpmi-shared/methodcontext.h | 7 +- .../superpmi-shim-collector/icorjitinfo.cpp | 11 + .../superpmi-shim-counter/icorjitinfo.cpp | 7 + .../superpmi-shim-simple/icorjitinfo.cpp | 6 + .../ToolBox/superpmi/superpmi/icorjitinfo.cpp | 8 + src/coreclr/inc/corinfo.h | 3 + src/coreclr/inc/icorjitinfoimpl_generated.h | 3 + src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/jit/ICorJitInfo_API_names.h | 1 + src/coreclr/jit/ICorJitInfo_API_wrapper.hpp | 9 + src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/ee_il_dll.hpp | 6 + src/coreclr/jit/fgbasic.cpp | 59 +-- .../tools/Common/JitInterface/CorInfoBase.cs | 364 +++++++++--------- .../tools/Common/JitInterface/CorInfoImpl.cs | 6 + .../ThunkGenerator/ThunkInput.txt | 1 + .../tools/aot/jitinterface/jitinterface.h | 10 + src/coreclr/vm/jitinterface.cpp | 29 +- src/coreclr/zap/zapinfo.cpp | 5 + 21 files changed, 344 insertions(+), 225 deletions(-) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h index 7826a25ae00d48..71f9eca564a34d 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h @@ -101,6 +101,7 @@ LWM(GetJustMyCodeHandle, DWORDLONG, DLDL) LWM(GetLazyStringLiteralHelper, DWORDLONG, DWORD) LWM(GetLikelyClass, Agnostic_GetLikelyClass, Agnostic_GetLikelyClassResult) LWM(GetLocationOfThisType, DWORDLONG, Agnostic_CORINFO_LOOKUP_KIND) +LWM(IsJitIntrinsic, DWORDLONG, DWORD) LWM(GetMethodAttribs, DWORDLONG, DWORD) LWM(GetClassModule, DWORDLONG, DWORDLONG) LWM(GetModuleAssembly, DWORDLONG, DWORDLONG) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp index 7c7296b803634c..0654207f2817be 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -771,6 +771,28 @@ DWORD MethodContext::repGetClassAttribs(CORINFO_CLASS_HANDLE classHandle) return value; } +void MethodContext::recIsJitIntrinsic(CORINFO_METHOD_HANDLE ftn, bool result) +{ + if (IsJitIntrinsic == nullptr) + IsJitIntrinsic = new LightWeightMap(); + + IsJitIntrinsic->Add(CastHandle(ftn), (DWORD)result); + DEBUG_REC(dmpIsJitIntrinsic(CastHandle(ftn), (DWORD)result)); +} +void MethodContext::dmpIsJitIntrinsic(DWORDLONG key, DWORD value) +{ + printf("IsJitIntrinsic key ftn-%016llX, value res-%u", key, value); +} +bool MethodContext::repIsJitIntrinsic(CORINFO_METHOD_HANDLE ftn) +{ + AssertCodeMsg((IsJitIntrinsic != nullptr) && (IsJitIntrinsic->GetIndex(CastHandle(ftn)) != -1), EXCEPTIONCODE_MC, + "Didn't find %016llX", CastHandle(ftn)); + + bool result = (BOOL)IsJitIntrinsic->Get(CastHandle(ftn)); + DEBUG_REP(dmpIsJitIntrinsic(CastHandle(ftn), (DWORD)result)); + return result; +} + void MethodContext::recGetMethodAttribs(CORINFO_METHOD_HANDLE methodHandle, DWORD attribs) { if (GetMethodAttribs == nullptr) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h index d71f8d35f3b332..69837068f23f2f 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h @@ -119,6 +119,10 @@ class MethodContext void dmpGetClassAttribs(DWORDLONG key, DWORD value); DWORD repGetClassAttribs(CORINFO_CLASS_HANDLE classHandle); + void recIsJitIntrinsic(CORINFO_METHOD_HANDLE ftn, bool result); + void dmpIsJitIntrinsic(DWORDLONG key, DWORD value); + bool repIsJitIntrinsic(CORINFO_METHOD_HANDLE ftn); + void recGetMethodAttribs(CORINFO_METHOD_HANDLE methodHandle, DWORD attribs); void dmpGetMethodAttribs(DWORDLONG key, DWORD value); DWORD repGetMethodAttribs(CORINFO_METHOD_HANDLE methodHandle); @@ -898,7 +902,7 @@ class MethodContext }; // ********************* Please keep this up-to-date to ease adding more *************** -// Highest packet number: 191 +// Highest packet number: 192 // ************************************************************************************* enum mcPackets { @@ -1000,6 +1004,7 @@ enum mcPackets Packet_GetJustMyCodeHandle = 68, Packet_GetLikelyClass = 182, // Added 9/27/2020 Packet_GetLocationOfThisType = 69, + Packet_IsJitIntrinsic = 192, Packet_GetMethodAttribs = 70, Packet_GetMethodClass = 71, Packet_GetMethodModule = 181, // Added 11/20/2020 diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 85cca8a129d774..281533e4affec7 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -17,6 +17,17 @@ // ICorMethodInfo // /**********************************************************************************/ + +// Quick check whether the method is a jit intrinsic. Returns the same value as getMethodAttribs(ftn) & +// CORINFO_FLG_JIT_INTRINSIC, except faster. +bool interceptor_ICJI::isJitIntrinsic(CORINFO_METHOD_HANDLE ftn) +{ + mc->cr->AddCall("isJitIntrinsic"); + bool temp = original_ICorJitInfo->isJitIntrinsic(ftn); + mc->recIsJitIntrinsic(ftn, temp); + return temp; +} + // return flags (defined above, CORINFO_FLG_PUBLIC ...) uint32_t interceptor_ICJI::getMethodAttribs(CORINFO_METHOD_HANDLE ftn /* IN */) { diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp index 674d9f4e1fd102..d5ee05be454813 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp @@ -12,6 +12,13 @@ #include "spmiutil.h" +bool interceptor_ICJI::isJitIntrinsic( + CORINFO_METHOD_HANDLE ftn) +{ + mcs->AddCall("isJitIntrinsic"); + return original_ICorJitInfo->isJitIntrinsic(ftn); +} + uint32_t interceptor_ICJI::getMethodAttribs( CORINFO_METHOD_HANDLE ftn) { diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp index a276453ba69c99..3aed50b5b51a3c 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp @@ -12,6 +12,12 @@ #include "spmiutil.h" +bool interceptor_ICJI::isJitIntrinsic( + CORINFO_METHOD_HANDLE ftn) +{ + return original_ICorJitInfo->isJitIntrinsic(ftn); +} + uint32_t interceptor_ICJI::getMethodAttribs( CORINFO_METHOD_HANDLE ftn) { diff --git a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp index 9d59f4449be613..4f86025dfc8192 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp @@ -23,6 +23,14 @@ ICorJitInfo* InitICorJitInfo(JitInstance* jitInstance) // /**********************************************************************************/ +// Quick check whether the method is a jit intrinsic. Returns the same value as getMethodAttribs(ftn) & +// CORINFO_FLG_JIT_INTRINSIC, except faster. +bool MyICJI::isJitIntrinsic(CORINFO_METHOD_HANDLE ftn) +{ + jitInstance->mc->cr->AddCall("isJitIntrinsic"); + return jitInstance->mc->repIsJitIntrinsic(ftn); +} + // return flags (defined above, CORINFO_FLG_PUBLIC ...) uint32_t MyICJI::getMethodAttribs(CORINFO_METHOD_HANDLE ftn /* IN */) { diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index c971d807a800b8..f62759467de10d 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -1953,6 +1953,9 @@ class ICorStaticInfo // /**********************************************************************************/ + // Quick check whether the method is a jit intrinsic. Returns the same value as getMethodAttribs(ftn) & CORINFO_FLG_JIT_INTRINSIC, except faster. + virtual bool isJitIntrinsic(CORINFO_METHOD_HANDLE ftn) = 0; + // return flags (a bitfield of CorInfoFlags values) virtual uint32_t getMethodAttribs ( CORINFO_METHOD_HANDLE ftn /* IN */ diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index beb71ca0d3fa70..922a854795ec6a 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -21,6 +21,9 @@ public: +bool isJitIntrinsic( + CORINFO_METHOD_HANDLE ftn) override; + uint32_t getMethodAttribs( CORINFO_METHOD_HANDLE ftn) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 5e6a2ef98993e2..f76ceb70b11fcb 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 1776ab48-edfa-49be-a11f-ec216b28174c */ - 0x1776ab48, - 0xedfa, - 0x49be, - {0xa1, 0x1f, 0xec, 0x21, 0x6b, 0x28, 0x17, 0x4c} +constexpr GUID JITEEVersionIdentifier = { /* a33f2f79-dd8d-49dd-b4c3-ac86f34f6a87 */ + 0xa33f2f79, + 0xdd8d, + 0x49dd, + {0xb4, 0xc3, 0xac, 0x86, 0xf3, 0x4f, 0x6a, 0x87} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/ICorJitInfo_API_names.h b/src/coreclr/jit/ICorJitInfo_API_names.h index 2e1e67cc24a773..dd0063573a3e14 100644 --- a/src/coreclr/jit/ICorJitInfo_API_names.h +++ b/src/coreclr/jit/ICorJitInfo_API_names.h @@ -4,6 +4,7 @@ // DO NOT EDIT THIS FILE! IT IS AUTOGENERATED // To regenerate run the gen script in src/coreclr/tools/Common/JitInterface/ThunkGenerator // and follow the instructions in docs/project/updating-jitinterface.md +DEF_CLR_API(isJitIntrinsic) DEF_CLR_API(getMethodAttribs) DEF_CLR_API(setMethodAttribs) DEF_CLR_API(getMethodSig) diff --git a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp index d80bb83ef059a7..e06bdb51279f52 100644 --- a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp +++ b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp @@ -12,6 +12,15 @@ // clang-format off /**********************************************************************************/ +bool WrapICorJitInfo::isJitIntrinsic( + CORINFO_METHOD_HANDLE ftn) +{ + API_ENTER(isJitIntrinsic); + bool temp = wrapHnd->isJitIntrinsic(ftn); + API_LEAVE(isJitIntrinsic); + return temp; +} + uint32_t WrapICorJitInfo::getMethodAttribs( CORINFO_METHOD_HANDLE ftn) { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 584e3568f7f51a..5c4ab9c8e22cad 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7542,6 +7542,7 @@ class Compiler // Get the flags bool eeIsValueClass(CORINFO_CLASS_HANDLE clsHnd); + bool eeIsJitIntrinsic(CORINFO_METHOD_HANDLE ftn); #if defined(DEBUG) || defined(FEATURE_JIT_METHOD_PERF) || defined(FEATURE_SIMD) || defined(TRACK_LSRA_STATS) diff --git a/src/coreclr/jit/ee_il_dll.hpp b/src/coreclr/jit/ee_il_dll.hpp index b0626489f6a97d..5a45f22bce6d2d 100644 --- a/src/coreclr/jit/ee_il_dll.hpp +++ b/src/coreclr/jit/ee_il_dll.hpp @@ -53,6 +53,12 @@ bool Compiler::eeIsValueClass(CORINFO_CLASS_HANDLE clsHnd) return info.compCompHnd->isValueClass(clsHnd); } +FORCEINLINE +bool Compiler::eeIsJitIntrinsic(CORINFO_METHOD_HANDLE ftn) +{ + return info.compCompHnd->isJitIntrinsic(ftn); +} + FORCEINLINE void Compiler::eeGetSig(unsigned sigTok, CORINFO_MODULE_HANDLE scope, diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 024c32c22427c7..052ab147f78667 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -893,8 +893,6 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed } CORINFO_RESOLVED_TOKEN resolvedToken; - CORINFO_RESOLVED_TOKEN constrainedResolvedToken; - CORINFO_CALL_INFO callInfo; while (codeAddr < codeEndp) { @@ -958,54 +956,35 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed break; } - CORINFO_METHOD_HANDLE methodHnd = nullptr; - unsigned methodFlags = 0; - bool mustExpand = false; - CorInfoIntrinsics intrinsicID = CORINFO_INTRINSIC_Illegal; - NamedIntrinsic ni = NI_Illegal; + CORINFO_METHOD_HANDLE methodHnd = nullptr; + bool isJitIntrinsic = false; + bool mustExpand = false; + NamedIntrinsic ni = NI_Illegal; if (resolveTokens) { impResolveToken(codeAddr, &resolvedToken, CORINFO_TOKENKIND_Method); - eeGetCallInfo(&resolvedToken, - (prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr, - combine(combine(CORINFO_CALLINFO_KINDONLY, CORINFO_CALLINFO_ALLOWINSTPARAM), - (opcode == CEE_CALLVIRT) ? CORINFO_CALLINFO_CALLVIRT : CORINFO_CALLINFO_NONE), - &callInfo); - - methodHnd = callInfo.hMethod; - methodFlags = callInfo.methodFlags; + methodHnd = resolvedToken.hMethod; + isJitIntrinsic = eeIsJitIntrinsic(methodHnd); } - if ((methodFlags & (CORINFO_FLG_INTRINSIC | CORINFO_FLG_JIT_INTRINSIC)) != 0) + if (isJitIntrinsic) { intrinsicCalls++; + ni = lookupNamedIntrinsic(methodHnd); - if ((methodFlags & CORINFO_FLG_INTRINSIC) != 0) + switch (ni) { - intrinsicID = info.compCompHnd->getIntrinsicID(methodHnd, &mustExpand); - } + case NI_IsSupported_True: + case NI_IsSupported_False: + { + pushedStack.PushConstant(); + break; + } - if ((methodFlags & CORINFO_FLG_JIT_INTRINSIC) != 0) - { - if (intrinsicID == CORINFO_INTRINSIC_Illegal) + default: { - ni = lookupNamedIntrinsic(methodHnd); - - switch (ni) - { - case NI_IsSupported_True: - case NI_IsSupported_False: - { - pushedStack.PushConstant(); - break; - } - - default: - { - break; - } - } + break; } } } @@ -1162,10 +1141,6 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed noway_assert(sz == sizeof(unsigned)); prefixFlags |= PREFIX_CONSTRAINED; - if (resolveTokens) - { - impResolveToken(codeAddr, &constrainedResolvedToken, CORINFO_TOKENKIND_Constrained); - } codeAddr += sizeof(unsigned); { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs index 80fc5e6ea34a65..e7224e1d73af0c 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs @@ -12,6 +12,21 @@ namespace Internal.JitInterface { unsafe partial class CorInfoImpl { + [UnmanagedCallersOnly] + static byte _isJitIntrinsic(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* ftn) + { + var _this = GetThis(thisHandle); + try + { + return _this.isJitIntrinsic(ftn) ? (byte)1 : (byte)0; + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] static uint _getMethodAttribs(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* ftn) { @@ -2551,180 +2566,181 @@ static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_FLAGS* f static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 172); - - callbacks[0] = (delegate* unmanaged)&_getMethodAttribs; - callbacks[1] = (delegate* unmanaged)&_setMethodAttribs; - callbacks[2] = (delegate* unmanaged)&_getMethodSig; - callbacks[3] = (delegate* unmanaged)&_getMethodInfo; - callbacks[4] = (delegate* unmanaged)&_canInline; - callbacks[5] = (delegate* unmanaged)&_reportInliningDecision; - callbacks[6] = (delegate* unmanaged)&_canTailCall; - callbacks[7] = (delegate* unmanaged)&_reportTailCallDecision; - callbacks[8] = (delegate* unmanaged)&_getEHinfo; - callbacks[9] = (delegate* unmanaged)&_getMethodClass; - callbacks[10] = (delegate* unmanaged)&_getMethodModule; - callbacks[11] = (delegate* unmanaged)&_getMethodVTableOffset; - callbacks[12] = (delegate* unmanaged)&_resolveVirtualMethod; - callbacks[13] = (delegate* unmanaged)&_getUnboxedEntry; - callbacks[14] = (delegate* unmanaged)&_getDefaultComparerClass; - callbacks[15] = (delegate* unmanaged)&_getDefaultEqualityComparerClass; - callbacks[16] = (delegate* unmanaged)&_expandRawHandleIntrinsic; - callbacks[17] = (delegate* unmanaged)&_getIntrinsicID; - callbacks[18] = (delegate* unmanaged)&_isIntrinsicType; - callbacks[19] = (delegate* unmanaged)&_getUnmanagedCallConv; - callbacks[20] = (delegate* unmanaged)&_pInvokeMarshalingRequired; - callbacks[21] = (delegate* unmanaged)&_satisfiesMethodConstraints; - callbacks[22] = (delegate* unmanaged)&_isCompatibleDelegate; - callbacks[23] = (delegate* unmanaged)&_methodMustBeLoadedBeforeCodeIsRun; - callbacks[24] = (delegate* unmanaged)&_mapMethodDeclToMethodImpl; - callbacks[25] = (delegate* unmanaged)&_getGSCookie; - callbacks[26] = (delegate* unmanaged)&_setPatchpointInfo; - callbacks[27] = (delegate* unmanaged)&_getOSRInfo; - callbacks[28] = (delegate* unmanaged)&_resolveToken; - callbacks[29] = (delegate* unmanaged)&_tryResolveToken; - callbacks[30] = (delegate* unmanaged)&_findSig; - callbacks[31] = (delegate* unmanaged)&_findCallSiteSig; - callbacks[32] = (delegate* unmanaged)&_getTokenTypeAsHandle; - callbacks[33] = (delegate* unmanaged)&_isValidToken; - callbacks[34] = (delegate* unmanaged)&_isValidStringRef; - callbacks[35] = (delegate* unmanaged)&_getStringLiteral; - callbacks[36] = (delegate* unmanaged)&_asCorInfoType; - callbacks[37] = (delegate* unmanaged)&_getClassName; - callbacks[38] = (delegate* unmanaged)&_getClassNameFromMetadata; - callbacks[39] = (delegate* unmanaged)&_getTypeInstantiationArgument; - callbacks[40] = (delegate* unmanaged)&_appendClassName; - callbacks[41] = (delegate* unmanaged)&_isValueClass; - callbacks[42] = (delegate* unmanaged)&_canInlineTypeCheck; - callbacks[43] = (delegate* unmanaged)&_getClassAttribs; - callbacks[44] = (delegate* unmanaged)&_isStructRequiringStackAllocRetBuf; - callbacks[45] = (delegate* unmanaged)&_getClassModule; - callbacks[46] = (delegate* unmanaged)&_getModuleAssembly; - callbacks[47] = (delegate* unmanaged)&_getAssemblyName; - callbacks[48] = (delegate* unmanaged)&_LongLifetimeMalloc; - callbacks[49] = (delegate* unmanaged)&_LongLifetimeFree; - callbacks[50] = (delegate* unmanaged)&_getClassModuleIdForStatics; - callbacks[51] = (delegate* unmanaged)&_getClassSize; - callbacks[52] = (delegate* unmanaged)&_getHeapClassSize; - callbacks[53] = (delegate* unmanaged)&_canAllocateOnStack; - callbacks[54] = (delegate* unmanaged)&_getClassAlignmentRequirement; - callbacks[55] = (delegate* unmanaged)&_getClassGClayout; - callbacks[56] = (delegate* unmanaged)&_getClassNumInstanceFields; - callbacks[57] = (delegate* unmanaged)&_getFieldInClass; - callbacks[58] = (delegate* unmanaged)&_checkMethodModifier; - callbacks[59] = (delegate* unmanaged)&_getNewHelper; - callbacks[60] = (delegate* unmanaged)&_getNewArrHelper; - callbacks[61] = (delegate* unmanaged)&_getCastingHelper; - callbacks[62] = (delegate* unmanaged)&_getSharedCCtorHelper; - callbacks[63] = (delegate* unmanaged)&_getTypeForBox; - callbacks[64] = (delegate* unmanaged)&_getBoxHelper; - callbacks[65] = (delegate* unmanaged)&_getUnBoxHelper; - callbacks[66] = (delegate* unmanaged)&_getReadyToRunHelper; - callbacks[67] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; - callbacks[68] = (delegate* unmanaged)&_getHelperName; - callbacks[69] = (delegate* unmanaged)&_initClass; - callbacks[70] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; - callbacks[71] = (delegate* unmanaged)&_getBuiltinClass; - callbacks[72] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; - callbacks[73] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; - callbacks[74] = (delegate* unmanaged)&_canCast; - callbacks[75] = (delegate* unmanaged)&_areTypesEquivalent; - callbacks[76] = (delegate* unmanaged)&_compareTypesForCast; - callbacks[77] = (delegate* unmanaged)&_compareTypesForEquality; - callbacks[78] = (delegate* unmanaged)&_mergeClasses; - callbacks[79] = (delegate* unmanaged)&_isMoreSpecificType; - callbacks[80] = (delegate* unmanaged)&_getParentType; - callbacks[81] = (delegate* unmanaged)&_getChildType; - callbacks[82] = (delegate* unmanaged)&_satisfiesClassConstraints; - callbacks[83] = (delegate* unmanaged)&_isSDArray; - callbacks[84] = (delegate* unmanaged)&_getArrayRank; - callbacks[85] = (delegate* unmanaged)&_getArrayInitializationData; - callbacks[86] = (delegate* unmanaged)&_canAccessClass; - callbacks[87] = (delegate* unmanaged)&_getFieldName; - callbacks[88] = (delegate* unmanaged)&_getFieldClass; - callbacks[89] = (delegate* unmanaged)&_getFieldType; - callbacks[90] = (delegate* unmanaged)&_getFieldOffset; - callbacks[91] = (delegate* unmanaged)&_getFieldInfo; - callbacks[92] = (delegate* unmanaged)&_isFieldStatic; - callbacks[93] = (delegate* unmanaged)&_getBoundaries; - callbacks[94] = (delegate* unmanaged)&_setBoundaries; - callbacks[95] = (delegate* unmanaged)&_getVars; - callbacks[96] = (delegate* unmanaged)&_setVars; - callbacks[97] = (delegate* unmanaged)&_allocateArray; - callbacks[98] = (delegate* unmanaged)&_freeArray; - callbacks[99] = (delegate* unmanaged)&_getArgNext; - callbacks[100] = (delegate* unmanaged)&_getArgType; - callbacks[101] = (delegate* unmanaged)&_getArgClass; - callbacks[102] = (delegate* unmanaged)&_getHFAType; - callbacks[103] = (delegate* unmanaged)&_GetErrorHRESULT; - callbacks[104] = (delegate* unmanaged)&_GetErrorMessage; - callbacks[105] = (delegate* unmanaged)&_FilterException; - callbacks[106] = (delegate* unmanaged)&_HandleException; - callbacks[107] = (delegate* unmanaged)&_ThrowExceptionForJitResult; - callbacks[108] = (delegate* unmanaged)&_ThrowExceptionForHelper; - callbacks[109] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[110] = (delegate* unmanaged)&_getEEInfo; - callbacks[111] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[112] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[113] = (delegate* unmanaged)&_getMethodName; - callbacks[114] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[115] = (delegate* unmanaged)&_getMethodHash; - callbacks[116] = (delegate* unmanaged)&_findNameOfToken; - callbacks[117] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[118] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[119] = (delegate* unmanaged)&_getInlinedCallFrameVptr; - callbacks[120] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[121] = (delegate* unmanaged)&_getHelperFtn; - callbacks[122] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[123] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[124] = (delegate* unmanaged)&_getMethodSync; - callbacks[125] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[126] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[127] = (delegate* unmanaged)&_embedClassHandle; - callbacks[128] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[129] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[130] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[131] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[132] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[133] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[134] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[135] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[136] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[137] = (delegate* unmanaged)&_getCallInfo; - callbacks[138] = (delegate* unmanaged)&_canAccessFamily; - callbacks[139] = (delegate* unmanaged)&_isRIDClassDomainID; - callbacks[140] = (delegate* unmanaged)&_getClassDomainID; - callbacks[141] = (delegate* unmanaged)&_getFieldAddress; - callbacks[142] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[143] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[144] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[145] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[146] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[147] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[148] = (delegate* unmanaged)&_setOverride; - callbacks[149] = (delegate* unmanaged)&_addActiveDependency; - callbacks[150] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[151] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[152] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[153] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[154] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[155] = (delegate* unmanaged)&_allocMem; - callbacks[156] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[157] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[158] = (delegate* unmanaged)&_allocGCInfo; - callbacks[159] = (delegate* unmanaged)&_setEHcount; - callbacks[160] = (delegate* unmanaged)&_setEHinfo; - callbacks[161] = (delegate* unmanaged)&_logMsg; - callbacks[162] = (delegate* unmanaged)&_doAssert; - callbacks[163] = (delegate* unmanaged)&_reportFatalError; - callbacks[164] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[165] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[166] = (delegate* unmanaged)&_getLikelyClass; - callbacks[167] = (delegate* unmanaged)&_recordCallSite; - callbacks[168] = (delegate* unmanaged)&_recordRelocation; - callbacks[169] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[170] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[171] = (delegate* unmanaged)&_getJitFlags; + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 173); + + callbacks[0] = (delegate* unmanaged)&_isJitIntrinsic; + callbacks[1] = (delegate* unmanaged)&_getMethodAttribs; + callbacks[2] = (delegate* unmanaged)&_setMethodAttribs; + callbacks[3] = (delegate* unmanaged)&_getMethodSig; + callbacks[4] = (delegate* unmanaged)&_getMethodInfo; + callbacks[5] = (delegate* unmanaged)&_canInline; + callbacks[6] = (delegate* unmanaged)&_reportInliningDecision; + callbacks[7] = (delegate* unmanaged)&_canTailCall; + callbacks[8] = (delegate* unmanaged)&_reportTailCallDecision; + callbacks[9] = (delegate* unmanaged)&_getEHinfo; + callbacks[10] = (delegate* unmanaged)&_getMethodClass; + callbacks[11] = (delegate* unmanaged)&_getMethodModule; + callbacks[12] = (delegate* unmanaged)&_getMethodVTableOffset; + callbacks[13] = (delegate* unmanaged)&_resolveVirtualMethod; + callbacks[14] = (delegate* unmanaged)&_getUnboxedEntry; + callbacks[15] = (delegate* unmanaged)&_getDefaultComparerClass; + callbacks[16] = (delegate* unmanaged)&_getDefaultEqualityComparerClass; + callbacks[17] = (delegate* unmanaged)&_expandRawHandleIntrinsic; + callbacks[18] = (delegate* unmanaged)&_getIntrinsicID; + callbacks[19] = (delegate* unmanaged)&_isIntrinsicType; + callbacks[20] = (delegate* unmanaged)&_getUnmanagedCallConv; + callbacks[21] = (delegate* unmanaged)&_pInvokeMarshalingRequired; + callbacks[22] = (delegate* unmanaged)&_satisfiesMethodConstraints; + callbacks[23] = (delegate* unmanaged)&_isCompatibleDelegate; + callbacks[24] = (delegate* unmanaged)&_methodMustBeLoadedBeforeCodeIsRun; + callbacks[25] = (delegate* unmanaged)&_mapMethodDeclToMethodImpl; + callbacks[26] = (delegate* unmanaged)&_getGSCookie; + callbacks[27] = (delegate* unmanaged)&_setPatchpointInfo; + callbacks[28] = (delegate* unmanaged)&_getOSRInfo; + callbacks[29] = (delegate* unmanaged)&_resolveToken; + callbacks[30] = (delegate* unmanaged)&_tryResolveToken; + callbacks[31] = (delegate* unmanaged)&_findSig; + callbacks[32] = (delegate* unmanaged)&_findCallSiteSig; + callbacks[33] = (delegate* unmanaged)&_getTokenTypeAsHandle; + callbacks[34] = (delegate* unmanaged)&_isValidToken; + callbacks[35] = (delegate* unmanaged)&_isValidStringRef; + callbacks[36] = (delegate* unmanaged)&_getStringLiteral; + callbacks[37] = (delegate* unmanaged)&_asCorInfoType; + callbacks[38] = (delegate* unmanaged)&_getClassName; + callbacks[39] = (delegate* unmanaged)&_getClassNameFromMetadata; + callbacks[40] = (delegate* unmanaged)&_getTypeInstantiationArgument; + callbacks[41] = (delegate* unmanaged)&_appendClassName; + callbacks[42] = (delegate* unmanaged)&_isValueClass; + callbacks[43] = (delegate* unmanaged)&_canInlineTypeCheck; + callbacks[44] = (delegate* unmanaged)&_getClassAttribs; + callbacks[45] = (delegate* unmanaged)&_isStructRequiringStackAllocRetBuf; + callbacks[46] = (delegate* unmanaged)&_getClassModule; + callbacks[47] = (delegate* unmanaged)&_getModuleAssembly; + callbacks[48] = (delegate* unmanaged)&_getAssemblyName; + callbacks[49] = (delegate* unmanaged)&_LongLifetimeMalloc; + callbacks[50] = (delegate* unmanaged)&_LongLifetimeFree; + callbacks[51] = (delegate* unmanaged)&_getClassModuleIdForStatics; + callbacks[52] = (delegate* unmanaged)&_getClassSize; + callbacks[53] = (delegate* unmanaged)&_getHeapClassSize; + callbacks[54] = (delegate* unmanaged)&_canAllocateOnStack; + callbacks[55] = (delegate* unmanaged)&_getClassAlignmentRequirement; + callbacks[56] = (delegate* unmanaged)&_getClassGClayout; + callbacks[57] = (delegate* unmanaged)&_getClassNumInstanceFields; + callbacks[58] = (delegate* unmanaged)&_getFieldInClass; + callbacks[59] = (delegate* unmanaged)&_checkMethodModifier; + callbacks[60] = (delegate* unmanaged)&_getNewHelper; + callbacks[61] = (delegate* unmanaged)&_getNewArrHelper; + callbacks[62] = (delegate* unmanaged)&_getCastingHelper; + callbacks[63] = (delegate* unmanaged)&_getSharedCCtorHelper; + callbacks[64] = (delegate* unmanaged)&_getTypeForBox; + callbacks[65] = (delegate* unmanaged)&_getBoxHelper; + callbacks[66] = (delegate* unmanaged)&_getUnBoxHelper; + callbacks[67] = (delegate* unmanaged)&_getReadyToRunHelper; + callbacks[68] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; + callbacks[69] = (delegate* unmanaged)&_getHelperName; + callbacks[70] = (delegate* unmanaged)&_initClass; + callbacks[71] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; + callbacks[72] = (delegate* unmanaged)&_getBuiltinClass; + callbacks[73] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; + callbacks[74] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; + callbacks[75] = (delegate* unmanaged)&_canCast; + callbacks[76] = (delegate* unmanaged)&_areTypesEquivalent; + callbacks[77] = (delegate* unmanaged)&_compareTypesForCast; + callbacks[78] = (delegate* unmanaged)&_compareTypesForEquality; + callbacks[79] = (delegate* unmanaged)&_mergeClasses; + callbacks[80] = (delegate* unmanaged)&_isMoreSpecificType; + callbacks[81] = (delegate* unmanaged)&_getParentType; + callbacks[82] = (delegate* unmanaged)&_getChildType; + callbacks[83] = (delegate* unmanaged)&_satisfiesClassConstraints; + callbacks[84] = (delegate* unmanaged)&_isSDArray; + callbacks[85] = (delegate* unmanaged)&_getArrayRank; + callbacks[86] = (delegate* unmanaged)&_getArrayInitializationData; + callbacks[87] = (delegate* unmanaged)&_canAccessClass; + callbacks[88] = (delegate* unmanaged)&_getFieldName; + callbacks[89] = (delegate* unmanaged)&_getFieldClass; + callbacks[90] = (delegate* unmanaged)&_getFieldType; + callbacks[91] = (delegate* unmanaged)&_getFieldOffset; + callbacks[92] = (delegate* unmanaged)&_getFieldInfo; + callbacks[93] = (delegate* unmanaged)&_isFieldStatic; + callbacks[94] = (delegate* unmanaged)&_getBoundaries; + callbacks[95] = (delegate* unmanaged)&_setBoundaries; + callbacks[96] = (delegate* unmanaged)&_getVars; + callbacks[97] = (delegate* unmanaged)&_setVars; + callbacks[98] = (delegate* unmanaged)&_allocateArray; + callbacks[99] = (delegate* unmanaged)&_freeArray; + callbacks[100] = (delegate* unmanaged)&_getArgNext; + callbacks[101] = (delegate* unmanaged)&_getArgType; + callbacks[102] = (delegate* unmanaged)&_getArgClass; + callbacks[103] = (delegate* unmanaged)&_getHFAType; + callbacks[104] = (delegate* unmanaged)&_GetErrorHRESULT; + callbacks[105] = (delegate* unmanaged)&_GetErrorMessage; + callbacks[106] = (delegate* unmanaged)&_FilterException; + callbacks[107] = (delegate* unmanaged)&_HandleException; + callbacks[108] = (delegate* unmanaged)&_ThrowExceptionForJitResult; + callbacks[109] = (delegate* unmanaged)&_ThrowExceptionForHelper; + callbacks[110] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[111] = (delegate* unmanaged)&_getEEInfo; + callbacks[112] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[113] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[114] = (delegate* unmanaged)&_getMethodName; + callbacks[115] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[116] = (delegate* unmanaged)&_getMethodHash; + callbacks[117] = (delegate* unmanaged)&_findNameOfToken; + callbacks[118] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[119] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[120] = (delegate* unmanaged)&_getInlinedCallFrameVptr; + callbacks[121] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[122] = (delegate* unmanaged)&_getHelperFtn; + callbacks[123] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[124] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[125] = (delegate* unmanaged)&_getMethodSync; + callbacks[126] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[127] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[128] = (delegate* unmanaged)&_embedClassHandle; + callbacks[129] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[130] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[131] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[132] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[133] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[134] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[135] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[136] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[137] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[138] = (delegate* unmanaged)&_getCallInfo; + callbacks[139] = (delegate* unmanaged)&_canAccessFamily; + callbacks[140] = (delegate* unmanaged)&_isRIDClassDomainID; + callbacks[141] = (delegate* unmanaged)&_getClassDomainID; + callbacks[142] = (delegate* unmanaged)&_getFieldAddress; + callbacks[143] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[144] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[145] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[146] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[147] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[148] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[149] = (delegate* unmanaged)&_setOverride; + callbacks[150] = (delegate* unmanaged)&_addActiveDependency; + callbacks[151] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[152] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[153] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[154] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[155] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[156] = (delegate* unmanaged)&_allocMem; + callbacks[157] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[158] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[159] = (delegate* unmanaged)&_allocGCInfo; + callbacks[160] = (delegate* unmanaged)&_setEHcount; + callbacks[161] = (delegate* unmanaged)&_setEHinfo; + callbacks[162] = (delegate* unmanaged)&_logMsg; + callbacks[163] = (delegate* unmanaged)&_doAssert; + callbacks[164] = (delegate* unmanaged)&_reportFatalError; + callbacks[165] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[166] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[167] = (delegate* unmanaged)&_getLikelyClass; + callbacks[168] = (delegate* unmanaged)&_recordCallSite; + callbacks[169] = (delegate* unmanaged)&_recordRelocation; + callbacks[170] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[171] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[172] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index c138b9500c8822..fa5b01adc89a37 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -858,6 +858,12 @@ private TypeSystemEntity entityFromContext(CORINFO_CONTEXT_STRUCT* contextStruct return (TypeSystemEntity)HandleToObject((IntPtr)((ulong)contextStruct & ~(ulong)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_MASK)); } + private bool isJitIntrinsic(CORINFO_METHOD_STRUCT_* ftn) + { + MethodDesc method = HandleToObject(ftn); + return method.IsIntrinsic; + } + private uint getMethodAttribsInternal(MethodDesc method) { CorInfoFlag result = 0; diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index d4640311e8818a..03ddae062ba693 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -150,6 +150,7 @@ struct _EXCEPTION_POINTERS*,_EXCEPTION_POINTERS* ICorJitInfo::errorTrapFunction,void* FUNCTIONS + bool isJitIntrinsic( CORINFO_METHOD_HANDLE ftn ); uint32_t getMethodAttribs( CORINFO_METHOD_HANDLE ftn ); void setMethodAttribs( CORINFO_METHOD_HANDLE ftn, CorInfoMethodRuntimeFlags attribs ); void getMethodSig( CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO *sig, CORINFO_CLASS_HANDLE memberParent ); diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface.h b/src/coreclr/tools/aot/jitinterface/jitinterface.h index 3e61333832c257..b39c924f718495 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface.h @@ -11,6 +11,7 @@ struct JitInterfaceCallbacks { + bool (* isJitIntrinsic)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn); uint32_t (* getMethodAttribs)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn); void (* setMethodAttribs)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, CorInfoMethodRuntimeFlags attribs); void (* getMethodSig)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO* sig, CORINFO_CLASS_HANDLE memberParent); @@ -198,6 +199,15 @@ class JitInterfaceWrapper : public ICorJitInfo } + virtual bool isJitIntrinsic( + CORINFO_METHOD_HANDLE ftn) +{ + CorInfoExceptionClass* pException = nullptr; + bool temp = _callbacks->isJitIntrinsic(_thisHandle, &pException, ftn); + if (pException != nullptr) throw pException; + return temp; +} + virtual uint32_t getMethodAttribs( CORINFO_METHOD_HANDLE ftn) { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index dc8bdb96db7c94..50e44d2837996a 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -6613,6 +6613,29 @@ CORINFO_CLASS_HANDLE CEEInfo::getTypeInstantiationArgument(CORINFO_CLASS_HANDLE return result; } +/*********************************************************************/ +bool CEEInfo::isJitIntrinsic(CORINFO_METHOD_HANDLE ftn) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + bool ret = false; + + JIT_TO_EE_TRANSITION_LEAF(); + + _ASSERTE(ftn); + + MethodDesc *pMD = (MethodDesc*)ftn; + ret = pMD->IsJitIntrinsic(); + + EE_TO_JIT_TRANSITION_LEAF(); + + return ret; +} + /*********************************************************************/ uint32_t CEEInfo::getMethodAttribs (CORINFO_METHOD_HANDLE ftn) { @@ -9109,7 +9132,7 @@ CORINFO_CLASS_HANDLE CEEInfo::getDefaultComparerClassHelper(CORINFO_CLASS_HANDLE // Mirrors the logic in BCL's CompareHelpers.CreateDefaultComparer // And in compile.cpp's SpecializeComparer - // + // // We need to find the appropriate instantiation Instantiation inst(&elemTypeHnd, 1); @@ -9210,7 +9233,7 @@ CORINFO_CLASS_HANDLE CEEInfo::getDefaultEqualityComparerClassHelper(CORINFO_CLAS // Mirrors the logic in BCL's CompareHelpers.CreateDefaultComparer // And in compile.cpp's SpecializeComparer - // + // // We need to find the appropriate instantiation Instantiation inst(&elemTypeHnd, 1); @@ -12094,7 +12117,7 @@ HRESULT CEEJitInfo::getPgoInstrumentationResults( newPgoData->m_next = m_foundPgoData; m_foundPgoData = newPgoData; newPgoData.SuppressRelease(); - + newPgoData->m_hr = PgoManager::getPgoInstrumentationResults(pMD, &newPgoData->m_allocatedData, &newPgoData->m_schema, &newPgoData->m_cSchemaElems, &newPgoData->m_pInstrumentationData); pDataCur = m_foundPgoData; } diff --git a/src/coreclr/zap/zapinfo.cpp b/src/coreclr/zap/zapinfo.cpp index 12fe2ab074ce55..4f23cc5d4bbb7f 100644 --- a/src/coreclr/zap/zapinfo.cpp +++ b/src/coreclr/zap/zapinfo.cpp @@ -3959,6 +3959,11 @@ unsigned ZapInfo::getMethodHash(CORINFO_METHOD_HANDLE ftn) return m_pEEJitInfo->getMethodHash(ftn); } +bool ZapInfo::isJitIntrinsic(CORINFO_METHOD_HANDLE ftn) +{ + return m_pEEJitInfo->isJitIntrinsic(ftn); +} + uint32_t ZapInfo::getMethodAttribs(CORINFO_METHOD_HANDLE ftn) { DWORD result = m_pEEJitInfo->getMethodAttribs(ftn); From defd712bc6690a7401d873dd2bad7ee2082144c7 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Tue, 13 Apr 2021 21:08:52 -0700 Subject: [PATCH 09/11] IANA To/From Windows Ids Conversion APIs (#51093) * Iana To/From Windows Ids Conversion APIs * Address the feedback and fix the browser build breaks * Address the feedback --- .../src/Interop/Interop.TimeZoneInfo.cs | 2 +- .../pal_timeZoneInfo.c | 6 +- .../pal_timeZoneInfo.h | 2 +- .../System.Private.CoreLib.Shared.projitems | 3 +- ...TimeZoneInfo.FullGlobalizationData.Unix.cs | 241 ++++++++++++++++ .../TimeZoneInfo.FullGlobalizationData.cs | 273 +++--------------- .../TimeZoneInfo.MinimalGlobalizationData.cs | 15 +- .../src/System/TimeZoneInfo.Unix.cs | 2 + .../src/System/TimeZoneInfo.Win32.cs | 25 +- .../src/System/TimeZoneInfo.cs | 54 +++- .../System.Runtime/ref/System.Runtime.cs | 4 + .../tests/System/TimeZoneInfoTests.cs | 59 ++++ 12 files changed, 421 insertions(+), 265 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.Unix.cs diff --git a/src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs b/src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs index faa7e4f3a90738..10e73ee4b1d791 100644 --- a/src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs +++ b/src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs @@ -16,7 +16,7 @@ internal static extern unsafe ResultCode GetTimeZoneDisplayName( int resultLength); [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_WindowsIdToIanaId")] - internal static extern unsafe int WindowsIdToIanaId(string windowsId, char* ianaId, int ianaIdLength); + internal static extern unsafe int WindowsIdToIanaId(string windowsId, [MarshalAs(UnmanagedType.LPStr)] string? region, char* ianaId, int ianaIdLength); [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IanaIdToWindowsId")] internal static extern unsafe int IanaIdToWindowsId(string ianaId, char* windowsId, int windowsIdLength); diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.c b/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.c index 9173931716c0e7..2620c146b53a0e 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.c +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.c @@ -21,13 +21,13 @@ static const UChar EXEMPLAR_CITY_PATTERN_UCHAR[] = {'V', 'V', 'V', '\0'}; /* Convert Windows Time Zone Id to IANA Id */ -int32_t GlobalizationNative_WindowsIdToIanaId(const UChar* windowsId, UChar* ianaId, int32_t ianaIdLength) +int32_t GlobalizationNative_WindowsIdToIanaId(const UChar* windowsId, const char* region, UChar* ianaId, int32_t ianaIdLength) { UErrorCode status = U_ZERO_ERROR; if (ucal_getTimeZoneIDForWindowsID_ptr != NULL) { - int32_t ianaIdFilledLength = ucal_getTimeZoneIDForWindowsID(windowsId, -1, NULL, ianaId, ianaIdLength, &status); + int32_t ianaIdFilledLength = ucal_getTimeZoneIDForWindowsID(windowsId, -1, region, ianaId, ianaIdLength, &status); if (U_SUCCESS(status)) { return ianaIdFilledLength; @@ -91,7 +91,7 @@ static void GetTimeZoneDisplayName_FromPattern(const char* locale, const UChar* if (U_SUCCESS(*err)) { udat_format(dateFormatter, timestamp, result, resultLength, NULL, err); - udat_close(dateFormatter); + udat_close(dateFormatter); } } diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.h b/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.h index 49bfb6250eb507..b91469e6a1341a 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.h +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.h @@ -20,6 +20,6 @@ typedef enum TimeZoneDisplayName_ExemplarCity = 4, } TimeZoneDisplayNameType; -PALEXPORT int32_t GlobalizationNative_WindowsIdToIanaId(const UChar* windowsId, UChar* ianaId, int32_t ianaIdLength); +PALEXPORT int32_t GlobalizationNative_WindowsIdToIanaId(const UChar* windowsId, const char* region, UChar* ianaId, int32_t ianaIdLength); PALEXPORT int32_t GlobalizationNative_IanaIdToWindowsId(const UChar* ianaId, UChar* windowsId, int32_t windowsIdLength); PALEXPORT ResultCode GlobalizationNative_GetTimeZoneDisplayName(const UChar* localeName, const UChar* timeZoneId, TimeZoneDisplayNameType type, UChar* result, int32_t resultLength); diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index c67c432163c73d..faea9cca673fdd 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1046,6 +1046,7 @@ + @@ -1904,7 +1905,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.Unix.cs new file mode 100644 index 00000000000000..d1f306abb0f6df --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.Unix.cs @@ -0,0 +1,241 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; + +namespace System +{ + public sealed partial class TimeZoneInfo + { + private const string InvariantUtcStandardDisplayName = "Coordinated Universal Time"; + private const string FallbackCultureName = "en-US"; + private const string GmtId = "GMT"; + + // Some time zones may give better display names using their location names rather than their generic name. + // We can update this list as need arises. + private static readonly string[] s_ZonesThatUseLocationName = new[] { + "Europe/Minsk", // Prefer "Belarus Time" over "Moscow Standard Time (Minsk)" + "Europe/Moscow", // Prefer "Moscow Time" over "Moscow Standard Time" + "Europe/Simferopol", // Prefer "Simferopol Time" over "Moscow Standard Time (Simferopol)" + "Pacific/Apia", // Prefer "Samoa Time" over "Apia Time" + "Pacific/Pitcairn" // Prefer "Pitcairn Islands Time" over "Pitcairn Time" + }; + + // Main function that is called during construction to populate the three display names + private static void TryPopulateTimeZoneDisplayNamesFromGlobalizationData(string timeZoneId, TimeSpan baseUtcOffset, ref string? standardDisplayName, ref string? daylightDisplayName, ref string? displayName) + { + // Determine the culture to use + CultureInfo uiCulture = CultureInfo.CurrentUICulture; + if (uiCulture.Name.Length == 0) + uiCulture = CultureInfo.GetCultureInfo(FallbackCultureName); // ICU doesn't work nicely with InvariantCulture + + // Attempt to populate the fields backing the StandardName, DaylightName, and DisplayName from globalization data. + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture.Name, ref standardDisplayName); + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.DaylightSavings, uiCulture.Name, ref daylightDisplayName); + GetFullValueForDisplayNameField(timeZoneId, baseUtcOffset, uiCulture, ref displayName); + } + + // Helper function to get the standard display name for the UTC static time zone instance + private static string GetUtcStandardDisplayName() + { + // Don't bother looking up the name for invariant or English cultures + CultureInfo uiCulture = CultureInfo.CurrentUICulture; + if (GlobalizationMode.Invariant || uiCulture.Name.Length == 0 || uiCulture.TwoLetterISOLanguageName == "en") + return InvariantUtcStandardDisplayName; + + // Try to get a localized version of "Coordinated Universal Time" from the globalization data + string? standardDisplayName = null; + GetDisplayName(UtcId, Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture.Name, ref standardDisplayName); + + // Final safety check. Don't allow null or abbreviations + if (standardDisplayName == null || standardDisplayName == "GMT" || standardDisplayName == "UTC") + standardDisplayName = InvariantUtcStandardDisplayName; + + return standardDisplayName; + } + + // Helper function to get the full display name for the UTC static time zone instance + private static string GetUtcFullDisplayName(string timeZoneId, string standardDisplayName) + { + return $"(UTC) {standardDisplayName}"; + } + + // Helper function that retrieves various forms of time zone display names from ICU + private static unsafe void GetDisplayName(string timeZoneId, Interop.Globalization.TimeZoneDisplayNameType nameType, string uiCulture, ref string? displayName) + { + if (GlobalizationMode.Invariant) + { + return; + } + + string? timeZoneDisplayName; + bool result = Interop.CallStringMethod( + (buffer, locale, id, type) => + { + fixed (char* bufferPtr = buffer) + { + return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); + } + }, + uiCulture, + timeZoneId, + nameType, + out timeZoneDisplayName); + + if (!result && uiCulture != FallbackCultureName) + { + // Try to fallback using FallbackCultureName just in case we can make it work. + result = Interop.CallStringMethod( + (buffer, locale, id, type) => + { + fixed (char* bufferPtr = buffer) + { + return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); + } + }, + FallbackCultureName, + timeZoneId, + nameType, + out timeZoneDisplayName); + } + + // If there is an unknown error, don't set the displayName field. + // It will be set to the abbreviation that was read out of the tzfile. + if (result && !string.IsNullOrEmpty(timeZoneDisplayName)) + { + displayName = timeZoneDisplayName; + } + } + + // Helper function that builds the value backing the DisplayName field from globalization data. + private static void GetFullValueForDisplayNameField(string timeZoneId, TimeSpan baseUtcOffset, CultureInfo uiCulture, ref string? displayName) + { + // There are a few diffent ways we might show the display name depending on the data. + // The algorithm used below should avoid duplicating the same words while still achieving the + // goal of providing a unique, discoverable, and intuitive name. + + // Try to get the generic name for this time zone. + string? genericName = null; + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.Generic, uiCulture.Name, ref genericName); + if (genericName == null) + { + // We'll use the fallback display name value already set. + return; + } + + // Get the base offset to prefix in front of the time zone. + // Only UTC and its aliases have "(UTC)", handled earlier. All other zones include an offset, even if it's zero. + string baseOffsetText = $"(UTC{(baseUtcOffset >= TimeSpan.Zero ? '+' : '-')}{baseUtcOffset:hh\\:mm})"; + + // Get the generic location name. + string? genericLocationName = null; + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.GenericLocation, uiCulture.Name, ref genericLocationName); + + // Some edge cases only apply when the offset is +00:00. + if (baseUtcOffset == TimeSpan.Zero) + { + // GMT and its aliases will just use the equivalent of "Greenwich Mean Time". + string? gmtLocationName = null; + GetDisplayName(GmtId, Interop.Globalization.TimeZoneDisplayNameType.GenericLocation, uiCulture.Name, ref gmtLocationName); + if (genericLocationName == gmtLocationName) + { + displayName = $"{baseOffsetText} {genericName}"; + return; + } + + // Other zones with a zero offset and the equivalent of "Greenwich Mean Time" should only use the location name. + // For example, prefer "Iceland Time" over "Greenwich Mean Time (Reykjavik)". + string? gmtGenericName = null; + GetDisplayName(GmtId, Interop.Globalization.TimeZoneDisplayNameType.Generic, uiCulture.Name, ref gmtGenericName); + if (genericName == gmtGenericName) + { + displayName = $"{baseOffsetText} {genericLocationName}"; + return; + } + } + + if (genericLocationName == genericName) + { + // When the location name is the same as the generic name, + // then it is generally good enough to show by itself. + + // *** Example (en-US) *** + // id = "America/Havana" + // baseOffsetText = "(UTC-05:00)" + // standardName = "Cuba Standard Time" + // genericName = "Cuba Time" + // genericLocationName = "Cuba Time" + // exemplarCityName = "Havana" + // displayName = "(UTC-05:00) Cuba Time" + + displayName = $"{baseOffsetText} {genericLocationName}"; + return; + } + + // Prefer location names in some special cases. + if (StringArrayContains(timeZoneId, s_ZonesThatUseLocationName, StringComparison.OrdinalIgnoreCase)) + { + displayName = $"{baseOffsetText} {genericLocationName}"; + return; + } + + // See if we should include the exemplar city name. + string exemplarCityName = GetExemplarCityName(timeZoneId, uiCulture.Name); + if (uiCulture.CompareInfo.IndexOf(genericName, exemplarCityName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0 && genericLocationName != null) + { + // When an exemplar city is already part of the generic name, + // there's no need to repeat it again so just use the generic name. + + // *** Example (fr-FR) *** + // id = "Australia/Lord_Howe" + // baseOffsetText = "(UTC+10:30)" + // standardName = "heure normale de Lord Howe" + // genericName = "heure de Lord Howe" + // genericLocationName = "heure : Lord Howe" + // exemplarCityName = "Lord Howe" + // displayName = "(UTC+10:30) heure de Lord Howe" + + displayName = $"{baseOffsetText} {genericName}"; + } + else + { + // Finally, use the generic name and the exemplar city together. + // This provides an intuitive name and still disambiguates. + + // *** Example (en-US) *** + // id = "Europe/Rome" + // baseOffsetText = "(UTC+01:00)" + // standardName = "Central European Standard Time" + // genericName = "Central European Time" + // genericLocationName = "Italy Time" + // exemplarCityName = "Rome" + // displayName = "(UTC+01:00) Central European Time (Rome)" + + displayName = $"{baseOffsetText} {genericName} ({exemplarCityName})"; + } + } + + // Helper function that gets an exmplar city name either from ICU or from the IANA time zone ID itself + private static string GetExemplarCityName(string timeZoneId, string uiCultureName) + { + // First try to get the name through the localization data. + string? exemplarCityName = null; + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.ExemplarCity, uiCultureName, ref exemplarCityName); + if (!string.IsNullOrEmpty(exemplarCityName)) + return exemplarCityName; + + // Support for getting exemplar city names was added in ICU 51. + // We may have an older version. For example, in Helix we test on RHEL 7.5 which uses ICU 50.1.2. + // We'll fallback to using an English name generated from the time zone ID. + int i = timeZoneId.LastIndexOf('/'); + return timeZoneId.Substring(i + 1).Replace('_', ' '); + } + + // Helper function that returns an alternative ID using ICU data. Used primarily for converting from Windows IDs. + private static unsafe string? GetAlternativeId(string id, out bool idIsIana) + { + idIsIana = false; + return TryConvertWindowsIdToIanaId(id, null, out string? ianaId) ? ianaId : null; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs index 3e0230f9e81b2f..557e333d4eb9a5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs @@ -7,260 +7,75 @@ namespace System { public sealed partial class TimeZoneInfo { - private const string InvariantUtcStandardDisplayName = "Coordinated Universal Time"; - private const string FallbackCultureName = "en-US"; - private const string GmtId = "GMT"; - - // Some time zones may give better display names using their location names rather than their generic name. - // We can update this list as need arises. - private static readonly string[] s_ZonesThatUseLocationName = new[] { - "Europe/Minsk", // Prefer "Belarus Time" over "Moscow Standard Time (Minsk)" - "Europe/Moscow", // Prefer "Moscow Time" over "Moscow Standard Time" - "Europe/Simferopol", // Prefer "Simferopol Time" over "Moscow Standard Time (Simferopol)" - "Pacific/Apia", // Prefer "Samoa Time" over "Apia Time" - "Pacific/Pitcairn" // Prefer "Pitcairn Islands Time" over "Pitcairn Time" - }; - - // Main function that is called during construction to populate the three display names - private static void TryPopulateTimeZoneDisplayNamesFromGlobalizationData(string timeZoneId, TimeSpan baseUtcOffset, ref string? standardDisplayName, ref string? daylightDisplayName, ref string? displayName) - { - // Determine the culture to use - CultureInfo uiCulture = CultureInfo.CurrentUICulture; - if (uiCulture.Name.Length == 0) - uiCulture = CultureInfo.GetCultureInfo(FallbackCultureName); // ICU doesn't work nicely with InvariantCulture - - // Attempt to populate the fields backing the StandardName, DaylightName, and DisplayName from globalization data. - GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture.Name, ref standardDisplayName); - GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.DaylightSavings, uiCulture.Name, ref daylightDisplayName); - GetFullValueForDisplayNameField(timeZoneId, baseUtcOffset, uiCulture, ref displayName); - } - - // Helper function to get the standard display name for the UTC static time zone instance - private static string GetUtcStandardDisplayName() - { - // Don't bother looking up the name for invariant or English cultures - CultureInfo uiCulture = CultureInfo.CurrentUICulture; - if (GlobalizationMode.Invariant || uiCulture.Name.Length == 0 || uiCulture.TwoLetterISOLanguageName == "en") - return InvariantUtcStandardDisplayName; - - // Try to get a localized version of "Coordinated Universal Time" from the globalization data - string? standardDisplayName = null; - GetDisplayName(UtcId, Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture.Name, ref standardDisplayName); - - // Final safety check. Don't allow null or abbreviations - if (standardDisplayName == null || standardDisplayName == "GMT" || standardDisplayName == "UTC") - standardDisplayName = InvariantUtcStandardDisplayName; - - return standardDisplayName; - } - - // Helper function to get the full display name for the UTC static time zone instance - private static string GetUtcFullDisplayName(string timeZoneId, string standardDisplayName) - { - return $"(UTC) {standardDisplayName}"; - } - - // Helper function that retrieves various forms of time zone display names from ICU - private static unsafe void GetDisplayName(string timeZoneId, Interop.Globalization.TimeZoneDisplayNameType nameType, string uiCulture, ref string? displayName) + private static unsafe bool TryConvertIanaIdToWindowsId(string ianaId, bool allocate, out string? windowsId) { - if (GlobalizationMode.Invariant) + if (GlobalizationMode.Invariant || GlobalizationMode.UseNls || ianaId is null) { - return; + windowsId = null; + return false; } - string? timeZoneDisplayName; - bool result = Interop.CallStringMethod( - (buffer, locale, id, type) => - { - fixed (char* bufferPtr = buffer) - { - return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); - } - }, - uiCulture, - timeZoneId, - nameType, - out timeZoneDisplayName); - - if (!result && uiCulture != FallbackCultureName) + foreach (char c in ianaId) { - // Try to fallback using FallbackCultureName just in case we can make it work. - result = Interop.CallStringMethod( - (buffer, locale, id, type) => - { - fixed (char* bufferPtr = buffer) - { - return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); - } - }, - FallbackCultureName, - timeZoneId, - nameType, - out timeZoneDisplayName); + // ICU uses some characters as a separator and trim the id at that character. + // while we should fail if the Id contained one of these characters. + if (c == '\\' || c == '\n' || c == '\r') + { + windowsId = null; + return false; + } } - // If there is an unknown error, don't set the displayName field. - // It will be set to the abbreviation that was read out of the tzfile. - if (result && !string.IsNullOrEmpty(timeZoneDisplayName)) + char* buffer = stackalloc char[100]; + int length = Interop.Globalization.IanaIdToWindowsId(ianaId, buffer, 100); + if (length > 0) { - displayName = timeZoneDisplayName; + windowsId = allocate ? new string(buffer, 0, length) : null; + return true; } + + windowsId = null; + return false; } - // Helper function that builds the value backing the DisplayName field from globalization data. - private static void GetFullValueForDisplayNameField(string timeZoneId, TimeSpan baseUtcOffset, CultureInfo uiCulture, ref string? displayName) + private static unsafe bool TryConvertWindowsIdToIanaId(string windowsId, string? region, bool allocate, out string? ianaId) { - // There are a few diffent ways we might show the display name depending on the data. - // The algorithm used below should avoid duplicating the same words while still achieving the - // goal of providing a unique, discoverable, and intuitive name. - - // Try to get the generic name for this time zone. - string? genericName = null; - GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.Generic, uiCulture.Name, ref genericName); - if (genericName == null) + // This functionality is not enabled in the browser for the sake of size reduction. + if (GlobalizationMode.Invariant || GlobalizationMode.UseNls || windowsId is null) { - // We'll use the fallback display name value already set. - return; + ianaId = null; + return false; } - // Get the base offset to prefix in front of the time zone. - // Only UTC and its aliases have "(UTC)", handled earlier. All other zones include an offset, even if it's zero. - string baseOffsetText = $"(UTC{(baseUtcOffset >= TimeSpan.Zero ? '+' : '-')}{baseUtcOffset:hh\\:mm})"; - - // Get the generic location name. - string? genericLocationName = null; - GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.GenericLocation, uiCulture.Name, ref genericLocationName); - - // Some edge cases only apply when the offset is +00:00. - if (baseUtcOffset == TimeSpan.Zero) + if (windowsId.Equals("utc", StringComparison.OrdinalIgnoreCase)) { - // GMT and its aliases will just use the equivalent of "Greenwich Mean Time". - string? gmtLocationName = null; - GetDisplayName(GmtId, Interop.Globalization.TimeZoneDisplayNameType.GenericLocation, uiCulture.Name, ref gmtLocationName); - if (genericLocationName == gmtLocationName) - { - displayName = $"{baseOffsetText} {genericName}"; - return; - } - - // Other zones with a zero offset and the equivalent of "Greenwich Mean Time" should only use the location name. - // For example, prefer "Iceland Time" over "Greenwich Mean Time (Reykjavik)". - string? gmtGenericName = null; - GetDisplayName(GmtId, Interop.Globalization.TimeZoneDisplayNameType.Generic, uiCulture.Name, ref gmtGenericName); - if (genericName == gmtGenericName) - { - displayName = $"{baseOffsetText} {genericLocationName}"; - return; - } + // Special case UTC, as previously ICU would convert it to "Etc/GMT" which is incorrect name for UTC. + ianaId = "Etc/UTC"; + return true; } - if (genericLocationName == genericName) + foreach (char c in windowsId) { - // When the location name is the same as the generic name, - // then it is generally good enough to show by itself. - - // *** Example (en-US) *** - // id = "America/Havana" - // baseOffsetText = "(UTC-05:00)" - // standardName = "Cuba Standard Time" - // genericName = "Cuba Time" - // genericLocationName = "Cuba Time" - // exemplarCityName = "Havana" - // displayName = "(UTC-05:00) Cuba Time" - - displayName = $"{baseOffsetText} {genericLocationName}"; - return; - } - - // Prefer location names in some special cases. - if (StringArrayContains(timeZoneId, s_ZonesThatUseLocationName, StringComparison.OrdinalIgnoreCase)) - { - displayName = $"{baseOffsetText} {genericLocationName}"; - return; + // ICU uses some characters as a separator and trim the id at that character. + // while we should fail if the Id contained one of these characters. + if (c == '\\' || c == '\n' || c == '\r') + { + ianaId = null; + return false; + } } - // See if we should include the exemplar city name. - string exemplarCityName = GetExemplarCityName(timeZoneId, uiCulture.Name); - if (uiCulture.CompareInfo.IndexOf(genericName, exemplarCityName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0 && genericLocationName != null) - { - // When an exemplar city is already part of the generic name, - // there's no need to repeat it again so just use the generic name. - - // *** Example (fr-FR) *** - // id = "Australia/Lord_Howe" - // baseOffsetText = "(UTC+10:30)" - // standardName = "heure normale de Lord Howe" - // genericName = "heure de Lord Howe" - // genericLocationName = "heure : Lord Howe" - // exemplarCityName = "Lord Howe" - // displayName = "(UTC+10:30) heure de Lord Howe" - - displayName = $"{baseOffsetText} {genericName}"; - } - else + char* buffer = stackalloc char[100]; + int length = Interop.Globalization.WindowsIdToIanaId(windowsId, region, buffer, 100); + if (length > 0) { - // Finally, use the generic name and the exemplar city together. - // This provides an intuitive name and still disambiguates. - - // *** Example (en-US) *** - // id = "Europe/Rome" - // baseOffsetText = "(UTC+01:00)" - // standardName = "Central European Standard Time" - // genericName = "Central European Time" - // genericLocationName = "Italy Time" - // exemplarCityName = "Rome" - // displayName = "(UTC+01:00) Central European Time (Rome)" - - displayName = $"{baseOffsetText} {genericName} ({exemplarCityName})"; + ianaId = allocate ? new string(buffer, 0, length) : null; + return true; } - } - - // Helper function that gets an exmplar city name either from ICU or from the IANA time zone ID itself - private static string GetExemplarCityName(string timeZoneId, string uiCultureName) - { - // First try to get the name through the localization data. - string? exemplarCityName = null; - GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.ExemplarCity, uiCultureName, ref exemplarCityName); - if (!string.IsNullOrEmpty(exemplarCityName)) - return exemplarCityName; - // Support for getting exemplar city names was added in ICU 51. - // We may have an older version. For example, in Helix we test on RHEL 7.5 which uses ICU 50.1.2. - // We'll fallback to using an English name generated from the time zone ID. - int i = timeZoneId.LastIndexOf('/'); - return timeZoneId.Substring(i + 1).Replace('_', ' '); + ianaId = null; + return false; } - // Helper function that returns an alternative ID using ICU data. Used primarily for converting from Windows IDs. - private static unsafe string? GetAlternativeId(string id) - { - if (!GlobalizationMode.Invariant) - { - if (id.Equals("utc", StringComparison.OrdinalIgnoreCase)) - { - // Special case UTC, as previously ICU would convert it to "Etc/GMT" which is incorrect name for UTC. - return "Etc/UTC"; - } - - foreach (char c in id) - { - // ICU uses some characters as a separator and trim the id at that character. - // while we should fail if the Id contained one of these characters. - if (c == '\\' || c == '\n' || c == '\r') - { - return null; - } - } - - char* buffer = stackalloc char[100]; - int length = Interop.Globalization.WindowsIdToIanaId(id, buffer, 100); - if (length > 0) - { - return new string(buffer, 0, length); - } - } - - return null; - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.MinimalGlobalizationData.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.MinimalGlobalizationData.cs index 234d63366f3e13..5ef4d16edaa152 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.MinimalGlobalizationData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.MinimalGlobalizationData.cs @@ -22,10 +22,23 @@ private static string GetUtcFullDisplayName(string timeZoneId, string standardDi return $"(UTC) {timeZoneId}"; } - private static string? GetAlternativeId(string id) + private static string? GetAlternativeId(string id, out bool idIsIana) { // No alternative IDs in this target. + idIsIana = false; return null; } + + private static unsafe bool TryConvertIanaIdToWindowsId(string ianaId, bool allocate, out string? windowsId) + { + windowsId = null; + return false; + } + + private static unsafe bool TryConvertWindowsIdToIanaId(string windowsId, string? region, bool allocate, out string? ianaId) + { + ianaId = null; + return false; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs index 6c7efd4b662b3f..b664b1f9906964 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs @@ -41,6 +41,8 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled) { _id = id; + HasIanaId = true; + // Handle UTC and its aliases if (StringArrayContains(_id, s_UtcAliases, StringComparison.OrdinalIgnoreCase)) { diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs index a13f48d6258de2..c42bfabd6b74ea 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs @@ -106,29 +106,10 @@ private static void PopulateAllSystemTimeZones(CachedData cachedData) } } - private static unsafe string? GetAlternativeId(string id) + private static string? GetAlternativeId(string id, out bool idIsIana) { - if (!GlobalizationMode.Invariant && !GlobalizationMode.UseNls) - { - foreach (char c in id) - { - // ICU uses some characters as a separator and trim the id at that character. - // while we should fail if the Id contained one of these characters. - if (c == '\\' || c == '\n' || c == '\r') - { - return null; - } - } - - char* buffer = stackalloc char[100]; - int length = Interop.Globalization.IanaIdToWindowsId(id, buffer, 100); - if (length > 0) - { - return new string(buffer, 0, length); - } - } - - return null; + idIsIana = true; + return TryConvertIanaIdToWindowsId(id, out string? windowsId) ? windowsId : null; } private TimeZoneInfo(in TIME_ZONE_INFORMATION zone, bool dstDisabled) diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs index 1ef3a1841ffd11..cb41702cdbd766 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Threading; @@ -136,6 +137,11 @@ public DateTimeKind GetCorrespondingKind(TimeZoneInfo? timeZone) public string Id => _id; + /// + /// Returns true if this TimeZoneInfo object has IANA Id. + /// + public bool HasIanaId { get; } + public string DisplayName => _displayName ?? string.Empty; public string StandardName => _standardDisplayName ?? string.Empty; @@ -880,7 +886,8 @@ private TimeZoneInfo( string? standardDisplayName, string? daylightDisplayName, AdjustmentRule[]? adjustmentRules, - bool disableDaylightSavingTime) + bool disableDaylightSavingTime, + bool hasIanaId = false) { ValidateTimeZoneInfo(id, baseUtcOffset, adjustmentRules, out bool adjustmentRulesSupportDst); @@ -891,6 +898,8 @@ private TimeZoneInfo( _daylightDisplayName = disableDaylightSavingTime ? null : daylightDisplayName; _supportsDaylightSavingTime = adjustmentRulesSupportDst && !disableDaylightSavingTime; _adjustmentRules = adjustmentRules; + + HasIanaId = _id.Equals(UtcId, StringComparison.OrdinalIgnoreCase) ? true : hasIanaId; } /// @@ -902,6 +911,8 @@ public static TimeZoneInfo CreateCustomTimeZone( string? displayName, string? standardDisplayName) { + bool hasIanaId = TimeZoneInfo.TryConvertIanaIdToWindowsId(id, allocate: false, out string _); + return new TimeZoneInfo( id, baseUtcOffset, @@ -909,7 +920,8 @@ public static TimeZoneInfo CreateCustomTimeZone( standardDisplayName, standardDisplayName, adjustmentRules: null, - disableDaylightSavingTime: false); + disableDaylightSavingTime: false, + hasIanaId); } /// @@ -950,6 +962,8 @@ public static TimeZoneInfo CreateCustomTimeZone( adjustmentRules = (AdjustmentRule[])adjustmentRules.Clone(); } + bool hasIanaId = TimeZoneInfo.TryConvertIanaIdToWindowsId(id, allocate: false, out string _); + return new TimeZoneInfo( id, baseUtcOffset, @@ -957,9 +971,35 @@ public static TimeZoneInfo CreateCustomTimeZone( standardDisplayName, daylightDisplayName, adjustmentRules, - disableDaylightSavingTime); + disableDaylightSavingTime, + hasIanaId); } + /// + /// Tries to convert IANA time zone Id to Windows Id. + /// + /// The IANA time zone Id. + /// String object hold the Windows Id which resulted from the IANA Id conversion. + /// True if the Id conversion succeed, false otherwise . + public static unsafe bool TryConvertIanaIdToWindowsId(string ianaId, [NotNullWhen(true)] out string? windowsId) => TryConvertIanaIdToWindowsId(ianaId, allocate: true, out windowsId); + + /// + /// Tries to convert Windows time zone Id to IANA Id. + /// + /// The Windows time zone Id. + /// String object hold the IANA Id which resulted from the Windows Id conversion. + /// True if the Id conversion succeed, false otherwise . + public static bool TryConvertWindowsIdToIanaId(string windowsId, [NotNullWhen(true)] out string? ianaId) => TryConvertWindowsIdToIanaId(windowsId, region: null, allocate: true, out ianaId); + + /// + /// Tries to convert Windows time zone Id to IANA Id. + /// + /// The Windows time zone Id. + /// The ISO 3166 for the country/region. + /// String object hold the IANA Id which resulted from the Windows Id conversion. + /// True if the Id conversion succeed, false otherwise . + public static unsafe bool TryConvertWindowsIdToIanaId(string windowsId, string? region, [NotNullWhen(true)] out string? ianaId) => TryConvertWindowsIdToIanaId(windowsId, region, allocate: true, out ianaId); + void IDeserializationCallback.OnDeserialization(object? sender) { try @@ -1794,7 +1834,7 @@ private static TimeZoneInfoResult TryGetTimeZone(string id, bool dstDisabled, ou TimeZoneInfoResult result = TryGetTimeZoneUsingId(id, dstDisabled, out value, out e, cachedData, alwaysFallbackToLocalMachine); if (result != TimeZoneInfoResult.Success) { - string? alternativeId = GetAlternativeId(id); + string? alternativeId = GetAlternativeId(id, out bool idIsIana); if (alternativeId != null) { result = TryGetTimeZoneUsingId(alternativeId, dstDisabled, out value, out e, cachedData, alwaysFallbackToLocalMachine); @@ -1804,7 +1844,7 @@ private static TimeZoneInfoResult TryGetTimeZone(string id, bool dstDisabled, ou if (value!._equivalentZones == null) { zone = new TimeZoneInfo(id, value!._baseUtcOffset, value!._displayName, value!._standardDisplayName, - value!._daylightDisplayName, value!._adjustmentRules, dstDisabled && value!._supportsDaylightSavingTime); + value!._daylightDisplayName, value!._adjustmentRules, dstDisabled && value!._supportsDaylightSavingTime, idIsIana); value!._equivalentZones = new List(); lock (value!._equivalentZones) { @@ -1824,7 +1864,7 @@ private static TimeZoneInfoResult TryGetTimeZone(string id, bool dstDisabled, ou if (zone == null) { zone = new TimeZoneInfo(id, value!._baseUtcOffset, value!._displayName, value!._standardDisplayName, - value!._daylightDisplayName, value!._adjustmentRules, dstDisabled && value!._supportsDaylightSavingTime); + value!._daylightDisplayName, value!._adjustmentRules, dstDisabled && value!._supportsDaylightSavingTime, idIsIana); lock (value!._equivalentZones) { value!._equivalentZones.Add(zone); @@ -1861,7 +1901,7 @@ private static TimeZoneInfoResult TryGetTimeZoneUsingId(string id, bool dstDisab else { value = new TimeZoneInfo(match._id, match._baseUtcOffset, match._displayName, match._standardDisplayName, - match._daylightDisplayName, match._adjustmentRules, disableDaylightSavingTime: false); + match._daylightDisplayName, match._adjustmentRules, disableDaylightSavingTime: false, match.HasIanaId); } return result; diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index c1d6f802c556dc..bb934e3312fbca 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -3853,6 +3853,7 @@ internal TimeZoneInfo() { } public System.TimeSpan BaseUtcOffset { get { throw null; } } public string DaylightName { get { throw null; } } public string DisplayName { get { throw null; } } + public bool HasIanaId { get; } public string Id { get { throw null; } } public static System.TimeZoneInfo Local { get { throw null; } } public string StandardName { get { throw null; } } @@ -3892,6 +3893,9 @@ void System.Runtime.Serialization.IDeserializationCallback.OnDeserialization(obj void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public string ToSerializedString() { throw null; } public override string ToString() { throw null; } + public static bool TryConvertIanaIdToWindowsId(string ianaId, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out string? windowsId) { throw null; } + public static bool TryConvertWindowsIdToIanaId(string windowsId, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out string? ianaId) { throw null; } + public static bool TryConvertWindowsIdToIanaId(string windowsId, string? region, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out string? ianaId) { throw null; } public sealed partial class AdjustmentRule : System.IEquatable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable { internal AdjustmentRule() { } diff --git a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs index 32a2f6c722db47..3b72bf0bdf8a53 100644 --- a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs @@ -2638,6 +2638,65 @@ public static void UsingAlternativeTimeZoneIdsTest(string windowsId, string iana } } + public static bool SupportIanaNamesConversion => PlatformDetection.IsNotBrowser && PlatformDetection.ICUVersion.Major >= 52; + + [ConditionalFact(nameof(SupportIanaNamesConversion))] + public static void IsIanaIdTest() + { + bool expected = !s_isWindows; + + foreach (TimeZoneInfo tzi in TimeZoneInfo.GetSystemTimeZones()) + { + Assert.True((expected || tzi.Id.Equals("Utc", StringComparison.OrdinalIgnoreCase)) == tzi.HasIanaId, $"`{tzi.Id}` has wrong IANA Id indicator"); + } + + Assert.False(TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time").HasIanaId, $"`Pacific Standard Time` should not be IANA Id."); + Assert.True(TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles").HasIanaId, $"'America/Los_Angeles' should be IANA Id"); + } + + [ConditionalTheory(nameof(SupportIanaNamesConversion))] + [InlineData("Pacific Standard Time", "America/Los_Angeles")] + [InlineData("AUS Eastern Standard Time", "Australia/Sydney")] + [InlineData("GMT Standard Time", "Europe/London")] + [InlineData("Tonga Standard Time", "Pacific/Tongatapu")] + [InlineData("W. Australia Standard Time", "Australia/Perth")] + [InlineData("E. South America Standard Time", "America/Sao_Paulo")] + [InlineData("E. Africa Standard Time", "Africa/Nairobi")] + [InlineData("W. Europe Standard Time", "Europe/Berlin")] + [InlineData("Russian Standard Time", "Europe/Moscow")] + [InlineData("Libya Standard Time", "Africa/Tripoli")] + [InlineData("South Africa Standard Time", "Africa/Johannesburg")] + [InlineData("Morocco Standard Time", "Africa/Casablanca")] + [InlineData("Argentina Standard Time", "America/Buenos_Aires")] + [InlineData("Newfoundland Standard Time", "America/St_Johns")] + [InlineData("Iran Standard Time", "Asia/Tehran")] + public static void IdsConversionsTest(string windowsId, string ianaId) + { + Assert.True(TimeZoneInfo.TryConvertIanaIdToWindowsId(ianaId, out string winId)); + Assert.Equal(windowsId, winId); + + Assert.True(TimeZoneInfo.TryConvertWindowsIdToIanaId(winId, out string ianaConvertedId)); + Assert.Equal(ianaId, ianaConvertedId); + } + + [ConditionalTheory(nameof(SupportIanaNamesConversion))] + [InlineData("Pacific Standard Time", "America/Vancouver", "CA")] + [InlineData("Pacific Standard Time", "America/Los_Angeles", "US")] + [InlineData("Pacific Standard Time", "America/Los_Angeles", "\u0600NotValidRegion")] + [InlineData("Central Europe Standard Time", "Europe/Budapest", "DJ")] + [InlineData("Central Europe Standard Time", "Europe/Budapest", "\uFFFFNotValidRegion")] + [InlineData("Central Europe Standard Time", "Europe/Prague", "CZ")] + [InlineData("Central Europe Standard Time", "Europe/Ljubljana", "SI")] + [InlineData("Central Europe Standard Time", "Europe/Bratislava", "SK")] + [InlineData("Central Europe Standard Time", "Europe/Tirane", "AL")] + [InlineData("Central Europe Standard Time", "Europe/Podgorica", "ME")] + [InlineData("Central Europe Standard Time", "Europe/Belgrade", "RS")] + public static void IdsConversionsWithRegionTest(string windowsId, string ianaId, string region) + { + Assert.True(TimeZoneInfo.TryConvertWindowsIdToIanaId(windowsId, region, out string ianaConvertedId)); + Assert.Equal(ianaId, ianaConvertedId); + } + // We test the existence of a specific English time zone name to avoid failures on non-English platforms. [ConditionalFact(nameof(IsEnglishUILanguageAndRemoteExecutorSupported))] public static void TestNameWithInvariantCulture() From 19caa608b9d226d0041d5ed1c7b0d5911fb13d2b Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 13 Apr 2021 21:12:15 -0700 Subject: [PATCH 10/11] Put native library loading helpers in a separate file from dllimport.cpp (#51201) --- src/coreclr/vm/CMakeLists.txt | 3 +- src/coreclr/vm/dllimport.cpp | 2397 ++++++++---------------- src/coreclr/vm/dllimport.h | 168 -- src/coreclr/vm/nativelibrary.cpp | 909 +++++++++ src/coreclr/vm/nativelibrary.h | 22 + src/coreclr/vm/nativelibrarynative.cpp | 10 +- 6 files changed, 1753 insertions(+), 1756 deletions(-) create mode 100644 src/coreclr/vm/nativelibrary.cpp create mode 100644 src/coreclr/vm/nativelibrary.h diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 0e1c2513fbca03..c93cbd79dc0f1a 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -67,7 +67,6 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON debuginfostore.cpp decodemd.cpp disassembler.cpp - dllimport.cpp domainfile.cpp dynamicmethod.cpp ecall.cpp @@ -323,6 +322,7 @@ set(VM_SOURCES_WKS customattribute.cpp custommarshalerinfo.cpp autotrace.cpp + dllimport.cpp dllimportcallback.cpp dynamicinterfacecastable.cpp eeconfig.cpp @@ -358,6 +358,7 @@ set(VM_SOURCES_WKS multicorejitplayer.cpp # Condition="'$(FeatureMulticoreJIT)' == 'true' nativeeventsource.cpp nativeoverlapped.cpp + nativelibrary.cpp nativelibrarynative.cpp objectlist.cpp olevariant.cpp diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index e3dd139540783d..7324dfd8feb7f1 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -31,10 +31,10 @@ #include "typestring.h" #include "sigbuilder.h" #include "sigformat.h" -#include "strongnameholders.h" #include "ecall.h" #include "fieldmarshaler.h" #include "pinvokeoverride.h" +#include "nativelibrary.h" #include #include "../md/compiler/custattr.h" @@ -49,57 +49,46 @@ #endif // FEATURE_PREJIT #include "eventtrace.h" -#include "clr/fs/path.h" -using namespace clr::fs; -// Specifies whether coreclr is embedded or standalone -extern bool g_coreclr_embedded; - -// Specifies whether hostpolicy is embedded in executable or standalone -extern bool g_hostpolicy_embedded; - -// remove when we get an updated SDK -#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100 -#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000 - -#ifndef DACCESS_COMPILE -void AppendEHClause(int nClauses, COR_ILMETHOD_SECT_EH * pEHSect, ILStubEHClause * pClause, int * pCurIdx) +namespace { - LIMITED_METHOD_CONTRACT; - if (pClause->kind == ILStubEHClause::kNone) - return; + void AppendEHClause(int nClauses, COR_ILMETHOD_SECT_EH * pEHSect, ILStubEHClause * pClause, int * pCurIdx) + { + LIMITED_METHOD_CONTRACT; + if (pClause->kind == ILStubEHClause::kNone) + return; - int idx = *pCurIdx; - *pCurIdx = idx + 1; + int idx = *pCurIdx; + *pCurIdx = idx + 1; - CorExceptionFlag flags; - switch (pClause->kind) - { - case ILStubEHClause::kFinally: flags = COR_ILEXCEPTION_CLAUSE_FINALLY; break; - case ILStubEHClause::kTypedCatch: flags = COR_ILEXCEPTION_CLAUSE_NONE; break; - default: - UNREACHABLE_MSG("unexpected ILStubEHClause kind"); + CorExceptionFlag flags; + switch (pClause->kind) + { + case ILStubEHClause::kFinally: flags = COR_ILEXCEPTION_CLAUSE_FINALLY; break; + case ILStubEHClause::kTypedCatch: flags = COR_ILEXCEPTION_CLAUSE_NONE; break; + default: + UNREACHABLE_MSG("unexpected ILStubEHClause kind"); + } + _ASSERTE(idx < nClauses); + pEHSect->Fat.Clauses[idx].Flags = flags; + pEHSect->Fat.Clauses[idx].TryOffset = pClause->dwTryBeginOffset; + pEHSect->Fat.Clauses[idx].TryLength = pClause->cbTryLength; + pEHSect->Fat.Clauses[idx].HandlerOffset = pClause->dwHandlerBeginOffset; + pEHSect->Fat.Clauses[idx].HandlerLength = pClause->cbHandlerLength; + pEHSect->Fat.Clauses[idx].ClassToken = pClause->dwTypeToken; } - _ASSERTE(idx < nClauses); - pEHSect->Fat.Clauses[idx].Flags = flags; - pEHSect->Fat.Clauses[idx].TryOffset = pClause->dwTryBeginOffset; - pEHSect->Fat.Clauses[idx].TryLength = pClause->cbTryLength; - pEHSect->Fat.Clauses[idx].HandlerOffset = pClause->dwHandlerBeginOffset; - pEHSect->Fat.Clauses[idx].HandlerLength = pClause->cbHandlerLength; - pEHSect->Fat.Clauses[idx].ClassToken = pClause->dwTypeToken; -} -VOID PopulateEHSect(COR_ILMETHOD_SECT_EH * pEHSect, int nClauses, ILStubEHClause * pOne, ILStubEHClause * pTwo) -{ - LIMITED_METHOD_CONTRACT; - pEHSect->Fat.Kind = (CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat); - pEHSect->Fat.DataSize = COR_ILMETHOD_SECT_EH_FAT::Size(nClauses); + VOID PopulateEHSect(COR_ILMETHOD_SECT_EH * pEHSect, int nClauses, ILStubEHClause * pOne, ILStubEHClause * pTwo) + { + LIMITED_METHOD_CONTRACT; + pEHSect->Fat.Kind = (CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat); + pEHSect->Fat.DataSize = COR_ILMETHOD_SECT_EH_FAT::Size(nClauses); - int curIdx = 0; - AppendEHClause(nClauses, pEHSect, pOne, &curIdx); - AppendEHClause(nClauses, pEHSect, pTwo, &curIdx); + int curIdx = 0; + AppendEHClause(nClauses, pEHSect, pOne, &curIdx); + AppendEHClause(nClauses, pEHSect, pTwo, &curIdx); + } } -#endif StubSigDesc::StubSigDesc(MethodDesc *pMD, PInvokeStaticSigInfo* pSigInfo /*= NULL*/) { @@ -936,6 +925,21 @@ class ILStubState : public StubState } + // + // Truncates a SString by first converting it to unicode and truncate it + // if it is larger than size. "..." will be appended if it is truncated. + // + void TruncateUnicodeString(SString &string, COUNT_T bufSize) + { + string.Normalize(); + if ((string.GetCount() + 1) * sizeof(WCHAR) > bufSize) + { + _ASSERTE(bufSize / sizeof(WCHAR) > 4); + string.Truncate(string.Begin() + bufSize / sizeof(WCHAR) - 4); + string.Append(W("...")); + } + } + //--------------------------------------------------------------------------------------- // void @@ -3766,255 +3770,300 @@ static void CreateStructStub(ILStubState* pss, pss->FinishEmit(pMD); } -class NDirectStubHashBlob : public ILStubHashBlobBase +namespace { -public: - Module* m_pModule; - MethodTable* m_pMT; + class NDirectStubParameters + { + public: + + NDirectStubParameters(Signature sig, + SigTypeContext* pTypeContext, + Module* pModule, + Module* pLoaderModule, + CorNativeLinkType nlType, + CorNativeLinkFlags nlFlags, + CorInfoCallConvExtension unmgdCallConv, + DWORD dwStubFlags, // NDirectStubFlags + int nParamTokens, + mdParamDef* pParamTokenArray, + int iLCIDArg, + MethodTable* pMT + ) : + m_sig(sig), + m_pTypeContext(pTypeContext), + m_pModule(pModule), + m_pLoaderModule(pLoaderModule), + m_pParamTokenArray(pParamTokenArray), + m_unmgdCallConv(unmgdCallConv), + m_nlType(nlType), + m_nlFlags(nlFlags), + m_dwStubFlags(dwStubFlags), + m_iLCIDArg(iLCIDArg), + m_nParamTokens(nParamTokens), + m_pMT(pMT) + { + LIMITED_METHOD_CONTRACT; + } + + Signature m_sig; + SigTypeContext* m_pTypeContext; + Module* m_pModule; + Module* m_pLoaderModule; + mdParamDef* m_pParamTokenArray; + CorInfoCallConvExtension m_unmgdCallConv; + CorNativeLinkType m_nlType; + CorNativeLinkFlags m_nlFlags; + DWORD m_dwStubFlags; + int m_iLCIDArg; + int m_nParamTokens; + MethodTable* m_pMT; + }; + + class NDirectStubHashBlob : public ILStubHashBlobBase + { + public: + Module* m_pModule; + MethodTable* m_pMT; - WORD m_unmgdCallConv; - BYTE m_nlType; // C_ASSERTS are in NDirect::CreateHashBlob - BYTE m_nlFlags; + WORD m_unmgdCallConv; + BYTE m_nlType; // C_ASSERTS are in NDirect::CreateHashBlob + BYTE m_nlFlags; - DWORD m_StubFlags; + DWORD m_StubFlags; - INT32 m_iLCIDArg; - INT32 m_nParams; - BYTE m_rgbSigAndParamData[1]; - // (dwParamAttr, cbNativeType) // length: number of parameters - // NativeTypeBlob // length: number of parameters - // BYTE m_rgbSigData[]; // length: determined by sig walk -}; + INT32 m_iLCIDArg; + INT32 m_nParams; + BYTE m_rgbSigAndParamData[1]; + // (dwParamAttr, cbNativeType) // length: number of parameters + // NativeTypeBlob // length: number of parameters + // BYTE m_rgbSigData[]; // length: determined by sig walk + }; -// For better performance and less memory fragmentation, -// I'm using structure here to avoid allocating 3 different arrays. -struct ParamInfo -{ - DWORD dwParamAttr; - ULONG cbNativeType; - PCCOR_SIGNATURE pvNativeType; -}; + // For better performance and less memory fragmentation, + // I'm using structure here to avoid allocating 3 different arrays. + struct ParamInfo + { + DWORD dwParamAttr; + ULONG cbNativeType; + PCCOR_SIGNATURE pvNativeType; + }; -ILStubHashBlob* NDirect::CreateHashBlob(NDirectStubParameters* pParams) -{ - STANDARD_VM_CONTRACT; + ILStubHashBlob* CreateHashBlob(NDirectStubParameters* pParams) + { + STANDARD_VM_CONTRACT; - NDirectStubHashBlob* pBlob; + NDirectStubHashBlob* pBlob; - IMDInternalImport* pInternalImport = pParams->m_pModule->GetMDImport(); + IMDInternalImport* pInternalImport = pParams->m_pModule->GetMDImport(); - CQuickBytes paramInfoBytes; - paramInfoBytes.AllocThrows(sizeof(ParamInfo)*pParams->m_nParamTokens); - ParamInfo *paramInfos = (ParamInfo *)paramInfoBytes.Ptr(); - ::ZeroMemory(paramInfos, sizeof(ParamInfo) * pParams->m_nParamTokens); + CQuickBytes paramInfoBytes; + paramInfoBytes.AllocThrows(sizeof(ParamInfo)*pParams->m_nParamTokens); + ParamInfo *paramInfos = (ParamInfo *)paramInfoBytes.Ptr(); + ::ZeroMemory(paramInfos, sizeof(ParamInfo) * pParams->m_nParamTokens); - size_t cbNativeTypeTotal = 0; + size_t cbNativeTypeTotal = 0; - // - // Collect information for function parameters - // - for (int idx = 0; idx < pParams->m_nParamTokens; idx++) - { - mdParamDef token = pParams->m_pParamTokenArray[idx]; - if (TypeFromToken(token) == mdtParamDef && mdParamDefNil != token) + // + // Collect information for function parameters + // + for (int idx = 0; idx < pParams->m_nParamTokens; idx++) { - USHORT usSequence_Ignore; // We don't need usSequence in the hash as the param array is already sorted - LPCSTR szParamName_Ignore; - IfFailThrow(pInternalImport->GetParamDefProps(token, &usSequence_Ignore, ¶mInfos[idx].dwParamAttr, &szParamName_Ignore)); - - if (paramInfos[idx].dwParamAttr & pdHasFieldMarshal) + mdParamDef token = pParams->m_pParamTokenArray[idx]; + if (TypeFromToken(token) == mdtParamDef && mdParamDefNil != token) { - IfFailThrow(pInternalImport->GetFieldMarshal(token, ¶mInfos[idx].pvNativeType, ¶mInfos[idx].cbNativeType)); - cbNativeTypeTotal += paramInfos[idx].cbNativeType; + USHORT usSequence_Ignore; // We don't need usSequence in the hash as the param array is already sorted + LPCSTR szParamName_Ignore; + IfFailThrow(pInternalImport->GetParamDefProps(token, &usSequence_Ignore, ¶mInfos[idx].dwParamAttr, &szParamName_Ignore)); + + if (paramInfos[idx].dwParamAttr & pdHasFieldMarshal) + { + IfFailThrow(pInternalImport->GetFieldMarshal(token, ¶mInfos[idx].pvNativeType, ¶mInfos[idx].cbNativeType)); + cbNativeTypeTotal += paramInfos[idx].cbNativeType; + } } } - } - - SigPointer sigPtr = pParams->m_sig.CreateSigPointer(); - // note that ConvertToInternalSignature also resolves generics so different instantiations will get different - // hash blobs for methods that have generic parameters in their signature - SigBuilder sigBuilder; - sigPtr.ConvertToInternalSignature(pParams->m_pModule, pParams->m_pTypeContext, &sigBuilder, /* bSkipCustomModifier = */ FALSE); + SigPointer sigPtr = pParams->m_sig.CreateSigPointer(); - DWORD cbSig; - PVOID pSig = sigBuilder.GetSignature(&cbSig); + // note that ConvertToInternalSignature also resolves generics so different instantiations will get different + // hash blobs for methods that have generic parameters in their signature + SigBuilder sigBuilder; + sigPtr.ConvertToInternalSignature(pParams->m_pModule, pParams->m_pTypeContext, &sigBuilder, /* bSkipCustomModifier = */ FALSE); - // - // Build hash blob for IL stub sharing - // - S_SIZE_T cbSizeOfBlob = S_SIZE_T(offsetof(NDirectStubHashBlob, m_rgbSigAndParamData)) + - S_SIZE_T(sizeof(ULONG)) * S_SIZE_T(pParams->m_nParamTokens) + // Parameter attributes - S_SIZE_T(sizeof(DWORD)) * S_SIZE_T(pParams->m_nParamTokens) + // Native type blob size - S_SIZE_T(cbNativeTypeTotal) + // Native type blob data - S_SIZE_T(cbSig); // Signature + DWORD cbSig; + PVOID pSig = sigBuilder.GetSignature(&cbSig); - if (cbSizeOfBlob.IsOverflow()) - COMPlusThrowHR(COR_E_OVERFLOW); + // + // Build hash blob for IL stub sharing + // + S_SIZE_T cbSizeOfBlob = S_SIZE_T(offsetof(NDirectStubHashBlob, m_rgbSigAndParamData)) + + S_SIZE_T(sizeof(ULONG)) * S_SIZE_T(pParams->m_nParamTokens) + // Parameter attributes + S_SIZE_T(sizeof(DWORD)) * S_SIZE_T(pParams->m_nParamTokens) + // Native type blob size + S_SIZE_T(cbNativeTypeTotal) + // Native type blob data + S_SIZE_T(cbSig); // Signature - static_assert_no_msg(nltMaxValue <= 0xFF); - static_assert_no_msg(nlfMaxValue <= 0xFF); - static_assert_no_msg(pmMaxValue <= 0xFFFF); + if (cbSizeOfBlob.IsOverflow()) + COMPlusThrowHR(COR_E_OVERFLOW); - NewArrayHolder pBytes = new BYTE[cbSizeOfBlob.Value()]; - // zero out the hash bytes to ensure all bit fields are deterministically set - ZeroMemory(pBytes, cbSizeOfBlob.Value()); - pBlob = (NDirectStubHashBlob*)(BYTE*)pBytes; + static_assert_no_msg(nltMaxValue <= 0xFF); + static_assert_no_msg(nlfMaxValue <= 0xFF); + static_assert_no_msg(pmMaxValue <= 0xFFFF); - pBlob->m_pModule = NULL; + NewArrayHolder pBytes = new BYTE[cbSizeOfBlob.Value()]; + // zero out the hash bytes to ensure all bit fields are deterministically set + ZeroMemory(pBytes, cbSizeOfBlob.Value()); + pBlob = (NDirectStubHashBlob*)(BYTE*)pBytes; - if (SF_IsNGENedStub(pParams->m_dwStubFlags)) - { - // don't share across modules if we are ngening the stub - pBlob->m_pModule = pParams->m_pModule; - } + pBlob->m_pModule = NULL; - pBlob->m_pMT = pParams->m_pMT; - pBlob->m_cbSizeOfBlob = cbSizeOfBlob.Value(); - pBlob->m_unmgdCallConv = static_cast(pParams->m_unmgdCallConv); - pBlob->m_nlType = static_cast(pParams->m_nlType); - pBlob->m_nlFlags = static_cast(pParams->m_nlFlags & ~nlfNoMangle); // this flag does not affect the stub - pBlob->m_iLCIDArg = pParams->m_iLCIDArg; + if (SF_IsNGENedStub(pParams->m_dwStubFlags)) + { + // don't share across modules if we are ngening the stub + pBlob->m_pModule = pParams->m_pModule; + } - pBlob->m_StubFlags = pParams->m_dwStubFlags; - pBlob->m_nParams = pParams->m_nParamTokens; + pBlob->m_pMT = pParams->m_pMT; + pBlob->m_cbSizeOfBlob = cbSizeOfBlob.Value(); + pBlob->m_unmgdCallConv = static_cast(pParams->m_unmgdCallConv); + pBlob->m_nlType = static_cast(pParams->m_nlType); + pBlob->m_nlFlags = static_cast(pParams->m_nlFlags & ~nlfNoMangle); // this flag does not affect the stub + pBlob->m_iLCIDArg = pParams->m_iLCIDArg; - BYTE* pBlobParams = &pBlob->m_rgbSigAndParamData[0]; + pBlob->m_StubFlags = pParams->m_dwStubFlags; + pBlob->m_nParams = pParams->m_nParamTokens; - // - // Write (dwParamAttr, cbNativeType) for parameters - // - // Note that these need to be aligned and it is why they are written before the byte blobs - // I'm putting asserts here so that it will assert even in non-IA64 platforms to catch bugs - // - _ASSERTE((DWORD_PTR)pBlobParams % sizeof(DWORD) == 0); - _ASSERTE(sizeof(DWORD) == sizeof(ULONG)); + BYTE* pBlobParams = &pBlob->m_rgbSigAndParamData[0]; - for (int i = 0; i < pParams->m_nParamTokens; ++i) - { - // We only care about In/Out/HasFieldMarshal - // Other attr are about optional/default values which are not used in marshalling, - // but only used in compilers - *((DWORD *)pBlobParams) = paramInfos[i].dwParamAttr & (pdIn | pdOut | pdHasFieldMarshal); - pBlobParams += sizeof(DWORD); + // + // Write (dwParamAttr, cbNativeType) for parameters + // + // Note that these need to be aligned and it is why they are written before the byte blobs + // I'm putting asserts here so that it will assert even in non-IA64 platforms to catch bugs + // + _ASSERTE((DWORD_PTR)pBlobParams % sizeof(DWORD) == 0); + _ASSERTE(sizeof(DWORD) == sizeof(ULONG)); - *((ULONG *)pBlobParams) = paramInfos[i].cbNativeType; - pBlobParams += sizeof(ULONG); - } + for (int i = 0; i < pParams->m_nParamTokens; ++i) + { + // We only care about In/Out/HasFieldMarshal + // Other attr are about optional/default values which are not used in marshalling, + // but only used in compilers + *((DWORD *)pBlobParams) = paramInfos[i].dwParamAttr & (pdIn | pdOut | pdHasFieldMarshal); + pBlobParams += sizeof(DWORD); - // - // Write native type blob for parameters - // - for (int i = 0; i < pParams->m_nParamTokens; ++i) - { - memcpy(pBlobParams, paramInfos[i].pvNativeType, paramInfos[i].cbNativeType); - pBlobParams += paramInfos[i].cbNativeType; - } + *((ULONG *)pBlobParams) = paramInfos[i].cbNativeType; + pBlobParams += sizeof(ULONG); + } - // - // Copy signature - // - memcpy(pBlobParams, pSig, cbSig); + // + // Write native type blob for parameters + // + for (int i = 0; i < pParams->m_nParamTokens; ++i) + { + memcpy(pBlobParams, paramInfos[i].pvNativeType, paramInfos[i].cbNativeType); + pBlobParams += paramInfos[i].cbNativeType; + } - // Verify that we indeed have reached the end - _ASSERTE(pBlobParams + cbSig == (BYTE *)pBlob + cbSizeOfBlob.Value()); + // + // Copy signature + // + memcpy(pBlobParams, pSig, cbSig); - pBytes.SuppressRelease(); - return (ILStubHashBlob*)pBlob; -} + // Verify that we indeed have reached the end + _ASSERTE(pBlobParams + cbSig == (BYTE *)pBlob + cbSizeOfBlob.Value()); -// static inline -ILStubCache* NDirect::GetILStubCache(NDirectStubParameters* pParams) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; + pBytes.SuppressRelease(); + return (ILStubHashBlob*)pBlob; } - CONTRACTL_END; - - // Use the m_pLoaderModule instead of m_pModule - // They could be different for methods on generic types. - return pParams->m_pLoaderModule->GetILStubCache(); -} -// static -MethodDesc* NDirect::GetStubMethodDesc( - MethodDesc *pTargetMD, - NDirectStubParameters* pParams, - ILStubHashBlob* pHashParams, - AllocMemTracker* pamTracker, - bool& bILStubCreator, - MethodDesc* pLastMD) -{ - CONTRACT(MethodDesc*) + ILStubCache* GetILStubCache(NDirectStubParameters* pParams) { - STANDARD_VM_CHECK; + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; - PRECONDITION(CheckPointer(pParams)); - PRECONDITION(!pParams->m_sig.IsEmpty()); - PRECONDITION(CheckPointer(pParams->m_pModule)); - PRECONDITION(CheckPointer(pTargetMD, NULL_OK)); - POSTCONDITION(CheckPointer(RETVAL)); + // Use the m_pLoaderModule instead of m_pModule + // They could be different for methods on generic types. + return pParams->m_pLoaderModule->GetILStubCache(); } - CONTRACT_END; - MethodDesc* pMD; + MethodDesc* GetStubMethodDesc( + MethodDesc *pTargetMD, + NDirectStubParameters* pParams, + ILStubHashBlob* pHashParams, + AllocMemTracker* pamTracker, + bool& bILStubCreator, + MethodDesc* pLastMD) + { + CONTRACT(MethodDesc*) + { + STANDARD_VM_CHECK; - ILStubCache* pCache = NDirect::GetILStubCache(pParams); + PRECONDITION(CheckPointer(pParams)); + PRECONDITION(!pParams->m_sig.IsEmpty()); + PRECONDITION(CheckPointer(pParams->m_pModule)); + PRECONDITION(CheckPointer(pTargetMD, NULL_OK)); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; - pMD = pCache->GetStubMethodDesc(pTargetMD, - pHashParams, - pParams->m_dwStubFlags, - pParams->m_pModule, - pParams->m_sig.GetRawSig(), - pParams->m_sig.GetRawSigLen(), - pamTracker, - bILStubCreator, - pLastMD); + MethodDesc* pMD; - RETURN pMD; -} + ILStubCache* pCache = GetILStubCache(pParams); + pMD = pCache->GetStubMethodDesc(pTargetMD, + pHashParams, + pParams->m_dwStubFlags, + pParams->m_pModule, + pParams->m_sig.GetRawSig(), + pParams->m_sig.GetRawSigLen(), + pamTracker, + bILStubCreator, + pLastMD); -// static -void NDirect::RemoveILStubCacheEntry(NDirectStubParameters* pParams, ILStubHashBlob* pHashParams) -{ - CONTRACTL + RETURN pMD; + } + + void RemoveILStubCacheEntry(NDirectStubParameters* pParams, ILStubHashBlob* pHashParams) { - STANDARD_VM_CHECK; + CONTRACTL + { + STANDARD_VM_CHECK; - PRECONDITION(CheckPointer(pParams)); - PRECONDITION(CheckPointer(pHashParams)); - PRECONDITION(!pParams->m_sig.IsEmpty()); - PRECONDITION(CheckPointer(pParams->m_pModule)); - } - CONTRACTL_END; + PRECONDITION(CheckPointer(pParams)); + PRECONDITION(CheckPointer(pHashParams)); + PRECONDITION(!pParams->m_sig.IsEmpty()); + PRECONDITION(CheckPointer(pParams->m_pModule)); + } + CONTRACTL_END; - LOG((LF_STUBS, LL_INFO1000, "Exception happened when generating IL of stub clr!CreateInteropILStub StubMD: %p, HashBlob: %p \n", pParams, pHashParams)); + LOG((LF_STUBS, LL_INFO1000, "Exception happened when generating IL of stub clr!CreateInteropILStub StubMD: %p, HashBlob: %p \n", pParams, pHashParams)); - ILStubCache* pCache = NDirect::GetILStubCache(pParams); + ILStubCache* pCache = GetILStubCache(pParams); - pCache->DeleteEntry(pHashParams); -} + pCache->DeleteEntry(pHashParams); + } -// static -void NDirect::AddMethodDescChunkWithLockTaken(NDirectStubParameters* pParams, MethodDesc *pMD) -{ - CONTRACTL + void AddMethodDescChunkWithLockTaken(NDirectStubParameters* pParams, MethodDesc *pMD) { - STANDARD_VM_CHECK; + CONTRACTL + { + STANDARD_VM_CHECK; - PRECONDITION(CheckPointer(pParams)); - PRECONDITION(!pParams->m_sig.IsEmpty()); - PRECONDITION(CheckPointer(pParams->m_pModule)); - } - CONTRACTL_END; + PRECONDITION(CheckPointer(pParams)); + PRECONDITION(!pParams->m_sig.IsEmpty()); + PRECONDITION(CheckPointer(pParams->m_pModule)); + } + CONTRACTL_END; - ILStubCache* pCache = NDirect::GetILStubCache(pParams); + ILStubCache* pCache = GetILStubCache(pParams); - pCache->AddMethodDescChunkWithLockTaken(pMD); + pCache->AddMethodDescChunkWithLockTaken(pMD); + } } // @@ -4443,326 +4492,420 @@ HRESULT FindPredefinedILStubMethod(MethodDesc *pTargetMD, DWORD dwStubFlags, Met } #endif // FEATURE_COMINTEROP -MethodDesc* CreateInteropILStub( - ILStubState* pss, - StubSigDesc* pSigDesc, - CorNativeLinkType nlType, - CorNativeLinkFlags nlFlags, - CorInfoCallConvExtension unmgdCallConv, - int nParamTokens, - mdParamDef* pParamTokenArray, - int iLCIDArg, - bool* pGeneratedNewStub = nullptr - ) +namespace { - CONTRACT(MethodDesc*) + //======================================================================= + // ILStubCreatorHelper + // The class is used as a helper class in CreateInteropILStub. It mainly + // puts two methods NDirect::GetStubMethodDesc and NDirect::RemoveILStubCacheEntry + // into a holder. See CreateInteropILStub for more information + //======================================================================= + class ILStubCreatorHelper + { + public: + ILStubCreatorHelper(MethodDesc *pTargetMD, + NDirectStubParameters* pParams + ) : + m_pTargetMD(pTargetMD), + m_pParams(pParams), + m_pStubMD(NULL), + m_bILStubCreator(false) + { + STANDARD_VM_CONTRACT; + m_pHashParams = CreateHashBlob(m_pParams); + } + + ~ILStubCreatorHelper() + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + RemoveILStubCacheEntry(); + } + + inline void GetStubMethodDesc() + { + WRAPPER_NO_CONTRACT; + + m_pStubMD = ::GetStubMethodDesc(m_pTargetMD, m_pParams, m_pHashParams, &m_amTracker, m_bILStubCreator, m_pStubMD); + } + + inline void RemoveILStubCacheEntry() + { + WRAPPER_NO_CONTRACT; + + if (m_bILStubCreator) + { + ::RemoveILStubCacheEntry(m_pParams, m_pHashParams); + m_bILStubCreator = false; + } + } + + inline MethodDesc* GetStubMD() + { + LIMITED_METHOD_CONTRACT; + return m_pStubMD; + } + + inline void SuppressRelease() + { + WRAPPER_NO_CONTRACT; + m_bILStubCreator = false; + m_amTracker.SuppressRelease(); + } + + DEBUG_NOINLINE static void HolderEnter(ILStubCreatorHelper *pThis) + { + WRAPPER_NO_CONTRACT; + ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT; + pThis->GetStubMethodDesc(); + } + + DEBUG_NOINLINE static void HolderLeave(ILStubCreatorHelper *pThis) + { + WRAPPER_NO_CONTRACT; + ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT; + pThis->RemoveILStubCacheEntry(); + } + + private: + MethodDesc* m_pTargetMD; + NDirectStubParameters* m_pParams; + NewArrayHolder m_pHashParams; + AllocMemTracker* m_pAmTracker; + MethodDesc* m_pStubMD; + AllocMemTracker m_amTracker; + bool m_bILStubCreator; // Only the creator can remove the ILStub from the Cache + }; //ILStubCreatorHelper + + typedef Wrapper ILStubCreatorHelperHolder; + + MethodDesc* CreateInteropILStub( + ILStubState* pss, + StubSigDesc* pSigDesc, + CorNativeLinkType nlType, + CorNativeLinkFlags nlFlags, + CorInfoCallConvExtension unmgdCallConv, + int nParamTokens, + mdParamDef* pParamTokenArray, + int iLCIDArg, + bool* pGeneratedNewStub = nullptr + ) { - STANDARD_VM_CHECK; + CONTRACT(MethodDesc*) + { + STANDARD_VM_CHECK; - PRECONDITION(CheckPointer(pSigDesc)); - POSTCONDITION(CheckPointer(RETVAL)); - } - CONTRACT_END; + PRECONDITION(CheckPointer(pSigDesc)); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; - /////////////////////////////// - // - // MethodDesc creation - // - /////////////////////////////// + /////////////////////////////// + // + // MethodDesc creation + // + /////////////////////////////// - MethodDesc* pStubMD = NULL; + MethodDesc* pStubMD = NULL; - Module* pModule = pSigDesc->m_pModule; - Module* pLoaderModule = pSigDesc->m_pLoaderModule; - MethodDesc* pTargetMD = pSigDesc->m_pMD; - MethodTable* pTargetMT = pSigDesc->m_pMT; - // - // pTargetMD may be null in the case of calli pinvoke - // and vararg pinvoke. - // + Module* pModule = pSigDesc->m_pModule; + Module* pLoaderModule = pSigDesc->m_pLoaderModule; + MethodDesc* pTargetMD = pSigDesc->m_pMD; + MethodTable* pTargetMT = pSigDesc->m_pMT; + // + // pTargetMD may be null in the case of calli pinvoke + // and vararg pinvoke. + // - DWORD dwStubFlags = pss->GetFlags(); + DWORD dwStubFlags = pss->GetFlags(); #ifdef FEATURE_COMINTEROP - // - // Try to locate predefined IL stub either defined in user code or hardcoded in CLR - // If there is one, use the pointed method as the stub. - // Skip pTargetMD == NULL case for reverse interop calls - // - if (pTargetMD && SUCCEEDED(FindPredefinedILStubMethod(pTargetMD, dwStubFlags, &pStubMD))) - { + // + // Try to locate predefined IL stub either defined in user code or hardcoded in CLR + // If there is one, use the pointed method as the stub. + // Skip pTargetMD == NULL case for reverse interop calls + // + if (pTargetMD && SUCCEEDED(FindPredefinedILStubMethod(pTargetMD, dwStubFlags, &pStubMD))) + { #ifndef CROSSGEN_COMPILE - // We are about to execute method in pStubMD which could be in another module. - // Call EnsureActive before make the call - // This cannot be done during NGEN/PEVerify (in PASSIVE_DOMAIN) so I've moved it here - pStubMD->EnsureActive(); + // We are about to execute method in pStubMD which could be in another module. + // Call EnsureActive before make the call + // This cannot be done during NGEN/PEVerify (in PASSIVE_DOMAIN) so I've moved it here + pStubMD->EnsureActive(); - if (pStubMD->IsPreImplemented()) - RestoreNGENedStub(pStubMD); + if (pStubMD->IsPreImplemented()) + RestoreNGENedStub(pStubMD); #endif - RETURN pStubMD; - } + RETURN pStubMD; + } #endif // FEATURE_COMINTEROP - // Otherwise, fall back to generating IL stub on-the-fly - NDirectStubParameters params(pSigDesc->m_sig, - &pSigDesc->m_typeContext, - pModule, - pLoaderModule, - nlType, - nlFlags, - unmgdCallConv, - dwStubFlags, - nParamTokens, - pParamTokenArray, - iLCIDArg, - pSigDesc->m_pMT - ); - - // The following two ILStubCreatorHelperHolder are to recover the status when an - // exception happen during the generation of the IL stubs. We need to free the - // memory allocated and restore the ILStubCache. - // - // The following block is logically divided into two phases. The first phase is - // CreateOrGet IL Stub phase which we take a domain level lock. The second phase - // is IL generation phase which we take a MethodDesc level lock. Taking two locks - // is mainly designed for performance. - // - // ilStubCreatorHelper contains an instance of AllocMemTracker which tracks the - // allocated memory during the creation of MethodDesc so that we are able to remove - // them when releasing the ILStubCreatorHelperHolder or destructing ILStubCreatorHelper - - // When removing IL Stub from Cache, we have a constraint that only the thread which - // creates the stub can remove it. Otherwise, any thread hits cache and gets the stub will - // remove it from cache if OOM occurs + // Otherwise, fall back to generating IL stub on-the-fly + NDirectStubParameters params(pSigDesc->m_sig, + &pSigDesc->m_typeContext, + pModule, + pLoaderModule, + nlType, + nlFlags, + unmgdCallConv, + dwStubFlags, + nParamTokens, + pParamTokenArray, + iLCIDArg, + pSigDesc->m_pMT + ); - { - ILStubCreatorHelper ilStubCreatorHelper(pTargetMD, ¶ms); + // The following two ILStubCreatorHelperHolder are to recover the status when an + // exception happen during the generation of the IL stubs. We need to free the + // memory allocated and restore the ILStubCache. + // + // The following block is logically divided into two phases. The first phase is + // CreateOrGet IL Stub phase which we take a domain level lock. The second phase + // is IL generation phase which we take a MethodDesc level lock. Taking two locks + // is mainly designed for performance. + // + // ilStubCreatorHelper contains an instance of AllocMemTracker which tracks the + // allocated memory during the creation of MethodDesc so that we are able to remove + // them when releasing the ILStubCreatorHelperHolder or destructing ILStubCreatorHelper - // take the domain level lock - ListLockHolder pILStubLock(pLoaderModule->GetDomain()->GetILStubGenLock()); + // When removing IL Stub from Cache, we have a constraint that only the thread which + // creates the stub can remove it. Otherwise, any thread hits cache and gets the stub will + // remove it from cache if OOM occurs { - // The holder will free the allocated MethodDesc and restore the ILStubCache - // if exception happen. - ILStubCreatorHelperHolder pCreateOrGetStubHolder(&ilStubCreatorHelper); - pStubMD = pCreateOrGetStubHolder->GetStubMD(); + ILStubCreatorHelper ilStubCreatorHelper(pTargetMD, ¶ms); - /////////////////////////////// - // - // IL generation - // - /////////////////////////////// + // take the domain level lock + ListLockHolder pILStubLock(pLoaderModule->GetDomain()->GetILStubGenLock()); { - // take the MethodDesc level locker - ListLockEntryHolder pEntry(ListLockEntry::Find(pILStubLock, pStubMD, "il stub gen lock")); + // The holder will free the allocated MethodDesc and restore the ILStubCache + // if exception happen. + ILStubCreatorHelperHolder pCreateOrGetStubHolder(&ilStubCreatorHelper); + pStubMD = pCreateOrGetStubHolder->GetStubMD(); - ListLockEntryLockHolder pEntryLock(pEntry, FALSE); + /////////////////////////////// + // + // IL generation + // + /////////////////////////////// - // We can release the holder for the first phase now - pCreateOrGetStubHolder.SuppressRelease(); + { + // take the MethodDesc level locker + ListLockEntryHolder pEntry(ListLockEntry::Find(pILStubLock, pStubMD, "il stub gen lock")); - // We have the entry lock we need to use, so we can release the global lock. - pILStubLock.Release(); + ListLockEntryLockHolder pEntryLock(pEntry, FALSE); - { - // The holder will free the allocated MethodDesc and restore the ILStubCache - // if exception happen. The reason to get the holder again is to - ILStubCreatorHelperHolder pGenILHolder(&ilStubCreatorHelper); + // We can release the holder for the first phase now + pCreateOrGetStubHolder.SuppressRelease(); - if (!pEntryLock.DeadlockAwareAcquire()) - { - // the IL generation is not recursive. - // However, we can encounter a recursive situation when attempting to - // marshal a struct containing a layout class containing another struct. - // Throw an exception here instead of asserting. - if (SF_IsStructMarshalStub(dwStubFlags)) - { - _ASSERTE(pSigDesc->m_pMT != nullptr); - StackSString strTypeName; - TypeString::AppendType(strTypeName, TypeHandle(pSigDesc->m_pMT)); - COMPlusThrow(kTypeLoadException, IDS_CANNOT_MARSHAL_RECURSIVE_DEF, strTypeName.GetUnicode()); - } - UNREACHABLE_MSG("unexpected deadlock in IL stub generation!"); - } + // We have the entry lock we need to use, so we can release the global lock. + pILStubLock.Release(); - if (SF_IsSharedStub(params.m_dwStubFlags)) { - // We need to re-acquire the lock in case we need to get a new pStubMD - // in the case that the owner of the shared stub was destroyed. - pILStubLock.Acquire(); - - // Assure that pStubMD we have now has not been destroyed by other threads - pGenILHolder->GetStubMethodDesc(); + // The holder will free the allocated MethodDesc and restore the ILStubCache + // if exception happen. The reason to get the holder again is to + ILStubCreatorHelperHolder pGenILHolder(&ilStubCreatorHelper); - while (pStubMD != pGenILHolder->GetStubMD()) + if (!pEntryLock.DeadlockAwareAcquire()) { - pStubMD = pGenILHolder->GetStubMD(); - - pEntry.Assign(ListLockEntry::Find(pILStubLock, pStubMD, "il stub gen lock")); - pEntryLock.Assign(pEntry, FALSE); - - // We have the entry lock we need to use, so we can release the global lock. - pILStubLock.Release(); - - if (!pEntryLock.DeadlockAwareAcquire()) + // the IL generation is not recursive. + // However, we can encounter a recursive situation when attempting to + // marshal a struct containing a layout class containing another struct. + // Throw an exception here instead of asserting. + if (SF_IsStructMarshalStub(dwStubFlags)) { - // the IL generation is not recursive. - // However, we can encounter a recursive situation when attempting to - // marshal a struct containing a layout class containing another struct. - // Throw an exception here instead of asserting. - if (SF_IsStructMarshalStub(dwStubFlags)) - { - _ASSERTE(pSigDesc->m_pMT != nullptr); - StackSString strTypeName; - TypeString::AppendType(strTypeName, TypeHandle(pSigDesc->m_pMT)); - COMPlusThrow(kTypeLoadException, IDS_CANNOT_MARSHAL_RECURSIVE_DEF, strTypeName.GetUnicode()); - } - UNREACHABLE_MSG("unexpected deadlock in IL stub generation!"); + _ASSERTE(pSigDesc->m_pMT != nullptr); + StackSString strTypeName; + TypeString::AppendType(strTypeName, TypeHandle(pSigDesc->m_pMT)); + COMPlusThrow(kTypeLoadException, IDS_CANNOT_MARSHAL_RECURSIVE_DEF, strTypeName.GetUnicode()); } + UNREACHABLE_MSG("unexpected deadlock in IL stub generation!"); + } + if (SF_IsSharedStub(params.m_dwStubFlags)) + { + // We need to re-acquire the lock in case we need to get a new pStubMD + // in the case that the owner of the shared stub was destroyed. pILStubLock.Acquire(); + // Assure that pStubMD we have now has not been destroyed by other threads pGenILHolder->GetStubMethodDesc(); - } - } - for (;;) - { - // We have the entry lock now, we can release the global lock - pILStubLock.Release(); + while (pStubMD != pGenILHolder->GetStubMD()) + { + pStubMD = pGenILHolder->GetStubMD(); - _ASSERTE(pEntryLock.GetValue()->HasLock()); + pEntry.Assign(ListLockEntry::Find(pILStubLock, pStubMD, "il stub gen lock")); + pEntryLock.Assign(pEntry, FALSE); - if (pEntry->m_hrResultCode != S_FALSE) - { - // We came in to generate the IL but someone - // beat us so there's nothing to do - break; - } + // We have the entry lock we need to use, so we can release the global lock. + pILStubLock.Release(); - ILStubResolver* pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); + if (!pEntryLock.DeadlockAwareAcquire()) + { + // the IL generation is not recursive. + // However, we can encounter a recursive situation when attempting to + // marshal a struct containing a layout class containing another struct. + // Throw an exception here instead of asserting. + if (SF_IsStructMarshalStub(dwStubFlags)) + { + _ASSERTE(pSigDesc->m_pMT != nullptr); + StackSString strTypeName; + TypeString::AppendType(strTypeName, TypeHandle(pSigDesc->m_pMT)); + COMPlusThrow(kTypeLoadException, IDS_CANNOT_MARSHAL_RECURSIVE_DEF, strTypeName.GetUnicode()); + } + UNREACHABLE_MSG("unexpected deadlock in IL stub generation!"); + } - CONSISTENCY_CHECK((NULL == pResolver->GetStubMethodDesc()) || (pStubMD == pResolver->GetStubMethodDesc())); + pILStubLock.Acquire(); - if (pResolver->IsILGenerated()) - { - // this stub already has its IL generated - break; + pGenILHolder->GetStubMethodDesc(); + } } - // - // Check that the stub signature and MethodDesc are compatible. The JIT - // interface functions depend on this. - // - + for (;;) { - SigPointer ptr = pSigDesc->m_sig.CreateSigPointer(); - - uint32_t callConvInfo; - IfFailThrow(ptr.GetCallingConvInfo(&callConvInfo)); - - BOOL fSigIsStatic = !(callConvInfo & IMAGE_CEE_CS_CALLCONV_HASTHIS); + // We have the entry lock now, we can release the global lock + pILStubLock.Release(); - // CreateNDirectStubWorker will throw an exception for these cases. - BOOL fCanHaveThis = SF_IsDelegateStub(dwStubFlags) || SF_IsCOMStub(dwStubFlags); + _ASSERTE(pEntryLock.GetValue()->HasLock()); - if (fSigIsStatic || fCanHaveThis) + if (pEntry->m_hrResultCode != S_FALSE) { - CONSISTENCY_CHECK(pStubMD->IsStatic() == (DWORD)fSigIsStatic); + // We came in to generate the IL but someone + // beat us so there's nothing to do + break; } - } - { - ILStubGenHolder sgh(pResolver); + ILStubResolver* pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); - pResolver->SetStubMethodDesc(pStubMD); - pResolver->SetStubTargetMethodDesc(pTargetMD); + CONSISTENCY_CHECK((NULL == pResolver->GetStubMethodDesc()) || (pStubMD == pResolver->GetStubMethodDesc())); - if (SF_IsStructMarshalStub(dwStubFlags)) - { - CreateStructStub(pss, pSigDesc, pTargetMT, dwStubFlags, pStubMD); - } - else + if (pResolver->IsILGenerated()) { - CreateNDirectStubWorker(pss, - pSigDesc, - nlType, - nlFlags, - unmgdCallConv, - dwStubFlags, - pStubMD, - pParamTokenArray, - iLCIDArg); + // this stub already has its IL generated + break; } + // + // Check that the stub signature and MethodDesc are compatible. The JIT + // interface functions depend on this. + // - pResolver->SetTokenLookupMap(pss->GetTokenLookupMap()); + { + SigPointer ptr = pSigDesc->m_sig.CreateSigPointer(); - pResolver->SetStubTargetMethodSig( - pss->GetStubTargetMethodSig(), - pss->GetStubTargetMethodSigLength()); + uint32_t callConvInfo; + IfFailThrow(ptr.GetCallingConvInfo(&callConvInfo)); - // we successfully generated the IL stub - sgh.SuppressRelease(); - } + BOOL fSigIsStatic = !(callConvInfo & IMAGE_CEE_CS_CALLCONV_HASTHIS); - if (pGeneratedNewStub) - { - *pGeneratedNewStub = true; - } + // CreateNDirectStubWorker will throw an exception for these cases. + BOOL fCanHaveThis = SF_IsDelegateStub(dwStubFlags) || SF_IsCOMStub(dwStubFlags); - pEntry->m_hrResultCode = S_OK; - break; - } + if (fSigIsStatic || fCanHaveThis) + { + CONSISTENCY_CHECK(pStubMD->IsStatic() == (DWORD)fSigIsStatic); + } + } - // Link the MethodDesc onto the method table with the lock taken - NDirect::AddMethodDescChunkWithLockTaken(¶ms, pStubMD); + { + ILStubGenHolder sgh(pResolver); - pGenILHolder.SuppressRelease(); - } - } - } - ilStubCreatorHelper.SuppressRelease(); - } + pResolver->SetStubMethodDesc(pStubMD); + pResolver->SetStubTargetMethodDesc(pTargetMD); -#if defined(TARGET_X86) - if (SF_IsForwardStub(dwStubFlags) && pTargetMD != NULL && !pTargetMD->IsVarArg()) - { - // copy the stack arg byte count from the stub MD to the target MD - this number is computed - // during stub generation and is copied to all target MDs that share the stub - // (we don't set it for varargs - the number is call site specific) - // also copy the "takes parameters with copy constructors" flag which is needed to generate - // appropriate intercept stub + if (SF_IsStructMarshalStub(dwStubFlags)) + { + CreateStructStub(pss, pSigDesc, pTargetMT, dwStubFlags, pStubMD); + } + else + { + CreateNDirectStubWorker(pss, + pSigDesc, + nlType, + nlFlags, + unmgdCallConv, + dwStubFlags, + pStubMD, + pParamTokenArray, + iLCIDArg); + } - WORD cbStackArgSize = pStubMD->AsDynamicMethodDesc()->GetNativeStackArgSize(); - if (pTargetMD->IsNDirect()) - { - NDirectMethodDesc *pTargetNMD = (NDirectMethodDesc *)pTargetMD; - pTargetNMD->SetStackArgumentSize(cbStackArgSize, MetaSig::GetDefaultUnmanagedCallingConvention()); + pResolver->SetTokenLookupMap(pss->GetTokenLookupMap()); + + pResolver->SetStubTargetMethodSig( + pss->GetStubTargetMethodSig(), + pss->GetStubTargetMethodSigLength()); + + // we successfully generated the IL stub + sgh.SuppressRelease(); + } + + if (pGeneratedNewStub) + { + *pGeneratedNewStub = true; + } + + pEntry->m_hrResultCode = S_OK; + break; + } + + // Link the MethodDesc onto the method table with the lock taken + AddMethodDescChunkWithLockTaken(¶ms, pStubMD); + + pGenILHolder.SuppressRelease(); + } + } + } + ilStubCreatorHelper.SuppressRelease(); } -#ifdef FEATURE_COMINTEROP - else + +#if defined(TARGET_X86) + if (SF_IsForwardStub(dwStubFlags) && pTargetMD != NULL && !pTargetMD->IsVarArg()) { - if (SF_IsCOMStub(dwStubFlags)) + // copy the stack arg byte count from the stub MD to the target MD - this number is computed + // during stub generation and is copied to all target MDs that share the stub + // (we don't set it for varargs - the number is call site specific) + // also copy the "takes parameters with copy constructors" flag which is needed to generate + // appropriate intercept stub + + WORD cbStackArgSize = pStubMD->AsDynamicMethodDesc()->GetNativeStackArgSize(); + if (pTargetMD->IsNDirect()) { - ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pTargetMD); + NDirectMethodDesc *pTargetNMD = (NDirectMethodDesc *)pTargetMD; - if (pComInfo != NULL) + pTargetNMD->SetStackArgumentSize(cbStackArgSize, MetaSig::GetDefaultUnmanagedCallingConvention()); + } +#ifdef FEATURE_COMINTEROP + else + { + if (SF_IsCOMStub(dwStubFlags)) { - pComInfo->SetStackArgumentSize(cbStackArgSize); + ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pTargetMD); + + if (pComInfo != NULL) + { + pComInfo->SetStackArgumentSize(cbStackArgSize); + } } } - } #endif // FEATURE_COMINTEROP - } + } #endif // defined(TARGET_X86) - RETURN pStubMD; + RETURN pStubMD; + } } MethodDesc* NDirect::CreateCLRToNativeILStub( @@ -5124,6 +5267,102 @@ MethodDesc* GetStubMethodDescFromInteropMethodDesc(MethodDesc* pMD, DWORD dwStub #ifndef CROSSGEN_COMPILE +namespace +{ + LPVOID NDirectGetEntryPoint(NDirectMethodDesc *pMD, NATIVE_LIBRARY_HANDLE hMod) + { + // GetProcAddress cannot be called while preemptive GC is disabled. + // It requires the OS to take the loader lock. + CONTRACT(LPVOID) + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(pMD)); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + g_IBCLogger.LogNDirectCodeAccess(pMD); + + RETURN pMD->FindEntryPoint(hMod); + } + + //--------------------------------------------------------- + // Loads the DLL and finds the procaddress for an N/Direct call. + //--------------------------------------------------------- + VOID NDirectLink(NDirectMethodDesc *pMD) + { + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(pMD)); + } + CONTRACTL_END; + + if (pMD->IsClassConstructorTriggeredAtLinkTime()) + { + pMD->GetMethodTable()->CheckRunClassInitThrowing(); + } + + if (pMD->IsQCall()) + { + LPVOID pvTarget = pMD->ndirect.m_pNativeNDirectTarget; + + // Do not repeat the lookup if the QCall was hardbound during ngen + if (pvTarget == NULL) + { + pvTarget = ECall::GetQCallImpl(pMD); + } + else + { + _ASSERTE(pvTarget == ECall::GetQCallImpl(pMD)); + } + + pMD->SetNDirectTarget(pvTarget); + return; + } + + // Loading unmanaged dlls can trigger dllmains which certainly count as code execution! + pMD->EnsureActive(); + + { + LPVOID pvTarget = (LPVOID)PInvokeOverride::GetMethodImpl(pMD->GetLibNameRaw(), pMD->GetEntrypointName()); + if (pvTarget != NULL) + { + pMD->SetNDirectTarget(pvTarget); + return; + } + } + + NATIVE_LIBRARY_HANDLE hmod = NativeLibrary::LoadLibraryFromMethodDesc(pMD); + _ASSERTE(hmod != NULL); + + BOOL fSuccess = FALSE; + LPVOID pvTarget = NDirectGetEntryPoint(pMD, hmod); + if (pvTarget) + { + pMD->SetNDirectTarget(pvTarget); + fSuccess = TRUE; + } + + if (!fSuccess) + { + StackSString ssLibName(SString::Utf8, pMD->GetLibName()); + + WCHAR wszEPName[50]; + if (WszMultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pMD->GetEntrypointName(), -1, wszEPName, sizeof(wszEPName)/sizeof(WCHAR)) == 0) + { + wszEPName[0] = W('?'); + wszEPName[1] = W('\0'); + } +#ifdef TARGET_UNIX + COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_UNIX, ssLibName.GetUnicode(), wszEPName); +#else + COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_WIN, ssLibName.GetUnicode(), wszEPName); +#endif + } + } +} + PCODE NDirect::GetStubForILStub(MethodDesc* pManagedMD, MethodDesc** ppStubMD, DWORD dwStubFlags) { CONTRACT(PCODE) @@ -5426,24 +5665,6 @@ void CreateCLRToDispatchCOMStub( #endif // FEATURE_COMINTEROP -/*static*/ -LPVOID NDirect::NDirectGetEntryPoint(NDirectMethodDesc *pMD, NATIVE_LIBRARY_HANDLE hMod) -{ - // GetProcAddress cannot be called while preemptive GC is disabled. - // It requires the OS to take the loader lock. - CONTRACT(LPVOID) - { - STANDARD_VM_CHECK; - PRECONDITION(CheckPointer(pMD)); - POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); - } - CONTRACT_END; - - g_IBCLogger.LogNDirectCodeAccess(pMD); - - RETURN pMD->FindEntryPoint(hMod); -} - VOID NDirectMethodDesc::SetNDirectTarget(LPVOID pTarget) { CONTRACTL @@ -5462,1111 +5683,138 @@ VOID NDirectMethodDesc::SetNDirectTarget(LPVOID pTarget) pWriteableData->m_pNDirectTarget = pTarget; } - - -// Preserving good error info from DllImport-driven LoadLibrary is tricky because we keep loading from different places -// if earlier loads fail and those later loads obliterate error codes. -// -// This tracker object will keep track of the error code in accordance to priority: -// -// low-priority: unknown error code (should never happen) -// medium-priority: dll not found -// high-priority: dll found but error during loading -// -// We will overwrite the previous load's error code only if the new error code is higher priority. -// - -class LoadLibErrorTracker +void MarshalStructViaILStub(MethodDesc* pStubMD, void* pManagedData, void* pNativeData, StructMarshalStubs::MarshalOperation operation, void** ppCleanupWorkList /* = nullptr */) { -private: - static const DWORD const_priorityNotFound = 10; - static const DWORD const_priorityAccessDenied = 20; - static const DWORD const_priorityCouldNotLoad = 99999; -public: - LoadLibErrorTracker() + CONTRACTL { - LIMITED_METHOD_CONTRACT; - m_hr = E_FAIL; - m_priorityOfLastError = 0; + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(pStubMD)); } + CONTRACTL_END; - VOID TrackErrorCode() + ARG_SLOT args[] = { - LIMITED_METHOD_CONTRACT; + PtrToArgSlot(pManagedData), + PtrToArgSlot(pNativeData), + (ARG_SLOT)operation, + PtrToArgSlot(ppCleanupWorkList) + }; - DWORD priority; + MethodDescCallSite callSite(pStubMD); -#ifdef TARGET_UNIX + callSite.Call(args); +} - SetMessage(PAL_GetLoadLibraryError()); -#else +void MarshalStructViaILStubCode(PCODE pStubCode, void* pManagedData, void* pNativeData, StructMarshalStubs::MarshalOperation operation, void** ppCleanupWorkList /* = nullptr */) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(pStubCode != NULL); + } + CONTRACTL_END; - DWORD dwLastError = GetLastError(); + PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(pStubCode); + DECLARE_ARGHOLDER_ARRAY(args, 4); + args[ARGNUM_0] = PTR_TO_ARGHOLDER(pManagedData); + args[ARGNUM_1] = PTR_TO_ARGHOLDER(pNativeData); + args[ARGNUM_2] = DWORD_TO_ARGHOLDER(operation); + args[ARGNUM_3] = PTR_TO_ARGHOLDER(ppCleanupWorkList); - switch (dwLastError) - { - case ERROR_FILE_NOT_FOUND: - case ERROR_PATH_NOT_FOUND: - case ERROR_MOD_NOT_FOUND: - case ERROR_DLL_NOT_FOUND: - priority = const_priorityNotFound; - break; + CALL_MANAGED_METHOD_NORET(args); +} - // If we can't access a location, we can't know if the dll's there or if it's good. - // Still, this is probably more unusual (and thus of more interest) than a dll-not-found - // so give it an intermediate priority. - case ERROR_ACCESS_DENIED: - priority = const_priorityAccessDenied; - // Assume all others are "dll found but couldn't load." - default: - priority = const_priorityCouldNotLoad; - break; - } - UpdateHR(priority, HRESULT_FROM_WIN32(dwLastError)); -#endif - } +//========================================================================== +// This function is reached only via NDirectImportThunk. It's purpose +// is to ensure that the target DLL is fully loaded and ready to run. +// +// FUN FACTS: Though this function is actually entered in unmanaged mode, +// it can reenter managed mode and throw a COM+ exception if the DLL linking +// fails. +//========================================================================== +EXTERN_C LPVOID STDCALL NDirectImportWorker(NDirectMethodDesc* pMD) +{ + LPVOID ret = NULL; - // Sets the error code to HRESULT as could not load DLL - void TrackHR_CouldNotLoad(HRESULT hr) - { - UpdateHR(const_priorityCouldNotLoad, hr); - } + BEGIN_PRESERVE_LAST_ERROR; - HRESULT GetHR() + CONTRACTL { - return m_hr; + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; } + CONTRACTL_END; - SString& GetMessage() - { - return m_message; - } + INSTALL_MANAGED_EXCEPTION_DISPATCHER; + // this function is called by CLR to native assembly stubs which are called by + // managed code as a result, we need an unwind and continue handler to translate + // any of our internal exceptions into managed exceptions. + INSTALL_UNWIND_AND_CONTINUE_HANDLER; - void DECLSPEC_NORETURN Throw(SString &libraryNameOrPath) + if (pMD->IsEarlyBound()) { - STANDARD_VM_CONTRACT; - -#if defined(__APPLE__) - COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_MAC, libraryNameOrPath.GetUnicode(), GetMessage()); -#elif defined(TARGET_UNIX) - COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_LINUX, libraryNameOrPath.GetUnicode(), GetMessage()); -#else // __APPLE__ - HRESULT theHRESULT = GetHR(); - if (theHRESULT == HRESULT_FROM_WIN32(ERROR_BAD_EXE_FORMAT)) - { - COMPlusThrow(kBadImageFormatException); - } - else + if (!pMD->IsZapped()) { - SString hrString; - GetHRMsg(theHRESULT, hrString); - COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_WIN, libraryNameOrPath.GetUnicode(), hrString); + // we need the MD to be populated in case we decide to build an intercept + // stub to wrap the target in InitEarlyBoundNDirectTarget + PInvokeStaticSigInfo sigInfo; + NDirect::PopulateNDirectMethodDesc(pMD, &sigInfo); } -#endif // TARGET_UNIX - __UNREACHABLE(); + pMD->InitEarlyBoundNDirectTarget(); } - -private: - void UpdateHR(DWORD priority, HRESULT hr) + else { - if (priority > m_priorityOfLastError) + // + // Otherwise we're in an inlined pinvoke late bound MD + // + INDEBUG(Thread *pThread = GetThread()); { - m_hr = hr; - m_priorityOfLastError = priority; - } - } - - void SetMessage(LPCSTR message) - { - m_message = SString(SString::Utf8, message); - } - - HRESULT m_hr; - DWORD m_priorityOfLastError; - SString m_message; -}; // class LoadLibErrorTracker - -// Load the library directly and return the raw system handle -static NATIVE_LIBRARY_HANDLE LocalLoadLibraryHelper( LPCWSTR name, DWORD flags, LoadLibErrorTracker *pErrorTracker ) -{ - STANDARD_VM_CONTRACT; + _ASSERTE(pMD->ShouldSuppressGCTransition() + || pThread->GetFrame()->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr()); - NATIVE_LIBRARY_HANDLE hmod = NULL; + CONSISTENCY_CHECK(pMD->IsNDirect()); + // + // With IL stubs, we don't have to do anything but ensure the DLL is loaded. + // -#ifndef TARGET_UNIX + if (!pMD->IsZapped()) + { + PInvokeStaticSigInfo sigInfo; + NDirect::PopulateNDirectMethodDesc(pMD, &sigInfo); + } + else + { + // must have been populated at NGEN time + _ASSERTE(pMD->GetLibName() != NULL); + } - if ((flags & 0xFFFFFF00) != 0) - { - hmod = CLRLoadLibraryEx(name, NULL, flags & 0xFFFFFF00); - if (hmod != NULL) - { - return hmod; - } + pMD->CheckRestore(); - DWORD dwLastError = GetLastError(); - if (dwLastError != ERROR_INVALID_PARAMETER) - { - pErrorTracker->TrackErrorCode(); - return hmod; + NDirectLink(pMD); } } - hmod = CLRLoadLibraryEx(name, NULL, flags & 0xFF); + ret = pMD->GetNDirectTarget(); -#else // !TARGET_UNIX - hmod = PAL_LoadLibraryDirect(name); -#endif // !TARGET_UNIX + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; + UNINSTALL_MANAGED_EXCEPTION_DISPATCHER; - if (hmod == NULL) - { - pErrorTracker->TrackErrorCode(); - } + END_PRESERVE_LAST_ERROR; - return hmod; + return ret; } -#define TOLOWER(a) (((a) >= W('A') && (a) <= W('Z')) ? (W('a') + (a - W('A'))) : (a)) -#define TOHEX(a) ((a)>=10 ? W('a')+(a)-10 : W('0')+(a)) - -#ifdef TARGET_UNIX -#define PLATFORM_SHARED_LIB_SUFFIX_W PAL_SHLIB_SUFFIX_W -#define PLATFORM_SHARED_LIB_PREFIX_W PAL_SHLIB_PREFIX_W -#else // !TARGET_UNIX -#define PLATFORM_SHARED_LIB_SUFFIX_W W(".dll") -#define PLATFORM_SHARED_LIB_PREFIX_W W("") -#endif // !TARGET_UNIX - -// The Bit 0x2 has different semantics in DllImportSearchPath and LoadLibraryExA flags. -// In DllImportSearchPath enum, bit 0x2 represents SearchAssemblyDirectory -- which is performed by CLR. -// Unlike other bits in this enum, this bit shouldn't be directly passed on to LoadLibrary() -#define DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY 0x2 - -// DllImportSearchPathFlags is a special enumeration, whose values are tied closely with LoadLibrary flags. -// There is no "default" value DllImportSearchPathFlags. In the absence of DllImportSearchPath attribute, -// CoreCLR's LoadLibrary implementation uses the following defaults. -// Other implementations of LoadLibrary callbacks/events are free to use other default conventions. -void GetDefaultDllImportSearchPathFlags(DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory) -{ - STANDARD_VM_CONTRACT; - - *searchAssemblyDirectory = TRUE; - *dllImportSearchPathFlags = 0; -} - -// If a module has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and return true. -// Otherwise, get CoreCLR's default value for DllImportSearchPathFlags, and return false. -BOOL GetDllImportSearchPathFlags(Module *pModule, DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory) -{ - STANDARD_VM_CONTRACT; - - if (pModule->HasDefaultDllImportSearchPathsAttribute()) - { - *dllImportSearchPathFlags = pModule->DefaultDllImportSearchPathsAttributeCachedValue(); - *searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory(); - return TRUE; - } - - GetDefaultDllImportSearchPathFlags(dllImportSearchPathFlags, searchAssemblyDirectory); - return FALSE; -} - -// If a pInvoke has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and returns true. -// Otherwise, if the containing assembly has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and returns true. -// Otherwise, get CoreCLR's default value for DllImportSearchPathFlags, and return false. -BOOL GetDllImportSearchPathFlags(NDirectMethodDesc * pMD, DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory) -{ - STANDARD_VM_CONTRACT; - - if (pMD->HasDefaultDllImportSearchPathsAttribute()) - { - *dllImportSearchPathFlags = pMD->DefaultDllImportSearchPathsAttributeCachedValue(); - *searchAssemblyDirectory = pMD->DllImportSearchAssemblyDirectory(); - return TRUE; - } - - return GetDllImportSearchPathFlags(pMD->GetModule(), dllImportSearchPathFlags, searchAssemblyDirectory); -} - -// static -NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError) -{ - CONTRACTL - { - STANDARD_VM_CHECK; - PRECONDITION(CheckPointer(libraryPath)); - } - CONTRACTL_END; - - LoadLibErrorTracker errorTracker; - const NATIVE_LIBRARY_HANDLE hmod = - LocalLoadLibraryHelper(libraryPath, GetLoadWithAlteredSearchPathFlag(), &errorTracker); - - if (throwOnError && (hmod == nullptr)) - { - SString libraryPathSString(libraryPath); - errorTracker.Throw(libraryPathSString); - } - return hmod; -} - -// static -void NDirect::FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle) -{ - STANDARD_VM_CONTRACT; - _ASSERTE(handle != NULL); - -#ifndef TARGET_UNIX - BOOL retVal = FreeLibrary(handle); -#else // !TARGET_UNIX - BOOL retVal = PAL_FreeLibraryDirect(handle); -#endif // !TARGET_UNIX - - if (retVal == 0) - COMPlusThrow(kInvalidOperationException, W("Arg_InvalidOperationException")); -} - -//static -INT_PTR NDirect::GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError) -{ - CONTRACTL - { - STANDARD_VM_CHECK; - PRECONDITION(CheckPointer(handle)); - PRECONDITION(CheckPointer(symbolName)); - } - CONTRACTL_END; - - MAKE_UTF8PTR_FROMWIDE(lpstr, symbolName); - -#ifndef TARGET_UNIX - INT_PTR address = reinterpret_cast(GetProcAddress((HMODULE)handle, lpstr)); - if ((address == NULL) && throwOnError) - COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL, symbolName); -#else // !TARGET_UNIX - INT_PTR address = reinterpret_cast(PAL_GetProcAddressDirect(handle, lpstr)); - if ((address == NULL) && throwOnError) - COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO, symbolName); -#endif // !TARGET_UNIX - - return address; -} - -namespace -{ -#ifndef TARGET_UNIX - BOOL IsWindowsAPISet(PCWSTR wszLibName) - { - STANDARD_VM_CONTRACT; - - // This is replicating quick check from the OS implementation of api sets. - return SString::_wcsnicmp(wszLibName, W("api-"), 4) == 0 || - SString::_wcsnicmp(wszLibName, W("ext-"), 4) == 0; - } -#endif // !TARGET_UNIX - - NATIVE_LIBRARY_HANDLE LoadNativeLibraryViaAssemblyLoadContext(Assembly * pAssembly, PCWSTR wszLibName) - { - STANDARD_VM_CONTRACT; - -#ifndef TARGET_UNIX - if (IsWindowsAPISet(wszLibName)) - { - // Prevent Overriding of Windows API sets. - return NULL; - } -#endif // !TARGET_UNIX - - NATIVE_LIBRARY_HANDLE hmod = NULL; - AppDomain* pDomain = GetAppDomain(); - CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext(); - - PEFile *pManifestFile = pAssembly->GetManifestFile(); - PTR_ICLRPrivBinder pBindingContext = pManifestFile->GetBindingContext(); - - //Step 0: Check if the assembly was bound using TPA. - // The Binding Context can be null or an overridden TPA context - if (pBindingContext == NULL) - { - // If we do not have any binder associated, then return to the default resolution mechanism. - return NULL; - } - - UINT_PTR assemblyBinderID = 0; - IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID)); - - ICLRPrivBinder *pCurrentBinder = reinterpret_cast(assemblyBinderID); - - // For assemblies bound via TPA binder, we should use the standard mechanism to make the pinvoke call. - if (AreSameBinderInstance(pCurrentBinder, pTPABinder)) - { - return NULL; - } - - //Step 1: If the assembly was not bound using TPA, - // Call System.Runtime.Loader.AssemblyLoadContext.ResolveUnmanagedDll to give - // The custom assembly context a chance to load the unmanaged dll. - - GCX_COOP(); - - STRINGREF pUnmanagedDllName; - pUnmanagedDllName = StringObject::NewString(wszLibName); - - GCPROTECT_BEGIN(pUnmanagedDllName); - - // Get the pointer to the managed assembly load context - INT_PTR ptrManagedAssemblyLoadContext = ((CLRPrivBinderAssemblyLoadContext *)pCurrentBinder)->GetManagedAssemblyLoadContext(); - - // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.ResolveUnmanagedDll method. - PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUNMANAGEDDLL); - DECLARE_ARGHOLDER_ARRAY(args, 2); - args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(pUnmanagedDllName); - args[ARGNUM_1] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext); - - // Make the call - CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args); - - GCPROTECT_END(); - - return hmod; - } - - // Return the AssemblyLoadContext for an assembly - INT_PTR GetManagedAssemblyLoadContext(Assembly* pAssembly) - { - STANDARD_VM_CONTRACT; - - PTR_ICLRPrivBinder pBindingContext = pAssembly->GetManifestFile()->GetBindingContext(); - if (pBindingContext == NULL) - { - // GetBindingContext() returns NULL for System.Private.CoreLib - return NULL; - } - - UINT_PTR assemblyBinderID = 0; - IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID)); - - AppDomain *pDomain = GetAppDomain(); - ICLRPrivBinder *pCurrentBinder = reinterpret_cast(assemblyBinderID); - - // The code here deals with two implementations of ICLRPrivBinder interface: - // - CLRPrivBinderCoreCLR for the TPA binder in the default ALC, and - // - CLRPrivBinderAssemblyLoadContext for custom ALCs. - // in order obtain the associated ALC handle. - INT_PTR ptrManagedAssemblyLoadContext = AreSameBinderInstance(pCurrentBinder, pDomain->GetTPABinderContext()) - ? ((CLRPrivBinderCoreCLR *)pCurrentBinder)->GetManagedAssemblyLoadContext() - : ((CLRPrivBinderAssemblyLoadContext *)pCurrentBinder)->GetManagedAssemblyLoadContext(); - - return ptrManagedAssemblyLoadContext; - } - - NATIVE_LIBRARY_HANDLE LoadNativeLibraryViaAssemblyLoadContextEvent(Assembly * pAssembly, PCWSTR wszLibName) - { - STANDARD_VM_CONTRACT; - - INT_PTR ptrManagedAssemblyLoadContext = GetManagedAssemblyLoadContext(pAssembly); - if (ptrManagedAssemblyLoadContext == NULL) - { - return NULL; - } - - NATIVE_LIBRARY_HANDLE hmod = NULL; - - GCX_COOP(); - - struct { - STRINGREF DllName; - OBJECTREF AssemblyRef; - } gc = { NULL, NULL }; - - GCPROTECT_BEGIN(gc); - - gc.DllName = StringObject::NewString(wszLibName); - gc.AssemblyRef = pAssembly->GetExposedObject(); - - // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.ResolveUnmanagedDllUsingEvent method - // While ResolveUnmanagedDllUsingEvent() could compute the AssemblyLoadContext using the AssemblyRef - // argument, it will involve another pInvoke to the runtime. So AssemblyLoadContext is passed in - // as an additional argument. - PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUNMANAGEDDLLUSINGEVENT); - DECLARE_ARGHOLDER_ARRAY(args, 3); - args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(gc.DllName); - args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.AssemblyRef); - args[ARGNUM_2] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext); - - // Make the call - CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args); - - GCPROTECT_END(); - - return hmod; - } - - NATIVE_LIBRARY_HANDLE LoadNativeLibraryViaDllImportResolver(NDirectMethodDesc * pMD, LPCWSTR wszLibName) - { - STANDARD_VM_CONTRACT; - - if (pMD->GetModule()->IsSystem()) - { - // Don't attempt to callback on Corelib itself. - // The LoadLibrary callback stub is managed code that requires CoreLib - return NULL; - } - - DWORD dllImportSearchPathFlags; - BOOL searchAssemblyDirectory; - BOOL hasDllImportSearchPathFlags = GetDllImportSearchPathFlags(pMD, &dllImportSearchPathFlags, &searchAssemblyDirectory); - dllImportSearchPathFlags |= searchAssemblyDirectory ? DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY : 0; - - Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly(); - NATIVE_LIBRARY_HANDLE handle = NULL; - - GCX_COOP(); - - struct { - STRINGREF libNameRef; - OBJECTREF assemblyRef; - } gc = { NULL, NULL }; - - GCPROTECT_BEGIN(gc); - - gc.libNameRef = StringObject::NewString(wszLibName); - gc.assemblyRef = pAssembly->GetExposedObject(); - - PREPARE_NONVIRTUAL_CALLSITE(METHOD__NATIVELIBRARY__LOADLIBRARYCALLBACKSTUB); - DECLARE_ARGHOLDER_ARRAY(args, 4); - args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(gc.libNameRef); - args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.assemblyRef); - args[ARGNUM_2] = BOOL_TO_ARGHOLDER(hasDllImportSearchPathFlags); - args[ARGNUM_3] = DWORD_TO_ARGHOLDER(dllImportSearchPathFlags); - - // Make the call - CALL_MANAGED_METHOD(handle, NATIVE_LIBRARY_HANDLE, args); - GCPROTECT_END(); - - return handle; - } - - // Try to load the module alongside the assembly where the PInvoke was declared. - NATIVE_LIBRARY_HANDLE LoadFromPInvokeAssemblyDirectory(Assembly *pAssembly, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker) - { - STANDARD_VM_CONTRACT; - - NATIVE_LIBRARY_HANDLE hmod = NULL; - - SString path = pAssembly->GetManifestFile()->GetPath(); - - SString::Iterator lastPathSeparatorIter = path.End(); - if (PEAssembly::FindLastPathSeparator(path, lastPathSeparatorIter)) - { - lastPathSeparatorIter++; - path.Truncate(lastPathSeparatorIter); - - path.Append(libName); - hmod = LocalLoadLibraryHelper(path, flags, pErrorTracker); - } - - return hmod; - } - - // Try to load the module from the native DLL search directories - NATIVE_LIBRARY_HANDLE LoadFromNativeDllSearchDirectories(LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker) - { - STANDARD_VM_CONTRACT; - - NATIVE_LIBRARY_HANDLE hmod = NULL; - AppDomain* pDomain = GetAppDomain(); - - if (pDomain->HasNativeDllSearchDirectories()) - { - AppDomain::PathIterator pathIter = pDomain->IterateNativeDllSearchDirectories(); - while (hmod == NULL && pathIter.Next()) - { - SString qualifiedPath(*(pathIter.GetPath())); - qualifiedPath.Append(libName); - if (!Path::IsRelative(qualifiedPath)) - { - hmod = LocalLoadLibraryHelper(qualifiedPath, flags, pErrorTracker); - } - } - } - - return hmod; - } - -#ifdef TARGET_UNIX - const int MaxVariationCount = 4; - void DetermineLibNameVariations(const WCHAR** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath) - { - // Supported lib name variations - static auto NameFmt = W("%.0s%s%.0s"); - static auto PrefixNameFmt = W("%s%s%.0s"); - static auto NameSuffixFmt = W("%.0s%s%s"); - static auto PrefixNameSuffixFmt = W("%s%s%s"); - - _ASSERTE(*numberOfVariations >= MaxVariationCount); - - int varCount = 0; - if (!libNameIsRelativePath) - { - libNameVariations[varCount++] = NameFmt; - } - else - { - // We check if the suffix is contained in the name, because on Linux it is common to append - // a version number to the library name (e.g. 'libicuuc.so.57'). - bool containsSuffix = false; - SString::CIterator it = libName.Begin(); - if (libName.Find(it, PLATFORM_SHARED_LIB_SUFFIX_W)) - { - it += COUNTOF(PLATFORM_SHARED_LIB_SUFFIX_W); - containsSuffix = it == libName.End() || *it == (WCHAR)'.'; - } - - // If the path contains a path delimiter, we don't add a prefix - it = libName.Begin(); - bool containsDelim = libName.Find(it, DIRECTORY_SEPARATOR_STR_W); - - if (containsSuffix) - { - libNameVariations[varCount++] = NameFmt; - - if (!containsDelim) - libNameVariations[varCount++] = PrefixNameFmt; - - libNameVariations[varCount++] = NameSuffixFmt; - - if (!containsDelim) - libNameVariations[varCount++] = PrefixNameSuffixFmt; - } - else - { - libNameVariations[varCount++] = NameSuffixFmt; - - if (!containsDelim) - libNameVariations[varCount++] = PrefixNameSuffixFmt; - - libNameVariations[varCount++] = NameFmt; - - if (!containsDelim) - libNameVariations[varCount++] = PrefixNameFmt; - } - } - - *numberOfVariations = varCount; - } -#else // TARGET_UNIX - const int MaxVariationCount = 2; - void DetermineLibNameVariations(const WCHAR** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath) - { - // Supported lib name variations - static auto NameFmt = W("%.0s%s%.0s"); - static auto NameSuffixFmt = W("%.0s%s%s"); - - _ASSERTE(*numberOfVariations >= MaxVariationCount); - - int varCount = 0; - - // The purpose of following code is to workaround LoadLibrary limitation: - // LoadLibrary won't append extension if filename itself contains '.'. Thus it will break the following scenario: - // [DllImport("A.B")] // The full name for file is "A.B.dll". This is common code pattern for cross-platform PInvoke - // The workaround for above scenario is to call LoadLibrary with "A.B" first, if it fails, then call LoadLibrary with "A.B.dll" - auto it = libName.Begin(); - if (!libNameIsRelativePath || - !libName.Find(it, W('.')) || - libName.EndsWith(W(".")) || - libName.EndsWithCaseInsensitive(W(".dll")) || - libName.EndsWithCaseInsensitive(W(".exe"))) - { - // Follow LoadLibrary rules in MSDN doc: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684175(v=vs.85).aspx - // If the string specifies a full path, the function searches only that path for the module. - // If the string specifies a module name without a path and the file name extension is omitted, the function appends the default library extension .dll to the module name. - // To prevent the function from appending .dll to the module name, include a trailing point character (.) in the module name string. - libNameVariations[varCount++] = NameFmt; - } - else - { - libNameVariations[varCount++] = NameFmt; - libNameVariations[varCount++] = NameSuffixFmt; - } - - *numberOfVariations = varCount; - } -#endif // TARGET_UNIX - - // Search for the library and variants of its name in probing directories. - NATIVE_LIBRARY_HANDLE LoadNativeLibraryBySearch(Assembly *callingAssembly, - BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlags, - LoadLibErrorTracker * pErrorTracker, LPCWSTR wszLibName) - { - STANDARD_VM_CONTRACT; - - NATIVE_LIBRARY_HANDLE hmod = NULL; - -#if defined(FEATURE_CORESYSTEM) && !defined(TARGET_UNIX) - // Try to go straight to System32 for Windows API sets. This is replicating quick check from - // the OS implementation of api sets. - if (IsWindowsAPISet(wszLibName)) - { - hmod = LocalLoadLibraryHelper(wszLibName, LOAD_LIBRARY_SEARCH_SYSTEM32, pErrorTracker); - if (hmod != NULL) - { - return hmod; - } - } -#endif // FEATURE_CORESYSTEM && !TARGET_UNIX - - if (g_hostpolicy_embedded) - { -#ifdef TARGET_WINDOWS - if (wcscmp(wszLibName, W("hostpolicy.dll")) == 0) - { - return WszGetModuleHandle(NULL); - } -#else - if (wcscmp(wszLibName, W("libhostpolicy")) == 0) - { - return PAL_LoadLibraryDirect(NULL); - } -#endif - } - - AppDomain* pDomain = GetAppDomain(); - DWORD loadWithAlteredPathFlags = GetLoadWithAlteredSearchPathFlag(); - bool libNameIsRelativePath = Path::IsRelative(wszLibName); - - // P/Invokes are often declared with variations on the actual library name. - // For example, it's common to leave off the extension/suffix of the library - // even if it has one, or to leave off a prefix like "lib" even if it has one - // (both of these are typically done to smooth over cross-platform differences). - // We try to dlopen with such variations on the original. - const WCHAR* prefixSuffixCombinations[MaxVariationCount] = {}; - int numberOfVariations = COUNTOF(prefixSuffixCombinations); - DetermineLibNameVariations(prefixSuffixCombinations, &numberOfVariations, wszLibName, libNameIsRelativePath); - for (int i = 0; i < numberOfVariations; i++) - { - SString currLibNameVariation; - currLibNameVariation.Printf(prefixSuffixCombinations[i], PLATFORM_SHARED_LIB_PREFIX_W, wszLibName, PLATFORM_SHARED_LIB_SUFFIX_W); - - // NATIVE_DLL_SEARCH_DIRECTORIES set by host is considered well known path - hmod = LoadFromNativeDllSearchDirectories(currLibNameVariation, loadWithAlteredPathFlags, pErrorTracker); - if (hmod != NULL) - { - return hmod; - } - - if (!libNameIsRelativePath) - { - DWORD flags = loadWithAlteredPathFlags; - if ((dllImportSearchPathFlags & LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR) != 0) - { - // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR is the only flag affecting absolute path. Don't OR the flags - // unconditionally as all absolute path P/Invokes could then lose LOAD_WITH_ALTERED_SEARCH_PATH. - flags |= dllImportSearchPathFlags; - } - - hmod = LocalLoadLibraryHelper(currLibNameVariation, flags, pErrorTracker); - if (hmod != NULL) - { - return hmod; - } - } - else if ((callingAssembly != nullptr) && searchAssemblyDirectory) - { - hmod = LoadFromPInvokeAssemblyDirectory(callingAssembly, currLibNameVariation, loadWithAlteredPathFlags | dllImportSearchPathFlags, pErrorTracker); - if (hmod != NULL) - { - return hmod; - } - } - - hmod = LocalLoadLibraryHelper(currLibNameVariation, dllImportSearchPathFlags, pErrorTracker); - if (hmod != NULL) - { - return hmod; - } - } - - // This may be an assembly name - // Format is "fileName, assemblyDisplayName" - MAKE_UTF8PTR_FROMWIDE(szLibName, wszLibName); - char *szComma = strchr(szLibName, ','); - if (szComma) - { - *szComma = '\0'; - // Trim white spaces - while (COMCharacter::nativeIsWhiteSpace(*(++szComma))); - - AssemblySpec spec; - if (SUCCEEDED(spec.Init(szComma))) - { - // Need to perform case insensitive hashing. - SString moduleName(SString::Utf8, szLibName); - moduleName.LowerCase(); - - StackScratchBuffer buffer; - szLibName = (LPSTR)moduleName.GetUTF8(buffer); - - Assembly *pAssembly = spec.LoadAssembly(FILE_LOADED); - Module *pModule = pAssembly->FindModuleByName(szLibName); - - hmod = LocalLoadLibraryHelper(pModule->GetPath(), loadWithAlteredPathFlags | dllImportSearchPathFlags, pErrorTracker); - } - } - - return hmod; - } - - NATIVE_LIBRARY_HANDLE LoadNativeLibraryBySearch(NDirectMethodDesc *pMD, LoadLibErrorTracker *pErrorTracker, PCWSTR wszLibName) - { - STANDARD_VM_CONTRACT; - - BOOL searchAssemblyDirectory; - DWORD dllImportSearchPathFlags; - - GetDllImportSearchPathFlags(pMD, &dllImportSearchPathFlags, &searchAssemblyDirectory); - - Assembly *pAssembly = pMD->GetMethodTable()->GetAssembly(); - return LoadNativeLibraryBySearch(pAssembly, searchAssemblyDirectory, dllImportSearchPathFlags, pErrorTracker, wszLibName); - } -} - -// static -NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryByName(LPCWSTR libraryName, Assembly *callingAssembly, - BOOL hasDllImportSearchFlags, DWORD dllImportSearchFlags, - BOOL throwOnError) -{ - CONTRACTL - { - STANDARD_VM_CHECK; - PRECONDITION(CheckPointer(libraryName)); - PRECONDITION(CheckPointer(callingAssembly)); - } - CONTRACTL_END; - - NATIVE_LIBRARY_HANDLE hmod = nullptr; - - // Resolve using the AssemblyLoadContext.LoadUnmanagedDll implementation - hmod = LoadNativeLibraryViaAssemblyLoadContext(callingAssembly, libraryName); - if (hmod != nullptr) - return hmod; - - // Check if a default dllImportSearchPathFlags was passed in. If so, use that value. - // Otherwise, check if the assembly has the DefaultDllImportSearchPathsAttribute attribute. - // If so, use that value. - BOOL searchAssemblyDirectory; - DWORD dllImportSearchPathFlags; - if (hasDllImportSearchFlags) - { - dllImportSearchPathFlags = dllImportSearchFlags & ~DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY; - searchAssemblyDirectory = dllImportSearchFlags & DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY; - - } - else - { - GetDllImportSearchPathFlags(callingAssembly->GetManifestModule(), - &dllImportSearchPathFlags, &searchAssemblyDirectory); - } - - LoadLibErrorTracker errorTracker; - hmod = LoadNativeLibraryBySearch(callingAssembly, searchAssemblyDirectory, dllImportSearchPathFlags, &errorTracker, libraryName); - if (hmod != nullptr) - return hmod; - - // Resolve using the AssemblyLoadContext.ResolvingUnmanagedDll event - hmod = LoadNativeLibraryViaAssemblyLoadContextEvent(callingAssembly, libraryName); - if (hmod != nullptr) - return hmod; - - if (throwOnError) - { - SString libraryPathSString(libraryName); - errorTracker.Throw(libraryPathSString); - } - - return hmod; -} - -NATIVE_LIBRARY_HANDLE NDirect::LoadNativeLibrary(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker) -{ - CONTRACTL - { - STANDARD_VM_CHECK; - PRECONDITION( CheckPointer( pMD ) ); - } - CONTRACTL_END; - - LPCUTF8 name = pMD->GetLibName(); - if ( !name || !*name ) - return NULL; - - PREFIX_ASSUME( name != NULL ); - MAKE_WIDEPTR_FROMUTF8( wszLibName, name ); - - NativeLibraryHandleHolder hmod = LoadNativeLibraryViaDllImportResolver(pMD, wszLibName); - if (hmod != NULL) - { - return hmod.Extract(); - } - - AppDomain* pDomain = GetAppDomain(); - Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly(); - - hmod = LoadNativeLibraryViaAssemblyLoadContext(pAssembly, wszLibName); - if (hmod != NULL) - { - return hmod.Extract(); - } - - hmod = pDomain->FindUnmanagedImageInCache(wszLibName); - if (hmod != NULL) - { - return hmod.Extract(); - } - - hmod = LoadNativeLibraryBySearch(pMD, pErrorTracker, wszLibName); - if (hmod != NULL) - { - // If we have a handle add it to the cache. - pDomain->AddUnmanagedImageToCache(wszLibName, hmod); - return hmod.Extract(); - } - - hmod = LoadNativeLibraryViaAssemblyLoadContextEvent(pAssembly, wszLibName); - if (hmod != NULL) - { - return hmod.Extract(); - } - - return hmod.Extract(); -} - -//--------------------------------------------------------- -// Loads the DLL and finds the procaddress for an N/Direct call. -//--------------------------------------------------------- -/* static */ -VOID NDirect::NDirectLink(NDirectMethodDesc *pMD) -{ - CONTRACTL - { - STANDARD_VM_CHECK; - PRECONDITION(CheckPointer(pMD)); - } - CONTRACTL_END; - - // - // On the phone, we only allow platform assemblies to define pinvokes - // unless the host has asked us otherwise. - // - - if (pMD->IsClassConstructorTriggeredAtLinkTime()) - { - pMD->GetMethodTable()->CheckRunClassInitThrowing(); - } - - if (pMD->IsQCall()) - { - LPVOID pvTarget = pMD->ndirect.m_pNativeNDirectTarget; - - // Do not repeat the lookup if the QCall was hardbound during ngen - if (pvTarget == NULL) - { - pvTarget = ECall::GetQCallImpl(pMD); - } - else - { - _ASSERTE(pvTarget == ECall::GetQCallImpl(pMD)); - } - - pMD->SetNDirectTarget(pvTarget); - return; - } - - // Loading unmanaged dlls can trigger dllmains which certainly count as code execution! - pMD->EnsureActive(); - - { - LPVOID pvTarget = (LPVOID)PInvokeOverride::GetMethodImpl(pMD->GetLibNameRaw(), pMD->GetEntrypointName()); - if (pvTarget != NULL) - { - pMD->SetNDirectTarget(pvTarget); - return; - } - } - - LoadLibErrorTracker errorTracker; - - BOOL fSuccess = FALSE; - NATIVE_LIBRARY_HANDLE hmod = LoadNativeLibrary( pMD, &errorTracker ); - if ( hmod ) - { - LPVOID pvTarget = NDirectGetEntryPoint(pMD, hmod); - if (pvTarget) - { - pMD->SetNDirectTarget(pvTarget); - fSuccess = TRUE; - } - } - - if (!fSuccess) - { - if (pMD->GetLibName() == NULL) - COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_NONAME); - - StackSString ssLibName(SString::Utf8, pMD->GetLibName()); - - if (!hmod) - { - errorTracker.Throw(ssLibName); - } - - WCHAR wszEPName[50]; - if (WszMultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pMD->GetEntrypointName(), -1, wszEPName, sizeof(wszEPName)/sizeof(WCHAR)) == 0) - { - wszEPName[0] = W('?'); - wszEPName[1] = W('\0'); - } -#ifdef TARGET_UNIX - COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_UNIX, ssLibName.GetUnicode(), wszEPName); -#else - COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_WIN, ssLibName.GetUnicode(), wszEPName); -#endif - } -} - -void MarshalStructViaILStub(MethodDesc* pStubMD, void* pManagedData, void* pNativeData, StructMarshalStubs::MarshalOperation operation, void** ppCleanupWorkList /* = nullptr */) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pStubMD)); - } - CONTRACTL_END; - - ARG_SLOT args[] = - { - PtrToArgSlot(pManagedData), - PtrToArgSlot(pNativeData), - (ARG_SLOT)operation, - PtrToArgSlot(ppCleanupWorkList) - }; - - MethodDescCallSite callSite(pStubMD); - - callSite.Call(args); -} - -void MarshalStructViaILStubCode(PCODE pStubCode, void* pManagedData, void* pNativeData, StructMarshalStubs::MarshalOperation operation, void** ppCleanupWorkList /* = nullptr */) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(pStubCode != NULL); - } - CONTRACTL_END; - - PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(pStubCode); - DECLARE_ARGHOLDER_ARRAY(args, 4); - args[ARGNUM_0] = PTR_TO_ARGHOLDER(pManagedData); - args[ARGNUM_1] = PTR_TO_ARGHOLDER(pNativeData); - args[ARGNUM_2] = DWORD_TO_ARGHOLDER(operation); - args[ARGNUM_3] = PTR_TO_ARGHOLDER(ppCleanupWorkList); - - CALL_MANAGED_METHOD_NORET(args); -} - - -//========================================================================== -// This function is reached only via NDirectImportThunk. It's purpose -// is to ensure that the target DLL is fully loaded and ready to run. -// -// FUN FACTS: Though this function is actually entered in unmanaged mode, -// it can reenter managed mode and throw a COM+ exception if the DLL linking -// fails. -//========================================================================== -EXTERN_C LPVOID STDCALL NDirectImportWorker(NDirectMethodDesc* pMD) -{ - LPVOID ret = NULL; - - BEGIN_PRESERVE_LAST_ERROR; - - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - INSTALL_MANAGED_EXCEPTION_DISPATCHER; - // this function is called by CLR to native assembly stubs which are called by - // managed code as a result, we need an unwind and continue handler to translate - // any of our internal exceptions into managed exceptions. - INSTALL_UNWIND_AND_CONTINUE_HANDLER; - - if (pMD->IsEarlyBound()) - { - if (!pMD->IsZapped()) - { - // we need the MD to be populated in case we decide to build an intercept - // stub to wrap the target in InitEarlyBoundNDirectTarget - PInvokeStaticSigInfo sigInfo; - NDirect::PopulateNDirectMethodDesc(pMD, &sigInfo); - } - - pMD->InitEarlyBoundNDirectTarget(); - } - else - { - // - // Otherwise we're in an inlined pinvoke late bound MD - // - INDEBUG(Thread *pThread = GetThread()); - { - _ASSERTE(pMD->ShouldSuppressGCTransition() - || pThread->GetFrame()->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr()); - - CONSISTENCY_CHECK(pMD->IsNDirect()); - // - // With IL stubs, we don't have to do anything but ensure the DLL is loaded. - // - - if (!pMD->IsZapped()) - { - PInvokeStaticSigInfo sigInfo; - NDirect::PopulateNDirectMethodDesc(pMD, &sigInfo); - } - else - { - // must have been populated at NGEN time - _ASSERTE(pMD->GetLibName() != NULL); - } - - pMD->CheckRestore(); - - NDirect::NDirectLink(pMD); - } - } - - ret = pMD->GetNDirectTarget(); - - UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; - UNINSTALL_MANAGED_EXCEPTION_DISPATCHER; - - END_PRESERVE_LAST_ERROR; - - return ret; -} - -//=========================================================================== -// Support for Pinvoke Calli instruction -// -//=========================================================================== +//=========================================================================== +// Support for Pinvoke Calli instruction +// +//=========================================================================== EXTERN_C void STDCALL VarargPInvokeStubWorker(TransitionBlock * pTransitionBlock, VASigCookie *pVASigCookie, MethodDesc *pMD) { @@ -6763,18 +6011,3 @@ PCODE GetILStubForCalli(VASigCookie *pVASigCookie, MethodDesc *pMD) #endif // CROSSGEN_COMPILE #endif // #ifndef DACCESS_COMPILE - -// -// Truncates a SString by first converting it to unicode and truncate it -// if it is larger than size. "..." will be appended if it is truncated. -// -void TruncateUnicodeString(SString &string, COUNT_T bufSize) -{ - string.Normalize(); - if ((string.GetCount() + 1) * sizeof(WCHAR) > bufSize) - { - _ASSERTE(bufSize / sizeof(WCHAR) > 4); - string.Truncate(string.Begin() + bufSize / sizeof(WCHAR) - 4); - string.Append(W("...")); - } -} diff --git a/src/coreclr/vm/dllimport.h b/src/coreclr/vm/dllimport.h index 74bd35c1530fa0..6625ec11ca7acd 100644 --- a/src/coreclr/vm/dllimport.h +++ b/src/coreclr/vm/dllimport.h @@ -9,10 +9,7 @@ #include "util.hpp" -class ILStubHashBlob; -class NDirectStubParameters; struct PInvokeStaticSigInfo; -class LoadLibErrorTracker; // This structure groups together data that describe the signature for which a marshaling stub is being generated. struct StubSigDesc @@ -58,8 +55,6 @@ struct StubSigDesc //======================================================================= class NDirect { - friend class NDirectMethodDesc; - public: //--------------------------------------------------------- // Does a class or method have a NAT_L CustomAttribute? @@ -70,17 +65,6 @@ class NDirect //--------------------------------------------------------- static HRESULT HasNAT_LAttribute(IMDInternalImport *pInternalImport, mdToken token, DWORD dwMemberAttrs); - static LPVOID NDirectGetEntryPoint(NDirectMethodDesc *pMD, NATIVE_LIBRARY_HANDLE hMod); - static NATIVE_LIBRARY_HANDLE LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError); - static NATIVE_LIBRARY_HANDLE LoadLibraryByName(LPCWSTR name, Assembly *callingAssembly, - BOOL hasDllImportSearchPathFlags, DWORD dllImportSearchPathFlags, - BOOL throwOnError); - static NATIVE_LIBRARY_HANDLE LoadNativeLibrary(NDirectMethodDesc * pMD, LoadLibErrorTracker *pErrorTracker); - static void FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle); - static INT_PTR GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError); - - static VOID NDirectLink(NDirectMethodDesc *pMD); - // Either MD or signature & module must be given. static BOOL MarshalingRequired( _In_opt_ MethodDesc* pMD, @@ -114,15 +98,9 @@ class NDirect MethodDesc* pMD); static MethodDesc* GetILStubMethodDesc(NDirectMethodDesc* pNMD, PInvokeStaticSigInfo* pSigInfo, DWORD dwNGenStubFlags); - static MethodDesc* GetStubMethodDesc(MethodDesc *pTargetMD, NDirectStubParameters* pParams, ILStubHashBlob* pHashParams, AllocMemTracker* pamTracker, bool& bILStubCreator, MethodDesc* pLastMD); - static void AddMethodDescChunkWithLockTaken(NDirectStubParameters* pParams, MethodDesc *pMD); - static void RemoveILStubCacheEntry(NDirectStubParameters* pParams, ILStubHashBlob* pHashParams); - static ILStubHashBlob* CreateHashBlob(NDirectStubParameters* pParams); static PCODE GetStubForILStub(NDirectMethodDesc* pNMD, MethodDesc** ppStubMD, DWORD dwStubFlags); static PCODE GetStubForILStub(MethodDesc* pMD, MethodDesc** ppStubMD, DWORD dwStubFlags); - inline static ILStubCache* GetILStubCache(NDirectStubParameters* pParams); - private: NDirect() {LIMITED_METHOD_CONTRACT;}; // prevent "new"'s on this class }; @@ -565,53 +543,6 @@ class NDirectStubLinker : public ILStubLinker BOOL HeuristicDoesThisLookLikeAGetLastErrorCall(LPBYTE pTarget); DWORD STDMETHODCALLTYPE FalseGetLastError(); -class NDirectStubParameters -{ -public: - - NDirectStubParameters(Signature sig, - SigTypeContext* pTypeContext, - Module* pModule, - Module* pLoaderModule, - CorNativeLinkType nlType, - CorNativeLinkFlags nlFlags, - CorInfoCallConvExtension unmgdCallConv, - DWORD dwStubFlags, // NDirectStubFlags - int nParamTokens, - mdParamDef* pParamTokenArray, - int iLCIDArg, - MethodTable* pMT - ) : - m_sig(sig), - m_pTypeContext(pTypeContext), - m_pModule(pModule), - m_pLoaderModule(pLoaderModule), - m_pParamTokenArray(pParamTokenArray), - m_unmgdCallConv(unmgdCallConv), - m_nlType(nlType), - m_nlFlags(nlFlags), - m_dwStubFlags(dwStubFlags), - m_iLCIDArg(iLCIDArg), - m_nParamTokens(nParamTokens), - m_pMT(pMT) - { - LIMITED_METHOD_CONTRACT; - } - - Signature m_sig; - SigTypeContext* m_pTypeContext; - Module* m_pModule; - Module* m_pLoaderModule; - mdParamDef* m_pParamTokenArray; - CorInfoCallConvExtension m_unmgdCallConv; - CorNativeLinkType m_nlType; - CorNativeLinkFlags m_nlFlags; - DWORD m_dwStubFlags; - int m_iLCIDArg; - int m_nParamTokens; - MethodTable* m_pMT; -}; - PCODE GetILStubForCalli(VASigCookie *pVASigCookie, MethodDesc *pMD); MethodDesc *GetStubMethodDescFromInteropMethodDesc(MethodDesc* pMD, DWORD dwStubFlags); @@ -636,103 +567,4 @@ void MarshalStructViaILStubCode(PCODE pStubCode, void* pManagedData, void* pNati #define ETW_IL_STUB_EVENT_STRING_FIELD_MAXSIZE (1024) #define ETW_IL_STUB_EVENT_CODE_STRING_FIELD_MAXSIZE (1024*32) -class SString; - -// -// Truncates a SString by first converting it to unicode and truncate it -// if it is larger than size. "..." will be appened if it is truncated. -// -void TruncateUnicodeString(SString &string, COUNT_T bufSize); - -//======================================================================= -// ILStubCreatorHelper -// The class is used as a helper class in CreateInteropILStub. It mainly -// puts two methods NDirect::GetStubMethodDesc and NDirect::RemoveILStubCacheEntry -// into a holder. See CreateInteropILStub for more information -//======================================================================= -class ILStubCreatorHelper -{ -public: - ILStubCreatorHelper(MethodDesc *pTargetMD, - NDirectStubParameters* pParams - ) : - m_pTargetMD(pTargetMD), - m_pParams(pParams), - m_pStubMD(NULL), - m_bILStubCreator(false) - { - STANDARD_VM_CONTRACT; - m_pHashParams = NDirect::CreateHashBlob(m_pParams); - } - - ~ILStubCreatorHelper() - { - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - RemoveILStubCacheEntry(); - } - - inline void GetStubMethodDesc() - { - WRAPPER_NO_CONTRACT; - - m_pStubMD = NDirect::GetStubMethodDesc(m_pTargetMD, m_pParams, m_pHashParams, &m_amTracker, m_bILStubCreator, m_pStubMD); - } - - inline void RemoveILStubCacheEntry() - { - WRAPPER_NO_CONTRACT; - - if (m_bILStubCreator) - { - NDirect::RemoveILStubCacheEntry(m_pParams, m_pHashParams); - m_bILStubCreator = false; - } - } - - inline MethodDesc* GetStubMD() - { - LIMITED_METHOD_CONTRACT; - return m_pStubMD; - } - - inline void SuppressRelease() - { - WRAPPER_NO_CONTRACT; - m_bILStubCreator = false; - m_amTracker.SuppressRelease(); - } - - DEBUG_NOINLINE static void HolderEnter(ILStubCreatorHelper *pThis) - { - WRAPPER_NO_CONTRACT; - ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT; - pThis->GetStubMethodDesc(); - } - - DEBUG_NOINLINE static void HolderLeave(ILStubCreatorHelper *pThis) - { - WRAPPER_NO_CONTRACT; - ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT; - pThis->RemoveILStubCacheEntry(); - } - -private: - MethodDesc* m_pTargetMD; - NDirectStubParameters* m_pParams; - NewArrayHolder m_pHashParams; - AllocMemTracker* m_pAmTracker; - MethodDesc* m_pStubMD; - AllocMemTracker m_amTracker; - bool m_bILStubCreator; // Only the creator can remove the ILStub from the Cache -}; //ILStubCreatorHelper - -typedef Wrapper ILStubCreatorHelperHolder; - #endif // __dllimport_h__ diff --git a/src/coreclr/vm/nativelibrary.cpp b/src/coreclr/vm/nativelibrary.cpp new file mode 100644 index 00000000000000..8a894f2a115f04 --- /dev/null +++ b/src/coreclr/vm/nativelibrary.cpp @@ -0,0 +1,909 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "common.h" +#include "nativelibrary.h" + +#include "clr/fs/path.h" +using namespace clr::fs; + +// Specifies whether hostpolicy is embedded in executable or standalone +extern bool g_hostpolicy_embedded; + +// remove when we get an updated SDK +#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100 + +#ifdef TARGET_UNIX +#define PLATFORM_SHARED_LIB_SUFFIX_W PAL_SHLIB_SUFFIX_W +#define PLATFORM_SHARED_LIB_PREFIX_W PAL_SHLIB_PREFIX_W +#else // !TARGET_UNIX +#define PLATFORM_SHARED_LIB_SUFFIX_W W(".dll") +#define PLATFORM_SHARED_LIB_PREFIX_W W("") +#endif // !TARGET_UNIX + +// The Bit 0x2 has different semantics in DllImportSearchPath and LoadLibraryExA flags. +// In DllImportSearchPath enum, bit 0x2 represents SearchAssemblyDirectory -- which is performed by CLR. +// Unlike other bits in this enum, this bit shouldn't be directly passed on to LoadLibrary() +#define DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY 0x2 + +namespace +{ + // Preserving good error info from DllImport-driven LoadLibrary is tricky because we keep loading from different places + // if earlier loads fail and those later loads obliterate error codes. + // + // This tracker object will keep track of the error code in accordance to priority: + // + // low-priority: unknown error code (should never happen) + // medium-priority: dll not found + // high-priority: dll found but error during loading + // + // We will overwrite the previous load's error code only if the new error code is higher priority. + // + class LoadLibErrorTracker + { + private: + static const DWORD const_priorityNotFound = 10; + static const DWORD const_priorityAccessDenied = 20; + static const DWORD const_priorityCouldNotLoad = 99999; + public: + LoadLibErrorTracker() + { + LIMITED_METHOD_CONTRACT; + m_hr = E_FAIL; + m_priorityOfLastError = 0; + } + + VOID TrackErrorCode() + { + LIMITED_METHOD_CONTRACT; + + DWORD priority; + +#ifdef TARGET_UNIX + SetMessage(PAL_GetLoadLibraryError()); +#else + DWORD dwLastError = GetLastError(); + + switch (dwLastError) + { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_MOD_NOT_FOUND: + case ERROR_DLL_NOT_FOUND: + priority = const_priorityNotFound; + break; + + // If we can't access a location, we can't know if the dll's there or if it's good. + // Still, this is probably more unusual (and thus of more interest) than a dll-not-found + // so give it an intermediate priority. + case ERROR_ACCESS_DENIED: + priority = const_priorityAccessDenied; + + // Assume all others are "dll found but couldn't load." + default: + priority = const_priorityCouldNotLoad; + break; + } + UpdateHR(priority, HRESULT_FROM_WIN32(dwLastError)); +#endif + } + + HRESULT GetHR() + { + return m_hr; + } + + SString& GetMessage() + { + return m_message; + } + + void DECLSPEC_NORETURN Throw(SString &libraryNameOrPath) + { + STANDARD_VM_CONTRACT; + +#if defined(__APPLE__) + COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_MAC, libraryNameOrPath.GetUnicode(), GetMessage()); +#elif defined(TARGET_UNIX) + COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_LINUX, libraryNameOrPath.GetUnicode(), GetMessage()); +#else // __APPLE__ + HRESULT theHRESULT = GetHR(); + if (theHRESULT == HRESULT_FROM_WIN32(ERROR_BAD_EXE_FORMAT)) + { + COMPlusThrow(kBadImageFormatException); + } + else + { + SString hrString; + GetHRMsg(theHRESULT, hrString); + COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_WIN, libraryNameOrPath.GetUnicode(), hrString); + } +#endif // TARGET_UNIX + + __UNREACHABLE(); + } + + private: + void UpdateHR(DWORD priority, HRESULT hr) + { + if (priority > m_priorityOfLastError) + { + m_hr = hr; + m_priorityOfLastError = priority; + } + } + + void SetMessage(LPCSTR message) + { + m_message = SString(SString::Utf8, message); + } + + HRESULT m_hr; + DWORD m_priorityOfLastError; + SString m_message; + }; // class LoadLibErrorTracker + + // Load the library directly and return the raw system handle + NATIVE_LIBRARY_HANDLE LocalLoadLibraryHelper( LPCWSTR name, DWORD flags, LoadLibErrorTracker *pErrorTracker ) + { + STANDARD_VM_CONTRACT; + + NATIVE_LIBRARY_HANDLE hmod = NULL; + +#ifndef TARGET_UNIX + if ((flags & 0xFFFFFF00) != 0) + { + hmod = CLRLoadLibraryEx(name, NULL, flags & 0xFFFFFF00); + if (hmod != NULL) + { + return hmod; + } + + DWORD dwLastError = GetLastError(); + if (dwLastError != ERROR_INVALID_PARAMETER) + { + pErrorTracker->TrackErrorCode(); + return hmod; + } + } + + hmod = CLRLoadLibraryEx(name, NULL, flags & 0xFF); + +#else // !TARGET_UNIX + hmod = PAL_LoadLibraryDirect(name); +#endif // !TARGET_UNIX + + if (hmod == NULL) + { + pErrorTracker->TrackErrorCode(); + } + + return hmod; + } + + // DllImportSearchPathFlags is a special enumeration, whose values are tied closely with LoadLibrary flags. + // There is no "default" value DllImportSearchPathFlags. In the absence of DllImportSearchPath attribute, + // CoreCLR's LoadLibrary implementation uses the following defaults. + // Other implementations of LoadLibrary callbacks/events are free to use other default conventions. + void GetDefaultDllImportSearchPathFlags(DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory) + { + STANDARD_VM_CONTRACT; + + *searchAssemblyDirectory = TRUE; + *dllImportSearchPathFlags = 0; + } + + // If a module has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and return true. + // Otherwise, get CoreCLR's default value for DllImportSearchPathFlags, and return false. + BOOL GetDllImportSearchPathFlags(Module *pModule, DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory) + { + STANDARD_VM_CONTRACT; + + if (pModule->HasDefaultDllImportSearchPathsAttribute()) + { + *dllImportSearchPathFlags = pModule->DefaultDllImportSearchPathsAttributeCachedValue(); + *searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory(); + return TRUE; + } + + GetDefaultDllImportSearchPathFlags(dllImportSearchPathFlags, searchAssemblyDirectory); + return FALSE; + } + + // If a pInvoke has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and returns true. + // Otherwise, if the containing assembly has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and returns true. + // Otherwise, get CoreCLR's default value for DllImportSearchPathFlags, and return false. + BOOL GetDllImportSearchPathFlags(NDirectMethodDesc * pMD, DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory) + { + STANDARD_VM_CONTRACT; + + if (pMD->HasDefaultDllImportSearchPathsAttribute()) + { + *dllImportSearchPathFlags = pMD->DefaultDllImportSearchPathsAttributeCachedValue(); + *searchAssemblyDirectory = pMD->DllImportSearchAssemblyDirectory(); + return TRUE; + } + + return GetDllImportSearchPathFlags(pMD->GetModule(), dllImportSearchPathFlags, searchAssemblyDirectory); + } +} + +// static +NATIVE_LIBRARY_HANDLE NativeLibrary::LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError) +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(libraryPath)); + } + CONTRACTL_END; + + LoadLibErrorTracker errorTracker; + const NATIVE_LIBRARY_HANDLE hmod = + LocalLoadLibraryHelper(libraryPath, GetLoadWithAlteredSearchPathFlag(), &errorTracker); + + if (throwOnError && (hmod == nullptr)) + { + SString libraryPathSString(libraryPath); + errorTracker.Throw(libraryPathSString); + } + return hmod; +} + +// static +void NativeLibrary::FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle) +{ + STANDARD_VM_CONTRACT; + _ASSERTE(handle != NULL); + +#ifndef TARGET_UNIX + BOOL retVal = FreeLibrary(handle); +#else // !TARGET_UNIX + BOOL retVal = PAL_FreeLibraryDirect(handle); +#endif // !TARGET_UNIX + + if (retVal == 0) + COMPlusThrow(kInvalidOperationException, W("Arg_InvalidOperationException")); +} + +//static +INT_PTR NativeLibrary::GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError) +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(handle)); + PRECONDITION(CheckPointer(symbolName)); + } + CONTRACTL_END; + + MAKE_UTF8PTR_FROMWIDE(lpstr, symbolName); + +#ifndef TARGET_UNIX + INT_PTR address = reinterpret_cast(GetProcAddress((HMODULE)handle, lpstr)); + if ((address == NULL) && throwOnError) + COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL, symbolName); +#else // !TARGET_UNIX + INT_PTR address = reinterpret_cast(PAL_GetProcAddressDirect(handle, lpstr)); + if ((address == NULL) && throwOnError) + COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO, symbolName); +#endif // !TARGET_UNIX + + return address; +} + +namespace +{ +#ifndef TARGET_UNIX + BOOL IsWindowsAPISet(PCWSTR wszLibName) + { + STANDARD_VM_CONTRACT; + + // This is replicating quick check from the OS implementation of api sets. + return SString::_wcsnicmp(wszLibName, W("api-"), 4) == 0 || + SString::_wcsnicmp(wszLibName, W("ext-"), 4) == 0; + } +#endif // !TARGET_UNIX + + NATIVE_LIBRARY_HANDLE LoadNativeLibraryViaAssemblyLoadContext(Assembly * pAssembly, PCWSTR wszLibName) + { + STANDARD_VM_CONTRACT; + +#ifndef TARGET_UNIX + if (IsWindowsAPISet(wszLibName)) + { + // Prevent Overriding of Windows API sets. + return NULL; + } +#endif // !TARGET_UNIX + + NATIVE_LIBRARY_HANDLE hmod = NULL; + AppDomain* pDomain = GetAppDomain(); + CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext(); + + PEFile *pManifestFile = pAssembly->GetManifestFile(); + PTR_ICLRPrivBinder pBindingContext = pManifestFile->GetBindingContext(); + + //Step 0: Check if the assembly was bound using TPA. + // The Binding Context can be null or an overridden TPA context + if (pBindingContext == NULL) + { + // If we do not have any binder associated, then return to the default resolution mechanism. + return NULL; + } + + UINT_PTR assemblyBinderID = 0; + IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID)); + + ICLRPrivBinder *pCurrentBinder = reinterpret_cast(assemblyBinderID); + + // For assemblies bound via TPA binder, we should use the standard mechanism to make the pinvoke call. + if (AreSameBinderInstance(pCurrentBinder, pTPABinder)) + { + return NULL; + } + + //Step 1: If the assembly was not bound using TPA, + // Call System.Runtime.Loader.AssemblyLoadContext.ResolveUnmanagedDll to give + // The custom assembly context a chance to load the unmanaged dll. + + GCX_COOP(); + + STRINGREF pUnmanagedDllName; + pUnmanagedDllName = StringObject::NewString(wszLibName); + + GCPROTECT_BEGIN(pUnmanagedDllName); + + // Get the pointer to the managed assembly load context + INT_PTR ptrManagedAssemblyLoadContext = ((CLRPrivBinderAssemblyLoadContext *)pCurrentBinder)->GetManagedAssemblyLoadContext(); + + // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.ResolveUnmanagedDll method. + PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUNMANAGEDDLL); + DECLARE_ARGHOLDER_ARRAY(args, 2); + args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(pUnmanagedDllName); + args[ARGNUM_1] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext); + + // Make the call + CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args); + + GCPROTECT_END(); + + return hmod; + } + + // Return the AssemblyLoadContext for an assembly + INT_PTR GetManagedAssemblyLoadContext(Assembly* pAssembly) + { + STANDARD_VM_CONTRACT; + + PTR_ICLRPrivBinder pBindingContext = pAssembly->GetManifestFile()->GetBindingContext(); + if (pBindingContext == NULL) + { + // GetBindingContext() returns NULL for System.Private.CoreLib + return NULL; + } + + UINT_PTR assemblyBinderID = 0; + IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID)); + + AppDomain *pDomain = GetAppDomain(); + ICLRPrivBinder *pCurrentBinder = reinterpret_cast(assemblyBinderID); + + // The code here deals with two implementations of ICLRPrivBinder interface: + // - CLRPrivBinderCoreCLR for the TPA binder in the default ALC, and + // - CLRPrivBinderAssemblyLoadContext for custom ALCs. + // in order obtain the associated ALC handle. + INT_PTR ptrManagedAssemblyLoadContext = AreSameBinderInstance(pCurrentBinder, pDomain->GetTPABinderContext()) + ? ((CLRPrivBinderCoreCLR *)pCurrentBinder)->GetManagedAssemblyLoadContext() + : ((CLRPrivBinderAssemblyLoadContext *)pCurrentBinder)->GetManagedAssemblyLoadContext(); + + return ptrManagedAssemblyLoadContext; + } + + NATIVE_LIBRARY_HANDLE LoadNativeLibraryViaAssemblyLoadContextEvent(Assembly * pAssembly, PCWSTR wszLibName) + { + STANDARD_VM_CONTRACT; + + INT_PTR ptrManagedAssemblyLoadContext = GetManagedAssemblyLoadContext(pAssembly); + if (ptrManagedAssemblyLoadContext == NULL) + { + return NULL; + } + + NATIVE_LIBRARY_HANDLE hmod = NULL; + + GCX_COOP(); + + struct { + STRINGREF DllName; + OBJECTREF AssemblyRef; + } gc = { NULL, NULL }; + + GCPROTECT_BEGIN(gc); + + gc.DllName = StringObject::NewString(wszLibName); + gc.AssemblyRef = pAssembly->GetExposedObject(); + + // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.ResolveUnmanagedDllUsingEvent method + // While ResolveUnmanagedDllUsingEvent() could compute the AssemblyLoadContext using the AssemblyRef + // argument, it will involve another pInvoke to the runtime. So AssemblyLoadContext is passed in + // as an additional argument. + PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUNMANAGEDDLLUSINGEVENT); + DECLARE_ARGHOLDER_ARRAY(args, 3); + args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(gc.DllName); + args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.AssemblyRef); + args[ARGNUM_2] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext); + + // Make the call + CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args); + + GCPROTECT_END(); + + return hmod; + } + + NATIVE_LIBRARY_HANDLE LoadNativeLibraryViaDllImportResolver(NDirectMethodDesc * pMD, LPCWSTR wszLibName) + { + STANDARD_VM_CONTRACT; + + if (pMD->GetModule()->IsSystem()) + { + // Don't attempt to callback on Corelib itself. + // The LoadLibrary callback stub is managed code that requires CoreLib + return NULL; + } + + DWORD dllImportSearchPathFlags; + BOOL searchAssemblyDirectory; + BOOL hasDllImportSearchPathFlags = GetDllImportSearchPathFlags(pMD, &dllImportSearchPathFlags, &searchAssemblyDirectory); + dllImportSearchPathFlags |= searchAssemblyDirectory ? DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY : 0; + + Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly(); + NATIVE_LIBRARY_HANDLE handle = NULL; + + GCX_COOP(); + + struct { + STRINGREF libNameRef; + OBJECTREF assemblyRef; + } gc = { NULL, NULL }; + + GCPROTECT_BEGIN(gc); + + gc.libNameRef = StringObject::NewString(wszLibName); + gc.assemblyRef = pAssembly->GetExposedObject(); + + PREPARE_NONVIRTUAL_CALLSITE(METHOD__NATIVELIBRARY__LOADLIBRARYCALLBACKSTUB); + DECLARE_ARGHOLDER_ARRAY(args, 4); + args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(gc.libNameRef); + args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.assemblyRef); + args[ARGNUM_2] = BOOL_TO_ARGHOLDER(hasDllImportSearchPathFlags); + args[ARGNUM_3] = DWORD_TO_ARGHOLDER(dllImportSearchPathFlags); + + // Make the call + CALL_MANAGED_METHOD(handle, NATIVE_LIBRARY_HANDLE, args); + GCPROTECT_END(); + + return handle; + } + + // Try to load the module alongside the assembly where the PInvoke was declared. + NATIVE_LIBRARY_HANDLE LoadFromPInvokeAssemblyDirectory(Assembly *pAssembly, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker) + { + STANDARD_VM_CONTRACT; + + NATIVE_LIBRARY_HANDLE hmod = NULL; + + SString path = pAssembly->GetManifestFile()->GetPath(); + + SString::Iterator lastPathSeparatorIter = path.End(); + if (PEAssembly::FindLastPathSeparator(path, lastPathSeparatorIter)) + { + lastPathSeparatorIter++; + path.Truncate(lastPathSeparatorIter); + + path.Append(libName); + hmod = LocalLoadLibraryHelper(path, flags, pErrorTracker); + } + + return hmod; + } + + // Try to load the module from the native DLL search directories + NATIVE_LIBRARY_HANDLE LoadFromNativeDllSearchDirectories(LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker) + { + STANDARD_VM_CONTRACT; + + NATIVE_LIBRARY_HANDLE hmod = NULL; + AppDomain* pDomain = GetAppDomain(); + + if (pDomain->HasNativeDllSearchDirectories()) + { + AppDomain::PathIterator pathIter = pDomain->IterateNativeDllSearchDirectories(); + while (hmod == NULL && pathIter.Next()) + { + SString qualifiedPath(*(pathIter.GetPath())); + qualifiedPath.Append(libName); + if (!Path::IsRelative(qualifiedPath)) + { + hmod = LocalLoadLibraryHelper(qualifiedPath, flags, pErrorTracker); + } + } + } + + return hmod; + } + +#ifdef TARGET_UNIX + const int MaxVariationCount = 4; + void DetermineLibNameVariations(const WCHAR** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath) + { + // Supported lib name variations + static auto NameFmt = W("%.0s%s%.0s"); + static auto PrefixNameFmt = W("%s%s%.0s"); + static auto NameSuffixFmt = W("%.0s%s%s"); + static auto PrefixNameSuffixFmt = W("%s%s%s"); + + _ASSERTE(*numberOfVariations >= MaxVariationCount); + + int varCount = 0; + if (!libNameIsRelativePath) + { + libNameVariations[varCount++] = NameFmt; + } + else + { + // We check if the suffix is contained in the name, because on Linux it is common to append + // a version number to the library name (e.g. 'libicuuc.so.57'). + bool containsSuffix = false; + SString::CIterator it = libName.Begin(); + if (libName.Find(it, PLATFORM_SHARED_LIB_SUFFIX_W)) + { + it += COUNTOF(PLATFORM_SHARED_LIB_SUFFIX_W); + containsSuffix = it == libName.End() || *it == (WCHAR)'.'; + } + + // If the path contains a path delimiter, we don't add a prefix + it = libName.Begin(); + bool containsDelim = libName.Find(it, DIRECTORY_SEPARATOR_STR_W); + + if (containsSuffix) + { + libNameVariations[varCount++] = NameFmt; + + if (!containsDelim) + libNameVariations[varCount++] = PrefixNameFmt; + + libNameVariations[varCount++] = NameSuffixFmt; + + if (!containsDelim) + libNameVariations[varCount++] = PrefixNameSuffixFmt; + } + else + { + libNameVariations[varCount++] = NameSuffixFmt; + + if (!containsDelim) + libNameVariations[varCount++] = PrefixNameSuffixFmt; + + libNameVariations[varCount++] = NameFmt; + + if (!containsDelim) + libNameVariations[varCount++] = PrefixNameFmt; + } + } + + *numberOfVariations = varCount; + } +#else // TARGET_UNIX + const int MaxVariationCount = 2; + void DetermineLibNameVariations(const WCHAR** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath) + { + // Supported lib name variations + static auto NameFmt = W("%.0s%s%.0s"); + static auto NameSuffixFmt = W("%.0s%s%s"); + + _ASSERTE(*numberOfVariations >= MaxVariationCount); + + int varCount = 0; + + // The purpose of following code is to workaround LoadLibrary limitation: + // LoadLibrary won't append extension if filename itself contains '.'. Thus it will break the following scenario: + // [DllImport("A.B")] // The full name for file is "A.B.dll". This is common code pattern for cross-platform PInvoke + // The workaround for above scenario is to call LoadLibrary with "A.B" first, if it fails, then call LoadLibrary with "A.B.dll" + auto it = libName.Begin(); + if (!libNameIsRelativePath || + !libName.Find(it, W('.')) || + libName.EndsWith(W(".")) || + libName.EndsWithCaseInsensitive(W(".dll")) || + libName.EndsWithCaseInsensitive(W(".exe"))) + { + // Follow LoadLibrary rules in MSDN doc: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684175(v=vs.85).aspx + // If the string specifies a full path, the function searches only that path for the module. + // If the string specifies a module name without a path and the file name extension is omitted, the function appends the default library extension .dll to the module name. + // To prevent the function from appending .dll to the module name, include a trailing point character (.) in the module name string. + libNameVariations[varCount++] = NameFmt; + } + else + { + libNameVariations[varCount++] = NameFmt; + libNameVariations[varCount++] = NameSuffixFmt; + } + + *numberOfVariations = varCount; + } +#endif // TARGET_UNIX + + // Search for the library and variants of its name in probing directories. + NATIVE_LIBRARY_HANDLE LoadNativeLibraryBySearch(Assembly *callingAssembly, + BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlags, + LoadLibErrorTracker * pErrorTracker, LPCWSTR wszLibName) + { + STANDARD_VM_CONTRACT; + + NATIVE_LIBRARY_HANDLE hmod = NULL; + +#if defined(FEATURE_CORESYSTEM) && !defined(TARGET_UNIX) + // Try to go straight to System32 for Windows API sets. This is replicating quick check from + // the OS implementation of api sets. + if (IsWindowsAPISet(wszLibName)) + { + hmod = LocalLoadLibraryHelper(wszLibName, LOAD_LIBRARY_SEARCH_SYSTEM32, pErrorTracker); + if (hmod != NULL) + { + return hmod; + } + } +#endif // FEATURE_CORESYSTEM && !TARGET_UNIX + + if (g_hostpolicy_embedded) + { +#ifdef TARGET_WINDOWS + if (wcscmp(wszLibName, W("hostpolicy.dll")) == 0) + { + return WszGetModuleHandle(NULL); + } +#else + if (wcscmp(wszLibName, W("libhostpolicy")) == 0) + { + return PAL_LoadLibraryDirect(NULL); + } +#endif + } + + AppDomain* pDomain = GetAppDomain(); + DWORD loadWithAlteredPathFlags = GetLoadWithAlteredSearchPathFlag(); + bool libNameIsRelativePath = Path::IsRelative(wszLibName); + + // P/Invokes are often declared with variations on the actual library name. + // For example, it's common to leave off the extension/suffix of the library + // even if it has one, or to leave off a prefix like "lib" even if it has one + // (both of these are typically done to smooth over cross-platform differences). + // We try to dlopen with such variations on the original. + const WCHAR* prefixSuffixCombinations[MaxVariationCount] = {}; + int numberOfVariations = COUNTOF(prefixSuffixCombinations); + DetermineLibNameVariations(prefixSuffixCombinations, &numberOfVariations, wszLibName, libNameIsRelativePath); + for (int i = 0; i < numberOfVariations; i++) + { + SString currLibNameVariation; + currLibNameVariation.Printf(prefixSuffixCombinations[i], PLATFORM_SHARED_LIB_PREFIX_W, wszLibName, PLATFORM_SHARED_LIB_SUFFIX_W); + + // NATIVE_DLL_SEARCH_DIRECTORIES set by host is considered well known path + hmod = LoadFromNativeDllSearchDirectories(currLibNameVariation, loadWithAlteredPathFlags, pErrorTracker); + if (hmod != NULL) + { + return hmod; + } + + if (!libNameIsRelativePath) + { + DWORD flags = loadWithAlteredPathFlags; + if ((dllImportSearchPathFlags & LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR) != 0) + { + // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR is the only flag affecting absolute path. Don't OR the flags + // unconditionally as all absolute path P/Invokes could then lose LOAD_WITH_ALTERED_SEARCH_PATH. + flags |= dllImportSearchPathFlags; + } + + hmod = LocalLoadLibraryHelper(currLibNameVariation, flags, pErrorTracker); + if (hmod != NULL) + { + return hmod; + } + } + else if ((callingAssembly != nullptr) && searchAssemblyDirectory) + { + hmod = LoadFromPInvokeAssemblyDirectory(callingAssembly, currLibNameVariation, loadWithAlteredPathFlags | dllImportSearchPathFlags, pErrorTracker); + if (hmod != NULL) + { + return hmod; + } + } + + hmod = LocalLoadLibraryHelper(currLibNameVariation, dllImportSearchPathFlags, pErrorTracker); + if (hmod != NULL) + { + return hmod; + } + } + + // This may be an assembly name + // Format is "fileName, assemblyDisplayName" + MAKE_UTF8PTR_FROMWIDE(szLibName, wszLibName); + char *szComma = strchr(szLibName, ','); + if (szComma) + { + *szComma = '\0'; + // Trim white spaces + while (COMCharacter::nativeIsWhiteSpace(*(++szComma))); + + AssemblySpec spec; + if (SUCCEEDED(spec.Init(szComma))) + { + // Need to perform case insensitive hashing. + SString moduleName(SString::Utf8, szLibName); + moduleName.LowerCase(); + + StackScratchBuffer buffer; + szLibName = (LPSTR)moduleName.GetUTF8(buffer); + + Assembly *pAssembly = spec.LoadAssembly(FILE_LOADED); + Module *pModule = pAssembly->FindModuleByName(szLibName); + + hmod = LocalLoadLibraryHelper(pModule->GetPath(), loadWithAlteredPathFlags | dllImportSearchPathFlags, pErrorTracker); + } + } + + return hmod; + } + + NATIVE_LIBRARY_HANDLE LoadNativeLibraryBySearch(NDirectMethodDesc *pMD, LoadLibErrorTracker *pErrorTracker, PCWSTR wszLibName) + { + STANDARD_VM_CONTRACT; + + BOOL searchAssemblyDirectory; + DWORD dllImportSearchPathFlags; + + GetDllImportSearchPathFlags(pMD, &dllImportSearchPathFlags, &searchAssemblyDirectory); + + Assembly *pAssembly = pMD->GetMethodTable()->GetAssembly(); + return LoadNativeLibraryBySearch(pAssembly, searchAssemblyDirectory, dllImportSearchPathFlags, pErrorTracker, wszLibName); + } +} + +// static +NATIVE_LIBRARY_HANDLE NativeLibrary::LoadLibraryByName(LPCWSTR libraryName, Assembly *callingAssembly, + BOOL hasDllImportSearchFlags, DWORD dllImportSearchFlags, + BOOL throwOnError) +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(libraryName)); + PRECONDITION(CheckPointer(callingAssembly)); + } + CONTRACTL_END; + + NATIVE_LIBRARY_HANDLE hmod = nullptr; + + // Resolve using the AssemblyLoadContext.LoadUnmanagedDll implementation + hmod = LoadNativeLibraryViaAssemblyLoadContext(callingAssembly, libraryName); + if (hmod != nullptr) + return hmod; + + // Check if a default dllImportSearchPathFlags was passed in. If so, use that value. + // Otherwise, check if the assembly has the DefaultDllImportSearchPathsAttribute attribute. + // If so, use that value. + BOOL searchAssemblyDirectory; + DWORD dllImportSearchPathFlags; + if (hasDllImportSearchFlags) + { + dllImportSearchPathFlags = dllImportSearchFlags & ~DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY; + searchAssemblyDirectory = dllImportSearchFlags & DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY; + + } + else + { + GetDllImportSearchPathFlags(callingAssembly->GetManifestModule(), + &dllImportSearchPathFlags, &searchAssemblyDirectory); + } + + LoadLibErrorTracker errorTracker; + hmod = LoadNativeLibraryBySearch(callingAssembly, searchAssemblyDirectory, dllImportSearchPathFlags, &errorTracker, libraryName); + if (hmod != nullptr) + return hmod; + + // Resolve using the AssemblyLoadContext.ResolvingUnmanagedDll event + hmod = LoadNativeLibraryViaAssemblyLoadContextEvent(callingAssembly, libraryName); + if (hmod != nullptr) + return hmod; + + if (throwOnError) + { + SString libraryPathSString(libraryName); + errorTracker.Throw(libraryPathSString); + } + + return hmod; +} + +namespace +{ + NATIVE_LIBRARY_HANDLE LoadNativeLibrary(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker) + { + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION( CheckPointer( pMD ) ); + } + CONTRACTL_END; + + LPCUTF8 name = pMD->GetLibName(); + if ( !name || !*name ) + return NULL; + + PREFIX_ASSUME( name != NULL ); + MAKE_WIDEPTR_FROMUTF8( wszLibName, name ); + + NativeLibraryHandleHolder hmod = LoadNativeLibraryViaDllImportResolver(pMD, wszLibName); + if (hmod != NULL) + { + return hmod.Extract(); + } + + AppDomain* pDomain = GetAppDomain(); + Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly(); + + hmod = LoadNativeLibraryViaAssemblyLoadContext(pAssembly, wszLibName); + if (hmod != NULL) + { + return hmod.Extract(); + } + + hmod = pDomain->FindUnmanagedImageInCache(wszLibName); + if (hmod != NULL) + { + return hmod.Extract(); + } + + hmod = LoadNativeLibraryBySearch(pMD, pErrorTracker, wszLibName); + if (hmod != NULL) + { + // If we have a handle add it to the cache. + pDomain->AddUnmanagedImageToCache(wszLibName, hmod); + return hmod.Extract(); + } + + hmod = LoadNativeLibraryViaAssemblyLoadContextEvent(pAssembly, wszLibName); + if (hmod != NULL) + { + return hmod.Extract(); + } + + return hmod.Extract(); + } +} + +NATIVE_LIBRARY_HANDLE NativeLibrary::LoadLibraryFromMethodDesc(NDirectMethodDesc * pMD) +{ + CONTRACT(NATIVE_LIBRARY_HANDLE) + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(pMD)); + POSTCONDITION(RETVAL != NULL); + } + CONTRACT_END; + + LoadLibErrorTracker errorTracker; + NATIVE_LIBRARY_HANDLE hmod = LoadNativeLibrary(pMD, &errorTracker); + if (hmod == NULL) + { + if (pMD->GetLibName() == NULL) + COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_NONAME); + + StackSString ssLibName(SString::Utf8, pMD->GetLibName()); + errorTracker.Throw(ssLibName); + } + + RETURN hmod; +} diff --git a/src/coreclr/vm/nativelibrary.h b/src/coreclr/vm/nativelibrary.h new file mode 100644 index 00000000000000..f05146862c285d --- /dev/null +++ b/src/coreclr/vm/nativelibrary.h @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _NATIVELIBRARY_H_ +#define _NATIVELIBRARY_H_ + +#include + +class NativeLibrary +{ +public: + static NATIVE_LIBRARY_HANDLE LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError); + static NATIVE_LIBRARY_HANDLE LoadLibraryByName(LPCWSTR name, Assembly *callingAssembly, + BOOL hasDllImportSearchPathFlags, DWORD dllImportSearchPathFlags, + BOOL throwOnError); + static void FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle); + static INT_PTR GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError); + + static NATIVE_LIBRARY_HANDLE LoadLibraryFromMethodDesc(NDirectMethodDesc *pMD); +}; + +#endif // _NATIVELIBRARY_H_ diff --git a/src/coreclr/vm/nativelibrarynative.cpp b/src/coreclr/vm/nativelibrarynative.cpp index d059a3f4a9b9c7..f2c26e0d468e0f 100644 --- a/src/coreclr/vm/nativelibrarynative.cpp +++ b/src/coreclr/vm/nativelibrarynative.cpp @@ -5,7 +5,7 @@ // #include "common.h" -#include "dllimport.h" +#include "nativelibrary.h" #include "nativelibrarynative.h" // static @@ -17,7 +17,7 @@ INT_PTR QCALLTYPE NativeLibraryNative::LoadFromPath(LPCWSTR path, BOOL throwOnEr BEGIN_QCALL; - handle = NDirect::LoadLibraryFromPath(path, throwOnError); + handle = NativeLibrary::LoadLibraryFromPath(path, throwOnError); END_QCALL; @@ -36,7 +36,7 @@ INT_PTR QCALLTYPE NativeLibraryNative::LoadByName(LPCWSTR name, QCall::AssemblyH BEGIN_QCALL; - handle = NDirect::LoadLibraryByName(name, pAssembly, hasDllImportSearchPathFlag, dllImportSearchPathFlag, throwOnError); + handle = NativeLibrary::LoadLibraryByName(name, pAssembly, hasDllImportSearchPathFlag, dllImportSearchPathFlag, throwOnError); END_QCALL; @@ -50,7 +50,7 @@ void QCALLTYPE NativeLibraryNative::FreeLib(INT_PTR handle) BEGIN_QCALL; - NDirect::FreeNativeLibrary((NATIVE_LIBRARY_HANDLE) handle); + NativeLibrary::FreeNativeLibrary((NATIVE_LIBRARY_HANDLE) handle); END_QCALL; } @@ -64,7 +64,7 @@ INT_PTR QCALLTYPE NativeLibraryNative::GetSymbol(INT_PTR handle, LPCWSTR symbolN BEGIN_QCALL; - address = NDirect::GetNativeLibraryExport((NATIVE_LIBRARY_HANDLE)handle, symbolName, throwOnError); + address = NativeLibrary::GetNativeLibraryExport((NATIVE_LIBRARY_HANDLE)handle, symbolName, throwOnError); END_QCALL; From 209a9b1c1d3022f9178d74be3e0850f41f3f26f5 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 14 Apr 2021 08:08:57 -0400 Subject: [PATCH 11/11] [mono] Add a comment about freeing caches. (#51223) --- src/mono/mono/metadata/memory-manager.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mono/mono/metadata/memory-manager.c b/src/mono/mono/metadata/memory-manager.c index a1d235af6abbbe..77904a520d7e41 100644 --- a/src/mono/mono/metadata/memory-manager.c +++ b/src/mono/mono/metadata/memory-manager.c @@ -179,6 +179,8 @@ memory_manager_delete (MonoMemoryManager *memory_manager, gboolean debug_unload) mono_coop_mutex_destroy (&memory_manager->lock); + // FIXME: Free generics caches + if (debug_unload) { mono_mempool_invalidate (memory_manager->_mp); mono_code_manager_invalidate (memory_manager->code_mp);