From 8f54d67f5f5a451ce43f1a70a1fc5fd0e4e469f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 30 Apr 2019 04:16:10 +0200 Subject: Move NativeLibrary to the shared partition (#24143) I'm taking the LibraryNameVariation helper from System.Runtime.Loader for the ride as well because it's a general purpose probing logic that is useful in a managed implementation of NativeLibrary. --- .../System.Private.CoreLib.csproj | 2 +- .../shared/System.Private.CoreLib.Shared.projitems | 4 + .../Runtime/InteropServices/NativeLibrary.cs | 247 +++++++++++++++++++ .../Runtime/Loader/LibraryNameVariation.Unix.cs | 69 ++++++ .../Runtime/Loader/LibraryNameVariation.Windows.cs | 29 +++ .../System/Runtime/Loader/LibraryNameVariation.cs | 20 ++ .../InteropServices/NativeLibrary.CoreCLR.cs | 39 +++ .../Runtime/InteropServices/NativeLibrary.cs | 265 --------------------- .../Runtime/Loader/AssemblyDependencyResolver.cs | 85 +------ 9 files changed, 410 insertions(+), 350 deletions(-) create mode 100644 src/System.Private.CoreLib/shared/System/Runtime/InteropServices/NativeLibrary.cs create mode 100644 src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.Unix.cs create mode 100644 src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.Windows.cs create mode 100644 src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.cs create mode 100644 src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreCLR.cs delete mode 100644 src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs (limited to 'src') diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj index c012ba5cae..d97f405e61 100644 --- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -242,7 +242,7 @@ - + diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index 45628625d4..5337436bce 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -673,6 +673,7 @@ + @@ -700,6 +701,7 @@ + @@ -1105,6 +1107,7 @@ + @@ -1270,6 +1273,7 @@ + diff --git a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/NativeLibrary.cs b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/NativeLibrary.cs new file mode 100644 index 0000000000..8c410242cf --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/NativeLibrary.cs @@ -0,0 +1,247 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.Runtime.InteropServices +{ + /// + /// A delegate used to resolve native libraries via callback. + /// + /// The native library to resolve + /// The assembly requesting the resolution + /// + /// The DllImportSearchPathsAttribute on the PInvoke, if any. + /// Otherwise, the DllImportSearchPathsAttribute on the assembly, if any. + /// Otherwise null. + /// + /// The handle for the loaded native library on success, null on failure + public delegate IntPtr DllImportResolver(string libraryName, + Assembly assembly, + DllImportSearchPath? searchPath); + + /// + /// APIs for managing Native Libraries + /// + public static partial class NativeLibrary + { + /// + /// NativeLibrary Loader: Simple API + /// This method is a wrapper around OS loader, using "default" flags. + /// + /// The name of the native library to be loaded + /// The handle for the loaded native library + /// If libraryPath is null + /// If the library can't be found. + /// If the library is not valid. + public static IntPtr Load(string libraryPath) + { + if (libraryPath == null) + throw new ArgumentNullException(nameof(libraryPath)); + + return LoadFromPath(libraryPath, throwOnError: true); + } + + /// + /// NativeLibrary Loader: Simple API that doesn't throw + /// + /// The name of the native library to be loaded + /// The out-parameter for the loaded native library handle + /// True on successful load, false otherwise + /// If libraryPath is null + public static bool TryLoad(string libraryPath, out IntPtr handle) + { + if (libraryPath == null) + throw new ArgumentNullException(nameof(libraryPath)); + + handle = LoadFromPath(libraryPath, throwOnError: false); + return handle != IntPtr.Zero; + } + + /// + /// NativeLibrary Loader: High-level API + /// Given a library name, this function searches specific paths based on the + /// runtime configuration, input parameters, and attributes of the calling assembly. + /// If DllImportSearchPath parameter is non-null, the flags in this enumeration are used. + /// Otherwise, the flags specified by the DefaultDllImportSearchPaths attribute on the + /// calling assembly (if any) are used. + /// This LoadLibrary() method does not invoke the managed call-backs for native library resolution: + /// * The per-assembly registered callback + /// * AssemblyLoadContext.LoadUnmanagedDll() + /// * AssemblyLoadContext.ResolvingUnmanagedDllEvent + /// + /// The name of the native library to be loaded + /// The assembly loading the native library + /// The search path + /// The handle for the loaded library + /// If libraryPath or assembly is null + /// If assembly is not a RuntimeAssembly + /// If the library can't be found. + /// If the library is not valid. + public static IntPtr Load(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) + { + if (libraryName == null) + throw new ArgumentNullException(nameof(libraryName)); + if (assembly == null) + throw new ArgumentNullException(nameof(assembly)); + if (!assembly.IsRuntimeImplemented()) + throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly); + + return LoadLibraryByName(libraryName, + assembly, + searchPath, + throwOnError: true); + } + + /// + /// NativeLibrary Loader: High-level API that doesn't throw. + /// + /// The name of the native library to be loaded + /// The search path + /// The assembly loading the native library + /// The out-parameter for the loaded native library handle + /// True on successful load, false otherwise + /// If libraryPath or assembly is null + /// If assembly is not a RuntimeAssembly + public static bool TryLoad(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, out IntPtr handle) + { + if (libraryName == null) + throw new ArgumentNullException(nameof(libraryName)); + if (assembly == null) + throw new ArgumentNullException(nameof(assembly)); + if (!assembly.IsRuntimeImplemented()) + throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly); + + handle = LoadLibraryByName(libraryName, + assembly, + searchPath, + throwOnError: false); + return handle != IntPtr.Zero; + } + + /// + /// Free a loaded library + /// Given a library handle, free it. + /// No action if the input handle is null. + /// + /// The native library handle to be freed + public static void Free(IntPtr handle) + { + FreeLib(handle); + } + + /// + /// Get the address of an exported Symbol + /// This is a simple wrapper around OS calls, and does not perform any name mangling. + /// + /// The native library handle + /// The name of the exported symbol + /// The address of the symbol + /// If handle or name is null + /// If the symbol is not found + public static IntPtr GetExport(IntPtr handle, string name) + { + if (handle == IntPtr.Zero) + throw new ArgumentNullException(nameof(handle)); + if (name == null) + throw new ArgumentNullException(nameof(name)); + + return GetSymbol(handle, name, throwOnError: true); + } + + /// + /// Get the address of an exported Symbol, but do not throw + /// + /// The native library handle + /// The name of the exported symbol + /// The out-parameter for the symbol address, if it exists + /// True on success, false otherwise + /// If handle or name is null + public static bool TryGetExport(IntPtr handle, string name, out IntPtr address) + { + if (handle == IntPtr.Zero) + throw new ArgumentNullException(nameof(handle)); + if (name == null) + throw new ArgumentNullException(nameof(name)); + + address = GetSymbol(handle, name, throwOnError: false); + return address != IntPtr.Zero; + } + + /// + /// Map from assembly to native-library resolver. + /// Interop specific fields and properties are generally not added to Assembly class. + /// Therefore, this table uses weak assembly pointers to indirectly achieve + /// similar behavior. + /// + private static ConditionalWeakTable? s_nativeDllResolveMap; + + /// + /// Set a callback for resolving native library imports from an assembly. + /// This per-assembly resolver is the first attempt to resolve native library loads + /// initiated by this assembly. + /// + /// Only one resolver can be registered per assembly. + /// Trying to register a second resolver fails with InvalidOperationException. + /// + /// The assembly for which the resolver is registered + /// The resolver callback to register + /// If assembly or resolver is null + /// If a resolver is already set for this assembly + public static void SetDllImportResolver(Assembly assembly, DllImportResolver resolver) + { + if (assembly == null) + throw new ArgumentNullException(nameof(assembly)); + if (resolver == null) + throw new ArgumentNullException(nameof(resolver)); + if (!assembly.IsRuntimeImplemented()) + throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly); + + if (s_nativeDllResolveMap == null) + { + Interlocked.CompareExchange(ref s_nativeDllResolveMap, + new ConditionalWeakTable(), null); + } + + try + { + s_nativeDllResolveMap!.Add(assembly, resolver); // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 + } + catch (ArgumentException) + { + // ConditionalWealTable throws ArgumentException if the Key already exists + throw new InvalidOperationException(SR.InvalidOperation_CannotRegisterSecondResolver); + } + } + + /// + /// The helper function that calls the per-assembly native-library resolver + /// if one is registered for this assembly. + /// + /// The native library to load + /// The assembly trying load the native library + /// If the pInvoke has DefaultDllImportSearchPathAttribute + /// If hasdllImportSearchPathFlags is true, the flags in + /// DefaultDllImportSearchPathAttribute; meaningless otherwise + /// The handle for the loaded library on success. Null on failure. + internal static IntPtr LoadLibraryCallbackStub(string libraryName, Assembly assembly, + bool hasDllImportSearchPathFlags, uint dllImportSearchPathFlags) + { + if (s_nativeDllResolveMap == null) + { + return IntPtr.Zero; + } + + if (!s_nativeDllResolveMap.TryGetValue(assembly, out DllImportResolver resolver)) + { + return IntPtr.Zero; + } + + return resolver(libraryName, assembly, hasDllImportSearchPathFlags ? (DllImportSearchPath?)dllImportSearchPathFlags : null); + } + } +} diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.Unix.cs b/src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.Unix.cs new file mode 100644 index 0000000000..4895dc8685 --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.Unix.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable +using System.Collections.Generic; +using System.IO; + +namespace System.Runtime.Loader +{ + internal partial struct LibraryNameVariation + { + private const string LibraryNamePrefix = "lib"; +#if PLATFORM_OSX + private const string LibraryNameSuffix = ".dylib"; +#else + private const string LibraryNameSuffix = ".so"; +#endif + + internal static IEnumerable DetermineLibraryNameVariations(string libName, bool isRelativePath) + { + // This is a copy of the logic in DetermineLibNameVariations in dllimport.cpp in CoreCLR + + if (!isRelativePath) + { + yield return new LibraryNameVariation(string.Empty, string.Empty); + } + else + { + bool containsSuffix = false; + int indexOfSuffix = libName.IndexOf(LibraryNameSuffix, StringComparison.OrdinalIgnoreCase); + if (indexOfSuffix >= 0) + { + indexOfSuffix += LibraryNameSuffix.Length; + containsSuffix = indexOfSuffix == libName.Length || libName[indexOfSuffix] == '.'; + } + + bool containsDelim = libName.Contains(Path.DirectorySeparatorChar); + + if (containsSuffix) + { + yield return new LibraryNameVariation(string.Empty, string.Empty); + if (!containsDelim) + { + yield return new LibraryNameVariation(LibraryNamePrefix, string.Empty); + } + yield return new LibraryNameVariation(string.Empty, LibraryNameSuffix); + if (!containsDelim) + { + yield return new LibraryNameVariation(LibraryNamePrefix, LibraryNameSuffix); + } + } + else + { + yield return new LibraryNameVariation(string.Empty, LibraryNameSuffix); + if (!containsDelim) + { + yield return new LibraryNameVariation(LibraryNamePrefix, LibraryNameSuffix); + } + yield return new LibraryNameVariation(string.Empty, string.Empty); + if (!containsDelim) + { + yield return new LibraryNameVariation(LibraryNamePrefix, string.Empty); + } + } + } + } + } +} diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.Windows.cs b/src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.Windows.cs new file mode 100644 index 0000000000..99ccb1e55f --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.Windows.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable +using System.Collections.Generic; + +namespace System.Runtime.Loader +{ + internal partial struct LibraryNameVariation + { + private const string LibraryNameSuffix = ".dll"; + + internal static IEnumerable DetermineLibraryNameVariations(string libName, bool isRelativePath) + { + // This is a copy of the logic in DetermineLibNameVariations in dllimport.cpp in CoreCLR + + yield return new LibraryNameVariation(string.Empty, string.Empty); + + if (isRelativePath && + !libName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && + !libName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) + { + yield return new LibraryNameVariation(string.Empty, LibraryNameSuffix); + } + } + + } +} diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.cs b/src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.cs new file mode 100644 index 0000000000..029e79a191 --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Runtime/Loader/LibraryNameVariation.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +namespace System.Runtime.Loader +{ + internal partial struct LibraryNameVariation + { + public string Prefix; + public string Suffix; + + public LibraryNameVariation(string prefix, string suffix) + { + Prefix = prefix; + Suffix = suffix; + } + } +} diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreCLR.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreCLR.cs new file mode 100644 index 0000000000..2928f8ca5d --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreCLR.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.Runtime.InteropServices +{ + public static partial class NativeLibrary + { + internal static IntPtr LoadLibraryByName(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, bool throwOnError) + { + return LoadByName(libraryName, + ((RuntimeAssembly)assembly).GetNativeHandle(), + searchPath.HasValue, + (uint) searchPath.GetValueOrDefault(), + throwOnError); + } + + /// External functions that implement the NativeLibrary interface + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + internal static extern IntPtr LoadFromPath(string libraryName, bool throwOnError); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + internal static extern IntPtr LoadByName(string libraryName, RuntimeAssembly callingAssembly, + bool hasDllImportSearchPathFlag, uint dllImportSearchPathFlag, + bool throwOnError); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + internal static extern void FreeLib(IntPtr handle); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + internal static extern IntPtr GetSymbol(IntPtr handle, string symbolName, bool throwOnError); + } +} diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs deleted file mode 100644 index fd0318e021..0000000000 --- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs +++ /dev/null @@ -1,265 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable enable -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Threading; - -namespace System.Runtime.InteropServices -{ - /// - /// A delegate used to resolve native libraries via callback. - /// - /// The native library to resolve - /// The assembly requesting the resolution - /// - /// The DllImportSearchPathsAttribute on the PInvoke, if any. - /// Otherwise, the DllImportSearchPathsAttribute on the assembly, if any. - /// Otherwise null. - /// - /// The handle for the loaded native library on success, null on failure - public delegate IntPtr DllImportResolver(string libraryName, - Assembly assembly, - DllImportSearchPath? searchPath); - - /// - /// APIs for managing Native Libraries - /// - public static partial class NativeLibrary - { - /// - /// NativeLibrary Loader: Simple API - /// This method is a wrapper around OS loader, using "default" flags. - /// - /// The name of the native library to be loaded - /// The handle for the loaded native library - /// If libraryPath is null - /// If the library can't be found. - /// If the library is not valid. - public static IntPtr Load(string libraryPath) - { - if (libraryPath == null) - throw new ArgumentNullException(nameof(libraryPath)); - - return LoadFromPath(libraryPath, throwOnError: true); - } - - /// - /// NativeLibrary Loader: Simple API that doesn't throw - /// - /// The name of the native library to be loaded - /// The out-parameter for the loaded native library handle - /// True on successful load, false otherwise - /// If libraryPath is null - public static bool TryLoad(string libraryPath, out IntPtr handle) - { - if (libraryPath == null) - throw new ArgumentNullException(nameof(libraryPath)); - - handle = LoadFromPath(libraryPath, throwOnError: false); - return handle != IntPtr.Zero; - } - - /// - /// NativeLibrary Loader: High-level API - /// Given a library name, this function searches specific paths based on the - /// runtime configuration, input parameters, and attributes of the calling assembly. - /// If DllImportSearchPath parameter is non-null, the flags in this enumeration are used. - /// Otherwise, the flags specified by the DefaultDllImportSearchPaths attribute on the - /// calling assembly (if any) are used. - /// This LoadLibrary() method does not invoke the managed call-backs for native library resolution: - /// * The per-assembly registered callback - /// * AssemblyLoadContext.LoadUnmanagedDll() - /// * AssemblyLoadContext.ResolvingUnmanagedDllEvent - /// - /// The name of the native library to be loaded - /// The assembly loading the native library - /// The search path - /// The handle for the loaded library - /// If libraryPath or assembly is null - /// If assembly is not a RuntimeAssembly - /// If the library can't be found. - /// If the library is not valid. - public static IntPtr Load(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) - { - if (libraryName == null) - throw new ArgumentNullException(nameof(libraryName)); - if (assembly == null) - throw new ArgumentNullException(nameof(assembly)); - if (!(assembly is RuntimeAssembly)) - throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly); - - return LoadByName(libraryName, - ((RuntimeAssembly)assembly).GetNativeHandle(), - searchPath.HasValue, - (uint) searchPath.GetValueOrDefault(), - throwOnError: true); - } - - /// - /// NativeLibrary Loader: High-level API that doesn't throw. - /// - /// The name of the native library to be loaded - /// The search path - /// The assembly loading the native library - /// The out-parameter for the loaded native library handle - /// True on successful load, false otherwise - /// If libraryPath or assembly is null - /// If assembly is not a RuntimeAssembly - public static bool TryLoad(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, out IntPtr handle) - { - if (libraryName == null) - throw new ArgumentNullException(nameof(libraryName)); - if (assembly == null) - throw new ArgumentNullException(nameof(assembly)); - if (!(assembly is RuntimeAssembly)) - throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly); - - handle = LoadByName(libraryName, - ((RuntimeAssembly)assembly).GetNativeHandle(), - searchPath.HasValue, - (uint) searchPath.GetValueOrDefault(), - throwOnError: false); - return handle != IntPtr.Zero; - } - - /// - /// Free a loaded library - /// Given a library handle, free it. - /// No action if the input handle is null. - /// - /// The native library handle to be freed - public static void Free(IntPtr handle) - { - FreeLib(handle); - } - - /// - /// Get the address of an exported Symbol - /// This is a simple wrapper around OS calls, and does not perform any name mangling. - /// - /// The native library handle - /// The name of the exported symbol - /// The address of the symbol - /// If handle or name is null - /// If the symbol is not found - public static IntPtr GetExport(IntPtr handle, string name) - { - if (handle == IntPtr.Zero) - throw new ArgumentNullException(nameof(handle)); - if (name == null) - throw new ArgumentNullException(nameof(name)); - - return GetSymbol(handle, name, throwOnError: true); - } - - /// - /// Get the address of an exported Symbol, but do not throw - /// - /// The native library handle - /// The name of the exported symbol - /// The out-parameter for the symbol address, if it exists - /// True on success, false otherwise - /// If handle or name is null - public static bool TryGetExport(IntPtr handle, string name, out IntPtr address) - { - if (handle == IntPtr.Zero) - throw new ArgumentNullException(nameof(handle)); - if (name == null) - throw new ArgumentNullException(nameof(name)); - - address = GetSymbol(handle, name, throwOnError: false); - return address != IntPtr.Zero; - } - - /// - /// Map from assembly to native-library resolver. - /// Interop specific fields and properties are generally not added to Assembly class. - /// Therefore, this table uses weak assembly pointers to indirectly achieve - /// similar behavior. - /// - private static ConditionalWeakTable? s_nativeDllResolveMap; - - /// - /// Set a callback for resolving native library imports from an assembly. - /// This per-assembly resolver is the first attempt to resolve native library loads - /// initiated by this assembly. - /// - /// Only one resolver can be registered per assembly. - /// Trying to register a second resolver fails with InvalidOperationException. - /// - /// The assembly for which the resolver is registered - /// The resolver callback to register - /// If assembly or resolver is null - /// If a resolver is already set for this assembly - public static void SetDllImportResolver(Assembly assembly, DllImportResolver resolver) - { - if (assembly == null) - throw new ArgumentNullException(nameof(assembly)); - if (resolver == null) - throw new ArgumentNullException(nameof(resolver)); - if (!(assembly is RuntimeAssembly)) - throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly); - - if (s_nativeDllResolveMap == null) - { - Interlocked.CompareExchange(ref s_nativeDllResolveMap, - new ConditionalWeakTable(), null); - } - - try - { - s_nativeDllResolveMap!.Add(assembly, resolver); // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761 - } - catch (ArgumentException) - { - // ConditionalWealTable throws ArgumentException if the Key already exists - throw new InvalidOperationException(SR.InvalidOperation_CannotRegisterSecondResolver); - } - } - - /// - /// The helper function that calls the per-assembly native-library resolver - /// if one is registered for this assembly. - /// - /// The native library to load - /// The assembly trying load the native library - /// If the pInvoke has DefaultDllImportSearchPathAttribute - /// If hasdllImportSearchPathFlags is true, the flags in - /// DefaultDllImportSearchPathAttribute; meaningless otherwise - /// The handle for the loaded library on success. Null on failure. - internal static IntPtr LoadLibraryCallbackStub(string libraryName, Assembly assembly, - bool hasDllImportSearchPathFlags, uint dllImportSearchPathFlags) - { - if (s_nativeDllResolveMap == null) - { - return IntPtr.Zero; - } - - if (!s_nativeDllResolveMap.TryGetValue(assembly, out DllImportResolver resolver)) - { - return IntPtr.Zero; - } - - return resolver(libraryName, assembly, hasDllImportSearchPathFlags ? (DllImportSearchPath?)dllImportSearchPathFlags : null); - } - - /// External functions that implement the NativeLibrary interface - - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - internal static extern IntPtr LoadFromPath(string libraryName, bool throwOnError); - - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - internal static extern IntPtr LoadByName(string libraryName, RuntimeAssembly callingAssembly, - bool hasDllImportSearchPathFlag, uint dllImportSearchPathFlag, - bool throwOnError); - - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - internal static extern void FreeLib(IntPtr handle); - - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - internal static extern IntPtr GetSymbol(IntPtr handle, string symbolName, bool throwOnError); - } -} diff --git a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.cs b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.cs index db209718de..4d4c362910 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.cs @@ -181,7 +181,7 @@ namespace System.Runtime.Loader } bool isRelativePath = !Path.IsPathFullyQualified(unmanagedDllName); - foreach (LibraryNameVariation libraryNameVariation in DetermineLibraryNameVariations(unmanagedDllName, isRelativePath)) + foreach (LibraryNameVariation libraryNameVariation in LibraryNameVariation.DetermineLibraryNameVariations(unmanagedDllName, isRelativePath)) { string libraryName = libraryNameVariation.Prefix + unmanagedDllName + libraryNameVariation.Suffix; foreach (string searchPath in searchPaths) @@ -209,93 +209,10 @@ namespace System.Runtime.Loader } } - private struct LibraryNameVariation - { - public string Prefix; - public string Suffix; - - public LibraryNameVariation(string prefix, string suffix) - { - Prefix = prefix; - Suffix = suffix; - } - } - #if PLATFORM_WINDOWS private const CharSet HostpolicyCharSet = CharSet.Unicode; - private const string LibraryNameSuffix = ".dll"; - - private IEnumerable DetermineLibraryNameVariations(string libName, bool isRelativePath) - { - // This is a copy of the logic in DetermineLibNameVariations in dllimport.cpp in CoreCLR - - yield return new LibraryNameVariation(string.Empty, string.Empty); - - if (isRelativePath && - !libName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && - !libName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) - { - yield return new LibraryNameVariation(string.Empty, LibraryNameSuffix); - } - } #else private const CharSet HostpolicyCharSet = CharSet.Ansi; - - private const string LibraryNamePrefix = "lib"; -#if PLATFORM_OSX - private const string LibraryNameSuffix = ".dylib"; -#else - private const string LibraryNameSuffix = ".so"; -#endif - - private IEnumerable DetermineLibraryNameVariations(string libName, bool isRelativePath) - { - // This is a copy of the logic in DetermineLibNameVariations in dllimport.cpp in CoreCLR - - if (!isRelativePath) - { - yield return new LibraryNameVariation(string.Empty, string.Empty); - } - else - { - bool containsSuffix = false; - int indexOfSuffix = libName.IndexOf(LibraryNameSuffix, StringComparison.OrdinalIgnoreCase); - if (indexOfSuffix >= 0) - { - indexOfSuffix += LibraryNameSuffix.Length; - containsSuffix = indexOfSuffix == libName.Length || libName[indexOfSuffix] == '.'; - } - - bool containsDelim = libName.Contains(Path.DirectorySeparatorChar); - - if (containsSuffix) - { - yield return new LibraryNameVariation(string.Empty, string.Empty); - if (!containsDelim) - { - yield return new LibraryNameVariation(LibraryNamePrefix, string.Empty); - } - yield return new LibraryNameVariation(string.Empty, LibraryNameSuffix); - if (!containsDelim) - { - yield return new LibraryNameVariation(LibraryNamePrefix, LibraryNameSuffix); - } - } - else - { - yield return new LibraryNameVariation(string.Empty, LibraryNameSuffix); - if (!containsDelim) - { - yield return new LibraryNameVariation(LibraryNamePrefix, LibraryNameSuffix); - } - yield return new LibraryNameVariation(string.Empty, string.Empty); - if (!containsDelim) - { - yield return new LibraryNameVariation(LibraryNamePrefix, string.Empty); - } - } - } - } #endif [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = HostpolicyCharSet)] -- cgit v1.2.3