[2020-02] Fix leak in assembly-specific dllmap lookups (#21053)
[mono-project.git] / mono / metadata / native-library.c
blob0f556794a38001c7b628c180354c4add3e5b5ab7
1 #include "config.h"
2 #include "mono/metadata/assembly-internals.h"
3 #include "mono/metadata/class-internals.h"
4 #include "mono/metadata/icall-decl.h"
5 #include "mono/metadata/loader-internals.h"
6 #include "mono/metadata/loader.h"
7 #include "mono/metadata/object-internals.h"
8 #include "mono/metadata/reflection-internals.h"
9 #include "mono/utils/checked-build.h"
10 #include "mono/utils/mono-compiler.h"
11 #include "mono/utils/mono-logger-internals.h"
12 #include "mono/utils/mono-path.h"
14 #ifdef ENABLE_NETCORE
15 static int pinvoke_search_directories_count;
16 static char **pinvoke_search_directories;
18 // In DllImportSearchPath enum, bit 0x2 represents AssemblyDirectory. It is not passed on and is instead handled by the runtime.
19 #define DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY 0x2
21 // This lock may be taken within an ALC lock, and should never be the other way around.
22 static MonoCoopMutex native_library_module_lock;
23 static GHashTable *native_library_module_map;
25 * This blacklist is used as a set for cache invalidation purposes with netcore pinvokes.
26 * When pinvokes are resolved with anything other than the last-chance managed event,
27 * the results of that lookup are added to an ALC-level cache. However, if a library is then
28 * unloaded with NativeLibrary.Free(), this cache should be invalidated so that a newly called
29 * pinvoke will not attempt to use it, hence the blacklist. This design means that if another
30 * library is loaded at the same address, it will function with a perf hit, as the entry will
31 * repeatedly be added and removed from the cache due to its presence in the blacklist.
32 * This is a rare scenario and considered a worthwhile tradeoff.
34 static GHashTable *native_library_module_blacklist;
35 #endif
37 #ifndef DISABLE_DLLMAP
38 static MonoDllMap *global_dll_map;
39 #endif
41 static GHashTable *global_module_map; // should only be accessed with the global loader data lock
43 static MonoDl *internal_module; // used when pinvoking `__Internal`
45 // Did we initialize the temporary directory for dynamic libraries
46 // FIXME: this is racy
47 static gboolean bundle_save_library_initialized;
49 // List of bundled libraries we unpacked
50 static GSList *bundle_library_paths;
52 // Directory where we unpacked dynamic libraries
53 static char *bundled_dylibrary_directory;
55 typedef enum {
56 LOOKUP_PINVOKE_ERR_OK = 0, /* No error */
57 LOOKUP_PINVOKE_ERR_NO_LIB, /* DllNotFoundException */
58 LOOKUP_PINVOKE_ERR_NO_SYM, /* EntryPointNotFoundException */
59 } MonoLookupPInvokeErr;
61 /* We should just use a MonoError, but mono_lookup_pinvoke_call has this legacy
62 * error reporting mechanism where it returns an exception class and a string
63 * message. So instead we return an error code and message, and for internal
64 * callers convert it to a MonoError.
66 * Don't expose this type to the runtime. It's just an implementation
67 * detail for backward compatability.
69 typedef struct MonoLookupPInvokeStatus {
70 MonoLookupPInvokeErr err_code;
71 char *err_arg;
72 } MonoLookupPInvokeStatus;
74 /* Class lazy loading functions */
75 GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException")
76 GENERATE_TRY_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException")
77 #ifdef ENABLE_NETCORE
78 GENERATE_GET_CLASS_WITH_CACHE (native_library, "System.Runtime.InteropServices", "NativeLibrary");
79 #endif
81 #ifndef DISABLE_DLLMAP
83 * LOCKING: Assumes the relevant lock is held.
84 * For the global DllMap, this is `global_loader_data_mutex`, and for images it's their internal lock.
86 static gboolean
87 mono_dllmap_lookup_list (MonoDllMap *dll_map, const char *dll, const char* func, const char **rdll, const char **rfunc) {
88 gboolean found = FALSE;
90 *rdll = dll;
91 *rfunc = func;
93 if (!dll_map)
94 goto exit;
96 /*
97 * we use the first entry we find that matches, since entries from
98 * the config file are prepended to the list and we document that the
99 * later entries win.
101 for (; dll_map; dll_map = dll_map->next) {
102 // Check case-insensitively when the dll name is prefixed with 'i:'
103 gboolean case_insensitive_match = strncmp (dll_map->dll, "i:", 2) == 0 && g_ascii_strcasecmp (dll_map->dll + 2, dll) == 0;
104 gboolean case_sensitive_match = strcmp (dll_map->dll, dll) == 0;
105 if (!(case_insensitive_match || case_sensitive_match))
106 continue;
108 if (!found && dll_map->target) {
109 *rdll = dll_map->target;
110 found = TRUE;
111 /* we don't quit here, because we could find a full
112 * entry that also matches the function, which takes priority.
115 if (dll_map->func && strcmp (dll_map->func, func) == 0) {
116 *rdll = dll_map->target;
117 *rfunc = dll_map->target_func;
118 break;
122 exit:
123 return found;
127 * The locking and GC state transitions here are wonky due to the fact the image lock is a coop lock
128 * and the global loader data lock is an OS lock.
130 static gboolean
131 mono_dllmap_lookup (MonoImage *assembly, const char *dll, const char* func, const char **rdll, const char **rfunc)
133 gboolean res;
135 MONO_REQ_GC_UNSAFE_MODE;
137 if (assembly && assembly->dll_map) {
138 mono_image_lock (assembly);
139 res = mono_dllmap_lookup_list (assembly->dll_map, dll, func, rdll, rfunc);
140 mono_image_unlock (assembly);
141 if (res)
142 goto leave;
145 MONO_ENTER_GC_SAFE;
147 mono_global_loader_data_lock ();
148 res = mono_dllmap_lookup_list (global_dll_map, dll, func, rdll, rfunc);
149 mono_global_loader_data_unlock ();
151 MONO_EXIT_GC_SAFE;
153 leave:
154 *rdll = g_strdup (*rdll);
155 *rfunc = g_strdup (*rfunc);
157 return res;
160 static void
161 dllmap_insert_global (const char *dll, const char *func, const char *tdll, const char *tfunc)
163 MonoDllMap *entry;
165 entry = (MonoDllMap *)g_malloc0 (sizeof (MonoDllMap));
166 entry->dll = dll? g_strdup (dll): NULL;
167 entry->target = tdll? g_strdup (tdll): NULL;
168 entry->func = func? g_strdup (func): NULL;
169 entry->target_func = tfunc? g_strdup (tfunc): (func? g_strdup (func): NULL);
171 // No transition here because this is early in startup
172 mono_global_loader_data_lock ();
173 entry->next = global_dll_map;
174 global_dll_map = entry;
175 mono_global_loader_data_unlock ();
179 static void
180 dllmap_insert_image (MonoImage *assembly, const char *dll, const char *func, const char *tdll, const char *tfunc)
182 MonoDllMap *entry;
183 g_assert (assembly != NULL);
185 MONO_REQ_GC_UNSAFE_MODE;
187 entry = (MonoDllMap *)mono_image_alloc0 (assembly, sizeof (MonoDllMap));
188 entry->dll = dll? mono_image_strdup (assembly, dll): NULL;
189 entry->target = tdll? mono_image_strdup (assembly, tdll): NULL;
190 entry->func = func? mono_image_strdup (assembly, func): NULL;
191 entry->target_func = tfunc? mono_image_strdup (assembly, tfunc): (func? mono_image_strdup (assembly, func): NULL);
193 mono_image_lock (assembly);
194 entry->next = assembly->dll_map;
195 assembly->dll_map = entry;
196 mono_image_unlock (assembly);
200 * LOCKING: Assumes the relevant lock is held.
201 * For the global DllMap, this is `global_loader_data_mutex`, and for images it's their internal lock.
203 static void
204 free_dllmap (MonoDllMap *map)
206 while (map) {
207 MonoDllMap *next = map->next;
209 g_free (map->dll);
210 g_free (map->target);
211 g_free (map->func);
212 g_free (map->target_func);
213 g_free (map);
214 map = next;
218 void
219 mono_dllmap_insert_internal (MonoImage *assembly, const char *dll, const char *func, const char *tdll, const char *tfunc)
221 mono_loader_init ();
222 // The locking in here is _really_ wonky, and I'm not convinced this function should exist.
223 // I've split it into an internal version to offer flexibility in the future.
224 if (!assembly)
225 dllmap_insert_global (dll, func, tdll, tfunc);
226 else
227 dllmap_insert_image (assembly, dll, func, tdll, tfunc);
230 void
231 mono_global_dllmap_cleanup (void)
233 // No need for a transition here since the thread is already detached from the runtime
234 mono_global_loader_data_lock ();
236 free_dllmap (global_dll_map);
237 global_dll_map = NULL;
239 // We don't care about freeing native_library_module_map on netcore because of the expedited shutdown.
241 mono_global_loader_data_unlock ();
243 #endif
246 * mono_dllmap_insert:
247 * \param assembly if NULL, this is a global mapping, otherwise the remapping of the dynamic library will only apply to the specified assembly
248 * \param dll The name of the external library, as it would be found in the \c DllImport declaration. If prefixed with <code>i:</code> the matching of the library name is done without case sensitivity
249 * \param func if not null, the mapping will only applied to the named function (the value of <code>EntryPoint</code>)
250 * \param tdll The name of the library to map the specified \p dll if it matches.
251 * \param tfunc The name of the function that replaces the invocation. If NULL, it is replaced with a copy of \p func.
253 * LOCKING: Acquires the image lock, or the loader data lock if an image is not passed.
255 * This function is used to programatically add \c DllImport remapping in either
256 * a specific assembly, or as a global remapping. This is done by remapping
257 * references in a \c DllImport attribute from the \p dll library name into the \p tdll
258 * name. If the \p dll name contains the prefix <code>i:</code>, the comparison of the
259 * library name is done without case sensitivity.
261 * If you pass \p func, this is the name of the \c EntryPoint in a \c DllImport if specified
262 * or the name of the function as determined by \c DllImport. If you pass \p func, you
263 * must also pass \p tfunc which is the name of the target function to invoke on a match.
265 * Example:
267 * <code>mono_dllmap_insert (NULL, "i:libdemo.dll", NULL, relocated_demo_path, NULL);</code>
269 * The above will remap \c DllImport statements for \c libdemo.dll and \c LIBDEMO.DLL to
270 * the contents of \c relocated_demo_path for all assemblies in the Mono process.
272 * NOTE: This can be called before the runtime is initialized, for example from
273 * \c mono_config_parse.
275 void
276 mono_dllmap_insert (MonoImage *assembly, const char *dll, const char *func, const char *tdll, const char *tfunc)
278 #ifndef DISABLE_DLLMAP
279 mono_dllmap_insert_internal (assembly, dll, func, tdll, tfunc);
280 #endif
283 void
284 mono_loader_register_module (const char *name, MonoDl *module)
286 mono_loader_init ();
288 // No transition here because this is early in startup
289 mono_global_loader_data_lock ();
291 g_hash_table_insert (global_module_map, g_strdup (name), module);
293 mono_global_loader_data_unlock ();
296 #ifdef ENABLE_NETCORE
297 static MonoDl *
298 mono_loader_register_module_locking (const char *name, MonoDl *module)
300 MonoDl *result = NULL;
302 MONO_ENTER_GC_SAFE;
303 mono_global_loader_data_lock ();
304 MONO_EXIT_GC_SAFE;
306 result = (MonoDl *)g_hash_table_lookup (global_module_map, name);
307 if (result) {
308 g_free (module->full_name);
309 g_free (module);
310 goto exit;
313 g_hash_table_insert (global_module_map, g_strdup (name), module);
314 result = module;
316 exit:
317 MONO_ENTER_GC_SAFE;
318 mono_global_loader_data_unlock ();
319 MONO_EXIT_GC_SAFE;
321 return result;
323 #endif
325 static void
326 remove_cached_module (gpointer key, gpointer value, gpointer user_data)
328 mono_dl_close((MonoDl*)value);
331 void
332 mono_global_loader_cache_init (void)
334 if (!global_module_map)
335 global_module_map = g_hash_table_new (g_str_hash, g_str_equal);
337 #ifdef ENABLE_NETCORE
338 if (!native_library_module_map)
339 native_library_module_map = g_hash_table_new (g_direct_hash, g_direct_equal);
340 if (!native_library_module_blacklist)
341 native_library_module_blacklist = g_hash_table_new (g_direct_hash, g_direct_equal);
342 mono_coop_mutex_init (&native_library_module_lock);
343 #endif
346 void
347 mono_global_loader_cache_cleanup (void)
349 if (global_module_map != NULL) {
350 g_hash_table_foreach(global_module_map, remove_cached_module, NULL);
352 g_hash_table_destroy(global_module_map);
353 global_module_map = NULL;
356 // No need to clean up the native library hash tables since they're netcore-only, where this is never called
359 static gboolean
360 is_absolute_path (const char *path)
362 // FIXME: other platforms have similar prefixes, such as $ORIGIN
363 #ifdef HOST_DARWIN
364 if (!strncmp (path, "@executable_path/", 17) || !strncmp (path, "@loader_path/", 13) || !strncmp (path, "@rpath/", 7))
365 return TRUE;
366 #endif
367 return g_path_is_absolute (path);
370 static gpointer
371 lookup_pinvoke_call_impl (MonoMethod *method, MonoLookupPInvokeStatus *status_out);
373 static gpointer
374 pinvoke_probe_for_symbol (MonoDl *module, MonoMethodPInvoke *piinfo, const char *import, char **error_msg_out);
376 static void
377 pinvoke_probe_convert_status_for_api (MonoLookupPInvokeStatus *status, const char **exc_class, const char **exc_arg)
379 if (!exc_class)
380 return;
381 switch (status->err_code) {
382 case LOOKUP_PINVOKE_ERR_OK:
383 *exc_class = NULL;
384 *exc_arg = NULL;
385 break;
386 case LOOKUP_PINVOKE_ERR_NO_LIB:
387 *exc_class = "DllNotFoundException";
388 *exc_arg = status->err_arg;
389 status->err_arg = NULL;
390 break;
391 case LOOKUP_PINVOKE_ERR_NO_SYM:
392 *exc_class = "EntryPointNotFoundException";
393 *exc_arg = status->err_arg;
394 status->err_arg = NULL;
395 break;
396 default:
397 g_assert_not_reached ();
401 static void
402 pinvoke_probe_convert_status_to_error (MonoLookupPInvokeStatus *status, MonoError *error)
404 /* Note: this has to return a MONO_ERROR_GENERIC because mono_mb_emit_exception_for_error only knows how to decode generic errors. */
405 switch (status->err_code) {
406 case LOOKUP_PINVOKE_ERR_OK:
407 return;
408 case LOOKUP_PINVOKE_ERR_NO_LIB:
409 mono_error_set_generic_error (error, "System", "DllNotFoundException", "%s", status->err_arg);
410 g_free (status->err_arg);
411 status->err_arg = NULL;
412 break;
413 case LOOKUP_PINVOKE_ERR_NO_SYM:
414 mono_error_set_generic_error (error, "System", "EntryPointNotFoundException", "%s", status->err_arg);
415 g_free (status->err_arg);
416 status->err_arg = NULL;
417 break;
418 default:
419 g_assert_not_reached ();
424 * mono_lookup_pinvoke_call:
426 gpointer
427 mono_lookup_pinvoke_call (MonoMethod *method, const char **exc_class, const char **exc_arg)
429 gpointer result;
430 MONO_ENTER_GC_UNSAFE;
431 MonoLookupPInvokeStatus status;
432 memset (&status, 0, sizeof (status));
433 result = lookup_pinvoke_call_impl (method, &status);
434 pinvoke_probe_convert_status_for_api (&status, exc_class, exc_arg);
435 MONO_EXIT_GC_UNSAFE;
436 return result;
439 gpointer
440 mono_lookup_pinvoke_call_internal (MonoMethod *method, MonoError *error)
442 gpointer result;
443 MonoLookupPInvokeStatus status;
444 memset (&status, 0, sizeof (status));
445 result = lookup_pinvoke_call_impl (method, &status);
446 if (status.err_code)
447 pinvoke_probe_convert_status_to_error (&status, error);
448 return result;
451 #ifdef ENABLE_NETCORE
452 void
453 mono_set_pinvoke_search_directories (int dir_count, char **dirs)
455 pinvoke_search_directories_count = dir_count;
456 pinvoke_search_directories = dirs;
459 static void
460 native_library_lock (void)
462 mono_coop_mutex_lock (&native_library_module_lock);
465 static void
466 native_library_unlock (void)
468 mono_coop_mutex_unlock (&native_library_module_lock);
471 static void
472 alc_pinvoke_lock (MonoAssemblyLoadContext *alc)
474 mono_coop_mutex_lock (&alc->pinvoke_lock);
477 static void
478 alc_pinvoke_unlock (MonoAssemblyLoadContext *alc)
480 mono_coop_mutex_unlock (&alc->pinvoke_lock);
483 // LOCKING: expects you to hold native_library_module_lock
484 static MonoDl *
485 netcore_handle_lookup (gpointer handle)
487 return (MonoDl *)g_hash_table_lookup (native_library_module_map, handle);
490 // LOCKING: expects you to hold native_library_module_lock
491 static gboolean
492 netcore_check_blacklist (MonoDl *module)
494 return g_hash_table_contains (native_library_module_blacklist, module);
497 static MonoDl *
498 netcore_probe_for_module_variations (const char *mdirname, const char *file_name)
500 void *iter = NULL;
501 char *full_name;
502 MonoDl *module = NULL;
504 // This does not actually mirror CoreCLR's algorithm; if that becomes a problem, potentially use theirs
505 // FIXME: this appears to search *.dylib twice for some reason
506 while ((full_name = mono_dl_build_path (mdirname, file_name, &iter)) && module == NULL) {
507 char *error_msg;
508 module = mono_dl_open (full_name, MONO_DL_LAZY, &error_msg);
509 if (!module) {
510 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", full_name, error_msg);
511 g_free (error_msg);
513 g_free (full_name);
515 g_free (full_name);
517 return module;
520 static MonoDl *
521 netcore_probe_for_module (MonoImage *image, const char *file_name, int flags)
523 MonoDl *module = NULL;
524 gboolean search_assembly_dir = flags & DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
526 // Try without any path additions
527 module = netcore_probe_for_module_variations (NULL, file_name);
529 // Check the NATIVE_DLL_SEARCH_DIRECTORIES
530 for (int i = 0; i < pinvoke_search_directories_count && module == NULL; ++i)
531 module = netcore_probe_for_module_variations (pinvoke_search_directories[i], file_name);
533 // Check the assembly directory if the search flag is set and the image exists
534 if (search_assembly_dir && image != NULL && module == NULL) {
535 char *mdirname = g_path_get_dirname (image->filename);
536 if (mdirname)
537 module = netcore_probe_for_module_variations (mdirname, file_name);
538 g_free (mdirname);
541 return module;
544 static MonoDl *
545 netcore_resolve_with_dll_import_resolver (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope, guint32 flags, MonoError *error)
547 MonoDl *result = NULL;
548 gpointer lib = NULL;
549 MonoDomain *domain = mono_alc_domain (alc);
551 MONO_STATIC_POINTER_INIT (MonoMethod, resolve)
553 ERROR_DECL (local_error);
554 MonoClass *native_lib_class = mono_class_get_native_library_class ();
555 g_assert (native_lib_class);
556 resolve = mono_class_get_method_from_name_checked (native_lib_class, "MonoLoadLibraryCallbackStub", -1, 0, local_error);
557 mono_error_assert_ok (local_error);
559 MONO_STATIC_POINTER_INIT_END (MonoMethod, resolve)
560 g_assert (resolve);
562 if (mono_runtime_get_no_exec ())
563 return NULL;
565 HANDLE_FUNCTION_ENTER ();
567 MonoStringHandle scope_handle = mono_string_new_handle (domain, scope, error);
568 goto_if_nok (error, leave);
569 MonoReflectionAssemblyHandle assembly_handle = mono_assembly_get_object_handle (domain, assembly, error);
570 goto_if_nok (error, leave);
572 gboolean has_search_flags = flags != 0 ? TRUE : FALSE;
573 gpointer args [5];
574 args [0] = MONO_HANDLE_RAW (scope_handle);
575 args [1] = MONO_HANDLE_RAW (assembly_handle);
576 args [2] = &has_search_flags;
577 args [3] = &flags;
578 args [4] = &lib;
579 mono_runtime_invoke_checked (resolve, NULL, args, error);
580 goto_if_nok (error, leave);
582 native_library_lock ();
583 result = netcore_handle_lookup (lib);
584 native_library_unlock ();
586 leave:
587 HANDLE_FUNCTION_RETURN_VAL (result);
590 static MonoDl *
591 netcore_resolve_with_dll_import_resolver_nofail (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope, guint32 flags)
593 MonoDl *result = NULL;
594 ERROR_DECL (error);
596 result = netcore_resolve_with_dll_import_resolver (alc, assembly, scope, flags, error);
597 if (!is_ok (error))
598 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Error while invoking ALC DllImportResolver(\"%s\") delegate: '%s'", scope, mono_error_get_message (error));
600 mono_error_cleanup (error);
602 return result;
605 static MonoDl *
606 netcore_resolve_with_load (MonoAssemblyLoadContext *alc, const char *scope, MonoError *error)
608 MonoDl *result = NULL;
609 gpointer lib = NULL;
611 MONO_STATIC_POINTER_INIT (MonoMethod, resolve)
613 ERROR_DECL (local_error);
614 MonoClass *alc_class = mono_class_get_assembly_load_context_class ();
615 g_assert (alc_class);
616 resolve = mono_class_get_method_from_name_checked (alc_class, "MonoResolveUnmanagedDll", -1, 0, local_error);
617 mono_error_assert_ok (local_error);
619 MONO_STATIC_POINTER_INIT_END (MonoMethod, resolve)
620 g_assert (resolve);
622 if (mono_runtime_get_no_exec ())
623 return NULL;
625 HANDLE_FUNCTION_ENTER ();
627 MonoStringHandle scope_handle = mono_string_new_handle (mono_alc_domain (alc), scope, error);
628 goto_if_nok (error, leave);
630 gpointer gchandle = GUINT_TO_POINTER (alc->gchandle);
631 gpointer args [3];
632 args [0] = MONO_HANDLE_RAW (scope_handle);
633 args [1] = &gchandle;
634 args [2] = &lib;
635 mono_runtime_invoke_checked (resolve, NULL, args, error);
636 goto_if_nok (error, leave);
638 native_library_lock ();
639 result = netcore_handle_lookup (lib);
640 native_library_unlock ();
642 leave:
643 HANDLE_FUNCTION_RETURN_VAL (result);
646 static MonoDl *
647 netcore_resolve_with_load_nofail (MonoAssemblyLoadContext *alc, const char *scope)
649 MonoDl *result = NULL;
650 ERROR_DECL (error);
652 result = netcore_resolve_with_load (alc, scope, error);
653 if (!is_ok (error))
654 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Error while invoking ALC LoadUnmanagedDll(\"%s\") method: '%s'", scope, mono_error_get_message (error));
656 mono_error_cleanup (error);
658 return result;
661 static MonoDl *
662 netcore_resolve_with_resolving_event (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope, MonoError *error)
664 MonoDl *result = NULL;
665 gpointer lib = NULL;
666 MonoDomain *domain = mono_alc_domain (alc);
668 MONO_STATIC_POINTER_INIT (MonoMethod, resolve)
670 ERROR_DECL (local_error);
671 MonoClass *alc_class = mono_class_get_assembly_load_context_class ();
672 g_assert (alc_class);
673 resolve = mono_class_get_method_from_name_checked (alc_class, "MonoResolveUnmanagedDllUsingEvent", -1, 0, local_error);
674 mono_error_assert_ok (local_error);
676 MONO_STATIC_POINTER_INIT_END (MonoMethod, resolve)
677 g_assert (resolve);
679 if (mono_runtime_get_no_exec ())
680 return NULL;
682 HANDLE_FUNCTION_ENTER ();
684 MonoStringHandle scope_handle = mono_string_new_handle (domain, scope, error);
685 goto_if_nok (error, leave);
686 MonoReflectionAssemblyHandle assembly_handle = mono_assembly_get_object_handle (domain, assembly, error);
687 goto_if_nok (error, leave);
689 gpointer gchandle = GUINT_TO_POINTER (alc->gchandle);
690 gpointer args [4];
691 args [0] = MONO_HANDLE_RAW (scope_handle);
692 args [1] = MONO_HANDLE_RAW (assembly_handle);
693 args [2] = &gchandle;
694 args [3] = &lib;
695 mono_runtime_invoke_checked (resolve, NULL, args, error);
696 goto_if_nok (error, leave);
698 native_library_lock ();
699 result = netcore_handle_lookup (lib);
700 native_library_unlock ();
702 leave:
703 HANDLE_FUNCTION_RETURN_VAL (result);
706 static MonoDl *
707 netcore_resolve_with_resolving_event_nofail (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope)
709 MonoDl *result = NULL;
710 ERROR_DECL (error);
712 result = netcore_resolve_with_resolving_event (alc, assembly, scope, error);
713 if (!is_ok (error))
714 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Error while invoking ALC ResolvingUnmangedDll(\"%s\") event: '%s'", scope, mono_error_get_message (error));
716 mono_error_cleanup (error);
718 return result;
721 // LOCKING: expects you to hold the ALC's pinvoke lock
722 static MonoDl *
723 netcore_check_alc_cache (MonoAssemblyLoadContext *alc, const char *scope)
725 MonoDl *result = NULL;
727 result = (MonoDl *)g_hash_table_lookup (alc->pinvoke_scopes, scope);
729 if (result) {
730 gboolean blacklisted;
732 native_library_lock ();
733 blacklisted = netcore_check_blacklist (result);
734 native_library_unlock ();
736 if (blacklisted) {
737 g_hash_table_remove (alc->pinvoke_scopes, scope);
738 result = NULL;
742 return result;
745 static MonoDl *
746 netcore_lookup_native_library (MonoAssemblyLoadContext *alc, MonoImage *image, const char *scope, guint32 flags)
748 MonoDl *module = NULL;
749 MonoDl *cached;
750 MonoAssembly *assembly = mono_image_get_assembly (image);
751 char *error_msg = NULL;
753 MONO_REQ_GC_UNSAFE_MODE;
755 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport attempting to load: '%s'.", scope);
757 // We allow a special name to dlopen from the running process namespace, which is not present in CoreCLR
758 if (strcmp (scope, "__Internal") == 0) {
759 if (!internal_module)
760 internal_module = mono_dl_open (NULL, MONO_DL_LAZY, &error_msg);
761 module = internal_module;
763 if (!module) {
764 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '__Internal': '%s'.", error_msg);
765 g_free (error_msg);
768 return module;
772 * Try these until one of them succeeds:
774 * 1. Check the cache in the active ALC.
776 * 2. Call the DllImportResolver on the active assembly.
778 * 3. Call LoadUnmanagedDll on the active ALC.
780 * 4. Check the global cache.
782 * 5. Run the unmanaged probing logic.
784 * 6. Raise the ResolvingUnmanagedDll event on the active ALC.
786 * 7. Return NULL.
789 alc_pinvoke_lock (alc);
790 module = netcore_check_alc_cache (alc, scope);
791 alc_pinvoke_unlock (alc);
792 if (module)
793 goto leave;
795 module = (MonoDl *)netcore_resolve_with_dll_import_resolver_nofail (alc, assembly, scope, flags);
796 if (module)
797 goto add_to_alc_cache;
799 module = (MonoDl *)netcore_resolve_with_load_nofail (alc, scope);
800 if (module)
801 goto add_to_alc_cache;
803 MONO_ENTER_GC_SAFE;
804 mono_global_loader_data_lock ();
805 MONO_EXIT_GC_SAFE;
806 module = (MonoDl *)g_hash_table_lookup (global_module_map, scope);
807 MONO_ENTER_GC_SAFE;
808 mono_global_loader_data_unlock ();
809 MONO_EXIT_GC_SAFE;
810 if (module)
811 goto add_to_alc_cache;
813 module = netcore_probe_for_module (image, scope, flags);
814 if (module)
815 goto add_to_global_cache;
817 /* As this is last chance, I've opted not to put it in a cache, but that is not necessarily the correct decision.
818 * It is rather convenient here, however, because it means the global cache will only be populated by libraries
819 * resolved via netcore_probe_for_module and not NativeLibrary, eliminating potential races/conflicts.
821 module = netcore_resolve_with_resolving_event_nofail (alc, assembly, scope);
822 goto leave;
824 add_to_global_cache:
825 module = mono_loader_register_module_locking (scope, module);
827 add_to_alc_cache:
828 /* Nothing is closed here because the only two places this can come from are:
829 * 1. A managed callback that made use of NativeLibrary.Load, in which case closing is dependent on NativeLibrary.Free
830 * 2. The global cache, which is only populated by results of netcore_probe_for_module. When adding to the global cache,
831 * we free the new MonoDl if another thread beat us, so we don't have to repeat that here.
833 alc_pinvoke_lock (alc);
834 cached = netcore_check_alc_cache (alc, scope);
835 if (cached)
836 module = cached;
837 else
838 g_hash_table_insert (alc->pinvoke_scopes, g_strdup (scope), module);
839 alc_pinvoke_unlock (alc);
841 leave:
842 return module;
845 #else // ENABLE_NETCORE
847 static MonoDl *
848 cached_module_load (const char *name, int flags, char **err)
850 MonoDl *res;
852 if (err)
853 *err = NULL;
855 MONO_ENTER_GC_SAFE;
856 mono_global_loader_data_lock ();
857 MONO_EXIT_GC_SAFE;
859 res = (MonoDl *)g_hash_table_lookup (global_module_map, name);
860 if (res)
861 goto exit;
863 res = mono_dl_open (name, flags, err);
864 if (res)
865 g_hash_table_insert (global_module_map, g_strdup (name), res);
867 exit:
868 MONO_ENTER_GC_SAFE;
869 mono_global_loader_data_unlock ();
870 MONO_EXIT_GC_SAFE;
872 return res;
876 * legacy_probe_transform_path:
878 * Try transforming the library path given in \p new_scope in different ways
879 * depending on \p phase
881 * \returns \c TRUE if a transformation was applied and the transformed path
882 * components are written to the out arguments, or \c FALSE if a transformation
883 * did not apply.
885 static gboolean
886 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)
888 char *file_name = NULL, *base_name = NULL, *dir_name = NULL;
889 gboolean changed = FALSE;
890 gboolean is_absolute = is_absolute_path (new_scope);
891 switch (phase) {
892 case 0:
893 /* Try the original name */
894 file_name = g_strdup (new_scope);
895 changed = TRUE;
896 break;
897 case 1:
898 /* Try trimming the .dll extension */
899 if (strstr (new_scope, ".dll") == (new_scope + strlen (new_scope) - 4)) {
900 file_name = g_strdup (new_scope);
901 file_name [strlen (new_scope) - 4] = '\0';
902 changed = TRUE;
904 break;
905 case 2:
906 if (is_absolute) {
907 dir_name = g_path_get_dirname (new_scope);
908 base_name = g_path_get_basename (new_scope);
909 if (strstr (base_name, "lib") != base_name) {
910 char *tmp = g_strdup_printf ("lib%s", base_name);
911 g_free (base_name);
912 base_name = tmp;
913 file_name = g_strdup_printf ("%s%s%s", dir_name, G_DIR_SEPARATOR_S, base_name);
914 changed = TRUE;
916 } else if (strstr (new_scope, "lib") != new_scope) {
917 file_name = g_strdup_printf ("lib%s", new_scope);
918 changed = TRUE;
920 break;
921 case 3:
922 if (!is_absolute && mono_dl_get_system_dir ()) {
923 dir_name = (char*)mono_dl_get_system_dir ();
924 file_name = g_path_get_basename (new_scope);
925 base_name = NULL;
926 changed = TRUE;
928 break;
929 default:
930 #ifndef TARGET_WIN32
931 if (!g_ascii_strcasecmp ("user32.dll", new_scope) ||
932 !g_ascii_strcasecmp ("kernel32.dll", new_scope) ||
933 !g_ascii_strcasecmp ("user32", new_scope) ||
934 !g_ascii_strcasecmp ("kernel", new_scope)) {
935 file_name = g_strdup ("libMonoSupportW.so");
936 changed = TRUE;
938 #endif
939 break;
941 if (changed && is_absolute) {
942 if (!dir_name)
943 dir_name = g_path_get_dirname (file_name);
944 if (!base_name)
945 base_name = g_path_get_basename (file_name);
947 *file_name_out = file_name;
948 *base_name_out = base_name;
949 *dir_name_out = dir_name;
950 *is_absolute_out = is_absolute;
951 return changed;
954 static MonoDl *
955 legacy_probe_for_module_in_directory (const char *mdirname, const char *file_name)
957 void *iter = NULL;
958 char *full_name;
959 MonoDl* module = NULL;
961 while ((full_name = mono_dl_build_path (mdirname, file_name, &iter)) && module == NULL) {
962 char *error_msg;
963 module = cached_module_load (full_name, MONO_DL_LAZY, &error_msg);
964 if (!module) {
965 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", full_name, error_msg);
966 g_free (error_msg);
968 g_free (full_name);
970 g_free (full_name);
972 return module;
975 static MonoDl *
976 legacy_probe_for_module_relative_directories (MonoImage *image, const char *file_name)
978 MonoDl* module = NULL;
980 for (int j = 0; j < 3; ++j) {
981 char *mdirname = NULL;
983 switch (j) {
984 case 0:
985 mdirname = g_path_get_dirname (image->filename);
986 break;
987 case 1: // @executable_path@/../lib
989 char buf [4096]; // FIXME: MAX_PATH
990 int binl;
991 binl = mono_dl_get_executable_path (buf, sizeof (buf));
992 if (binl != -1) {
993 char *base, *newbase;
994 char *resolvedname;
995 buf [binl] = 0;
996 resolvedname = mono_path_resolve_symlinks (buf);
998 base = g_path_get_dirname (resolvedname);
999 newbase = g_path_get_dirname(base);
1001 // On Android the executable for the application is going to be /system/bin/app_process{32,64} depending on
1002 // the application's architecture. However, libraries for the different architectures live in different
1003 // subdirectories of `/system`: `lib` for 32-bit apps and `lib64` for 64-bit ones. Thus appending `/lib` below
1004 // will fail to load the DSO for a 64-bit app, even if it exists there, because it will have a different
1005 // architecture. This is the cause of https://github.com/xamarin/xamarin-android/issues/2780 and the ifdef
1006 // below is the fix.
1007 mdirname = g_strdup_printf (
1008 #if defined(TARGET_ANDROID) && (defined(TARGET_ARM64) || defined(TARGET_AMD64))
1009 "%s/lib64",
1010 #else
1011 "%s/lib",
1012 #endif
1013 newbase);
1014 g_free (resolvedname);
1015 g_free (base);
1016 g_free (newbase);
1018 break;
1020 #ifdef __MACH__
1021 case 2: // @executable_path@/../Libraries
1023 char buf [4096]; // FIXME: MAX_PATH
1024 int binl;
1025 binl = mono_dl_get_executable_path (buf, sizeof (buf));
1026 if (binl != -1) {
1027 char *base, *newbase;
1028 char *resolvedname;
1029 buf [binl] = 0;
1030 resolvedname = mono_path_resolve_symlinks (buf);
1032 base = g_path_get_dirname (resolvedname);
1033 newbase = g_path_get_dirname(base);
1034 mdirname = g_strdup_printf ("%s/Libraries", newbase);
1036 g_free (resolvedname);
1037 g_free (base);
1038 g_free (newbase);
1040 break;
1042 #endif
1045 if (!mdirname)
1046 continue;
1048 module = legacy_probe_for_module_in_directory (mdirname, file_name);
1049 g_free (mdirname);
1050 if (module)
1051 break;
1054 return module;
1057 static MonoDl *
1058 legacy_probe_for_module (MonoImage *image, const char *new_scope)
1060 char *full_name, *file_name;
1061 char *error_msg = NULL;
1062 int i;
1063 MonoDl *module = NULL;
1065 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport attempting to load: '%s'.", new_scope);
1067 /* we allow a special name to dlopen from the running process namespace */
1068 if (strcmp (new_scope, "__Internal") == 0) {
1069 if (!internal_module)
1070 internal_module = mono_dl_open (NULL, MONO_DL_LAZY, &error_msg);
1071 module = internal_module;
1073 if (!module) {
1074 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '__Internal': '%s'.", error_msg);
1075 g_free (error_msg);
1078 return module;
1082 * Try loading the module using a variety of names
1084 for (i = 0; i < 5; ++i) {
1085 char *base_name = NULL, *dir_name = NULL;
1086 gboolean is_absolute;
1088 gboolean changed = legacy_probe_transform_path (new_scope, i, &file_name, &base_name, &dir_name, &is_absolute);
1089 if (!changed)
1090 continue;
1092 if (!module && is_absolute) {
1093 module = cached_module_load (file_name, MONO_DL_LAZY, &error_msg);
1094 if (!module) {
1095 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT,
1096 "DllImport error loading library '%s': '%s'.",
1097 file_name, error_msg);
1098 g_free (error_msg);
1102 if (!module && !is_absolute) {
1103 module = legacy_probe_for_module_relative_directories (image, file_name);
1106 if (!module) {
1107 void *iter = NULL;
1108 char *file_or_base = is_absolute ? base_name : file_name;
1109 while ((full_name = mono_dl_build_path (dir_name, file_or_base, &iter))) {
1110 module = cached_module_load (full_name, MONO_DL_LAZY, &error_msg);
1111 if (!module) {
1112 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT,
1113 "DllImport error loading library '%s': '%s'.",
1114 full_name, error_msg);
1115 g_free (error_msg);
1117 g_free (full_name);
1118 if (module)
1119 break;
1123 if (!module) {
1124 module = cached_module_load (file_name, MONO_DL_LAZY, &error_msg);
1125 if (!module) {
1126 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT,
1127 "DllImport error loading library '%s': '%s'.",
1128 file_name, error_msg);
1129 g_free (error_msg);
1133 g_free (file_name);
1134 if (is_absolute) {
1135 g_free (base_name);
1136 g_free (dir_name);
1139 if (module)
1140 break;
1143 return module;
1146 static MonoDl *
1147 legacy_lookup_native_library (MonoImage *image, const char *scope)
1149 MonoDl *module = NULL;
1150 gboolean cached = FALSE;
1152 mono_image_lock (image);
1153 if (!image->pinvoke_scopes)
1154 image->pinvoke_scopes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1155 module = (MonoDl *)g_hash_table_lookup (image->pinvoke_scopes, scope);
1156 mono_image_unlock (image);
1157 if (module)
1158 cached = TRUE;
1160 if (!module)
1161 module = legacy_probe_for_module (image, scope);
1163 if (module && !cached) {
1164 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT,
1165 "DllImport loaded library '%s'.", module->full_name);
1166 mono_image_lock (image);
1167 if (!g_hash_table_lookup (image->pinvoke_scopes, scope)) {
1168 g_hash_table_insert (image->pinvoke_scopes, g_strdup (scope), module);
1170 mono_image_unlock (image);
1173 return module;
1176 #endif // ENABLE_NETCORE
1178 gpointer
1179 lookup_pinvoke_call_impl (MonoMethod *method, MonoLookupPInvokeStatus *status_out)
1181 MonoImage *image = m_class_get_image (method->klass);
1182 #ifdef ENABLE_NETCORE
1183 MonoAssemblyLoadContext *alc = mono_image_get_alc (image);
1184 #endif
1185 MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *)method;
1186 MonoTableInfo *tables = image->tables;
1187 MonoTableInfo *im = &tables [MONO_TABLE_IMPLMAP];
1188 MonoTableInfo *mr = &tables [MONO_TABLE_MODULEREF];
1189 guint32 im_cols [MONO_IMPLMAP_SIZE];
1190 guint32 scope_token;
1191 const char *orig_import = NULL;
1192 const char *new_import = NULL;
1193 const char *orig_scope = NULL;
1194 const char *new_scope = NULL;
1195 char *error_msg = NULL;
1196 MonoDl *module = NULL;
1197 gpointer addr = NULL;
1199 MONO_REQ_GC_UNSAFE_MODE;
1201 g_assert (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL);
1203 g_assert (status_out);
1205 if (piinfo->addr)
1206 return piinfo->addr;
1208 if (image_is_dynamic (image)) {
1209 MonoReflectionMethodAux *method_aux =
1210 (MonoReflectionMethodAux *)g_hash_table_lookup (
1211 ((MonoDynamicImage*)m_class_get_image (method->klass))->method_aux_hash, method);
1212 if (!method_aux)
1213 goto exit;
1215 orig_import = method_aux->dllentry;
1216 orig_scope = method_aux->dll;
1218 else {
1219 if (!piinfo->implmap_idx || piinfo->implmap_idx > im->rows)
1220 goto exit;
1222 mono_metadata_decode_row (im, piinfo->implmap_idx - 1, im_cols, MONO_IMPLMAP_SIZE);
1224 if (!im_cols [MONO_IMPLMAP_SCOPE] || im_cols [MONO_IMPLMAP_SCOPE] > mr->rows)
1225 goto exit;
1227 piinfo->piflags = im_cols [MONO_IMPLMAP_FLAGS];
1228 orig_import = mono_metadata_string_heap (image, im_cols [MONO_IMPLMAP_NAME]);
1229 scope_token = mono_metadata_decode_row_col (mr, im_cols [MONO_IMPLMAP_SCOPE] - 1, MONO_MODULEREF_NAME);
1230 orig_scope = mono_metadata_string_heap (image, scope_token);
1233 #ifndef DISABLE_DLLMAP
1234 // FIXME: The dllmap remaps System.Native to mono-native
1235 mono_dllmap_lookup (image, orig_scope, orig_import, &new_scope, &new_import);
1236 #else
1237 new_scope = g_strdup (orig_scope);
1238 new_import = g_strdup (orig_import);
1239 #endif
1241 #ifdef ENABLE_NETCORE
1242 // FIXME: these flags are not getting passed correctly
1243 module = netcore_lookup_native_library (alc, image, new_scope, 0);
1244 #else
1245 module = legacy_lookup_native_library (image, new_scope);
1246 #endif
1248 if (!module) {
1249 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_DLLIMPORT,
1250 "DllImport unable to load library '%s'.",
1251 new_scope);
1253 status_out->err_code = LOOKUP_PINVOKE_ERR_NO_LIB;
1254 status_out->err_arg = g_strdup (new_scope);
1255 goto exit;
1258 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT,
1259 "DllImport searching in: '%s' ('%s').", new_scope, module->full_name);
1261 addr = pinvoke_probe_for_symbol (module, piinfo, new_import, &error_msg);
1263 if (!addr) {
1264 status_out->err_code = LOOKUP_PINVOKE_ERR_NO_SYM;
1265 status_out->err_arg = g_strdup (new_import);
1266 goto exit;
1268 piinfo->addr = addr;
1270 exit:
1271 g_free ((char *)new_import);
1272 g_free ((char *)new_scope);
1273 g_free (error_msg);
1274 return addr;
1277 static gpointer
1278 pinvoke_probe_for_symbol (MonoDl *module, MonoMethodPInvoke *piinfo, const char *import, char **error_msg_out)
1280 char *error_msg = NULL;
1281 gpointer addr = NULL;
1283 g_assert (error_msg_out);
1285 #ifdef HOST_WIN32
1286 if (import && import [0] == '#' && isdigit (import [1])) {
1287 char *end;
1288 long id;
1290 id = strtol (import + 1, &end, 10);
1291 if (id > 0 && *end == '\0')
1292 import++;
1294 #endif
1295 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT,
1296 "Searching for '%s'.", import);
1298 if (piinfo->piflags & PINVOKE_ATTRIBUTE_NO_MANGLE)
1299 error_msg = mono_dl_symbol (module, import, &addr);
1300 else {
1302 * Search using a variety of mangled names
1304 for (int mangle_stdcall = 0; mangle_stdcall <= 1 && addr == NULL; mangle_stdcall++) {
1305 #if HOST_WIN32 && HOST_X86
1306 const int max_managle_param_count = (mangle_stdcall == 0) ? 0 : 256;
1307 #else
1308 const int max_managle_param_count = 0;
1309 #endif
1310 for (int mangle_charset = 0; mangle_charset <= 1 && addr == NULL; mangle_charset ++) {
1311 for (int mangle_param_count = 0; mangle_param_count <= max_managle_param_count && addr == NULL; mangle_param_count += 4) {
1313 char *mangled_name = (char*)import;
1314 switch (piinfo->piflags & PINVOKE_ATTRIBUTE_CHAR_SET_MASK) {
1315 case PINVOKE_ATTRIBUTE_CHAR_SET_UNICODE:
1316 /* Try the mangled name first */
1317 if (mangle_charset == 0)
1318 mangled_name = g_strconcat (import, "W", (const char*)NULL);
1319 break;
1320 case PINVOKE_ATTRIBUTE_CHAR_SET_AUTO:
1321 #ifdef HOST_WIN32
1322 if (mangle_charset == 0)
1323 mangled_name = g_strconcat (import, "W", (const char*)NULL);
1324 #else
1325 /* Try the mangled name last */
1326 if (mangle_charset == 1)
1327 mangled_name = g_strconcat (import, "A", (const char*)NULL);
1328 #endif
1329 break;
1330 case PINVOKE_ATTRIBUTE_CHAR_SET_ANSI:
1331 default:
1332 /* Try the mangled name last */
1333 if (mangle_charset == 1)
1334 mangled_name = g_strconcat (import, "A", (const char*)NULL);
1335 break;
1338 #if HOST_WIN32 && HOST_X86
1339 /* Try the stdcall mangled name */
1341 * gcc under windows creates mangled names without the underscore, but MS.NET
1342 * doesn't support it, so we doesn't support it either.
1344 if (mangle_stdcall == 1) {
1345 MonoMethod *method = &piinfo->method;
1346 int param_count;
1347 if (mangle_param_count == 0)
1348 param_count = mono_method_signature_internal (method)->param_count * sizeof (gpointer);
1349 else
1350 /* Try brute force, since it would be very hard to compute the stack usage correctly */
1351 param_count = mangle_param_count;
1353 char *mangled_stdcall_name = g_strdup_printf ("_%s@%d", mangled_name, param_count);
1355 if (mangled_name != import)
1356 g_free (mangled_name);
1358 mangled_name = mangled_stdcall_name;
1360 #endif
1361 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT,
1362 "Probing '%s'.", mangled_name);
1364 error_msg = mono_dl_symbol (module, mangled_name, &addr);
1366 if (addr)
1367 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT,
1368 "Found as '%s'.", mangled_name);
1369 else
1370 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT,
1371 "Could not find '%s' due to '%s'.", mangled_name, error_msg);
1373 g_free (error_msg);
1374 error_msg = NULL;
1376 if (mangled_name != import)
1377 g_free (mangled_name);
1383 *error_msg_out = error_msg;
1384 return addr;
1387 #ifdef ENABLE_NETCORE
1388 void
1389 ves_icall_System_Runtime_InteropServices_NativeLibrary_FreeLib (gpointer lib, MonoError *error)
1391 MonoDl *module;
1392 guint32 ref_count;
1394 g_assert (lib);
1396 // Don't free __Internal
1397 if (internal_module && lib == internal_module->handle)
1398 return;
1400 native_library_lock ();
1402 module = netcore_handle_lookup (lib);
1403 if (!module)
1404 goto leave;
1406 ref_count = mono_refcount_dec (module);
1407 if (ref_count > 0)
1408 goto leave;
1410 g_hash_table_remove (native_library_module_map, module->handle);
1411 g_hash_table_add (native_library_module_blacklist, module);
1412 mono_dl_close (module);
1414 leave:
1415 native_library_unlock ();
1418 gpointer
1419 ves_icall_System_Runtime_InteropServices_NativeLibrary_GetSymbol (gpointer lib, MonoStringHandle symbol_name_handle, MonoBoolean throw_on_error, MonoError *error)
1421 MonoDl *module;
1422 gpointer symbol = NULL;
1423 char *symbol_name;
1425 g_assert (lib);
1427 ERROR_LOCAL_BEGIN (local_error, error, throw_on_error)
1429 symbol_name = mono_string_handle_to_utf8 (symbol_name_handle, error);
1430 goto_if_nok (error, leave_nolock);
1432 native_library_lock ();
1434 module = netcore_handle_lookup (lib);
1435 if (!module)
1436 mono_error_set_generic_error (error, "System", "DllNotFoundException", "%p: %s", lib, symbol_name);
1437 goto_if_nok (error, leave);
1439 mono_dl_symbol (module, symbol_name, &symbol);
1440 if (!symbol)
1441 mono_error_set_generic_error (error, "System", "EntryPointNotFoundException", "%s: %s", module->full_name, symbol_name);
1442 goto_if_nok (error, leave);
1444 leave:
1445 native_library_unlock ();
1447 leave_nolock:
1448 ERROR_LOCAL_END (local_error);
1450 return symbol;
1453 // LOCKING: expects you to hold native_library_module_lock
1454 static MonoDl *
1455 check_native_library_cache (MonoDl *module)
1457 gpointer handle = module->handle;
1459 MonoDl *cached_module = netcore_handle_lookup (handle);
1460 if (cached_module) {
1461 g_free (module->full_name);
1462 g_free (module);
1463 mono_refcount_inc (cached_module);
1464 return cached_module;
1466 g_hash_table_insert (native_library_module_map, handle, (gpointer)module);
1468 return module;
1471 gpointer
1472 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)
1474 MonoDl *module;
1475 gpointer handle = NULL;
1476 MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_handle, assembly);
1477 MonoImage *image = mono_assembly_get_image_internal (assembly);
1478 char *lib_name;
1480 ERROR_LOCAL_BEGIN (local_error, error, throw_on_error)
1482 lib_name = mono_string_handle_to_utf8 (lib_name_handle, error);
1483 goto_if_nok (error, leave);
1485 // FIXME: implement search flag defaults properly
1486 module = netcore_probe_for_module (image, lib_name, has_search_flag ? search_flag : 0x2);
1487 if (!module)
1488 mono_error_set_generic_error (error, "System", "DllNotFoundException", "%s", lib_name);
1489 goto_if_nok (error, leave);
1491 native_library_lock ();
1492 module = check_native_library_cache (module);
1493 native_library_unlock ();
1495 handle = module->handle;
1497 leave:
1498 ERROR_LOCAL_END (local_error);
1500 return handle;
1503 gpointer
1504 ves_icall_System_Runtime_InteropServices_NativeLibrary_LoadFromPath (MonoStringHandle lib_path_handle, MonoBoolean throw_on_error, MonoError *error)
1506 MonoDl *module;
1507 gpointer handle = NULL;
1508 char *error_msg = NULL;
1509 char *lib_path;
1511 ERROR_LOCAL_BEGIN (local_error, error, throw_on_error)
1513 lib_path = mono_string_handle_to_utf8 (lib_path_handle, error);
1514 goto_if_nok (error, leave);
1516 module = mono_dl_open (lib_path, MONO_DL_LAZY, &error_msg);
1517 if (!module) {
1518 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", lib_path, error_msg);
1519 mono_error_set_generic_error (error, "System", "DllNotFoundException", "'%s': '%s'", lib_path, error_msg);
1520 g_free (error_msg);
1522 goto_if_nok (error, leave);
1524 native_library_lock ();
1525 module = check_native_library_cache (module);
1526 native_library_unlock ();
1528 handle = module->handle;
1530 leave:
1531 ERROR_LOCAL_END (local_error);
1533 return handle;
1535 #endif
1537 #ifdef HAVE_ATEXIT
1538 static void
1539 delete_bundled_libraries (void)
1541 GSList *list;
1543 for (list = bundle_library_paths; list != NULL; list = list->next){
1544 unlink ((const char*)list->data);
1546 rmdir (bundled_dylibrary_directory);
1548 #endif
1550 static void
1551 bundle_save_library_initialize (void)
1553 bundle_save_library_initialized = TRUE;
1554 char *path = g_build_filename (g_get_tmp_dir (), "mono-bundle-XXXXXX", (const char*)NULL);
1555 bundled_dylibrary_directory = g_mkdtemp (path);
1556 g_free (path);
1557 if (bundled_dylibrary_directory == NULL)
1558 return;
1559 #ifdef HAVE_ATEXIT
1560 atexit (delete_bundled_libraries);
1561 #endif
1564 void
1565 mono_loader_save_bundled_library (int fd, uint64_t offset, uint64_t size, const char *destfname)
1567 MonoDl *lib;
1568 char *file, *buffer, *err, *internal_path;
1569 if (!bundle_save_library_initialized)
1570 bundle_save_library_initialize ();
1572 file = g_build_filename (bundled_dylibrary_directory, destfname, (const char*)NULL);
1573 buffer = g_str_from_file_region (fd, offset, size);
1574 g_file_set_contents (file, buffer, size, NULL);
1576 lib = mono_dl_open (file, MONO_DL_LAZY, &err);
1577 if (lib == NULL){
1578 fprintf (stderr, "Error loading shared library: %s %s\n", file, err);
1579 exit (1);
1581 // Register the name with "." as this is how it will be found when embedded
1582 internal_path = g_build_filename (".", destfname, (const char*)NULL);
1583 mono_loader_register_module (internal_path, lib);
1584 g_free (internal_path);
1585 bundle_library_paths = g_slist_append (bundle_library_paths, file);
1587 g_free (buffer);