Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 4013035

Browse files
MichalStrehovskyjkotas
authored andcommitted
Move NativeLibrary to the shared partition (dotnet/coreclr#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. Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
1 parent 467d45e commit 4013035

File tree

5 files changed

+369
-0
lines changed

5 files changed

+369
-0
lines changed

src/Common/src/CoreLib/System.Private.CoreLib.Shared.projitems

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,7 @@
673673
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MemoryMarshal.cs" />
674674
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MemoryMarshal.Fast.cs" />
675675
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\NativeCallableAttribute.cs" />
676+
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\NativeLibrary.cs" />
676677
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OptionalAttribute.cs" />
677678
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OutAttribute.cs" />
678679
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PreserveSigAttribute.cs" />
@@ -700,6 +701,7 @@
700701
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\Vector256DebugView_1.cs" />
701702
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\X86\Enums.cs" />
702703
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Loader\AssemblyLoadContext.cs" Condition="'$(TargetsCoreRT)' != 'true'" />
704+
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Loader\LibraryNameVariation.cs" />
703705
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Remoting\ObjectHandle.cs" />
704706
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Serialization\DeserializationBlockedException.cs" />
705707
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Serialization\DeserializationToken.cs" />
@@ -1105,6 +1107,7 @@
11051107
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Windows.cs" />
11061108
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DisableMediaInsertionPrompt.cs" />
11071109
<Compile Include="$(MSBuildThisFileDirectory)System\PasteArguments.Windows.cs" />
1110+
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Loader\LibraryNameVariation.Windows.cs" />
11081111
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.Windows.cs" />
11091112
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshal.Windows.cs" Condition="'$(TargetsCoreRT)' != 'true'" />
11101113
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Windows.cs" />
@@ -1270,6 +1273,7 @@
12701273
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PersistedFiles.Unix.cs" />
12711274
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PersistedFiles.Names.Unix.cs" />
12721275
<Compile Include="$(MSBuildThisFileDirectory)System\PasteArguments.Unix.cs" />
1276+
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Loader\LibraryNameVariation.Unix.cs" />
12731277
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.Unix.cs" />
12741278
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshal.Unix.cs" Condition="'$(TargetsCoreRT)' != 'true'" />
12751279
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Unix.cs" />
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable enable
6+
using System.Reflection;
7+
using System.Runtime.CompilerServices;
8+
using System.Threading;
9+
10+
namespace System.Runtime.InteropServices
11+
{
12+
/// <summary>
13+
/// A delegate used to resolve native libraries via callback.
14+
/// </summary>
15+
/// <param name="libraryName">The native library to resolve</param>
16+
/// <param name="assembly">The assembly requesting the resolution</param>
17+
/// <param name="searchPath">
18+
/// The DllImportSearchPathsAttribute on the PInvoke, if any.
19+
/// Otherwise, the DllImportSearchPathsAttribute on the assembly, if any.
20+
/// Otherwise null.
21+
/// </param>
22+
/// <returns>The handle for the loaded native library on success, null on failure</returns>
23+
public delegate IntPtr DllImportResolver(string libraryName,
24+
Assembly assembly,
25+
DllImportSearchPath? searchPath);
26+
27+
/// <summary>
28+
/// APIs for managing Native Libraries
29+
/// </summary>
30+
public static partial class NativeLibrary
31+
{
32+
/// <summary>
33+
/// NativeLibrary Loader: Simple API
34+
/// This method is a wrapper around OS loader, using "default" flags.
35+
/// </summary>
36+
/// <param name="libraryPath">The name of the native library to be loaded</param>
37+
/// <returns>The handle for the loaded native library</returns>
38+
/// <exception cref="System.ArgumentNullException">If libraryPath is null</exception>
39+
/// <exception cref="System.DllNotFoundException ">If the library can't be found.</exception>
40+
/// <exception cref="System.BadImageFormatException">If the library is not valid.</exception>
41+
public static IntPtr Load(string libraryPath)
42+
{
43+
if (libraryPath == null)
44+
throw new ArgumentNullException(nameof(libraryPath));
45+
46+
return LoadFromPath(libraryPath, throwOnError: true);
47+
}
48+
49+
/// <summary>
50+
/// NativeLibrary Loader: Simple API that doesn't throw
51+
/// </summary>
52+
/// <param name="libraryPath">The name of the native library to be loaded</param>
53+
/// <param name="handle">The out-parameter for the loaded native library handle</param>
54+
/// <returns>True on successful load, false otherwise</returns>
55+
/// <exception cref="System.ArgumentNullException">If libraryPath is null</exception>
56+
public static bool TryLoad(string libraryPath, out IntPtr handle)
57+
{
58+
if (libraryPath == null)
59+
throw new ArgumentNullException(nameof(libraryPath));
60+
61+
handle = LoadFromPath(libraryPath, throwOnError: false);
62+
return handle != IntPtr.Zero;
63+
}
64+
65+
/// <summary>
66+
/// NativeLibrary Loader: High-level API
67+
/// Given a library name, this function searches specific paths based on the
68+
/// runtime configuration, input parameters, and attributes of the calling assembly.
69+
/// If DllImportSearchPath parameter is non-null, the flags in this enumeration are used.
70+
/// Otherwise, the flags specified by the DefaultDllImportSearchPaths attribute on the
71+
/// calling assembly (if any) are used.
72+
/// This LoadLibrary() method does not invoke the managed call-backs for native library resolution:
73+
/// * The per-assembly registered callback
74+
/// * AssemblyLoadContext.LoadUnmanagedDll()
75+
/// * AssemblyLoadContext.ResolvingUnmanagedDllEvent
76+
/// </summary>
77+
/// <param name="libraryName">The name of the native library to be loaded</param>
78+
/// <param name="assembly">The assembly loading the native library</param>
79+
/// <param name="searchPath">The search path</param>
80+
/// <returns>The handle for the loaded library</returns>
81+
/// <exception cref="System.ArgumentNullException">If libraryPath or assembly is null</exception>
82+
/// <exception cref="System.ArgumentException">If assembly is not a RuntimeAssembly</exception>
83+
/// <exception cref="System.DllNotFoundException ">If the library can't be found.</exception>
84+
/// <exception cref="System.BadImageFormatException">If the library is not valid.</exception>
85+
public static IntPtr Load(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
86+
{
87+
if (libraryName == null)
88+
throw new ArgumentNullException(nameof(libraryName));
89+
if (assembly == null)
90+
throw new ArgumentNullException(nameof(assembly));
91+
if (!assembly.IsRuntimeImplemented())
92+
throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly);
93+
94+
return LoadLibraryByName(libraryName,
95+
assembly,
96+
searchPath,
97+
throwOnError: true);
98+
}
99+
100+
/// <summary>
101+
/// NativeLibrary Loader: High-level API that doesn't throw.
102+
/// </summary>
103+
/// <param name="libraryName">The name of the native library to be loaded</param>
104+
/// <param name="searchPath">The search path</param>
105+
/// <param name="assembly">The assembly loading the native library</param>
106+
/// <param name="handle">The out-parameter for the loaded native library handle</param>
107+
/// <returns>True on successful load, false otherwise</returns>
108+
/// <exception cref="System.ArgumentNullException">If libraryPath or assembly is null</exception>
109+
/// <exception cref="System.ArgumentException">If assembly is not a RuntimeAssembly</exception>
110+
public static bool TryLoad(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, out IntPtr handle)
111+
{
112+
if (libraryName == null)
113+
throw new ArgumentNullException(nameof(libraryName));
114+
if (assembly == null)
115+
throw new ArgumentNullException(nameof(assembly));
116+
if (!assembly.IsRuntimeImplemented())
117+
throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly);
118+
119+
handle = LoadLibraryByName(libraryName,
120+
assembly,
121+
searchPath,
122+
throwOnError: false);
123+
return handle != IntPtr.Zero;
124+
}
125+
126+
/// <summary>
127+
/// Free a loaded library
128+
/// Given a library handle, free it.
129+
/// No action if the input handle is null.
130+
/// </summary>
131+
/// <param name="handle">The native library handle to be freed</param>
132+
public static void Free(IntPtr handle)
133+
{
134+
FreeLib(handle);
135+
}
136+
137+
/// <summary>
138+
/// Get the address of an exported Symbol
139+
/// This is a simple wrapper around OS calls, and does not perform any name mangling.
140+
/// </summary>
141+
/// <param name="handle">The native library handle</param>
142+
/// <param name="name">The name of the exported symbol</param>
143+
/// <returns>The address of the symbol</returns>
144+
/// <exception cref="System.ArgumentNullException">If handle or name is null</exception>
145+
/// <exception cref="System.EntryPointNotFoundException">If the symbol is not found</exception>
146+
public static IntPtr GetExport(IntPtr handle, string name)
147+
{
148+
if (handle == IntPtr.Zero)
149+
throw new ArgumentNullException(nameof(handle));
150+
if (name == null)
151+
throw new ArgumentNullException(nameof(name));
152+
153+
return GetSymbol(handle, name, throwOnError: true);
154+
}
155+
156+
/// <summary>
157+
/// Get the address of an exported Symbol, but do not throw
158+
/// </summary>
159+
/// <param name="handle">The native library handle</param>
160+
/// <param name="name">The name of the exported symbol</param>
161+
/// <param name="address"> The out-parameter for the symbol address, if it exists</param>
162+
/// <returns>True on success, false otherwise</returns>
163+
/// <exception cref="System.ArgumentNullException">If handle or name is null</exception>
164+
public static bool TryGetExport(IntPtr handle, string name, out IntPtr address)
165+
{
166+
if (handle == IntPtr.Zero)
167+
throw new ArgumentNullException(nameof(handle));
168+
if (name == null)
169+
throw new ArgumentNullException(nameof(name));
170+
171+
address = GetSymbol(handle, name, throwOnError: false);
172+
return address != IntPtr.Zero;
173+
}
174+
175+
/// <summary>
176+
/// Map from assembly to native-library resolver.
177+
/// Interop specific fields and properties are generally not added to Assembly class.
178+
/// Therefore, this table uses weak assembly pointers to indirectly achieve
179+
/// similar behavior.
180+
/// </summary>
181+
private static ConditionalWeakTable<Assembly, DllImportResolver>? s_nativeDllResolveMap;
182+
183+
/// <summary>
184+
/// Set a callback for resolving native library imports from an assembly.
185+
/// This per-assembly resolver is the first attempt to resolve native library loads
186+
/// initiated by this assembly.
187+
///
188+
/// Only one resolver can be registered per assembly.
189+
/// Trying to register a second resolver fails with InvalidOperationException.
190+
/// </summary>
191+
/// <param name="assembly">The assembly for which the resolver is registered</param>
192+
/// <param name="resolver">The resolver callback to register</param>
193+
/// <exception cref="System.ArgumentNullException">If assembly or resolver is null</exception>
194+
/// <exception cref="System.ArgumentException">If a resolver is already set for this assembly</exception>
195+
public static void SetDllImportResolver(Assembly assembly, DllImportResolver resolver)
196+
{
197+
if (assembly == null)
198+
throw new ArgumentNullException(nameof(assembly));
199+
if (resolver == null)
200+
throw new ArgumentNullException(nameof(resolver));
201+
if (!assembly.IsRuntimeImplemented())
202+
throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly);
203+
204+
if (s_nativeDllResolveMap == null)
205+
{
206+
Interlocked.CompareExchange(ref s_nativeDllResolveMap,
207+
new ConditionalWeakTable<Assembly, DllImportResolver>(), null);
208+
}
209+
210+
try
211+
{
212+
s_nativeDllResolveMap!.Add(assembly, resolver); // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761
213+
}
214+
catch (ArgumentException)
215+
{
216+
// ConditionalWealTable throws ArgumentException if the Key already exists
217+
throw new InvalidOperationException(SR.InvalidOperation_CannotRegisterSecondResolver);
218+
}
219+
}
220+
221+
/// <summary>
222+
/// The helper function that calls the per-assembly native-library resolver
223+
/// if one is registered for this assembly.
224+
/// </summary>
225+
/// <param name="libraryName">The native library to load</param>
226+
/// <param name="assembly">The assembly trying load the native library</param>
227+
/// <param name="hasDllImportSearchPathFlags">If the pInvoke has DefaultDllImportSearchPathAttribute</param>
228+
/// <param name="dllImportSearchPathFlags">If hasdllImportSearchPathFlags is true, the flags in
229+
/// DefaultDllImportSearchPathAttribute; meaningless otherwise </param>
230+
/// <returns>The handle for the loaded library on success. Null on failure.</returns>
231+
internal static IntPtr LoadLibraryCallbackStub(string libraryName, Assembly assembly,
232+
bool hasDllImportSearchPathFlags, uint dllImportSearchPathFlags)
233+
{
234+
if (s_nativeDllResolveMap == null)
235+
{
236+
return IntPtr.Zero;
237+
}
238+
239+
if (!s_nativeDllResolveMap.TryGetValue(assembly, out DllImportResolver resolver))
240+
{
241+
return IntPtr.Zero;
242+
}
243+
244+
return resolver(libraryName, assembly, hasDllImportSearchPathFlags ? (DllImportSearchPath?)dllImportSearchPathFlags : null);
245+
}
246+
}
247+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable enable
6+
using System.Collections.Generic;
7+
using System.IO;
8+
9+
namespace System.Runtime.Loader
10+
{
11+
internal partial struct LibraryNameVariation
12+
{
13+
private const string LibraryNamePrefix = "lib";
14+
#if PLATFORM_OSX
15+
private const string LibraryNameSuffix = ".dylib";
16+
#else
17+
private const string LibraryNameSuffix = ".so";
18+
#endif
19+
20+
internal static IEnumerable<LibraryNameVariation> DetermineLibraryNameVariations(string libName, bool isRelativePath)
21+
{
22+
// This is a copy of the logic in DetermineLibNameVariations in dllimport.cpp in CoreCLR
23+
24+
if (!isRelativePath)
25+
{
26+
yield return new LibraryNameVariation(string.Empty, string.Empty);
27+
}
28+
else
29+
{
30+
bool containsSuffix = false;
31+
int indexOfSuffix = libName.IndexOf(LibraryNameSuffix, StringComparison.OrdinalIgnoreCase);
32+
if (indexOfSuffix >= 0)
33+
{
34+
indexOfSuffix += LibraryNameSuffix.Length;
35+
containsSuffix = indexOfSuffix == libName.Length || libName[indexOfSuffix] == '.';
36+
}
37+
38+
bool containsDelim = libName.Contains(Path.DirectorySeparatorChar);
39+
40+
if (containsSuffix)
41+
{
42+
yield return new LibraryNameVariation(string.Empty, string.Empty);
43+
if (!containsDelim)
44+
{
45+
yield return new LibraryNameVariation(LibraryNamePrefix, string.Empty);
46+
}
47+
yield return new LibraryNameVariation(string.Empty, LibraryNameSuffix);
48+
if (!containsDelim)
49+
{
50+
yield return new LibraryNameVariation(LibraryNamePrefix, LibraryNameSuffix);
51+
}
52+
}
53+
else
54+
{
55+
yield return new LibraryNameVariation(string.Empty, LibraryNameSuffix);
56+
if (!containsDelim)
57+
{
58+
yield return new LibraryNameVariation(LibraryNamePrefix, LibraryNameSuffix);
59+
}
60+
yield return new LibraryNameVariation(string.Empty, string.Empty);
61+
if (!containsDelim)
62+
{
63+
yield return new LibraryNameVariation(LibraryNamePrefix, string.Empty);
64+
}
65+
}
66+
}
67+
}
68+
}
69+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable enable
6+
using System.Collections.Generic;
7+
8+
namespace System.Runtime.Loader
9+
{
10+
internal partial struct LibraryNameVariation
11+
{
12+
private const string LibraryNameSuffix = ".dll";
13+
14+
internal static IEnumerable<LibraryNameVariation> DetermineLibraryNameVariations(string libName, bool isRelativePath)
15+
{
16+
// This is a copy of the logic in DetermineLibNameVariations in dllimport.cpp in CoreCLR
17+
18+
yield return new LibraryNameVariation(string.Empty, string.Empty);
19+
20+
if (isRelativePath &&
21+
!libName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) &&
22+
!libName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
23+
{
24+
yield return new LibraryNameVariation(string.Empty, LibraryNameSuffix);
25+
}
26+
}
27+
28+
}
29+
}

0 commit comments

Comments
 (0)