Apply changes from https://github.com/dotnet/runtime/commit/eb1756e97d23df13bc6fe798e...
[mono-project.git] / mono / metadata / native-library.c
blobf53b8d01d78f453ee3da7acb8f6a66ad74e43dbd
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"
13 #include "mono/metadata/native-library.h"
14 #include "mono/metadata/custom-attrs-internals.h"
16 #ifdef ENABLE_NETCORE
17 static int pinvoke_search_directories_count;
18 static char **pinvoke_search_directories;
20 // sync with src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/DllImportSearchPath.cs
21 typedef enum
23 DLLIMPORTSEARCHPATH_LEGACY_BEHAVIOR = 0x0, // when no other flags are present, search the application directory and then call LoadLibraryEx with LOAD_WITH_ALTERED_SEARCH_PATH
24 DLLIMPORTSEARCHPATH_USE_DLL_DIRECTORY_FOR_DEPENDENCIES = 0x100,
25 DLLIMPORTSEARCHPATH_APPLICATION_DIRECTORY = 0x200,
26 DLLIMPORTSEARCHPATH_USER_DIRECTORIES = 0x400,
27 DLLIMPORTSEARCHPATH_SYSTEM32 = 0x800,
28 DLLIMPORTSEARCHPATH_SAFE_DIRECTORIES = 0x1000,
29 DLLIMPORTSEARCHPATH_ASSEMBLY_DIRECTORY = 0x2, // search the assembly directory first regardless of platform, not passed on to LoadLibraryEx
30 } DllImportSearchPath;
31 #ifdef HOST_WIN32
32 static const int DLLIMPORTSEARCHPATH_LOADLIBRARY_FLAG_MASK = DLLIMPORTSEARCHPATH_USE_DLL_DIRECTORY_FOR_DEPENDENCIES | DLLIMPORTSEARCHPATH_APPLICATION_DIRECTORY |
33 DLLIMPORTSEARCHPATH_USER_DIRECTORIES | DLLIMPORTSEARCHPATH_SYSTEM32 | DLLIMPORTSEARCHPATH_SAFE_DIRECTORIES;
34 #endif
36 // This lock may be taken within an ALC lock, and should never be the other way around.
37 static MonoCoopMutex native_library_module_lock;
38 static GHashTable *native_library_module_map;
40 * This blocklist is used as a set for cache invalidation purposes with netcore pinvokes.
41 * When pinvokes are resolved with anything other than the last-chance managed event,
42 * the results of that lookup are added to an ALC-level cache. However, if a library is then
43 * unloaded with NativeLibrary.Free(), this cache should be invalidated so that a newly called
44 * pinvoke will not attempt to use it, hence the blocklist. This design means that if another
45 * library is loaded at the same address, it will function with a perf hit, as the entry will
46 * repeatedly be added and removed from the cache due to its presence in the blocklist.
47 * This is a rare scenario and considered a worthwhile tradeoff.
49 static GHashTable *native_library_module_blocklist;
50 #endif
52 #ifndef DISABLE_DLLMAP
53 static MonoDllMap *global_dll_map;
54 #endif
56 static GHashTable *global_module_map; // should only be accessed with the global loader data lock
58 static MonoDl *internal_module; // used when pinvoking `__Internal`
60 // Did we initialize the temporary directory for dynamic libraries
61 // FIXME: this is racy
62 static gboolean bundle_save_library_initialized;
64 // List of bundled libraries we unpacked
65 static GSList *bundle_library_paths;
67 // Directory where we unpacked dynamic libraries
68 static char *bundled_dylibrary_directory;
70 /* Class lazy loading functions */
71 GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException")
72 GENERATE_TRY_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException")
73 #ifdef ENABLE_NETCORE
74 GENERATE_GET_CLASS_WITH_CACHE (native_library, "System.Runtime.InteropServices", "NativeLibrary");
75 static GENERATE_TRY_GET_CLASS_WITH_CACHE (dllimportsearchpath_attribute, "System.Runtime.InteropServices", "DefaultDllImportSearchPathsAttribute");
76 #endif
78 #ifndef DISABLE_DLLMAP
80 * LOCKING: Assumes the relevant lock is held.
81 * For the global DllMap, this is `global_loader_data_mutex`, and for images it's their internal lock.
83 static int
84 mono_dllmap_lookup_list (MonoDllMap *dll_map, const char *dll, const char* func, const char **rdll, const char **rfunc) {
85 int found = 0;
87 *rdll = dll;
88 *rfunc = func;
90 if (!dll_map)
91 goto exit;
93 /*
94 * we use the first entry we find that matches, since entries from
95 * the config file are prepended to the list and we document that the
96 * later entries win.
98 for (; dll_map; dll_map = dll_map->next) {
99 // Check case-insensitively when the dll name is prefixed with 'i:'
100 gboolean case_insensitive_match = strncmp (dll_map->dll, "i:", 2) == 0 && g_ascii_strcasecmp (dll_map->dll + 2, dll) == 0;
101 gboolean case_sensitive_match = strcmp (dll_map->dll, dll) == 0;
102 if (!(case_insensitive_match || case_sensitive_match))
103 continue;
105 if (!found && dll_map->target) {
106 *rdll = dll_map->target;
107 found = 1;
108 /* we don't quit here, because we could find a full
109 * entry that also matches the function, which takes priority.
112 if (dll_map->func && strcmp (dll_map->func, func) == 0) {
113 *rdll = dll_map->target;
114 *rfunc = dll_map->target_func;
115 break;
119 exit:
120 *rdll = g_strdup (*rdll);
121 *rfunc = g_strdup (*rfunc);
122 return found;
126 * The locking and GC state transitions here are wonky due to the fact the image lock is a coop lock
127 * and the global loader data lock is an OS lock.
129 static int
130 mono_dllmap_lookup (MonoImage *assembly, const char *dll, const char* func, const char **rdll, const char **rfunc)
132 int res;
134 MONO_REQ_GC_UNSAFE_MODE;
136 if (assembly && assembly->dll_map) {
137 mono_image_lock (assembly);
138 res = mono_dllmap_lookup_list (assembly->dll_map, dll, func, rdll, rfunc);
139 mono_image_unlock (assembly);
140 if (res)
141 return res;
144 MONO_ENTER_GC_SAFE;
146 mono_global_loader_data_lock ();
147 res = mono_dllmap_lookup_list (global_dll_map, dll, func, rdll, rfunc);
148 mono_global_loader_data_unlock ();
150 MONO_EXIT_GC_SAFE;
152 return res;
155 static void
156 dllmap_insert_global (const char *dll, const char *func, const char *tdll, const char *tfunc)
158 MonoDllMap *entry;
160 entry = (MonoDllMap *)g_malloc0 (sizeof (MonoDllMap));
161 entry->dll = dll? g_strdup (dll): NULL;
162 entry->target = tdll? g_strdup (tdll): NULL;
163 entry->func = func? g_strdup (func): NULL;
164 entry->target_func = tfunc? g_strdup (tfunc): (func? g_strdup (func): NULL);
166 // No transition here because this is early in startup
167 mono_global_loader_data_lock ();
168 entry->next = global_dll_map;
169 global_dll_map = entry;
170 mono_global_loader_data_unlock ();
174 static void
175 dllmap_insert_image (MonoImage *assembly, const char *dll, const char *func, const char *tdll, const char *tfunc)
177 MonoDllMap *entry;
178 g_assert (assembly != NULL);
180 MONO_REQ_GC_UNSAFE_MODE;
182 entry = (MonoDllMap *)mono_image_alloc0 (assembly, sizeof (MonoDllMap));
183 entry->dll = dll? mono_image_strdup (assembly, dll): NULL;
184 entry->target = tdll? mono_image_strdup (assembly, tdll): NULL;
185 entry->func = func? mono_image_strdup (assembly, func): NULL;
186 entry->target_func = tfunc? mono_image_strdup (assembly, tfunc): (func? mono_image_strdup (assembly, func): NULL);
188 mono_image_lock (assembly);
189 entry->next = assembly->dll_map;
190 assembly->dll_map = entry;
191 mono_image_unlock (assembly);
195 * LOCKING: Assumes the relevant lock is held.
196 * For the global DllMap, this is `global_loader_data_mutex`, and for images it's their internal lock.
198 static void
199 free_dllmap (MonoDllMap *map)
201 while (map) {
202 MonoDllMap *next = map->next;
204 g_free (map->dll);
205 g_free (map->target);
206 g_free (map->func);
207 g_free (map->target_func);
208 g_free (map);
209 map = next;
213 void
214 mono_dllmap_insert_internal (MonoImage *assembly, const char *dll, const char *func, const char *tdll, const char *tfunc)
216 mono_loader_init ();
217 // The locking in here is _really_ wonky, and I'm not convinced this function should exist.
218 // I've split it into an internal version to offer flexibility in the future.
219 if (!assembly)
220 dllmap_insert_global (dll, func, tdll, tfunc);
221 else
222 dllmap_insert_image (assembly, dll, func, tdll, tfunc);
225 void
226 mono_global_dllmap_cleanup (void)
228 // No need for a transition here since the thread is already detached from the runtime
229 mono_global_loader_data_lock ();
231 free_dllmap (global_dll_map);
232 global_dll_map = NULL;
234 // We don't care about freeing native_library_module_map on netcore because of the expedited shutdown.
236 mono_global_loader_data_unlock ();
238 #endif
241 * mono_dllmap_insert:
242 * \param assembly if NULL, this is a global mapping, otherwise the remapping of the dynamic library will only apply to the specified assembly
243 * \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
244 * \param func if not null, the mapping will only applied to the named function (the value of <code>EntryPoint</code>)
245 * \param tdll The name of the library to map the specified \p dll if it matches.
246 * \param tfunc The name of the function that replaces the invocation. If NULL, it is replaced with a copy of \p func.
248 * LOCKING: Acquires the image lock, or the loader data lock if an image is not passed.
250 * This function is used to programatically add \c DllImport remapping in either
251 * a specific assembly, or as a global remapping. This is done by remapping
252 * references in a \c DllImport attribute from the \p dll library name into the \p tdll
253 * name. If the \p dll name contains the prefix <code>i:</code>, the comparison of the
254 * library name is done without case sensitivity.
256 * If you pass \p func, this is the name of the \c EntryPoint in a \c DllImport if specified
257 * or the name of the function as determined by \c DllImport. If you pass \p func, you
258 * must also pass \p tfunc which is the name of the target function to invoke on a match.
260 * Example:
262 * <code>mono_dllmap_insert (NULL, "i:libdemo.dll", NULL, relocated_demo_path, NULL);</code>
264 * The above will remap \c DllImport statements for \c libdemo.dll and \c LIBDEMO.DLL to
265 * the contents of \c relocated_demo_path for all assemblies in the Mono process.
267 * NOTE: This can be called before the runtime is initialized, for example from
268 * \c mono_config_parse.
270 void
271 mono_dllmap_insert (MonoImage *assembly, const char *dll, const char *func, const char *tdll, const char *tfunc)
273 #ifndef DISABLE_DLLMAP
274 mono_dllmap_insert_internal (assembly, dll, func, tdll, tfunc);
275 #else
276 g_assert_not_reached ();
277 #endif
280 void
281 mono_loader_register_module (const char *name, MonoDl *module)
283 mono_loader_init ();
285 // No transition here because this is early in startup
286 mono_global_loader_data_lock ();
288 g_hash_table_insert (global_module_map, g_strdup (name), module);
290 mono_global_loader_data_unlock ();
293 #ifdef ENABLE_NETCORE
294 static MonoDl *
295 mono_loader_register_module_locking (const char *name, MonoDl *module)
297 MonoDl *result = NULL;
299 MONO_ENTER_GC_SAFE;
300 mono_global_loader_data_lock ();
301 MONO_EXIT_GC_SAFE;
303 result = (MonoDl *)g_hash_table_lookup (global_module_map, name);
304 if (result) {
305 g_free (module->full_name);
306 g_free (module);
307 goto exit;
310 g_hash_table_insert (global_module_map, g_strdup (name), module);
311 result = module;
313 exit:
314 MONO_ENTER_GC_SAFE;
315 mono_global_loader_data_unlock ();
316 MONO_EXIT_GC_SAFE;
318 return result;
320 #endif
322 static void
323 remove_cached_module (gpointer key, gpointer value, gpointer user_data)
325 mono_dl_close((MonoDl*)value);
328 void
329 mono_global_loader_cache_init (void)
331 if (!global_module_map)
332 global_module_map = g_hash_table_new (g_str_hash, g_str_equal);
334 #ifdef ENABLE_NETCORE
335 if (!native_library_module_map)
336 native_library_module_map = g_hash_table_new (g_direct_hash, g_direct_equal);
337 if (!native_library_module_blocklist)
338 native_library_module_blocklist = g_hash_table_new (g_direct_hash, g_direct_equal);
339 mono_coop_mutex_init (&native_library_module_lock);
340 #endif
343 void
344 mono_global_loader_cache_cleanup (void)
346 if (global_module_map != NULL) {
347 g_hash_table_foreach(global_module_map, remove_cached_module, NULL);
349 g_hash_table_destroy(global_module_map);
350 global_module_map = NULL;
353 // No need to clean up the native library hash tables since they're netcore-only, where this is never called
356 static gboolean
357 is_absolute_path (const char *path)
359 // FIXME: other platforms have similar prefixes, such as $ORIGIN
360 #ifdef HOST_DARWIN
361 if (!strncmp (path, "@executable_path/", 17) || !strncmp (path, "@loader_path/", 13) || !strncmp (path, "@rpath/", 7))
362 return TRUE;
363 #endif
364 return g_path_is_absolute (path);
367 static gpointer
368 lookup_pinvoke_call_impl (MonoMethod *method, MonoLookupPInvokeStatus *status_out);
370 static gpointer
371 pinvoke_probe_for_symbol (MonoDl *module, MonoMethodPInvoke *piinfo, const char *import, char **error_msg_out);
373 static void
374 pinvoke_probe_convert_status_for_api (MonoLookupPInvokeStatus *status, const char **exc_class, const char **exc_arg)
376 if (!exc_class)
377 return;
378 switch (status->err_code) {
379 case LOOKUP_PINVOKE_ERR_OK:
380 *exc_class = NULL;
381 *exc_arg = NULL;
382 break;
383 case LOOKUP_PINVOKE_ERR_NO_LIB:
384 *exc_class = "DllNotFoundException";
385 *exc_arg = status->err_arg;
386 status->err_arg = NULL;
387 break;
388 case LOOKUP_PINVOKE_ERR_NO_SYM:
389 *exc_class = "EntryPointNotFoundException";
390 *exc_arg = status->err_arg;
391 status->err_arg = NULL;
392 break;
393 default:
394 g_assert_not_reached ();
398 static void
399 pinvoke_probe_convert_status_to_error (MonoLookupPInvokeStatus *status, MonoError *error)
401 /* Note: this has to return a MONO_ERROR_GENERIC because mono_mb_emit_exception_for_error only knows how to decode generic errors. */
402 switch (status->err_code) {
403 case LOOKUP_PINVOKE_ERR_OK:
404 return;
405 case LOOKUP_PINVOKE_ERR_NO_LIB:
406 mono_error_set_generic_error (error, "System", "DllNotFoundException", "%s", status->err_arg);
407 g_free (status->err_arg);
408 status->err_arg = NULL;
409 break;
410 case LOOKUP_PINVOKE_ERR_NO_SYM:
411 mono_error_set_generic_error (error, "System", "EntryPointNotFoundException", "%s", status->err_arg);
412 g_free (status->err_arg);
413 status->err_arg = NULL;
414 break;
415 default:
416 g_assert_not_reached ();
421 * mono_lookup_pinvoke_call:
423 gpointer
424 mono_lookup_pinvoke_call (MonoMethod *method, const char **exc_class, const char **exc_arg)
426 gpointer result;
427 MONO_ENTER_GC_UNSAFE;
428 MonoLookupPInvokeStatus status;
429 memset (&status, 0, sizeof (status));
430 result = lookup_pinvoke_call_impl (method, &status);
431 pinvoke_probe_convert_status_for_api (&status, exc_class, exc_arg);
432 MONO_EXIT_GC_UNSAFE;
433 return result;
436 gpointer
437 mono_lookup_pinvoke_call_internal (MonoMethod *method, MonoError *error)
439 gpointer result;
440 MonoLookupPInvokeStatus status;
441 memset (&status, 0, sizeof (status));
442 result = lookup_pinvoke_call_impl (method, &status);
443 if (status.err_code)
444 pinvoke_probe_convert_status_to_error (&status, error);
445 return result;
448 #ifdef ENABLE_NETCORE
449 void
450 mono_set_pinvoke_search_directories (int dir_count, char **dirs)
452 pinvoke_search_directories_count = dir_count;
453 g_strfreev (pinvoke_search_directories);
454 pinvoke_search_directories = dirs;
457 static void
458 native_library_lock (void)
460 mono_coop_mutex_lock (&native_library_module_lock);
463 static void
464 native_library_unlock (void)
466 mono_coop_mutex_unlock (&native_library_module_lock);
469 static void
470 alc_pinvoke_lock (MonoAssemblyLoadContext *alc)
472 mono_coop_mutex_lock (&alc->pinvoke_lock);
475 static void
476 alc_pinvoke_unlock (MonoAssemblyLoadContext *alc)
478 mono_coop_mutex_unlock (&alc->pinvoke_lock);
481 // LOCKING: expects you to hold native_library_module_lock
482 static MonoDl *
483 netcore_handle_lookup (gpointer handle)
485 return (MonoDl *)g_hash_table_lookup (native_library_module_map, handle);
488 // LOCKING: expects you to hold native_library_module_lock
489 static gboolean
490 netcore_check_blocklist (MonoDl *module)
492 return g_hash_table_contains (native_library_module_blocklist, module);
495 static int
496 convert_dllimport_flags (int flags)
498 #ifdef HOST_WIN32
499 return flags & DLLIMPORTSEARCHPATH_LOADLIBRARY_FLAG_MASK;
500 #else
501 // DllImportSearchPath is Windows-only, other than DLLIMPORTSEARCHPATH_ASSEMBLY_DIRECTORY
502 return 0;
503 #endif
506 static MonoDl *
507 netcore_probe_for_module_variations (const char *mdirname, const char *file_name, int raw_flags)
509 void *iter = NULL;
510 char *full_name;
511 MonoDl *module = NULL;
513 // FIXME: this appears to search *.dylib twice for some reason
514 while ((full_name = mono_dl_build_path (mdirname, file_name, &iter)) && module == NULL) {
515 char *error_msg;
516 module = mono_dl_open_full (full_name, MONO_DL_LAZY, raw_flags, &error_msg);
517 if (!module) {
518 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", full_name, error_msg);
519 g_free (error_msg);
521 g_free (full_name);
523 g_free (full_name);
525 return module;
528 static MonoDl *
529 netcore_probe_for_module (MonoImage *image, const char *file_name, int flags)
531 MonoDl *module = NULL;
532 int lflags = convert_dllimport_flags (flags);
534 // TODO: this algorithm doesn't quite match CoreCLR, so respecting DLLIMPORTSEARCHPATH_LEGACY_BEHAVIOR makes little sense
535 // If the difference becomes a problem, overhaul this algorithm to match theirs exactly
537 // Try without any path additions
538 module = netcore_probe_for_module_variations (NULL, file_name, lflags);
540 // Check the NATIVE_DLL_SEARCH_DIRECTORIES
541 for (int i = 0; i < pinvoke_search_directories_count && module == NULL; ++i)
542 module = netcore_probe_for_module_variations (pinvoke_search_directories[i], file_name, lflags);
544 // Check the assembly directory if the search flag is set and the image exists
545 if (flags & DLLIMPORTSEARCHPATH_ASSEMBLY_DIRECTORY && image != NULL && module == NULL) {
546 char *mdirname = g_path_get_dirname (image->filename);
547 if (mdirname)
548 module = netcore_probe_for_module_variations (mdirname, file_name, lflags);
549 g_free (mdirname);
552 // TODO: Pass remaining flags on to LoadLibraryEx on Windows where appropriate, see https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.dllimportsearchpath?view=netcore-3.1
554 return module;
557 static MonoDl *
558 netcore_resolve_with_dll_import_resolver (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope, guint32 flags, MonoError *error)
560 MonoDl *result = NULL;
561 gpointer lib = NULL;
562 MonoDomain *domain = mono_alc_domain (alc);
564 MONO_STATIC_POINTER_INIT (MonoMethod, resolve)
566 ERROR_DECL (local_error);
567 MonoClass *native_lib_class = mono_class_get_native_library_class ();
568 g_assert (native_lib_class);
569 resolve = mono_class_get_method_from_name_checked (native_lib_class, "MonoLoadLibraryCallbackStub", -1, 0, local_error);
570 mono_error_assert_ok (local_error);
572 MONO_STATIC_POINTER_INIT_END (MonoMethod, resolve)
573 g_assert (resolve);
575 if (mono_runtime_get_no_exec ())
576 return NULL;
578 HANDLE_FUNCTION_ENTER ();
580 MonoStringHandle scope_handle;
581 scope_handle = mono_string_new_handle (domain, scope, error);
582 goto_if_nok (error, leave);
584 MonoReflectionAssemblyHandle assembly_handle;
585 assembly_handle = mono_assembly_get_object_handle (domain, assembly, error);
586 goto_if_nok (error, leave);
588 gboolean has_search_flags;
589 has_search_flags = flags != 0 ? TRUE : FALSE;
590 gpointer args [5];
591 args [0] = MONO_HANDLE_RAW (scope_handle);
592 args [1] = MONO_HANDLE_RAW (assembly_handle);
593 args [2] = &has_search_flags;
594 args [3] = &flags;
595 args [4] = &lib;
596 mono_runtime_invoke_checked (resolve, NULL, args, error);
597 goto_if_nok (error, leave);
599 native_library_lock ();
600 result = netcore_handle_lookup (lib);
601 native_library_unlock ();
603 leave:
604 HANDLE_FUNCTION_RETURN_VAL (result);
607 static MonoDl *
608 netcore_resolve_with_dll_import_resolver_nofail (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope, guint32 flags)
610 MonoDl *result = NULL;
611 ERROR_DECL (error);
613 result = netcore_resolve_with_dll_import_resolver (alc, assembly, scope, flags, error);
614 if (!is_ok (error))
615 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Error while invoking ALC DllImportResolver(\"%s\") delegate: '%s'", scope, mono_error_get_message (error));
617 mono_error_cleanup (error);
619 return result;
622 static MonoDl *
623 netcore_resolve_with_load (MonoAssemblyLoadContext *alc, const char *scope, MonoError *error)
625 MonoDl *result = NULL;
626 gpointer lib = NULL;
628 MONO_STATIC_POINTER_INIT (MonoMethod, resolve)
630 ERROR_DECL (local_error);
631 MonoClass *alc_class = mono_class_get_assembly_load_context_class ();
632 g_assert (alc_class);
633 resolve = mono_class_get_method_from_name_checked (alc_class, "MonoResolveUnmanagedDll", -1, 0, local_error);
634 mono_error_assert_ok (local_error);
636 MONO_STATIC_POINTER_INIT_END (MonoMethod, resolve)
637 g_assert (resolve);
639 if (mono_runtime_get_no_exec ())
640 return NULL;
642 HANDLE_FUNCTION_ENTER ();
644 MonoStringHandle scope_handle;
645 scope_handle = mono_string_new_handle (mono_alc_domain (alc), scope, error);
646 goto_if_nok (error, leave);
648 gpointer gchandle;
649 gchandle = GUINT_TO_POINTER (alc->gchandle);
650 gpointer args [3];
651 args [0] = MONO_HANDLE_RAW (scope_handle);
652 args [1] = &gchandle;
653 args [2] = &lib;
654 mono_runtime_invoke_checked (resolve, NULL, args, error);
655 goto_if_nok (error, leave);
657 native_library_lock ();
658 result = netcore_handle_lookup (lib);
659 native_library_unlock ();
661 leave:
662 HANDLE_FUNCTION_RETURN_VAL (result);
665 static MonoDl *
666 netcore_resolve_with_load_nofail (MonoAssemblyLoadContext *alc, const char *scope)
668 MonoDl *result = NULL;
669 ERROR_DECL (error);
671 result = netcore_resolve_with_load (alc, scope, error);
672 if (!is_ok (error))
673 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Error while invoking ALC LoadUnmanagedDll(\"%s\") method: '%s'", scope, mono_error_get_message (error));
675 mono_error_cleanup (error);
677 return result;
680 static MonoDl *
681 netcore_resolve_with_resolving_event (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope, MonoError *error)
683 MonoDl *result = NULL;
684 gpointer lib = NULL;
685 MonoDomain *domain = mono_alc_domain (alc);
687 MONO_STATIC_POINTER_INIT (MonoMethod, resolve)
689 ERROR_DECL (local_error);
690 MonoClass *alc_class = mono_class_get_assembly_load_context_class ();
691 g_assert (alc_class);
692 resolve = mono_class_get_method_from_name_checked (alc_class, "MonoResolveUnmanagedDllUsingEvent", -1, 0, local_error);
693 mono_error_assert_ok (local_error);
695 MONO_STATIC_POINTER_INIT_END (MonoMethod, resolve)
696 g_assert (resolve);
698 if (mono_runtime_get_no_exec ())
699 return NULL;
701 HANDLE_FUNCTION_ENTER ();
703 MonoStringHandle scope_handle;
704 scope_handle = mono_string_new_handle (domain, scope, error);
705 goto_if_nok (error, leave);
707 MonoReflectionAssemblyHandle assembly_handle;
708 assembly_handle = mono_assembly_get_object_handle (domain, assembly, error);
709 goto_if_nok (error, leave);
711 gpointer gchandle;
712 gchandle = GUINT_TO_POINTER (alc->gchandle);
713 gpointer args [4];
714 args [0] = MONO_HANDLE_RAW (scope_handle);
715 args [1] = MONO_HANDLE_RAW (assembly_handle);
716 args [2] = &gchandle;
717 args [3] = &lib;
718 mono_runtime_invoke_checked (resolve, NULL, args, error);
719 goto_if_nok (error, leave);
721 native_library_lock ();
722 result = netcore_handle_lookup (lib);
723 native_library_unlock ();
725 leave:
726 HANDLE_FUNCTION_RETURN_VAL (result);
729 static MonoDl *
730 netcore_resolve_with_resolving_event_nofail (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope)
732 MonoDl *result = NULL;
733 ERROR_DECL (error);
735 result = netcore_resolve_with_resolving_event (alc, assembly, scope, error);
736 if (!is_ok (error))
737 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Error while invoking ALC ResolvingUnmangedDll(\"%s\") event: '%s'", scope, mono_error_get_message (error));
739 mono_error_cleanup (error);
741 return result;
744 // LOCKING: expects you to hold the ALC's pinvoke lock
745 static MonoDl *
746 netcore_check_alc_cache (MonoAssemblyLoadContext *alc, const char *scope)
748 MonoDl *result = NULL;
750 result = (MonoDl *)g_hash_table_lookup (alc->pinvoke_scopes, scope);
752 if (result) {
753 gboolean blocklisted;
755 native_library_lock ();
756 blocklisted = netcore_check_blocklist (result);
757 native_library_unlock ();
759 if (blocklisted) {
760 g_hash_table_remove (alc->pinvoke_scopes, scope);
761 result = NULL;
765 return result;
768 static MonoDl *
769 netcore_lookup_native_library (MonoAssemblyLoadContext *alc, MonoImage *image, const char *scope, guint32 flags)
771 MonoDl *module = NULL;
772 MonoDl *cached;
773 MonoAssembly *assembly = mono_image_get_assembly (image);
774 char *error_msg = NULL;
776 MONO_REQ_GC_UNSAFE_MODE;
778 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "DllImport attempting to load: '%s'.", scope);
780 // We allow a special name to dlopen from the running process namespace, which is not present in CoreCLR
781 if (strcmp (scope, "__Internal") == 0) {
782 if (!internal_module)
783 internal_module = mono_dl_open_self (&error_msg);
784 module = internal_module;
786 if (!module) {
787 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '__Internal': '%s'.", error_msg);
788 g_free (error_msg);
791 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Native library found via __Internal: '%s'.", scope);
793 return module;
797 * Try these until one of them succeeds:
799 * 1. Check the cache in the active ALC.
801 * 2. Call the DllImportResolver on the active assembly.
803 * 3. Call LoadUnmanagedDll on the active ALC.
805 * 4. Check the global cache.
807 * 5. Run the unmanaged probing logic.
809 * 6. Raise the ResolvingUnmanagedDll event on the active ALC.
811 * 7. Return NULL.
814 alc_pinvoke_lock (alc);
815 module = netcore_check_alc_cache (alc, scope);
816 alc_pinvoke_unlock (alc);
817 if (module) {
818 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Native library found in the active ALC cache: '%s'.", scope);
819 goto leave;
822 module = (MonoDl *)netcore_resolve_with_dll_import_resolver_nofail (alc, assembly, scope, flags);
823 if (module) {
824 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Native library found via DllImportResolver: '%s'.", scope);
825 goto add_to_alc_cache;
828 module = (MonoDl *)netcore_resolve_with_load_nofail (alc, scope);
829 if (module) {
830 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Native library found via LoadUnmanagedDll: '%s'.", scope);
831 goto add_to_alc_cache;
834 MONO_ENTER_GC_SAFE;
835 mono_global_loader_data_lock ();
836 MONO_EXIT_GC_SAFE;
837 module = (MonoDl *)g_hash_table_lookup (global_module_map, scope);
838 MONO_ENTER_GC_SAFE;
839 mono_global_loader_data_unlock ();
840 MONO_EXIT_GC_SAFE;
841 if (module) {
842 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Native library found in the global cache: '%s'.", scope);
843 goto add_to_alc_cache;
846 module = netcore_probe_for_module (image, scope, flags);
847 if (module) {
848 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Native library found via filesystem probing: '%s'.", scope);
849 goto add_to_global_cache;
852 /* As this is last chance, I've opted not to put it in a cache, but that is not necessarily the correct decision.
853 * It is rather convenient here, however, because it means the global cache will only be populated by libraries
854 * resolved via netcore_probe_for_module and not NativeLibrary, eliminating potential races/conflicts.
856 module = netcore_resolve_with_resolving_event_nofail (alc, assembly, scope);
857 if (module)
858 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Native library found via the Resolving event: '%s'.", scope);
859 goto leave;
861 add_to_global_cache:
862 module = mono_loader_register_module_locking (scope, module);
864 add_to_alc_cache:
865 /* Nothing is closed here because the only two places this can come from are:
866 * 1. A managed callback that made use of NativeLibrary.Load, in which case closing is dependent on NativeLibrary.Free
867 * 2. The global cache, which is only populated by results of netcore_probe_for_module. When adding to the global cache,
868 * we free the new MonoDl if another thread beat us, so we don't have to repeat that here.
870 alc_pinvoke_lock (alc);
871 cached = netcore_check_alc_cache (alc, scope);
872 if (cached)
873 module = cached;
874 else
875 g_hash_table_insert (alc->pinvoke_scopes, g_strdup (scope), module);
876 alc_pinvoke_unlock (alc);
878 leave:
879 return module;
882 static int
883 get_dllimportsearchpath_flags (MonoCustomAttrInfo *cinfo)
885 ERROR_DECL (error);
886 MonoCustomAttrEntry *attr = NULL;
887 MonoClass *dllimportsearchpath = mono_class_try_get_dllimportsearchpath_attribute_class ();
888 int idx;
889 int flags;
891 if (!dllimportsearchpath)
892 return -1;
893 if (!cinfo)
894 return -2;
896 for (idx = 0; idx < cinfo->num_attrs; ++idx) {
897 MonoClass *ctor_class = cinfo->attrs [idx].ctor->klass;
898 if (ctor_class == dllimportsearchpath) {
899 attr = &cinfo->attrs [idx];
900 break;
903 if (!attr)
904 return -3;
906 gpointer *typed_args, *named_args;
907 CattrNamedArg *arginfo;
908 int num_named_args;
910 mono_reflection_create_custom_attr_data_args_noalloc (m_class_get_image (attr->ctor->klass), attr->ctor, attr->data, attr->data_size,
911 &typed_args, &named_args, &num_named_args, &arginfo, error);
912 if (!is_ok (error)) {
913 mono_error_cleanup (error);
914 return -4;
917 flags = *(gint32*)typed_args [0];
918 g_free (typed_args [0]);
919 g_free (typed_args);
920 g_free (named_args);
921 g_free (arginfo);
923 return flags;
926 #else // ENABLE_NETCORE
928 static MonoDl *
929 cached_module_load (const char *name, int flags, char **err)
931 MonoDl *res;
933 if (err)
934 *err = NULL;
936 MONO_ENTER_GC_SAFE;
937 mono_global_loader_data_lock ();
938 MONO_EXIT_GC_SAFE;
940 res = (MonoDl *)g_hash_table_lookup (global_module_map, name);
941 if (res)
942 goto exit;
944 res = mono_dl_open (name, flags, err);
945 if (res)
946 g_hash_table_insert (global_module_map, g_strdup (name), res);
948 exit:
949 MONO_ENTER_GC_SAFE;
950 mono_global_loader_data_unlock ();
951 MONO_EXIT_GC_SAFE;
953 return res;
957 * legacy_probe_transform_path:
959 * Try transforming the library path given in \p new_scope in different ways
960 * depending on \p phase
962 * \returns \c TRUE if a transformation was applied and the transformed path
963 * components are written to the out arguments, or \c FALSE if a transformation
964 * did not apply.
966 static gboolean
967 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)
969 char *file_name = NULL, *base_name = NULL, *dir_name = NULL;
970 gboolean changed = FALSE;
971 gboolean is_absolute = is_absolute_path (new_scope);
972 switch (phase) {
973 case 0:
974 /* Try the original name */
975 file_name = g_strdup (new_scope);
976 changed = TRUE;
977 break;
978 case 1:
979 /* Try trimming the .dll extension */
980 if (strstr (new_scope, ".dll") == (new_scope + strlen (new_scope) - 4)) {
981 file_name = g_strdup (new_scope);
982 file_name [strlen (new_scope) - 4] = '\0';
983 changed = TRUE;
985 break;
986 case 2:
987 if (is_absolute) {
988 dir_name = g_path_get_dirname (new_scope);
989 base_name = g_path_get_basename (new_scope);
990 if (strstr (base_name, "lib") != base_name) {
991 char *tmp = g_strdup_printf ("lib%s", base_name);
992 g_free (base_name);
993 base_name = tmp;
994 file_name = g_strdup_printf ("%s%s%s", dir_name, G_DIR_SEPARATOR_S, base_name);
995 changed = TRUE;
997 } else if (strstr (new_scope, "lib") != new_scope) {
998 file_name = g_strdup_printf ("lib%s", new_scope);
999 changed = TRUE;
1001 break;
1002 case 3:
1003 if (!is_absolute && mono_dl_get_system_dir ()) {
1004 dir_name = (char*)mono_dl_get_system_dir ();
1005 file_name = g_path_get_basename (new_scope);
1006 base_name = NULL;
1007 changed = TRUE;
1009 break;
1010 default:
1011 #ifndef TARGET_WIN32
1012 if (!g_ascii_strcasecmp ("user32.dll", new_scope) ||
1013 !g_ascii_strcasecmp ("kernel32.dll", new_scope) ||
1014 !g_ascii_strcasecmp ("user32", new_scope) ||
1015 !g_ascii_strcasecmp ("kernel", new_scope)) {
1016 file_name = g_strdup ("libMonoSupportW.so");
1017 changed = TRUE;
1019 #endif
1020 break;
1022 if (changed && is_absolute) {
1023 if (!dir_name)
1024 dir_name = g_path_get_dirname (file_name);
1025 if (!base_name)
1026 base_name = g_path_get_basename (file_name);
1028 *file_name_out = file_name;
1029 *base_name_out = base_name;
1030 *dir_name_out = dir_name;
1031 *is_absolute_out = is_absolute;
1032 return changed;
1035 static MonoDl *
1036 legacy_probe_for_module_in_directory (const char *mdirname, const char *file_name)
1038 void *iter = NULL;
1039 char *full_name;
1040 MonoDl* module = NULL;
1042 while ((full_name = mono_dl_build_path (mdirname, file_name, &iter)) && module == NULL) {
1043 char *error_msg;
1044 module = cached_module_load (full_name, MONO_DL_LAZY, &error_msg);
1045 if (!module) {
1046 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", full_name, error_msg);
1047 g_free (error_msg);
1049 g_free (full_name);
1051 g_free (full_name);
1053 return module;
1056 static MonoDl *
1057 legacy_probe_for_module_relative_directories (MonoImage *image, const char *file_name)
1059 MonoDl* module = NULL;
1061 for (int j = 0; j < 3; ++j) {
1062 char *mdirname = NULL;
1064 switch (j) {
1065 case 0:
1066 mdirname = g_path_get_dirname (image->filename);
1067 break;
1068 case 1: // @executable_path@/../lib
1070 char buf [4096]; // FIXME: MAX_PATH
1071 int binl;
1072 binl = mono_dl_get_executable_path (buf, sizeof (buf));
1073 if (binl != -1) {
1074 char *base, *newbase;
1075 char *resolvedname;
1076 buf [binl] = 0;
1077 resolvedname = mono_path_resolve_symlinks (buf);
1079 base = g_path_get_dirname (resolvedname);
1080 newbase = g_path_get_dirname(base);
1082 // On Android the executable for the application is going to be /system/bin/app_process{32,64} depending on
1083 // the application's architecture. However, libraries for the different architectures live in different
1084 // subdirectories of `/system`: `lib` for 32-bit apps and `lib64` for 64-bit ones. Thus appending `/lib` below
1085 // will fail to load the DSO for a 64-bit app, even if it exists there, because it will have a different
1086 // architecture. This is the cause of https://github.com/xamarin/xamarin-android/issues/2780 and the ifdef
1087 // below is the fix.
1088 mdirname = g_strdup_printf (
1089 #if defined(TARGET_ANDROID) && (defined(TARGET_ARM64) || defined(TARGET_AMD64))
1090 "%s/lib64",
1091 #else
1092 "%s/lib",
1093 #endif
1094 newbase);
1095 g_free (resolvedname);
1096 g_free (base);
1097 g_free (newbase);
1099 break;
1101 #ifdef __MACH__
1102 case 2: // @executable_path@/../Libraries
1104 char buf [4096]; // FIXME: MAX_PATH
1105 int binl;
1106 binl = mono_dl_get_executable_path (buf, sizeof (buf));
1107 if (binl != -1) {
1108 char *base, *newbase;
1109 char *resolvedname;
1110 buf [binl] = 0;
1111 resolvedname = mono_path_resolve_symlinks (buf);
1113 base = g_path_get_dirname (resolvedname);
1114 newbase = g_path_get_dirname(base);
1115 mdirname = g_strdup_printf ("%s/Libraries", newbase);
1117 g_free (resolvedname);
1118 g_free (base);
1119 g_free (newbase);
1121 break;
1123 #endif
1126 if (!mdirname)
1127 continue;
1129 module = legacy_probe_for_module_in_directory (mdirname, file_name);
1130 g_free (mdirname);
1131 if (module)
1132 break;
1135 return module;
1138 static MonoDl *
1139 legacy_probe_for_module (MonoImage *image, const char *new_scope)
1141 char *full_name, *file_name;
1142 char *error_msg = NULL;
1143 int i;
1144 MonoDl *module = NULL;
1146 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "DllImport attempting to load: '%s'.", new_scope);
1148 /* we allow a special name to dlopen from the running process namespace */
1149 if (strcmp (new_scope, "__Internal") == 0) {
1150 if (!internal_module)
1151 internal_module = mono_dl_open (NULL, MONO_DL_LAZY, &error_msg);
1152 module = internal_module;
1154 if (!module) {
1155 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '__Internal': '%s'.", error_msg);
1156 g_free (error_msg);
1159 return module;
1163 * Try loading the module using a variety of names
1165 for (i = 0; i < 5; ++i) {
1166 char *base_name = NULL, *dir_name = NULL;
1167 gboolean is_absolute;
1169 gboolean changed = legacy_probe_transform_path (new_scope, i, &file_name, &base_name, &dir_name, &is_absolute);
1170 if (!changed)
1171 continue;
1173 if (!module && is_absolute) {
1174 module = cached_module_load (file_name, MONO_DL_LAZY, &error_msg);
1175 if (!module) {
1176 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT,
1177 "DllImport error loading library '%s': '%s'.",
1178 file_name, error_msg);
1179 g_free (error_msg);
1183 if (!module && !is_absolute) {
1184 module = legacy_probe_for_module_relative_directories (image, file_name);
1187 if (!module) {
1188 void *iter = NULL;
1189 char *file_or_base = is_absolute ? base_name : file_name;
1190 while ((full_name = mono_dl_build_path (dir_name, file_or_base, &iter))) {
1191 module = cached_module_load (full_name, MONO_DL_LAZY, &error_msg);
1192 if (!module) {
1193 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT,
1194 "DllImport error loading library '%s': '%s'.",
1195 full_name, error_msg);
1196 g_free (error_msg);
1198 g_free (full_name);
1199 if (module)
1200 break;
1204 if (!module) {
1205 module = cached_module_load (file_name, MONO_DL_LAZY, &error_msg);
1206 if (!module) {
1207 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT,
1208 "DllImport error loading library '%s': '%s'.",
1209 file_name, error_msg);
1210 g_free (error_msg);
1214 g_free (file_name);
1215 if (is_absolute) {
1216 g_free (base_name);
1217 g_free (dir_name);
1220 if (module)
1221 break;
1224 return module;
1227 static MonoDl *
1228 legacy_lookup_native_library (MonoImage *image, const char *scope)
1230 MonoDl *module = NULL;
1231 gboolean cached = FALSE;
1233 mono_image_lock (image);
1234 if (!image->pinvoke_scopes)
1235 image->pinvoke_scopes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1236 module = (MonoDl *)g_hash_table_lookup (image->pinvoke_scopes, scope);
1237 mono_image_unlock (image);
1238 if (module)
1239 cached = TRUE;
1241 if (!module)
1242 module = legacy_probe_for_module (image, scope);
1244 if (module && !cached) {
1245 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT,
1246 "DllImport loaded library '%s'.", module->full_name);
1247 mono_image_lock (image);
1248 if (!g_hash_table_lookup (image->pinvoke_scopes, scope)) {
1249 g_hash_table_insert (image->pinvoke_scopes, g_strdup (scope), module);
1251 mono_image_unlock (image);
1254 return module;
1257 #endif // ENABLE_NETCORE
1259 gpointer
1260 lookup_pinvoke_call_impl (MonoMethod *method, MonoLookupPInvokeStatus *status_out)
1262 MonoImage *image = m_class_get_image (method->klass);
1263 #ifdef ENABLE_NETCORE
1264 MonoAssemblyLoadContext *alc = mono_image_get_alc (image);
1265 MonoCustomAttrInfo *cinfo;
1266 int flags;
1267 #endif
1268 MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *)method;
1269 MonoTableInfo *tables = image->tables;
1270 MonoTableInfo *im = &tables [MONO_TABLE_IMPLMAP];
1271 MonoTableInfo *mr = &tables [MONO_TABLE_MODULEREF];
1272 guint32 im_cols [MONO_IMPLMAP_SIZE];
1273 guint32 scope_token;
1274 const char *orig_import = NULL;
1275 const char *new_import = NULL;
1276 const char *orig_scope = NULL;
1277 const char *new_scope = NULL;
1278 char *error_msg = NULL;
1279 MonoDl *module = NULL;
1280 gpointer addr = NULL;
1282 MONO_REQ_GC_UNSAFE_MODE;
1284 g_assert (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL);
1286 g_assert (status_out);
1288 if (piinfo->addr)
1289 return piinfo->addr;
1291 if (image_is_dynamic (image)) {
1292 MonoReflectionMethodAux *method_aux =
1293 (MonoReflectionMethodAux *)g_hash_table_lookup (
1294 ((MonoDynamicImage*)m_class_get_image (method->klass))->method_aux_hash, method);
1295 if (!method_aux)
1296 goto exit;
1298 orig_import = method_aux->dllentry;
1299 orig_scope = method_aux->dll;
1301 else {
1302 if (!piinfo->implmap_idx || piinfo->implmap_idx > im->rows)
1303 goto exit;
1305 mono_metadata_decode_row (im, piinfo->implmap_idx - 1, im_cols, MONO_IMPLMAP_SIZE);
1307 if (!im_cols [MONO_IMPLMAP_SCOPE] || im_cols [MONO_IMPLMAP_SCOPE] > mr->rows)
1308 goto exit;
1310 piinfo->piflags = im_cols [MONO_IMPLMAP_FLAGS];
1311 orig_import = mono_metadata_string_heap (image, im_cols [MONO_IMPLMAP_NAME]);
1312 scope_token = mono_metadata_decode_row_col (mr, im_cols [MONO_IMPLMAP_SCOPE] - 1, MONO_MODULEREF_NAME);
1313 orig_scope = mono_metadata_string_heap (image, scope_token);
1316 #ifndef DISABLE_DLLMAP
1317 // FIXME: The dllmap remaps System.Native to mono-native
1318 mono_dllmap_lookup (image, orig_scope, orig_import, &new_scope, &new_import);
1319 #else
1320 new_scope = g_strdup (orig_scope);
1321 new_import = g_strdup (orig_import);
1322 #endif
1324 if (strcmp (new_scope, "QCall") == 0) {
1325 piinfo->addr = mono_lookup_pinvoke_qcall_internal (method, status_out);
1326 if (!piinfo->addr) {
1327 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_DLLIMPORT,
1328 "Unable to find qcall for '%s'.",
1329 new_import);
1330 status_out->err_code = LOOKUP_PINVOKE_ERR_NO_SYM;
1331 status_out->err_arg = g_strdup (new_import);
1333 return piinfo->addr;
1336 #ifdef ENABLE_NETCORE
1337 #ifndef HOST_WIN32
1338 retry_with_libcoreclr:
1339 #endif
1341 ERROR_DECL (local_error);
1342 cinfo = mono_custom_attrs_from_method_checked (method, local_error);
1343 mono_error_cleanup (local_error);
1345 flags = get_dllimportsearchpath_flags (cinfo);
1346 if (cinfo && !cinfo->cached)
1347 mono_custom_attrs_free (cinfo);
1349 if (flags < 0) {
1350 ERROR_DECL (local_error);
1351 cinfo = mono_custom_attrs_from_assembly_checked (m_class_get_image (method->klass)->assembly, TRUE, local_error);
1352 mono_error_cleanup (local_error);
1353 flags = get_dllimportsearchpath_flags (cinfo);
1354 if (cinfo && !cinfo->cached)
1355 mono_custom_attrs_free (cinfo);
1357 if (flags < 0)
1358 flags = 0;
1359 module = netcore_lookup_native_library (alc, image, new_scope, flags);
1360 #else
1361 module = legacy_lookup_native_library (image, new_scope);
1362 #endif
1364 if (!module) {
1365 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_DLLIMPORT,
1366 "DllImport unable to load library '%s'.",
1367 new_scope);
1369 status_out->err_code = LOOKUP_PINVOKE_ERR_NO_LIB;
1370 status_out->err_arg = g_strdup (new_scope);
1371 goto exit;
1374 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT,
1375 "DllImport searching in: '%s' ('%s').", new_scope, module->full_name);
1377 addr = pinvoke_probe_for_symbol (module, piinfo, new_import, &error_msg);
1379 if (!addr) {
1380 #if defined(ENABLE_NETCORE) && !defined(HOST_WIN32)
1381 if (strcmp (new_scope, "__Internal") == 0) {
1382 g_free ((char *)new_scope);
1383 new_scope = g_strdup (MONO_LOADER_LIBRARY_NAME);
1384 goto retry_with_libcoreclr;
1386 #endif
1387 status_out->err_code = LOOKUP_PINVOKE_ERR_NO_SYM;
1388 status_out->err_arg = g_strdup (new_import);
1389 goto exit;
1391 piinfo->addr = addr;
1393 exit:
1394 g_free ((char *)new_import);
1395 g_free ((char *)new_scope);
1396 g_free (error_msg);
1397 return addr;
1400 static gpointer
1401 pinvoke_probe_for_symbol (MonoDl *module, MonoMethodPInvoke *piinfo, const char *import, char **error_msg_out)
1403 char *error_msg = NULL;
1404 gpointer addr = NULL;
1406 g_assert (error_msg_out);
1408 #ifdef HOST_WIN32
1409 if (import && import [0] == '#' && isdigit (import [1])) {
1410 char *end;
1411 long id;
1413 id = strtol (import + 1, &end, 10);
1414 if (id > 0 && *end == '\0')
1415 import++;
1417 #endif
1418 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT,
1419 "Searching for '%s'.", import);
1421 #if !defined(ENABLE_NETCORE) || defined(HOST_WIN32) // For netcore, name mangling is Windows-exclusive
1422 if (piinfo->piflags & PINVOKE_ATTRIBUTE_NO_MANGLE)
1423 error_msg = mono_dl_symbol (module, import, &addr);
1424 else {
1426 * Search using a variety of mangled names
1428 for (int mangle_stdcall = 0; mangle_stdcall <= 1 && addr == NULL; mangle_stdcall++) {
1429 #if HOST_WIN32 && HOST_X86
1430 const int max_managle_param_count = (mangle_stdcall == 0) ? 0 : 256;
1431 #else
1432 const int max_managle_param_count = 0;
1433 #endif
1434 for (int mangle_charset = 0; mangle_charset <= 1 && addr == NULL; mangle_charset ++) {
1435 for (int mangle_param_count = 0; mangle_param_count <= max_managle_param_count && addr == NULL; mangle_param_count += 4) {
1437 char *mangled_name = (char*)import;
1438 switch (piinfo->piflags & PINVOKE_ATTRIBUTE_CHAR_SET_MASK) {
1439 case PINVOKE_ATTRIBUTE_CHAR_SET_UNICODE:
1440 /* Try the mangled name first */
1441 if (mangle_charset == 0)
1442 mangled_name = g_strconcat (import, "W", (const char*)NULL);
1443 break;
1444 case PINVOKE_ATTRIBUTE_CHAR_SET_AUTO:
1445 #ifdef HOST_WIN32
1446 if (mangle_charset == 0)
1447 mangled_name = g_strconcat (import, "W", (const char*)NULL);
1448 #else
1449 /* Try the mangled name last */
1450 if (mangle_charset == 1)
1451 mangled_name = g_strconcat (import, "A", (const char*)NULL);
1452 #endif
1453 break;
1454 case PINVOKE_ATTRIBUTE_CHAR_SET_ANSI:
1455 default:
1456 /* Try the mangled name last */
1457 if (mangle_charset == 1)
1458 mangled_name = g_strconcat (import, "A", (const char*)NULL);
1459 break;
1462 #if HOST_WIN32 && HOST_X86
1463 /* Try the stdcall mangled name */
1465 * gcc under windows creates mangled names without the underscore, but MS.NET
1466 * doesn't support it, so we doesn't support it either.
1468 if (mangle_stdcall == 1) {
1469 MonoMethod *method = &piinfo->method;
1470 int param_count;
1471 if (mangle_param_count == 0)
1472 param_count = mono_method_signature_internal (method)->param_count * sizeof (gpointer);
1473 else
1474 /* Try brute force, since it would be very hard to compute the stack usage correctly */
1475 param_count = mangle_param_count;
1477 char *mangled_stdcall_name = g_strdup_printf ("_%s@%d", mangled_name, param_count);
1479 if (mangled_name != import)
1480 g_free (mangled_name);
1482 mangled_name = mangled_stdcall_name;
1484 #endif
1485 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT,
1486 "Probing '%s'.", mangled_name);
1488 error_msg = mono_dl_symbol (module, mangled_name, &addr);
1490 if (addr)
1491 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT,
1492 "Found as '%s'.", mangled_name);
1493 else
1494 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT,
1495 "Could not find '%s' due to '%s'.", mangled_name, error_msg);
1497 g_free (error_msg);
1498 error_msg = NULL;
1500 if (mangled_name != import)
1501 g_free (mangled_name);
1506 #else
1507 error_msg = mono_dl_symbol (module, import, &addr);
1508 #endif
1510 *error_msg_out = error_msg;
1511 return addr;
1514 #ifdef ENABLE_NETCORE
1515 void
1516 ves_icall_System_Runtime_InteropServices_NativeLibrary_FreeLib (gpointer lib, MonoError *error)
1518 MonoDl *module;
1519 guint32 ref_count;
1521 g_assert (lib);
1523 // Don't free __Internal
1524 if (internal_module && lib == internal_module->handle)
1525 return;
1527 native_library_lock ();
1529 module = netcore_handle_lookup (lib);
1530 if (!module)
1531 goto leave;
1533 ref_count = mono_refcount_dec (module);
1534 if (ref_count > 0)
1535 goto leave;
1537 g_hash_table_remove (native_library_module_map, module->handle);
1538 g_hash_table_add (native_library_module_blocklist, module);
1539 mono_dl_close (module);
1541 leave:
1542 native_library_unlock ();
1545 gpointer
1546 ves_icall_System_Runtime_InteropServices_NativeLibrary_GetSymbol (gpointer lib, MonoStringHandle symbol_name_handle, MonoBoolean throw_on_error, MonoError *error)
1548 MonoDl *module;
1549 gpointer symbol = NULL;
1550 char *symbol_name;
1552 g_assert (lib);
1554 ERROR_LOCAL_BEGIN (local_error, error, throw_on_error)
1556 symbol_name = mono_string_handle_to_utf8 (symbol_name_handle, error);
1557 goto_if_nok (error, leave_nolock);
1559 native_library_lock ();
1561 module = netcore_handle_lookup (lib);
1562 if (!module)
1563 mono_error_set_generic_error (error, "System", "DllNotFoundException", "%p: %s", lib, symbol_name);
1564 goto_if_nok (error, leave);
1566 mono_dl_symbol (module, symbol_name, &symbol);
1567 if (!symbol)
1568 mono_error_set_generic_error (error, "System", "EntryPointNotFoundException", "%s: %s", module->full_name, symbol_name);
1569 goto_if_nok (error, leave);
1571 leave:
1572 native_library_unlock ();
1574 leave_nolock:
1575 ERROR_LOCAL_END (local_error);
1576 g_free (symbol_name);
1578 return symbol;
1581 // LOCKING: expects you to hold native_library_module_lock
1582 static MonoDl *
1583 check_native_library_cache (MonoDl *module)
1585 gpointer handle = module->handle;
1587 MonoDl *cached_module = netcore_handle_lookup (handle);
1588 if (cached_module) {
1589 g_free (module->full_name);
1590 g_free (module);
1591 mono_refcount_inc (cached_module);
1592 return cached_module;
1594 g_hash_table_insert (native_library_module_map, handle, (gpointer)module);
1596 return module;
1599 gpointer
1600 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)
1602 MonoDl *module;
1603 gpointer handle = NULL;
1604 MonoAssembly *assembly = MONO_HANDLE_GETVAL (assembly_handle, assembly);
1605 MonoImage *image = mono_assembly_get_image_internal (assembly);
1606 char *lib_name;
1608 ERROR_LOCAL_BEGIN (local_error, error, throw_on_error)
1610 lib_name = mono_string_handle_to_utf8 (lib_name_handle, error);
1611 goto_if_nok (error, leave);
1613 // FIXME: implement search flag defaults properly
1614 module = netcore_probe_for_module (image, lib_name, has_search_flag ? search_flag : DLLIMPORTSEARCHPATH_ASSEMBLY_DIRECTORY);
1615 if (!module)
1616 mono_error_set_generic_error (error, "System", "DllNotFoundException", "%s", lib_name);
1617 goto_if_nok (error, leave);
1619 native_library_lock ();
1620 module = check_native_library_cache (module);
1621 native_library_unlock ();
1623 handle = module->handle;
1625 leave:
1626 ERROR_LOCAL_END (local_error);
1627 g_free (lib_name);
1629 return handle;
1632 gpointer
1633 ves_icall_System_Runtime_InteropServices_NativeLibrary_LoadFromPath (MonoStringHandle lib_path_handle, MonoBoolean throw_on_error, MonoError *error)
1635 MonoDl *module;
1636 gpointer handle = NULL;
1637 char *error_msg = NULL;
1638 char *lib_path;
1640 ERROR_LOCAL_BEGIN (local_error, error, throw_on_error)
1642 lib_path = mono_string_handle_to_utf8 (lib_path_handle, error);
1643 goto_if_nok (error, leave);
1645 module = mono_dl_open (lib_path, MONO_DL_LAZY, &error_msg);
1646 if (!module) {
1647 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", lib_path, error_msg);
1648 mono_error_set_generic_error (error, "System", "DllNotFoundException", "'%s': '%s'", lib_path, error_msg);
1649 g_free (error_msg);
1651 goto_if_nok (error, leave);
1653 native_library_lock ();
1654 module = check_native_library_cache (module);
1655 native_library_unlock ();
1657 handle = module->handle;
1659 leave:
1660 ERROR_LOCAL_END (local_error);
1661 g_free (lib_path);
1663 return handle;
1665 #endif
1667 #ifdef HAVE_ATEXIT
1668 static void
1669 delete_bundled_libraries (void)
1671 GSList *list;
1673 for (list = bundle_library_paths; list != NULL; list = list->next){
1674 unlink ((const char*)list->data);
1676 rmdir (bundled_dylibrary_directory);
1678 #endif
1680 static void
1681 bundle_save_library_initialize (void)
1683 bundle_save_library_initialized = TRUE;
1684 char *path = g_build_filename (g_get_tmp_dir (), "mono-bundle-XXXXXX", (const char*)NULL);
1685 bundled_dylibrary_directory = g_mkdtemp (path);
1686 g_free (path);
1687 if (bundled_dylibrary_directory == NULL)
1688 return;
1689 #ifdef HAVE_ATEXIT
1690 atexit (delete_bundled_libraries);
1691 #endif
1694 void
1695 mono_loader_save_bundled_library (int fd, uint64_t offset, uint64_t size, const char *destfname)
1697 MonoDl *lib;
1698 char *file, *buffer, *err, *internal_path;
1699 if (!bundle_save_library_initialized)
1700 bundle_save_library_initialize ();
1702 file = g_build_filename (bundled_dylibrary_directory, destfname, (const char*)NULL);
1703 buffer = g_str_from_file_region (fd, offset, size);
1704 g_file_set_contents (file, buffer, size, NULL);
1706 lib = mono_dl_open (file, MONO_DL_LAZY, &err);
1707 if (lib == NULL){
1708 fprintf (stderr, "Error loading shared library: %s %s\n", file, err);
1709 exit (1);
1711 // Register the name with "." as this is how it will be found when embedded
1712 internal_path = g_build_filename (".", destfname, (const char*)NULL);
1713 mono_loader_register_module (internal_path, lib);
1714 g_free (internal_path);
1715 bundle_library_paths = g_slist_append (bundle_library_paths, file);
1717 g_free (buffer);