From ede470409fa74c1eb28c312338322d6bd2c23916 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 24 Jun 2026 17:36:28 -0700 Subject: [PATCH] Pass correct search path to DllImportResolver for default flags on Mono When neither the P/Invoke nor the assembly has a DefaultDllImportSearchPaths attribute, Mono passed DllImportSearchPath.AssemblyDirectory to a registered DllImportResolver instead of null. lookup_pinvoke_call_impl defaults the flags to ASSEMBLY_DIRECTORY for the built-in probing logic, and netcore_resolve_with_dll_import_resolver derived "has search flags" from (flags != 0), so the defaulted value leaked to the resolver. Combined with #114756 - which made an explicit AssemblyDirectory no longer fall back to the OS search - this broke resolvers that forward the search path to NativeLibrary.Load, such as loading iOS @rpath frameworks. Thread the existing user_specified_flags signal through to the resolver so it receives the attribute value, or null when none was specified, matching CoreCLR and NativeAOT. Fixes #129798 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/mono/mono/metadata/native-library.c | 10 ++-- .../DllImportSearchPathsTest.cs | 46 +++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/mono/mono/metadata/native-library.c b/src/mono/mono/metadata/native-library.c index ee8aa071c59db5..4da97338201752 100644 --- a/src/mono/mono/metadata/native-library.c +++ b/src/mono/mono/metadata/native-library.c @@ -440,7 +440,7 @@ static MonoDl* native_handle_lookup_wrapper (gpointer handle) } static MonoDl * -netcore_resolve_with_dll_import_resolver (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope, guint32 flags, MonoError *error) +netcore_resolve_with_dll_import_resolver (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope, gboolean user_specified_flags, guint32 flags, MonoError *error) { MonoDl *result = NULL; gpointer lib = NULL; @@ -476,7 +476,7 @@ netcore_resolve_with_dll_import_resolver (MonoAssemblyLoadContext *alc, MonoAsse goto_if_nok (error, leave); gboolean has_search_flags; - has_search_flags = flags != 0 ? TRUE : FALSE; + has_search_flags = user_specified_flags; gpointer args [5]; args [0] = MONO_HANDLE_RAW (scope_handle); args [1] = MONO_HANDLE_RAW (assembly_handle); @@ -493,12 +493,12 @@ netcore_resolve_with_dll_import_resolver (MonoAssemblyLoadContext *alc, MonoAsse } static MonoDl * -netcore_resolve_with_dll_import_resolver_nofail (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope, guint32 flags) +netcore_resolve_with_dll_import_resolver_nofail (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, const char *scope, gboolean user_specified_flags, guint32 flags) { MonoDl *result = NULL; ERROR_DECL (error); - result = netcore_resolve_with_dll_import_resolver (alc, assembly, scope, flags, error); + result = netcore_resolve_with_dll_import_resolver (alc, assembly, scope, user_specified_flags, flags, error); if (!is_ok (error)) mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Error while invoking ALC DllImportResolver(\"%s\") delegate: '%s'", scope, mono_error_get_message (error)); @@ -703,7 +703,7 @@ netcore_lookup_native_library (MonoAssemblyLoadContext *alc, MonoImage *image, c goto leave; } - module = (MonoDl *)netcore_resolve_with_dll_import_resolver_nofail (alc, assembly, scope, flags); + module = (MonoDl *)netcore_resolve_with_dll_import_resolver_nofail (alc, assembly, scope, user_specified_flags, flags); if (module) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Native library found via DllImportResolver: '%s'.", scope); goto add_to_alc_cache; diff --git a/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs index a3b78f1f99f2ca..a5d52f32955045 100644 --- a/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs +++ b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs @@ -141,6 +141,31 @@ public static void AssemblyDirectory_SearchFlags_WithDependency_Found() Assert.Equal(3, sum); Console.WriteLine("NativeLibraryWithDependency.Sum returned {0}", sum); } + + [Fact] + public static void DllImportResolver_SearchPathMatchesAttributes() + { + DllImportSearchPath? observedSearchPath = null; + NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), (libraryName, assembly, searchPath) => + { + if (libraryName == SearchPathPInvoke.LibraryName) + observedSearchPath = searchPath; + + return IntPtr.Zero; + }); + + Assert.Throws(() => SearchPathPInvoke.AssemblyDirectory()); + Assert.Equal(DllImportSearchPath.AssemblyDirectory, observedSearchPath); + + Assert.Throws(() => SearchPathPInvoke.System32()); + Assert.Equal(DllImportSearchPath.System32, observedSearchPath); + + Assert.Throws(() => SearchPathPInvoke.LegacyBehavior()); + Assert.Equal(DllImportSearchPath.LegacyBehavior, observedSearchPath); + + Assert.Throws(() => SearchPathPInvoke.NoFlags()); + Assert.Null(observedSearchPath); + } } public class NativeLibraryPInvoke @@ -219,4 +244,25 @@ public static int Sum(int a, int b) [DllImport(nameof(NativeLibraryWithDependency))] [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.System32)] static extern int CallDependencySum(int a, int b); +} + +public class SearchPathPInvoke +{ + // Routed through the registered DllImportResolver, then fails the built-in search. + internal const string LibraryName = "DoesNotExist"; + + [DllImport(LibraryName)] + public static extern void NoFlags(); + + [DllImport(LibraryName)] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + public static extern void AssemblyDirectory(); + + [DllImport(LibraryName)] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + public static extern void System32(); + + [DllImport(LibraryName)] + [DefaultDllImportSearchPaths(DllImportSearchPath.LegacyBehavior)] + public static extern void LegacyBehavior(); } \ No newline at end of file