From: Ryan Lucia Date: Fri, 8 Nov 2019 18:42:56 +0000 (-0500) Subject: [loader] Unmanaged library refactoring and NativeLibrary implementation (mono/mono... X-Git-Tag: submit/tizen/20210909.063632~10331^2~5^2~216 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e20da52cb85f5e6b5622242b0360f7893d63260a;p=platform%2Fupstream%2Fdotnet%2Fruntime.git [loader] Unmanaged library refactoring and NativeLibrary implementation (mono/mono#17369) Commit migrated from https://github.com/mono/mono/commit/d5b6cf33b3d2a89189be4e6bddcf0207e9887ecd --- diff --git a/src/mono/mono/eglib/eglib-remap.h b/src/mono/mono/eglib/eglib-remap.h index 0bbbcab..f261003 100644 --- a/src/mono/mono/eglib/eglib-remap.h +++ b/src/mono/mono/eglib/eglib-remap.h @@ -68,6 +68,7 @@ #define g_hash_table_foreach_steal monoeg_g_hash_table_foreach_steal #define g_hash_table_get_keys monoeg_g_hash_table_get_keys #define g_hash_table_get_values monoeg_g_hash_table_get_values +#define g_hash_table_contains monoeg_g_hash_table_contains #define g_hash_table_insert_replace monoeg_g_hash_table_insert_replace #define g_hash_table_lookup monoeg_g_hash_table_lookup #define g_hash_table_lookup_extended monoeg_g_hash_table_lookup_extended diff --git a/src/mono/mono/eglib/ghashtable.c b/src/mono/mono/eglib/ghashtable.c index 03ae92e..5329fab 100644 --- a/src/mono/mono/eglib/ghashtable.c +++ b/src/mono/mono/eglib/ghashtable.c @@ -305,6 +305,14 @@ g_hash_table_size (GHashTable *hash) return hash->in_use; } +gboolean +g_hash_table_contains (GHashTable *hash, gconstpointer key) +{ + g_return_val_if_fail (key != NULL, FALSE); + + return g_hash_table_lookup_extended (hash, key, NULL, NULL); +} + gpointer g_hash_table_lookup (GHashTable *hash, gconstpointer key) { diff --git a/src/mono/mono/eglib/glib.h b/src/mono/mono/eglib/glib.h index 3e21c3c..d5ceb66 100644 --- a/src/mono/mono/eglib/glib.h +++ b/src/mono/mono/eglib/glib.h @@ -623,6 +623,7 @@ void g_hash_table_insert_replace (GHashTable *hash, gpointer key, gp guint g_hash_table_size (GHashTable *hash); GList *g_hash_table_get_keys (GHashTable *hash); GList *g_hash_table_get_values (GHashTable *hash); +gboolean g_hash_table_contains (GHashTable *hash, gconstpointer key); G_EXTERN_C // Used by MonoPosixHelper or MonoSupportW, at least. gpointer g_hash_table_lookup (GHashTable *hash, gconstpointer key); gboolean g_hash_table_lookup_extended (GHashTable *hash, gconstpointer key, gpointer *orig_key, gpointer *value); @@ -646,6 +647,7 @@ guint g_spaced_primes_closest (guint x); #define g_hash_table_insert(h,k,v) g_hash_table_insert_replace ((h),(k),(v),FALSE) #define g_hash_table_replace(h,k,v) g_hash_table_insert_replace ((h),(k),(v),TRUE) +#define g_hash_table_add(h,k) g_hash_table_insert_replace ((h),(k),(k),TRUE) G_EXTERN_C // Used by MonoPosixHelper or MonoSupportW, at least. gboolean g_direct_equal (gconstpointer v1, gconstpointer v2); diff --git a/src/mono/mono/metadata/assembly-load-context.c b/src/mono/mono/metadata/assembly-load-context.c index 7c972cf..708293c 100644 --- a/src/mono/mono/metadata/assembly-load-context.c +++ b/src/mono/mono/metadata/assembly-load-context.c @@ -22,7 +22,9 @@ mono_alc_init (MonoAssemblyLoadContext *alc, MonoDomain *domain) alc->domain = domain; alc->loaded_images = li; alc->loaded_assemblies = NULL; + alc->pinvoke_scopes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); mono_coop_mutex_init (&alc->assemblies_lock); + mono_coop_mutex_init (&alc->pinvoke_lock); } void @@ -53,7 +55,9 @@ mono_alc_cleanup (MonoAssemblyLoadContext *alc) */ alc->loaded_assemblies = NULL; + g_hash_table_destroy (alc->pinvoke_scopes); mono_coop_mutex_destroy (&alc->assemblies_lock); + mono_coop_mutex_destroy (&alc->pinvoke_lock); mono_loaded_images_free (alc->loaded_images); diff --git a/src/mono/mono/metadata/class-internals.h b/src/mono/mono/metadata/class-internals.h index 294d6f4..92bdc67 100644 --- a/src/mono/mono/metadata/class-internals.h +++ b/src/mono/mono/metadata/class-internals.h @@ -1059,6 +1059,7 @@ GENERATE_TRY_GET_CLASS_WITH_CACHE_DECL(handleref) #ifdef ENABLE_NETCORE GENERATE_GET_CLASS_WITH_CACHE_DECL (assembly_load_context) +GENERATE_GET_CLASS_WITH_CACHE_DECL (native_library) #endif /* If you need a MonoType, use one of the mono_get_*_type () functions in class-inlines.h */ diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h index 5656c57..145c443 100644 --- a/src/mono/mono/metadata/icall-def-netcore.h +++ b/src/mono/mono/metadata/icall-def-netcore.h @@ -368,6 +368,12 @@ HANDLES(MARSHAL_34, "StructureToPtr", ves_icall_System_Runtime_InteropServices_M HANDLES(MARSHAL_41, "copy_from_unmanaged_fixed", ves_icall_System_Runtime_InteropServices_Marshal_copy_from_unmanaged, void, 5, (gconstpointer, gint32, MonoArray, gint32, gpointer)) HANDLES(MARSHAL_42, "copy_to_unmanaged_fixed", ves_icall_System_Runtime_InteropServices_Marshal_copy_to_unmanaged, void, 5, (MonoArray, gint32, gpointer, gint32, gconstpointer)) +ICALL_TYPE(NATIVEL, "System.Runtime.InteropServices.NativeLibrary", NATIVEL_1) +HANDLES(NATIVEL_1, "FreeLib", ves_icall_System_Runtime_InteropServices_NativeLibrary_FreeLib, void, 1, (gpointer)) +HANDLES(NATIVEL_2, "GetSymbol", ves_icall_System_Runtime_InteropServices_NativeLibrary_GetSymbol, gpointer, 3, (gpointer, MonoString, MonoBoolean)) +HANDLES(NATIVEL_3, "LoadByName", ves_icall_System_Runtime_InteropServices_NativeLibrary_LoadByName, gpointer, 5, (MonoString, MonoReflectionAssembly, MonoBoolean, guint32, MonoBoolean)) +HANDLES(NATIVEL_4, "LoadFromPath", ves_icall_System_Runtime_InteropServices_NativeLibrary_LoadFromPath, gpointer, 2, (MonoString, MonoBoolean)) + ICALL_TYPE(ALC, "System.Runtime.Loader.AssemblyLoadContext", ALC_5) HANDLES(ALC_5, "GetLoadContextForAssembly", ves_icall_System_Runtime_Loader_AssemblyLoadContext_GetLoadContextForAssembly, gpointer, 1, (MonoReflectionAssembly)) HANDLES(ALC_4, "InternalGetLoadedAssemblies", ves_icall_System_Runtime_Loader_AssemblyLoadContext_InternalGetLoadedAssemblies, MonoArray, 0, ()) diff --git a/src/mono/mono/metadata/image.c b/src/mono/mono/metadata/image.c index 9e05d37..8a9f688 100644 --- a/src/mono/mono/metadata/image.c +++ b/src/mono/mono/metadata/image.c @@ -2441,8 +2441,9 @@ mono_image_close_except_pools (MonoImage *image) if (image->mvar_gparam_cache) mono_conc_hashtable_destroy (image->mvar_gparam_cache); free_hash (image->wrapper_param_names); +#ifndef ENABLE_NETCORE free_hash (image->pinvoke_scopes); - free_hash (image->pinvoke_scope_filenames); +#endif free_hash (image->native_func_wrapper_cache); mono_conc_hashtable_destroy (image->typespec_cache); free_hash (image->weak_field_indexes); diff --git a/src/mono/mono/metadata/loader-internals.h b/src/mono/mono/metadata/loader-internals.h index c6ae068..bd2f343 100644 --- a/src/mono/mono/metadata/loader-internals.h +++ b/src/mono/mono/metadata/loader-internals.h @@ -38,6 +38,11 @@ struct _MonoAssemblyLoadContext { * collectible, the handle is weak, otherwise it's strong. */ uint32_t gchandle; + + // Used in native-library.c for the hash table below; do not access anywhere else + MonoCoopMutex pinvoke_lock; + // Maps malloc-ed char* pinvoke scope -> MonoDl* + GHashTable *pinvoke_scopes; }; #endif /* ENABLE_NETCORE */ @@ -59,7 +64,10 @@ mono_global_dllmap_cleanup (void); #endif void -mono_cached_module_cleanup (void); +mono_global_loader_cache_init (void); + +void +mono_global_loader_cache_cleanup (void); #ifdef ENABLE_NETCORE void diff --git a/src/mono/mono/metadata/loader.c b/src/mono/mono/metadata/loader.c index 499fa7e..9de71c41 100644 --- a/src/mono/mono/metadata/loader.c +++ b/src/mono/mono/metadata/loader.c @@ -88,6 +88,8 @@ mono_loader_init () mono_os_mutex_init_recursive (&global_loader_data_mutex); loader_lock_inited = TRUE; + mono_global_loader_cache_init (); + mono_native_tls_alloc (&loader_lock_nest_id, NULL); mono_counters_init (); @@ -110,7 +112,7 @@ mono_loader_cleanup (void) #ifndef DISABLE_DLLMAP mono_global_dllmap_cleanup (); #endif - mono_cached_module_cleanup (); + mono_global_loader_cache_cleanup (); mono_native_tls_free (loader_lock_nest_id); diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index 15f66bf..d9a9b3e 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -562,11 +562,10 @@ struct _MonoImage { MonoConcurrentHashTable *var_gparam_cache; MonoConcurrentHashTable *mvar_gparam_cache; +#ifndef ENABLE_NETCORE /* Maps malloc-ed char* pinvoke scope -> MonoDl* */ GHashTable *pinvoke_scopes; - - /* Maps malloc-ed char* pinvoke scope -> malloced-ed char* filename */ - GHashTable *pinvoke_scope_filenames; +#endif /* Indexed by MonoGenericParam pointers */ GHashTable **gshared_types; diff --git a/src/mono/mono/metadata/native-library.c b/src/mono/mono/metadata/native-library.c index e33d821..b52dd8e 100644 --- a/src/mono/mono/metadata/native-library.c +++ b/src/mono/mono/metadata/native-library.c @@ -1,7 +1,11 @@ #include "config.h" +#include "mono/metadata/assembly-internals.h" #include "mono/metadata/class-internals.h" +#include "mono/metadata/icall-decl.h" #include "mono/metadata/loader-internals.h" #include "mono/metadata/loader.h" +#include "mono/metadata/object-internals.h" +#include "mono/metadata/reflection-internals.h" #include "mono/utils/checked-build.h" #include "mono/utils/mono-compiler.h" #include "mono/utils/mono-logger-internals.h" @@ -10,14 +14,33 @@ #ifdef ENABLE_NETCORE static int pinvoke_search_directories_count; static char **pinvoke_search_directories; + +// In DllImportSearchPath enum, bit 0x2 represents AssemblyDirectory. It is not passed on and is instead handled by the runtime. +#define DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY 0x2 + +// This lock may be taken within an ALC lock, and should never be the other way around. +static MonoCoopMutex native_library_module_lock; +static GHashTable *native_library_module_map; +/* + * This blacklist is used as a set for cache invalidation purposes with netcore pinvokes. + * When pinvokes are resolved with anything other than the last-chance managed event, + * the results of that lookup are added to an ALC-level cache. However, if a library is then + * unloaded with NativeLibrary.Free(), this cache should be invalidated so that a newly called + * pinvoke will not attempt to use it, hence the blacklist. This design means that if another + * library is loaded at the same address, it will function with a perf hit, as the entry will + * repeatedly be added and removed from the cache due to its presence in the blacklist. + * This is a rare scenario and considered a worthwhile tradeoff. + */ +static GHashTable *native_library_module_blacklist; #endif #ifndef DISABLE_DLLMAP static MonoDllMap *global_dll_map; #endif -static GHashTable *global_module_map; -static MonoDl *internal_module; +static GHashTable *global_module_map; // should only be accessed with the global loader data lock + +static MonoDl *internal_module; // used when pinvoking `__Internal` typedef enum { LOOKUP_PINVOKE_ERR_OK = 0, /* No error */ @@ -41,6 +64,9 @@ typedef struct MonoLookupPInvokeStatus { /* Class lazy loading functions */ GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException") GENERATE_TRY_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException") +#ifdef ENABLE_NETCORE +GENERATE_GET_CLASS_WITH_CACHE (native_library, "System.Runtime.InteropServices", "NativeLibrary"); +#endif #ifndef DISABLE_DLLMAP /* @@ -124,8 +150,6 @@ dllmap_insert_global (const char *dll, const char *func, const char *tdll, const { MonoDllMap *entry; - mono_loader_init (); - entry = (MonoDllMap *)g_malloc0 (sizeof (MonoDllMap)); entry->dll = dll? g_strdup (dll): NULL; entry->target = tdll? g_strdup (tdll): NULL; @@ -148,8 +172,6 @@ dllmap_insert_image (MonoImage *assembly, const char *dll, const char *func, con MONO_REQ_GC_UNSAFE_MODE; - mono_loader_init (); - entry = (MonoDllMap *)mono_image_alloc0 (assembly, sizeof (MonoDllMap)); entry->dll = dll? mono_image_strdup (assembly, dll): NULL; entry->target = tdll? mono_image_strdup (assembly, tdll): NULL; @@ -184,6 +206,7 @@ free_dllmap (MonoDllMap *map) void mono_dllmap_insert_internal (MonoImage *assembly, const char *dll, const char *func, const char *tdll, const char *tfunc) { + mono_loader_init (); // The locking in here is _really_ wonky, and I'm not convinced this function should exist. // I've split it into an internal version to offer flexibility in the future. if (!assembly) @@ -201,6 +224,8 @@ mono_global_dllmap_cleanup (void) free_dllmap (global_dll_map); global_dll_map = NULL; + // We don't care about freeing native_library_module_map on netcore because of the expedited shutdown. + mono_global_loader_data_unlock (); } #endif @@ -243,49 +268,44 @@ mono_dllmap_insert (MonoImage *assembly, const char *dll, const char *func, cons #endif } -static MonoDl* -cached_module_load (const char *name, int flags, char **err) +void +mono_loader_register_module (const char *name, MonoDl *module) { - MonoDl *res; - - if (err) - *err = NULL; - - MONO_ENTER_GC_SAFE; + mono_loader_init (); + // No transition here because this is early in startup mono_global_loader_data_lock (); - if (!global_module_map) - global_module_map = g_hash_table_new (g_str_hash, g_str_equal); - - res = (MonoDl *)g_hash_table_lookup (global_module_map, name); - if (res) - goto exit; - - res = mono_dl_open (name, flags, err); - if (res) - g_hash_table_insert (global_module_map, g_strdup (name), res); + g_hash_table_insert (global_module_map, g_strdup (name), module); -exit: mono_global_loader_data_unlock (); - - MONO_EXIT_GC_SAFE; - - return res; } -void -mono_loader_register_module (const char *name, MonoDl *module) +static MonoDl * +mono_loader_register_module_locking (const char *name, MonoDl *module) { - // No transition here because this is early in startup + MonoDl *result = NULL; + + MONO_ENTER_GC_SAFE; mono_global_loader_data_lock (); + MONO_EXIT_GC_SAFE; - if (!global_module_map) - global_module_map = g_hash_table_new (g_str_hash, g_str_equal); + result = (MonoDl *)g_hash_table_lookup (global_module_map, name); + if (result) { + g_free (module->full_name); + g_free (module); + goto exit; + } g_hash_table_insert (global_module_map, g_strdup (name), module); + result = module; +exit: + MONO_ENTER_GC_SAFE; mono_global_loader_data_unlock (); + MONO_EXIT_GC_SAFE; + + return result; } static void @@ -295,11 +315,23 @@ remove_cached_module (gpointer key, gpointer value, gpointer user_data) } void -mono_cached_module_cleanup (void) +mono_global_loader_cache_init (void) { - // No need for a transition here since the thread is already detached from the runtime - mono_global_loader_data_lock (); + if (!global_module_map) + global_module_map = g_hash_table_new (g_str_hash, g_str_equal); + +#ifdef ENABLE_NETCORE + if (!native_library_module_map) + native_library_module_map = g_hash_table_new (g_direct_hash, g_direct_equal); + if (!native_library_module_blacklist) + native_library_module_blacklist = g_hash_table_new (g_direct_hash, g_direct_equal); + mono_coop_mutex_init (&native_library_module_lock); +#endif +} +void +mono_global_loader_cache_cleanup (void) +{ if (global_module_map != NULL) { g_hash_table_foreach(global_module_map, remove_cached_module, NULL); @@ -307,16 +339,16 @@ mono_cached_module_cleanup (void) global_module_map = NULL; } - mono_global_loader_data_unlock (); + // No need to clean up the native library hash tables since they're netcore-only, where this is never called } static gboolean is_absolute_path (const char *path) { + // FIXME: other platforms have similar prefixes, such as $ORIGIN #ifdef HOST_DARWIN - if (!strncmp (path, "@executable_path/", 17) || !strncmp (path, "@loader_path/", 13) || - !strncmp (path, "@rpath/", 7)) - return TRUE; + if (!strncmp (path, "@executable_path/", 17) || !strncmp (path, "@loader_path/", 13) || !strncmp (path, "@rpath/", 7)) + return TRUE; #endif return g_path_is_absolute (path); } @@ -324,6 +356,9 @@ is_absolute_path (const char *path) static gpointer lookup_pinvoke_call_impl (MonoMethod *method, MonoLookupPInvokeStatus *status_out); +static gpointer +pinvoke_probe_for_symbol (MonoDl *module, MonoMethodPInvoke *piinfo, const char *import, char **error_msg_out); + static void pinvoke_probe_convert_status_for_api (MonoLookupPInvokeStatus *status, const char **exc_class, const char **exc_arg) { @@ -387,15 +422,6 @@ mono_lookup_pinvoke_call (MonoMethod *method, const char **exc_class, const char return result; } -static MonoDl* -pinvoke_probe_for_module (MonoImage *image, const char*new_scope, char **found_name_out, char **error_msg_out); - -static MonoDl* -pinvoke_probe_for_module_relative_directories (MonoImage *image, const char *file_name, char **found_name_out); - -static gpointer -pinvoke_probe_for_symbol (MonoDl *module, MonoMethodPInvoke *piinfo, const char *import, char **error_msg_out); - gpointer mono_lookup_pinvoke_call_internal (MonoMethod *method, MonoError *error) { @@ -408,138 +434,447 @@ mono_lookup_pinvoke_call_internal (MonoMethod *method, MonoError *error) return result; } -gpointer -lookup_pinvoke_call_impl (MonoMethod *method, MonoLookupPInvokeStatus *status_out) +#ifdef ENABLE_NETCORE +void +mono_set_pinvoke_search_directories (int dir_count, char **dirs) { - MonoImage *image = m_class_get_image (method->klass); - MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *)method; - MonoTableInfo *tables = image->tables; - MonoTableInfo *im = &tables [MONO_TABLE_IMPLMAP]; - MonoTableInfo *mr = &tables [MONO_TABLE_MODULEREF]; - guint32 im_cols [MONO_IMPLMAP_SIZE]; - guint32 scope_token; - const char *orig_import = NULL; - const char *new_import = NULL; - const char *orig_scope = NULL; - const char *new_scope = NULL; - char *error_msg = NULL; - char *found_name = NULL; - MonoDl *module = NULL; - gboolean cached = FALSE; - gpointer addr = NULL; + pinvoke_search_directories_count = dir_count; + pinvoke_search_directories = dirs; +} - g_assert (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL); +static void +native_library_lock (void) +{ + mono_coop_mutex_lock (&native_library_module_lock); +} - g_assert (status_out); +static void +native_library_unlock (void) +{ + mono_coop_mutex_unlock (&native_library_module_lock); +} - if (piinfo->addr) - return piinfo->addr; +static void +alc_pinvoke_lock (MonoAssemblyLoadContext *alc) +{ + mono_coop_mutex_lock (&alc->pinvoke_lock); +} - if (image_is_dynamic (m_class_get_image (method->klass))) { - MonoReflectionMethodAux *method_aux = - (MonoReflectionMethodAux *)g_hash_table_lookup ( - ((MonoDynamicImage*)m_class_get_image (method->klass))->method_aux_hash, method); - if (!method_aux) - goto exit; +static void +alc_pinvoke_unlock (MonoAssemblyLoadContext *alc) +{ + mono_coop_mutex_unlock (&alc->pinvoke_lock); +} - orig_import = method_aux->dllentry; - orig_scope = method_aux->dll; +// LOCKING: expects you to hold native_library_module_lock +static MonoDl * +netcore_handle_lookup (gpointer handle) +{ + return (MonoDl *)g_hash_table_lookup (native_library_module_map, handle); +} + +// LOCKING: expects you to hold native_library_module_lock +static gboolean +netcore_check_blacklist (MonoDl *module) +{ + return g_hash_table_contains (native_library_module_blacklist, module); +} + +static MonoDl * +netcore_probe_for_module_variations (const char *mdirname, const char *file_name) +{ + void *iter = NULL; + char *full_name; + MonoDl *module = NULL; + + // This does not actually mirror CoreCLR's algorithm; if that becomes a problem, potentially use theirs + // FIXME: this appears to search *.dylib twice for some reason + while ((full_name = mono_dl_build_path (mdirname, file_name, &iter)) && module == NULL) { + char *error_msg; + module = mono_dl_open (full_name, MONO_DL_LAZY, &error_msg); + if (!module) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", full_name, error_msg); + g_free (error_msg); + } + g_free (full_name); } - else { - if (!piinfo->implmap_idx || piinfo->implmap_idx > im->rows) - goto exit; + g_free (full_name); - mono_metadata_decode_row (im, piinfo->implmap_idx - 1, im_cols, MONO_IMPLMAP_SIZE); + return module; +} - if (!im_cols [MONO_IMPLMAP_SCOPE] || im_cols [MONO_IMPLMAP_SCOPE] > mr->rows) - goto exit; +static MonoDl * +netcore_probe_for_module (MonoImage *image, const char *file_name, int flags) +{ + MonoDl *module = NULL; + gboolean search_assembly_dir = flags & DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY; - piinfo->piflags = im_cols [MONO_IMPLMAP_FLAGS]; - orig_import = mono_metadata_string_heap (image, im_cols [MONO_IMPLMAP_NAME]); - scope_token = mono_metadata_decode_row_col (mr, im_cols [MONO_IMPLMAP_SCOPE] - 1, MONO_MODULEREF_NAME); - orig_scope = mono_metadata_string_heap (image, scope_token); + // Try without any path additions + module = netcore_probe_for_module_variations (NULL, file_name); + + // Check the NATIVE_DLL_SEARCH_DIRECTORIES + for (int i = 0; i < pinvoke_search_directories_count && module == NULL; ++i) + module = netcore_probe_for_module_variations (pinvoke_search_directories[i], file_name); + + // Check the assembly directory if the search flag is set and the image exists + if (search_assembly_dir && image != NULL && module == NULL) { + char *mdirname = g_path_get_dirname (image->filename); + if (mdirname) + module = netcore_probe_for_module_variations (mdirname, file_name); + g_free (mdirname); } -#ifndef DISABLE_DLLMAP - // FIXME: The dllmap remaps System.Native to mono-native - mono_dllmap_lookup (image, orig_scope, orig_import, &new_scope, &new_import); -#else - new_scope = g_strdup (orig_scope); - new_import = g_strdup (orig_import); -#endif + return module; +} - if (!module) { - mono_image_lock (image); - if (!image->pinvoke_scopes) { - image->pinvoke_scopes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - image->pinvoke_scope_filenames = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - } - module = (MonoDl *)g_hash_table_lookup (image->pinvoke_scopes, new_scope); - found_name = (char *)g_hash_table_lookup (image->pinvoke_scope_filenames, new_scope); - mono_image_unlock (image); - if (module) - cached = TRUE; - found_name = g_strdup (found_name); +static MonoDl * +netcore_resolve_with_dll_import_resolver (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope, guint32 flags, MonoError *error) +{ + MonoDl *result = NULL; + gpointer lib = NULL; + MonoDomain *domain = mono_alc_domain (alc); + + static MonoMethod *resolve; + + if (!resolve) { + ERROR_DECL (local_error); + MonoClass *native_lib_class = mono_class_get_native_library_class (); + g_assert (native_lib_class); + MonoMethod *m = mono_class_get_method_from_name_checked (native_lib_class, "MonoLoadLibraryCallbackStub", -1, 0, local_error); + mono_error_assert_ok (local_error); + resolve = m; } + g_assert (resolve); - if (!module) - module = pinvoke_probe_for_module (image, new_scope, &found_name, &error_msg); + if (mono_runtime_get_no_exec ()) + return NULL; - if (!module) { - mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_DLLIMPORT, - "DllImport unable to load library '%s'.", - error_msg); + HANDLE_FUNCTION_ENTER (); - status_out->err_code = LOOKUP_PINVOKE_ERR_NO_LIB; - status_out->err_arg = g_strdup (new_scope); - goto exit; + MonoStringHandle scope_handle = mono_string_new_handle (domain, scope, error); + goto_if_nok (error, leave); + MonoReflectionAssemblyHandle assembly_handle = mono_assembly_get_object_handle (domain, assembly, error); + goto_if_nok (error, leave); + + gboolean has_search_flags = flags != 0 ? TRUE : FALSE; + gpointer args [5]; + args [0] = &scope_handle; + args [1] = MONO_HANDLE_RAW (assembly_handle); + args [2] = &has_search_flags; + args [3] = &flags; + args [4] = &lib; + mono_runtime_invoke_checked (resolve, NULL, args, error); + goto_if_nok (error, leave); + + native_library_lock (); + result = netcore_handle_lookup (lib); + native_library_unlock (); + +leave: + HANDLE_FUNCTION_RETURN_VAL (result); +} + +static MonoDl * +netcore_resolve_with_dll_import_resolver_nofail (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope, guint32 flags) +{ + MonoDl *result = NULL; + ERROR_DECL (error); + + result = netcore_resolve_with_dll_import_resolver (alc, assembly, scope, flags, error); + if (!is_ok (error)) + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Error while invoking ALC DllImportResolver(\"%s\") delegate: '%s'", scope, mono_error_get_message (error)); + + mono_error_cleanup (error); + + return result; +} + +static MonoDl * +netcore_resolve_with_load (MonoAssemblyLoadContext *alc, const char *scope, MonoError *error) +{ + MonoDl *result = NULL; + gpointer lib = NULL; + + static MonoMethod *resolve; + + if (!resolve) { + ERROR_DECL (local_error); + MonoClass *alc_class = mono_class_get_assembly_load_context_class (); + g_assert (alc_class); + MonoMethod *m = mono_class_get_method_from_name_checked (alc_class, "MonoResolveUnmanagedDll", -1, 0, local_error); + mono_error_assert_ok (local_error); + resolve = m; } + g_assert (resolve); - if (!cached) { - mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, - "DllImport loaded library '%s'.", found_name); - mono_image_lock (image); - if (!g_hash_table_lookup (image->pinvoke_scopes, new_scope)) { - g_hash_table_insert (image->pinvoke_scopes, g_strdup (new_scope), module); - g_hash_table_insert (image->pinvoke_scope_filenames, g_strdup (new_scope), g_strdup (found_name)); - } - mono_image_unlock (image); + if (mono_runtime_get_no_exec ()) + return NULL; + + HANDLE_FUNCTION_ENTER (); + + MonoStringHandle scope_handle = mono_string_new_handle (mono_alc_domain (alc), scope, error); + goto_if_nok (error, leave); + + gpointer gchandle = GUINT_TO_POINTER (alc->gchandle); + gpointer args [3]; + args [0] = &scope_handle; + args [1] = &gchandle; + args [2] = &lib; + mono_runtime_invoke_checked (resolve, NULL, args, error); + goto_if_nok (error, leave); + + native_library_lock (); + result = netcore_handle_lookup (lib); + native_library_unlock (); + +leave: + HANDLE_FUNCTION_RETURN_VAL (result); +} + +static MonoDl * +netcore_resolve_with_load_nofail (MonoAssemblyLoadContext *alc, const char *scope) +{ + MonoDl *result = NULL; + ERROR_DECL (error); + + result = netcore_resolve_with_load (alc, scope, error); + if (!is_ok (error)) + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Error while invoking ALC LoadUnmanagedDll(\"%s\") method: '%s'", scope, mono_error_get_message (error)); + + mono_error_cleanup (error); + + return result; +} + +static MonoDl * +netcore_resolve_with_resolving_event (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope, MonoError *error) +{ + MonoDl *result = NULL; + gpointer lib = NULL; + MonoDomain *domain = mono_alc_domain (alc); + + static MonoMethod *resolve; + + if (!resolve) { + ERROR_DECL (local_error); + MonoClass *alc_class = mono_class_get_assembly_load_context_class (); + g_assert (alc_class); + MonoMethod *m = mono_class_get_method_from_name_checked (alc_class, "MonoResolveUnmanagedDllUsingEvent", -1, 0, local_error); + mono_error_assert_ok (local_error); + resolve = m; } + g_assert (resolve); - mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, - "DllImport searching in: '%s' ('%s').", new_scope, found_name); + if (mono_runtime_get_no_exec ()) + return NULL; - addr = pinvoke_probe_for_symbol (module, piinfo, new_import, &error_msg); + HANDLE_FUNCTION_ENTER (); - if (!addr) { - status_out->err_code = LOOKUP_PINVOKE_ERR_NO_SYM; - status_out->err_arg = g_strdup (new_import); - goto exit; + MonoStringHandle scope_handle = mono_string_new_handle (domain, scope, error); + goto_if_nok (error, leave); + MonoReflectionAssemblyHandle assembly_handle = mono_assembly_get_object_handle (domain, assembly, error); + goto_if_nok (error, leave); + + gpointer gchandle = GUINT_TO_POINTER (alc->gchandle); + gpointer args [4]; + args [0] = &scope_handle; + args [1] = MONO_HANDLE_RAW (assembly_handle); + args [2] = &gchandle; + args [3] = &lib; + mono_runtime_invoke_checked (resolve, NULL, args, error); + goto_if_nok (error, leave); + + native_library_lock (); + result = netcore_handle_lookup (lib); + native_library_unlock (); + +leave: + HANDLE_FUNCTION_RETURN_VAL (result); +} + +static MonoDl * +netcore_resolve_with_resolving_event_nofail (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope) +{ + MonoDl *result = NULL; + ERROR_DECL (error); + + result = netcore_resolve_with_resolving_event (alc, assembly, scope, error); + if (!is_ok (error)) + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Error while invoking ALC ResolvingUnmangedDll(\"%s\") event: '%s'", scope, mono_error_get_message (error)); + + mono_error_cleanup (error); + + return result; +} + +// LOCKING: expects you to hold the ALC's pinvoke lock +static MonoDl * +netcore_check_alc_cache (MonoAssemblyLoadContext *alc, const char *scope) +{ + MonoDl *result = NULL; + + result = (MonoDl *)g_hash_table_lookup (alc->pinvoke_scopes, scope); + + if (result) { + gboolean blacklisted; + + native_library_lock (); + blacklisted = netcore_check_blacklist (result); + native_library_unlock (); + + if (blacklisted) { + g_hash_table_remove (alc->pinvoke_scopes, scope); + result = NULL; + } } - piinfo->addr = addr; -exit: - g_free ((char *)new_import); - g_free ((char *)new_scope); - g_free (error_msg); - g_free (found_name); - return addr; + return result; } -/** - * pinvoke_probe_transform_path: - * - * Try transforming the library path given in \p new_scope in different ways - * depending on \p phase - * - * \returns \c TRUE if a transformation was applied and the transformed path - * components are written to the out arguments, or \c FALSE if a transformation - * did not apply. - */ -static gboolean -pinvoke_probe_transform_path (const char *new_scope, int phase, char **file_name_out, char **base_name_out, char **dir_name_out, gboolean *is_absolute_out) +static MonoDl * +netcore_lookup_native_library (MonoAssemblyLoadContext *alc, MonoImage *image, const char *scope, guint32 flags) { - char *file_name = NULL, *base_name = NULL, *dir_name = NULL; + MonoDl *module = NULL; + MonoDl *cached; + MonoAssembly *assembly = mono_image_get_assembly (image); + char *error_msg = NULL; + + MONO_REQ_GC_UNSAFE_MODE; + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport attempting to load: '%s'.", scope); + + // We allow a special name to dlopen from the running process namespace, which is not present in CoreCLR + if (strcmp (scope, "__Internal") == 0) { + if (!internal_module) + internal_module = mono_dl_open (NULL, MONO_DL_LAZY, &error_msg); + module = internal_module; + + if (!module) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '__Internal': '%s'.", error_msg); + g_free (error_msg); + } + + return module; + } + + /* + * Try these until one of them succeeds: + * + * 1. Check the cache in the active ALC. + * + * 2. Call the DllImportResolver on the active assembly. + * + * 3. Call LoadUnmanagedDll on the active ALC. + * + * 4. Check the global cache. + * + * 5. Run the unmanaged probing logic. + * + * 6. Raise the ResolvingUnmanagedDll event on the active ALC. + * + * 7. Return NULL. + */ + + alc_pinvoke_lock (alc); + module = netcore_check_alc_cache (alc, scope); + alc_pinvoke_unlock (alc); + if (module) + goto leave; + + module = (MonoDl *)netcore_resolve_with_dll_import_resolver_nofail (alc, assembly, scope, flags); + if (module) + goto add_to_alc_cache; + + module = (MonoDl *)netcore_resolve_with_load_nofail (alc, scope); + if (module) + goto add_to_alc_cache; + + MONO_ENTER_GC_SAFE; + mono_global_loader_data_lock (); + MONO_EXIT_GC_SAFE; + module = (MonoDl *)g_hash_table_lookup (global_module_map, scope); + MONO_ENTER_GC_SAFE; + mono_global_loader_data_unlock (); + MONO_EXIT_GC_SAFE; + if (module) + goto add_to_alc_cache; + + module = netcore_probe_for_module (image, scope, flags); + if (module) + goto add_to_global_cache; + + /* As this is last chance, I've opted not to put it in a cache, but that is not necessarily the correct decision. + * It is rather convenient here, however, because it means the global cache will only be populated by libraries + * resolved via netcore_probe_for_module and not NativeLibrary, eliminating potential races/conflicts. + */ + module = netcore_resolve_with_resolving_event_nofail (alc, assembly, scope); + goto leave; + +add_to_global_cache: + module = mono_loader_register_module_locking (scope, module); + +add_to_alc_cache: + /* Nothing is closed here because the only two places this can come from are: + * 1. A managed callback that made use of NativeLibrary.Load, in which case closing is dependent on NativeLibrary.Free + * 2. The global cache, which is only populated by results of netcore_probe_for_module. When adding to the global cache, + * we free the new MonoDl if another thread beat us, so we don't have to repeat that here. + */ + alc_pinvoke_lock (alc); + cached = netcore_check_alc_cache (alc, scope); + if (cached) + module = cached; + else + g_hash_table_insert (alc->pinvoke_scopes, g_strdup (scope), module); + alc_pinvoke_unlock (alc); + +leave: + return module; +} + +#else // ENABLE_NETCORE + +static MonoDl * +cached_module_load (const char *name, int flags, char **err) +{ + MonoDl *res; + + if (err) + *err = NULL; + + MONO_ENTER_GC_SAFE; + mono_global_loader_data_lock (); + MONO_EXIT_GC_SAFE; + + res = (MonoDl *)g_hash_table_lookup (global_module_map, name); + if (res) + goto exit; + + res = mono_dl_open (name, flags, err); + if (res) + g_hash_table_insert (global_module_map, g_strdup (name), res); + +exit: + MONO_ENTER_GC_SAFE; + mono_global_loader_data_unlock (); + MONO_EXIT_GC_SAFE; + + return res; +} + +/** + * legacy_probe_transform_path: + * + * Try transforming the library path given in \p new_scope in different ways + * depending on \p phase + * + * \returns \c TRUE if a transformation was applied and the transformed path + * components are written to the out arguments, or \c FALSE if a transformation + * did not apply. + */ +static gboolean +legacy_probe_transform_path (const char *new_scope, int phase, char **file_name_out, char **base_name_out, char **dir_name_out, gboolean *is_absolute_out) +{ + char *file_name = NULL, *base_name = NULL, *dir_name = NULL; gboolean changed = FALSE; gboolean is_absolute = is_absolute_path (new_scope); switch (phase) { @@ -605,29 +940,131 @@ pinvoke_probe_transform_path (const char *new_scope, int phase, char **file_name return changed; } -static MonoDl* -pinvoke_probe_for_module (MonoImage *image, const char *new_scope, char **found_name_out, char **error_msg_out) +static MonoDl * +legacy_probe_for_module_in_directory (const char *mdirname, const char *file_name) +{ + void *iter = NULL; + char *full_name; + MonoDl* module = NULL; + + while ((full_name = mono_dl_build_path (mdirname, file_name, &iter)) && module == NULL) { + char *error_msg; + module = cached_module_load (full_name, MONO_DL_LAZY, &error_msg); + if (!module) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", full_name, error_msg); + g_free (error_msg); + } + g_free (full_name); + } + g_free (full_name); + + return module; +} + +static MonoDl * +legacy_probe_for_module_relative_directories (MonoImage *image, const char *file_name) +{ + MonoDl* module = NULL; + + for (int j = 0; j < 3; ++j) { + char *mdirname = NULL; + + switch (j) { + case 0: + mdirname = g_path_get_dirname (image->filename); + break; + case 1: // @executable_path@/../lib + { + char buf [4096]; // FIXME: MAX_PATH + int binl; + binl = mono_dl_get_executable_path (buf, sizeof (buf)); + if (binl != -1) { + char *base, *newbase; + char *resolvedname; + buf [binl] = 0; + resolvedname = mono_path_resolve_symlinks (buf); + + base = g_path_get_dirname (resolvedname); + newbase = g_path_get_dirname(base); + + // On Android the executable for the application is going to be /system/bin/app_process{32,64} depending on + // the application's architecture. However, libraries for the different architectures live in different + // subdirectories of `/system`: `lib` for 32-bit apps and `lib64` for 64-bit ones. Thus appending `/lib` below + // will fail to load the DSO for a 64-bit app, even if it exists there, because it will have a different + // architecture. This is the cause of https://github.com/xamarin/xamarin-android/issues/2780 and the ifdef + // below is the fix. + mdirname = g_strdup_printf ( +#if defined(TARGET_ANDROID) && (defined(TARGET_ARM64) || defined(TARGET_AMD64)) + "%s/lib64", +#else + "%s/lib", +#endif + newbase); + g_free (resolvedname); + g_free (base); + g_free (newbase); + } + break; + } +#ifdef __MACH__ + case 2: // @executable_path@/../Libraries + { + char buf [4096]; // FIXME: MAX_PATH + int binl; + binl = mono_dl_get_executable_path (buf, sizeof (buf)); + if (binl != -1) { + char *base, *newbase; + char *resolvedname; + buf [binl] = 0; + resolvedname = mono_path_resolve_symlinks (buf); + + base = g_path_get_dirname (resolvedname); + newbase = g_path_get_dirname(base); + mdirname = g_strdup_printf ("%s/Libraries", newbase); + + g_free (resolvedname); + g_free (base); + g_free (newbase); + } + break; + } +#endif + } + + if (!mdirname) + continue; + + module = legacy_probe_for_module_in_directory (mdirname, file_name); + g_free (mdirname); + if (module) + break; + } + + return module; +} + +static MonoDl * +legacy_probe_for_module (MonoImage *image, const char *new_scope) { char *full_name, *file_name; char *error_msg = NULL; - char *found_name = NULL; int i; MonoDl *module = NULL; - g_assert (found_name_out); - g_assert (error_msg_out); + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport attempting to load: '%s'.", new_scope); - if (!module) { - mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, - "DllImport attempting to load: '%s'.", new_scope); - - /* we allow a special name to dlopen from the running process namespace */ - if (strcmp (new_scope, "__Internal") == 0){ - if (internal_module == NULL) - internal_module = mono_dl_open (NULL, MONO_DL_LAZY, &error_msg); - module = internal_module; - goto leave; + /* we allow a special name to dlopen from the running process namespace */ + if (strcmp (new_scope, "__Internal") == 0) { + if (!internal_module) + internal_module = mono_dl_open (NULL, MONO_DL_LAZY, &error_msg); + module = internal_module; + + if (!module) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '__Internal': '%s'.", error_msg); + g_free (error_msg); } + + return module; } /* @@ -637,7 +1074,7 @@ pinvoke_probe_for_module (MonoImage *image, const char *new_scope, char **found_ char *base_name = NULL, *dir_name = NULL; gboolean is_absolute; - gboolean changed = pinvoke_probe_transform_path (new_scope, i, &file_name, &base_name, &dir_name, &is_absolute); + gboolean changed = legacy_probe_transform_path (new_scope, i, &file_name, &base_name, &dir_name, &is_absolute); if (!changed) continue; @@ -648,13 +1085,11 @@ pinvoke_probe_for_module (MonoImage *image, const char *new_scope, char **found_ "DllImport error loading library '%s': '%s'.", file_name, error_msg); g_free (error_msg); - } else { - found_name = g_strdup (file_name); } } if (!module && !is_absolute) { - module = pinvoke_probe_for_module_relative_directories (image, file_name, &found_name); + module = legacy_probe_for_module_relative_directories (image, file_name); } if (!module) { @@ -667,8 +1102,6 @@ pinvoke_probe_for_module (MonoImage *image, const char *new_scope, char **found_ "DllImport error loading library '%s': '%s'.", full_name, error_msg); g_free (error_msg); - } else { - found_name = g_strdup (full_name); } g_free (full_name); if (module) @@ -682,8 +1115,7 @@ pinvoke_probe_for_module (MonoImage *image, const char *new_scope, char **found_ mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", file_name, error_msg); - } else { - found_name = g_strdup (file_name); + g_free (error_msg); } } @@ -697,145 +1129,138 @@ pinvoke_probe_for_module (MonoImage *image, const char *new_scope, char **found_ break; } -leave: - *found_name_out = found_name; - *error_msg_out = error_msg; return module; } -#ifdef ENABLE_NETCORE -void -mono_set_pinvoke_search_directories (int dir_count, char **dirs) +static MonoDl * +legacy_lookup_native_library (MonoImage *image, const char *scope) { - pinvoke_search_directories_count = dir_count; - pinvoke_search_directories = dirs; -} -#endif + MonoDl *module = NULL; + gboolean cached = FALSE; -static MonoDl* -pinvoke_probe_for_module_in_directory (const char *mdirname, const char *file_name, char **found_name_out) -{ - void *iter = NULL; - char *full_name; - MonoDl* module = NULL; + mono_image_lock (image); + if (!image->pinvoke_scopes) + image->pinvoke_scopes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + module = (MonoDl *)g_hash_table_lookup (image->pinvoke_scopes, scope); + mono_image_unlock (image); + if (module) + cached = TRUE; - while ((full_name = mono_dl_build_path (mdirname, file_name, &iter)) && module == NULL) { - char *error_msg; - module = cached_module_load (full_name, MONO_DL_LAZY, &error_msg); - if (!module) { - mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", full_name, error_msg); - g_free (error_msg); - } else { - *found_name_out = g_strdup (full_name); + if (!module) + module = legacy_probe_for_module (image, scope); + + if (module && !cached) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, + "DllImport loaded library '%s'.", module->full_name); + mono_image_lock (image); + if (!g_hash_table_lookup (image->pinvoke_scopes, scope)) { + g_hash_table_insert (image->pinvoke_scopes, g_strdup (scope), module); } - g_free (full_name); + mono_image_unlock (image); } - g_free (full_name); return module; } -static MonoDl* -pinvoke_probe_for_module_relative_directories (MonoImage *image, const char *file_name, char **found_name_out) +#endif // ENABLE_NETCORE + +gpointer +lookup_pinvoke_call_impl (MonoMethod *method, MonoLookupPInvokeStatus *status_out) { - char *found_name = NULL; - MonoDl* module = NULL; + MonoImage *image = m_class_get_image (method->klass); +#ifdef ENABLE_NETCORE + MonoAssemblyLoadContext *alc = mono_image_get_alc (image); +#endif + MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *)method; + MonoTableInfo *tables = image->tables; + MonoTableInfo *im = &tables [MONO_TABLE_IMPLMAP]; + MonoTableInfo *mr = &tables [MONO_TABLE_MODULEREF]; + guint32 im_cols [MONO_IMPLMAP_SIZE]; + guint32 scope_token; + const char *orig_import = NULL; + const char *new_import = NULL; + const char *orig_scope = NULL; + const char *new_scope = NULL; + char *error_msg = NULL; + MonoDl *module = NULL; + gpointer addr = NULL; + + MONO_REQ_GC_UNSAFE_MODE; - g_assert (found_name_out); + g_assert (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL); + + g_assert (status_out); + + if (piinfo->addr) + return piinfo->addr; -#if ENABLE_NETCORE - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "netcore DllImport handler: wanted '%s'", file_name); + if (image_is_dynamic (image)) { + MonoReflectionMethodAux *method_aux = + (MonoReflectionMethodAux *)g_hash_table_lookup ( + ((MonoDynamicImage*)m_class_get_image (method->klass))->method_aux_hash, method); + if (!method_aux) + goto exit; - // Search in predefined directories first - for (int j = 0; j < pinvoke_search_directories_count && module == NULL; ++j) { - module = pinvoke_probe_for_module_in_directory (pinvoke_search_directories[j], file_name, &found_name); + orig_import = method_aux->dllentry; + orig_scope = method_aux->dll; } + else { + if (!piinfo->implmap_idx || piinfo->implmap_idx > im->rows) + goto exit; - // Fallback to image directory - if (module == NULL) { - // TODO: Check DefaultDllImportSearchPathsAttribute, NativeLibrary callback - char *mdirname = g_path_get_dirname (image->name); - if (mdirname) - module = pinvoke_probe_for_module_in_directory (mdirname, file_name, &found_name); - g_free (mdirname); + mono_metadata_decode_row (im, piinfo->implmap_idx - 1, im_cols, MONO_IMPLMAP_SIZE); + + if (!im_cols [MONO_IMPLMAP_SCOPE] || im_cols [MONO_IMPLMAP_SCOPE] > mr->rows) + goto exit; + + piinfo->piflags = im_cols [MONO_IMPLMAP_FLAGS]; + orig_import = mono_metadata_string_heap (image, im_cols [MONO_IMPLMAP_NAME]); + scope_token = mono_metadata_decode_row_col (mr, im_cols [MONO_IMPLMAP_SCOPE] - 1, MONO_MODULEREF_NAME); + orig_scope = mono_metadata_string_heap (image, scope_token); } + +#ifndef DISABLE_DLLMAP + // FIXME: The dllmap remaps System.Native to mono-native + mono_dllmap_lookup (image, orig_scope, orig_import, &new_scope, &new_import); #else - for (int j = 0; j < 3; ++j) { - char *mdirname = NULL; - switch (j) { - case 0: - mdirname = g_path_get_dirname (image->name); - break; - case 1: /* @executable_path@/../lib */ - { - char buf [4096]; - int binl; - binl = mono_dl_get_executable_path (buf, sizeof (buf)); - if (binl != -1) { - char *base, *newbase; - char *resolvedname; - buf [binl] = 0; - resolvedname = mono_path_resolve_symlinks (buf); - - base = g_path_get_dirname (resolvedname); - newbase = g_path_get_dirname(base); - - // On Android the executable for the application is going to be /system/bin/app_process{32,64} depending on - // the application's architecture. However, libraries for the different architectures live in different - // subdirectories of `/system`: `lib` for 32-bit apps and `lib64` for 64-bit ones. Thus appending `/lib` below - // will fail to load the DSO for a 64-bit app, even if it exists there, because it will have a different - // architecture. This is the cause of https://github.com/xamarin/xamarin-android/issues/2780 and the ifdef - // below is the fix. - mdirname = g_strdup_printf ( -#if defined(TARGET_ANDROID) && (defined(TARGET_ARM64) || defined(TARGET_AMD64)) - "%s/lib64", -#else - "%s/lib", + new_scope = g_strdup (orig_scope); + new_import = g_strdup (orig_import); #endif - newbase); - g_free (resolvedname); - g_free (base); - g_free (newbase); - } - break; - } -#ifdef __MACH__ - case 2: /* @executable_path@/../Libraries */ - { - char buf [4096]; - int binl; - binl = mono_dl_get_executable_path (buf, sizeof (buf)); - if (binl != -1) { - char *base, *newbase; - char *resolvedname; - buf [binl] = 0; - resolvedname = mono_path_resolve_symlinks (buf); - - base = g_path_get_dirname (resolvedname); - newbase = g_path_get_dirname(base); - mdirname = g_strdup_printf ("%s/Libraries", newbase); - - g_free (resolvedname); - g_free (base); - g_free (newbase); - } - break; - } + +#ifdef ENABLE_NETCORE + // FIXME: these flags are not getting passed correctly + module = netcore_lookup_native_library (alc, image, new_scope, 0); +#else + module = legacy_lookup_native_library (image, new_scope); #endif - } - if (!mdirname) - continue; + if (!module) { + mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_DLLIMPORT, + "DllImport unable to load library '%s'.", + new_scope); - module = pinvoke_probe_for_module_in_directory (mdirname, file_name, &found_name); - g_free (mdirname); - if (module) - break; - } -#endif + status_out->err_code = LOOKUP_PINVOKE_ERR_NO_LIB; + status_out->err_arg = g_strdup (new_scope); + goto exit; + } - *found_name_out = found_name; - return module; + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, + "DllImport searching in: '%s' ('%s').", new_scope, module->full_name); + + addr = pinvoke_probe_for_symbol (module, piinfo, new_import, &error_msg); + + if (!addr) { + status_out->err_code = LOOKUP_PINVOKE_ERR_NO_SYM; + status_out->err_arg = g_strdup (new_import); + goto exit; + } + piinfo->addr = addr; + +exit: + g_free ((char *)new_import); + g_free ((char *)new_scope); + g_free (error_msg); + return addr; } static gpointer @@ -859,9 +1284,9 @@ pinvoke_probe_for_symbol (MonoDl *module, MonoMethodPInvoke *piinfo, const char mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "Searching for '%s'.", import); - if (piinfo->piflags & PINVOKE_ATTRIBUTE_NO_MANGLE) { - error_msg = mono_dl_symbol (module, import, &addr); - } else { + if (piinfo->piflags & PINVOKE_ATTRIBUTE_NO_MANGLE) + error_msg = mono_dl_symbol (module, import, &addr); + else { /* * Search using a variety of mangled names */ @@ -947,3 +1372,153 @@ pinvoke_probe_for_symbol (MonoDl *module, MonoMethodPInvoke *piinfo, const char *error_msg_out = error_msg; return addr; } + +#ifdef ENABLE_NETCORE +void +ves_icall_System_Runtime_InteropServices_NativeLibrary_FreeLib (gpointer lib, MonoError *error) +{ + MonoDl *module; + guint32 ref_count; + + g_assert (lib); + + // Don't free __Internal + if (internal_module && lib == internal_module->handle) + return; + + native_library_lock (); + + module = netcore_handle_lookup (lib); + if (!module) + goto leave; + + ref_count = mono_refcount_dec (module); + if (ref_count > 0) + goto leave; + + g_hash_table_remove (native_library_module_map, module->handle); + g_hash_table_add (native_library_module_blacklist, module); + mono_dl_close (module); + +leave: + native_library_unlock (); +} + +gpointer +ves_icall_System_Runtime_InteropServices_NativeLibrary_GetSymbol (gpointer lib, MonoStringHandle symbol_name_handle, MonoBoolean throw_on_error, MonoError *error) +{ + MonoDl *module; + gpointer symbol = NULL; + char *symbol_name; + + g_assert (lib); + + ERROR_LOCAL_BEGIN (local_error, error, throw_on_error) + + symbol_name = mono_string_handle_to_utf8 (symbol_name_handle, error); + goto_if_nok (error, leave_nolock); + + native_library_lock (); + + module = netcore_handle_lookup (lib); + if (!module) + mono_error_set_generic_error (error, "System", "DllNotFoundException", "%p: %s", lib, symbol_name); + goto_if_nok (error, leave); + + mono_dl_symbol (module, symbol_name, &symbol); + if (!symbol) + mono_error_set_generic_error (error, "System", "EntryPointNotFoundException", "%s: %s", module->full_name, symbol_name); + goto_if_nok (error, leave); + +leave: + native_library_unlock (); + +leave_nolock: + ERROR_LOCAL_END (local_error); + + return symbol; +} + +// LOCKING: expects you to hold native_library_module_lock +static MonoDl * +check_native_library_cache (MonoDl *module) +{ + gpointer handle = module->handle; + + MonoDl *cached_module = netcore_handle_lookup (handle); + if (cached_module) { + g_free (module->full_name); + g_free (module); + mono_refcount_inc (cached_module); + return cached_module; + } + g_hash_table_insert (native_library_module_map, handle, (gpointer)module); + + return module; +} + +gpointer +ves_icall_System_Runtime_InteropServices_NativeLibrary_LoadByName (MonoStringHandle lib_name_handle, MonoReflectionAssemblyHandle assembly_handle, MonoBoolean has_search_flag, guint32 search_flag, MonoBoolean throw_on_error, MonoError *error) +{ + MonoDl *module; + gpointer handle = NULL; + MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_handle, assembly); + MonoImage *image = mono_assembly_get_image_internal (assembly); + char *lib_name; + + ERROR_LOCAL_BEGIN (local_error, error, throw_on_error) + + lib_name = mono_string_handle_to_utf8 (lib_name_handle, error); + goto_if_nok (error, leave); + + // FIXME: implement search flag defaults properly + module = netcore_probe_for_module (image, lib_name, has_search_flag ? search_flag : 0x2); + if (!module) + mono_error_set_generic_error (error, "System", "DllNotFoundException", "%s", lib_name); + goto_if_nok (error, leave); + + native_library_lock (); + module = check_native_library_cache (module); + native_library_unlock (); + + handle = module->handle; + +leave: + ERROR_LOCAL_END (local_error); + + return handle; +} + +gpointer +ves_icall_System_Runtime_InteropServices_NativeLibrary_LoadFromPath (MonoStringHandle lib_path_handle, MonoBoolean throw_on_error, MonoError *error) +{ + MonoDl *module; + gpointer handle = NULL; + char *error_msg = NULL; + char *lib_path; + + ERROR_LOCAL_BEGIN (local_error, error, throw_on_error) + + lib_path = mono_string_handle_to_utf8 (lib_path_handle, error); + goto_if_nok (error, leave); + + module = mono_dl_open (lib_path, MONO_DL_LAZY, &error_msg); + if (!module) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", lib_path, error_msg); + mono_error_set_generic_error (error, "System", "DllNotFoundException", "'%s': '%s'", lib_path, error_msg); + g_free (error_msg); + } + goto_if_nok (error, leave); + + native_library_lock (); + module = check_native_library_cache (module); + native_library_unlock (); + + handle = module->handle; + +leave: + ERROR_LOCAL_END (local_error); + + return handle; +} +#endif diff --git a/src/mono/mono/utils/mono-dl.c b/src/mono/mono/utils/mono-dl.c index f52103a..bf5e8fc 100644 --- a/src/mono/mono/utils/mono-dl.c +++ b/src/mono/mono/utils/mono-dl.c @@ -191,6 +191,7 @@ mono_dl_open (const char *name, int flags, char **error_msg) void *lib; MonoDlFallbackHandler *dl_fallback = NULL; int lflags = mono_dl_convert_flags (flags); + char *found_name; if (error_msg) *error_msg = NULL; @@ -209,6 +210,8 @@ mono_dl_open (const char *name, int flags, char **error_msg) // No GC safe transition because this is called early in main.c lib = mono_dl_open_file (name, lflags); + if (lib) + found_name = g_strdup (name); if (!lib) { GSList *node; @@ -223,6 +226,7 @@ mono_dl_open (const char *name, int flags, char **error_msg) if (lib != NULL){ dl_fallback = handler; + found_name = g_strdup (name); break; } } @@ -247,6 +251,8 @@ mono_dl_open (const char *name, int flags, char **error_msg) g_free (lname); if (llname) { lib = mono_dl_open_file (llname, lflags); + if (lib) + found_name = g_strdup (llname); #if defined (_AIX) /* * HACK: deal with AIX archive members because libtool @@ -260,6 +266,8 @@ mono_dl_open (const char *name, int flags, char **error_msg) char *llaixname; llaixname = g_strconcat (llname, "(shr_64.o)", (const char*)NULL); lib = mono_dl_open_file (llaixname, lflags); + if (lib) + found_name = g_strdup (llaixname); /* XXX: try another suffix like (shr.o)? */ g_free (llaixname); } @@ -274,8 +282,10 @@ mono_dl_open (const char *name, int flags, char **error_msg) return NULL; } } + mono_refcount_init (module, NULL); module->handle = lib; module->dl_fallback = dl_fallback; + module->full_name = found_name; return module; } @@ -338,6 +348,7 @@ mono_dl_close (MonoDl *module) } else mono_dl_close_handle (module); + g_free (module->full_name); g_free (module); } diff --git a/src/mono/mono/utils/mono-dl.h b/src/mono/mono/utils/mono-dl.h index 3c99b30..9c8154c 100644 --- a/src/mono/mono/utils/mono-dl.h +++ b/src/mono/mono/utils/mono-dl.h @@ -7,6 +7,7 @@ #include "mono/utils/mono-compiler.h" #include "mono/utils/mono-dl-fallback.h" +#include "mono/utils/refcount.h" #ifdef TARGET_WIN32 #define MONO_SOLIB_EXT ".dll" @@ -21,9 +22,12 @@ #endif typedef struct { + // Only used in native-library.c, native library lock should be held when modifying + // Incremented on NativeLibrary.Load calls, decremented on NativeLibrary.Free + MonoRefCount ref; void *handle; int main_module; - + char *full_name; /* If not NULL, use the methods in MonoDlFallbackHandler instead of the LL_* methods */ MonoDlFallbackHandler *dl_fallback; } MonoDl; diff --git a/src/mono/mono/utils/mono-error-internals.h b/src/mono/mono/utils/mono-error-internals.h index 8d7a80d..8de15c5 100644 --- a/src/mono/mono/utils/mono-error-internals.h +++ b/src/mono/mono/utils/mono-error-internals.h @@ -98,6 +98,17 @@ Different names indicate different scenarios, but the same code. #define MONO_API_ERROR_INIT(error) error_init_internal (error) #define error_init_reuse(error) error_init_internal (error) +#define ERROR_LOCAL_BEGIN(local, parent, skip_overwrite) do { \ +MonoError local; \ +gboolean local ## _overwrite_temp = skip_overwrite; \ +error_init_internal (&local); \ +if (!local ## _overwrite_temp) \ + parent = &local; \ + +#define ERROR_LOCAL_END(local) if (!local ## _overwrite_temp) \ + mono_error_cleanup (&local); \ +} while (0) \ + // Historical deferred initialization was called error_init. // possible bug detection that did not work diff --git a/src/mono/mono/utils/refcount.h b/src/mono/mono/utils/refcount.h index 665529e..b8dc9dc 100644 --- a/src/mono/mono/utils/refcount.h +++ b/src/mono/mono/utils/refcount.h @@ -27,7 +27,7 @@ typedef struct { #define mono_refcount_init(v,destructor) do { mono_refcount_initialize (&(v)->ref, (destructor)); } while (0) #define mono_refcount_inc(v) (mono_refcount_increment (&(v)->ref),(v)) #define mono_refcount_tryinc(v) (mono_refcount_tryincrement (&(v)->ref)) -#define mono_refcount_dec(v) do { mono_refcount_decrement (&(v)->ref); } while (0) +#define mono_refcount_dec(v) (mono_refcount_decrement (&(v)->ref)) static inline void mono_refcount_initialize (MonoRefCount *refcount, void (*destructor) (gpointer data)) @@ -61,7 +61,7 @@ mono_refcount_increment (MonoRefCount *refcount) g_error ("%s: cannot increment a ref with value 0", __func__); } -static inline void +static inline guint32 mono_refcount_decrement (MonoRefCount *refcount) { guint32 oldref, newref; @@ -78,6 +78,8 @@ mono_refcount_decrement (MonoRefCount *refcount) if (newref == 0 && refcount->destructor) refcount->destructor ((gpointer) refcount); + + return newref; } #endif /* __MONO_UTILS_REFCOUNT_H__ */ diff --git a/src/mono/netcore/CoreFX.issues.rsp b/src/mono/netcore/CoreFX.issues.rsp index 3960d1b..090e5d9 100644 --- a/src/mono/netcore/CoreFX.issues.rsp +++ b/src/mono/netcore/CoreFX.issues.rsp @@ -933,7 +933,3 @@ -nomethod System.Security.Cryptography.Encoding.Tests.OidTests.LookupOidByFriendlyName_Ctor -nomethod System.Tests.StringComparerTests.CreateCultureOptions_InvalidArguments_Throws - -# Requires NativeLibrary implementation --nomethod System.Net.Tests.HttpWebRequestTest.ServicePoint_GetValue_ExpectedResult - diff --git a/src/mono/netcore/CoreFX.issues_linux.rsp b/src/mono/netcore/CoreFX.issues_linux.rsp index 1f31f31..0ad7f8f 100644 --- a/src/mono/netcore/CoreFX.issues_linux.rsp +++ b/src/mono/netcore/CoreFX.issues_linux.rsp @@ -23,6 +23,3 @@ # Requires precise GC (should be ignored in dotnet/corefx for mono) -nomethod System.Collections.Concurrent.Tests.ConcurrentQueueTests.ReferenceTypes_NulledAfterDequeue - -# Requires NativeLibrary implementation --nomethod System.Data.Odbc.Tests.DependencyCheckTest.OdbcConnection_OpenWhenOdbcNotInstalled_ThrowsException diff --git a/src/mono/netcore/System.Private.CoreLib/src/LinkerDescriptor/System.Private.CoreLib.xml b/src/mono/netcore/System.Private.CoreLib/src/LinkerDescriptor/System.Private.CoreLib.xml index f2bd945..2cb1b1f 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/LinkerDescriptor/System.Private.CoreLib.xml +++ b/src/mono/netcore/System.Private.CoreLib/src/LinkerDescriptor/System.Private.CoreLib.xml @@ -670,6 +670,13 @@ + + + + + + + diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.Mono.cs index f342485..32605af 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.Mono.cs @@ -4,25 +4,36 @@ using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; namespace System.Runtime.InteropServices { partial class NativeLibrary { - static IntPtr LoadLibraryByName (string libraryName, Assembly assembly, DllImportSearchPath? searchPath, bool throwOnError) => throw new NotImplementedException (); - - static IntPtr LoadFromPath (string libraryName, bool throwOnError) { - // TODO: implement - if (throwOnError) { - throw new DllNotFoundException(); - } - return IntPtr.Zero; + static IntPtr LoadLibraryByName (string libraryName, Assembly assembly, DllImportSearchPath? searchPath, bool throwOnError) + { + return LoadByName (libraryName, + (RuntimeAssembly)assembly, + searchPath.HasValue, + (uint)searchPath.GetValueOrDefault(), + throwOnError); } - static IntPtr LoadByName (string libraryName, RuntimeAssembly callingAssembly, bool hasDllImportSearchPathFlag, uint dllImportSearchPathFlag, bool throwOnError) => throw new NotImplementedException (); + [MethodImplAttribute (MethodImplOptions.InternalCall)] + extern static IntPtr LoadFromPath (string libraryName, bool throwOnError); + + [MethodImplAttribute (MethodImplOptions.InternalCall)] + extern static IntPtr LoadByName (string libraryName, RuntimeAssembly callingAssembly, bool hasDllImportSearchPathFlag, uint dllImportSearchPathFlag, bool throwOnError); - static void FreeLib (IntPtr handle) => throw new NotImplementedException (); + [MethodImplAttribute (MethodImplOptions.InternalCall)] + extern static void FreeLib (IntPtr handle); - static IntPtr GetSymbol (IntPtr handle, string symbolName, bool throwOnError) => throw new NotImplementedException (); + [MethodImplAttribute (MethodImplOptions.InternalCall)] + extern static IntPtr GetSymbol (IntPtr handle, string symbolName, bool throwOnError); + + private static void MonoLoadLibraryCallbackStub (string libraryName, Assembly assembly, bool hasDllImportSearchPathFlags, uint dllImportSearchPathFlags, ref IntPtr dll) + { + dll = LoadLibraryCallbackStub (libraryName, assembly, hasDllImportSearchPathFlags, dllImportSearchPathFlags); + } } } diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.Mono.cs index 61143c6..8dd1c3f 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.Mono.cs @@ -71,15 +71,7 @@ namespace System.Runtime.Loader if (rtAsm != null) { RuntimeAssembly runtimeAssembly = rtAsm; IntPtr ptrAssemblyLoadContext = GetLoadContextForAssembly (runtimeAssembly); - if (ptrAssemblyLoadContext == IntPtr.Zero) - { - // If the load context is returned null, then the assembly was bound using the TPA binder - // and we shall return reference to the active "Default" binder - which could be the TPA binder - // or an overridden CLRPrivBinderAssemblyLoadContext instance. - loadContextForAssembly = AssemblyLoadContext.Default; - } else { - loadContextForAssembly = (AssemblyLoadContext) (GCHandle.FromIntPtr (ptrAssemblyLoadContext).Target)!; - } + loadContextForAssembly = GetAssemblyLoadContext (ptrAssemblyLoadContext); } return loadContextForAssembly; @@ -130,6 +122,53 @@ namespace System.Runtime.Loader return ResolveSatelliteAssembly (gchALC, new AssemblyName (assemblyName)); } + private static AssemblyLoadContext GetAssemblyLoadContext (IntPtr gchManagedAssemblyLoadContext) + { + AssemblyLoadContext context; + // This check exists because the function can be called early in startup, before the default ALC is initialized + if (gchManagedAssemblyLoadContext == IntPtr.Zero) + context = AssemblyLoadContext.Default; + else + context = (AssemblyLoadContext)(GCHandle.FromIntPtr (gchManagedAssemblyLoadContext).Target)!; + return context; + } + + private static void MonoResolveUnmanagedDll (string unmanagedDllName, IntPtr gchManagedAssemblyLoadContext, ref IntPtr dll) + { + AssemblyLoadContext context = GetAssemblyLoadContext (gchManagedAssemblyLoadContext); + dll = context.LoadUnmanagedDll(unmanagedDllName); + } + + private static void MonoResolveUnmanagedDllUsingEvent (string unmanagedDllName, Assembly assembly, IntPtr gchManagedAssemblyLoadContext, ref IntPtr dll) + { + AssemblyLoadContext context = GetAssemblyLoadContext (gchManagedAssemblyLoadContext); + dll = context.GetResolvedUnmanagedDll(assembly, unmanagedDllName); + } + +#region Copied from AssemblyLoadContext.CoreCLR.cs, being moved to shared + private IntPtr GetResolvedUnmanagedDll(Assembly assembly, string unmanagedDllName) + { + IntPtr resolvedDll = IntPtr.Zero; + + Func? dllResolveHandler = _resolvingUnmanagedDll; + + if (dllResolveHandler != null) + { + // Loop through the event subscribers and return the first non-null native library handle + foreach (Func handler in dllResolveHandler.GetInvocationList()) + { + resolvedDll = handler(assembly, unmanagedDllName); + if (resolvedDll != IntPtr.Zero) + { + return resolvedDll; + } + } + } + + return IntPtr.Zero; + } +#endregion + #region Copied from AssemblyLoadContext.CoreCLR.cs, modified until our AssemblyBuilder implementation is functional private static RuntimeAssembly? GetRuntimeAssembly(Assembly? asm) {