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

Commit

Permalink
Add Per-assembly Load Native Library callbacks
Browse files Browse the repository at this point in the history
This Change implements the Native Library resolution
Call-backs proposed in https://github.com/dotnet/corefx/issues/32015
  • Loading branch information
swaroop-sridhar committed Jan 15, 2019
1 parent 8ede2d4 commit 7751a17
Show file tree
Hide file tree
Showing 13 changed files with 394 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@

namespace System.Runtime.InteropServices
{

/// <summary>
/// A delegate used to resolve native libraries via callback.
/// </summary>
/// <param name="libraryName">The native library to resolve</param>
/// <param name="assembly">The assembly requesting the resolution</param>
/// <param name="DllImportSearchPath?">
/// The DllImportSearchPathsAttribute on the PInvoke, if any.
/// Otherwise, the DllImportSearchPathsAttribute on the assembly, if any.
/// Otherwise null.
/// </param>
/// <returns>The handle for the loaded native library on success, null on failure</returns>
public delegate IntPtr DllImportResolver(string libraryName,
Assembly assembly,
DllImportSearchPath? searchPath);

/// <summary>
/// APIs for managing Native Libraries
/// </summary>
Expand Down Expand Up @@ -58,7 +74,9 @@ public static bool TryLoad(string libraryPath, out IntPtr handle)
/// 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
/// </summary>
/// <param name="libraryName">The name of the native library to be loaded</param>
/// <param name="assembly">The assembly loading the native library</param>
Expand Down Expand Up @@ -117,7 +135,6 @@ public static bool TryLoad(string libraryName, Assembly assembly, DllImportSearc
/// No action if the input handle is null.
/// </summary>
/// <param name="handle">The native library handle to be freed</param>
/// <exception cref="System.InvalidOperationException">If the operation fails</exception>
public static void Free(IntPtr handle)
{
FreeLib(handle);
Expand Down Expand Up @@ -161,6 +178,74 @@ public static bool TryGetExport(IntPtr handle, string name, out IntPtr address)
return address != IntPtr.Zero;
}

/// <summary>
/// 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.
/// </summary>
public static ConditionalWeakTable<Assembly, DllImportResolver> s_nativeDllResolveMap = null;

/// <summary>
/// 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.
/// </summary>
/// <param name="assembly">The assembly for which the resolver is registered</param>
/// <param name="resolver">The resolver callback to register</param>
/// <exception cref="System.ArgumentNullException">If assembly or resolver is null</exception>
/// <exception cref="System.ArgumentException">If a resolver is already set for this assembly</exception>
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)
{
s_nativeDllResolveMap = new ConditionalWeakTable<Assembly, DllImportResolver>();
}

try
{
s_nativeDllResolveMap.Add(assembly, resolver);
}
catch (ArgumentException e)
{
// ConditionalWealTable throws ArgumentException if the Key already exists
throw new InvalidOperationException("Resolver is alerady Set for the Assembly");
}
}

/// <summary>
/// The helper function that calls the per-assembly native-library resolver
/// if one is registered for this assembly.
/// </summary>
/// <param name="libraryName">The native library to load</param>
/// <param name="assembly">The assembly trying load the native library</param>
/// <param name="hasDllImportSearchPathFlags">If the pInvoke has DefaultDllImportSearchPathAttribute</param>
/// <param name="dllImportSearchPathFlags">If hasdllImportSearchPathFlags is true, the flags in
/// DefaultDllImportSearchPathAttribute; meaningless otherwise </param>
/// <returns>The handle for the loaded library on success. Null on failure.</returns>
internal static IntPtr LoadLibraryCallbackStub(string libraryName, Assembly assembly,
bool hasDllImportSearchPathFlags, uint dllImportSearchPathFlags)
{
DllImportResolver resolver;

if (!s_nativeDllResolveMap.TryGetValue(assembly, out 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)]
Expand Down
1 change: 1 addition & 0 deletions src/vm/callhelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ enum DispatchCallSimpleFlags
#define STRINGREF_TO_ARGHOLDER(x) (LPVOID)STRINGREFToObject(x)
#define PTR_TO_ARGHOLDER(x) (LPVOID)x
#define DWORD_TO_ARGHOLDER(x) (LPVOID)(SIZE_T)x
#define BOOL_TO_ARGHOLDER(x) DWORD_TO_ARGHOLDER(!!(x))

#define INIT_VARIABLES(count) \
DWORD __numArgs = count; \
Expand Down
Loading

0 comments on commit 7751a17

Please sign in to comment.