diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs
index 38712ac1b724..6b24cd79eab7 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs
@@ -12,6 +12,22 @@
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
///
@@ -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
///
/// The name of the native library to be loaded
/// The assembly loading the native library
@@ -117,7 +135,6 @@ public static bool TryLoad(string libraryName, Assembly assembly, DllImportSearc
/// No action if the input handle is null.
///
/// The native library handle to be freed
- /// If the operation fails
public static void Free(IntPtr handle)
{
FreeLib(handle);
@@ -161,6 +178,74 @@ public static bool TryGetExport(IntPtr handle, string name, out IntPtr address)
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.
+ ///
+ public static ConditionalWeakTable s_nativeDllResolveMap = null;
+
+ ///
+ /// 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)
+ {
+ s_nativeDllResolveMap = new ConditionalWeakTable();
+ }
+
+ 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");
+ }
+ }
+
+ ///
+ /// 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)
+ {
+ 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)]
diff --git a/src/vm/callhelpers.h b/src/vm/callhelpers.h
index bcd553dcc898..032cec43ced7 100644
--- a/src/vm/callhelpers.h
+++ b/src/vm/callhelpers.h
@@ -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; \
diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp
index 845ebf990753..a6670b385f9c 100644
--- a/src/vm/dllimport.cpp
+++ b/src/vm/dllimport.cpp
@@ -51,11 +51,6 @@
#include "clr/fs/path.h"
using namespace clr::fs;
-// The Bit 0x2 has different semantics in DllImportSearchPath and LoadLibraryExA flags.
-// In DllImportSearchPath enum, bit 0x2 represents SearchAssemblyDirectory -- which is performed by CLR.
-// Unlike other bits in this enum, this bit shouldn't be directly passed on to LoadLibrary()
-#define DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY 0x2
-
// remove when we get an updated SDK
#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100
#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
@@ -6127,6 +6122,57 @@ bool NDirect::s_fSecureLoadLibrarySupported = false;
#define PLATFORM_SHARED_LIB_PREFIX_W W("")
#endif // !FEATURE_PAL
+// The Bit 0x2 has different semantics in DllImportSearchPath and LoadLibraryExA flags.
+// In DllImportSearchPath enum, bit 0x2 represents SearchAssemblyDirectory -- which is performed by CLR.
+// Unlike other bits in this enum, this bit shouldn't be directly passed on to LoadLibrary()
+#define DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY 0x2
+
+// DllImportSearchPathFlags is a special enumeration, whose values are tied closely with LoadLibrary flags.
+// There is no "default" value DllImportSearchPathFlags. In the absence of DllImportSearchPath attribute,
+// CoreCLR's LoadLibrary implementation uses the following defaults.
+// Other implementations of LoadLibrary callbacks/events are free to use other default conventions.
+void GetDefaultDllImportSearchPathFlags(DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory)
+{
+ STANDARD_VM_CONTRACT;
+
+ *searchAssemblyDirectory = TRUE;
+ *dllImportSearchPathFlags = 0;
+}
+
+// If a module has the DllImportSearchPathAttribute, get DllImportSearchPathFlags from it, and return true.
+// Otherwise, get the default value for the flags, and return false.
+BOOL GetDllImportSearchPathFlags(Module *pModule, DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory)
+{
+ STANDARD_VM_CONTRACT;
+
+ if (pModule->HasDefaultDllImportSearchPathsAttribute())
+ {
+ *dllImportSearchPathFlags = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
+ *searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory();
+ return true;
+ }
+
+ GetDefaultDllImportSearchPathFlags(dllImportSearchPathFlags, searchAssemblyDirectory);
+ return false;
+}
+
+// If a pInvoke has DllImportSearchPathAttribute, get DllImportSearchPathFlags from it, and returns true.
+// Otherwise, if the containing assembly has the DllImportSearchPathAttribute, get DllImportSearchPathFlags from it, and returns true.
+// Otherwise, return false (out parameters are untouched).
+BOOL GetDllImportSearchPathFlags(NDirectMethodDesc * pMD, DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory)
+{
+ STANDARD_VM_CONTRACT;
+
+ if (pMD->HasDefaultDllImportSearchPathsAttribute())
+ {
+ *dllImportSearchPathFlags = pMD->DefaultDllImportSearchPathsAttributeCachedValue();
+ *searchAssemblyDirectory = pMD->DllImportSearchAssemblyDirectory();
+ return true;
+ }
+
+ return GetDllImportSearchPathFlags(pMD->GetModule(), dllImportSearchPathFlags, searchAssemblyDirectory);
+}
+
// static
NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError)
{
@@ -6165,9 +6211,10 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryByName(LPCWSTR libraryName, Assembly *
LoadLibErrorTracker errorTracker;
// First checks if a default dllImportSearchPathFlags was passed in, if so, use that value.
- // Otherwise checks if the assembly has the DefaultDllImportSearchPathsAttribute attribute. If so, use that value.
- BOOL searchAssemblyDirectory = TRUE;
- DWORD dllImportSearchPathFlags = 0;
+ // Otherwise checks if the assembly has the DefaultDllImportSearchPathsAttribute attribute.
+ // If so, use that value.
+ BOOL searchAssemblyDirectory;
+ DWORD dllImportSearchPathFlags;
if (hasDllImportSearchFlags)
{
@@ -6175,15 +6222,10 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryByName(LPCWSTR libraryName, Assembly *
searchAssemblyDirectory = dllImportSearchFlags & DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
}
- else
+ else
{
- Module * pModule = callingAssembly->GetManifestModule();
-
- if (pModule->HasDefaultDllImportSearchPathsAttribute())
- {
- dllImportSearchPathFlags = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
- searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory();
- }
+ GetDllImportSearchPathFlags(callingAssembly->GetManifestModule(),
+ &dllImportSearchPathFlags, &searchAssemblyDirectory);
}
NATIVE_LIBRARY_HANDLE hmod =
@@ -6203,26 +6245,10 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD
{
STANDARD_VM_CONTRACT;
- // First checks if the method has DefaultDllImportSearchPathsAttribute. If so, use that value.
- // Otherwise checks if the assembly has the attribute. If so, use that value.
- BOOL searchAssemblyDirectory = TRUE;
- DWORD dllImportSearchPathFlags = 0;
+ BOOL searchAssemblyDirectory;
+ DWORD dllImportSearchPathFlags;
- if (pMD->HasDefaultDllImportSearchPathsAttribute())
- {
- dllImportSearchPathFlags = pMD->DefaultDllImportSearchPathsAttributeCachedValue();
- searchAssemblyDirectory = pMD->DllImportSearchAssemblyDirectory();
- }
- else
- {
- Module * pModule = pMD->GetModule();
-
- if (pModule->HasDefaultDllImportSearchPathsAttribute())
- {
- dllImportSearchPathFlags = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
- searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory();
- }
- }
+ GetDllImportSearchPathFlags(pMD, &dllImportSearchPathFlags, &searchAssemblyDirectory);
Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
return LoadLibraryModuleBySearch(pAssembly, searchAssemblyDirectory, dllImportSearchPathFlags, pErrorTracker, wszLibName);
@@ -6452,18 +6478,20 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaCallback(NDirectMethodDesc *
{
STANDARD_VM_CONTRACT;
- NATIVE_LIBRARY_HANDLE handle = NULL;
-
- DWORD dllImportSearchPathFlags = 0;
- BOOL hasDllImportSearchPathFlags = pMD->HasDefaultDllImportSearchPathsAttribute();
- if (hasDllImportSearchPathFlags)
+ if (pMD->GetModule()->IsSystem())
{
- dllImportSearchPathFlags = pMD->DefaultDllImportSearchPathsAttributeCachedValue();
- if (pMD->DllImportSearchAssemblyDirectory())
- dllImportSearchPathFlags |= DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
+ // Don't attempt to callback on Corelib itself.
+ // The LoadLibrary callback stub is managed code that requires CoreLib
+ return NULL;
}
+ DWORD dllImportSearchPathFlags;
+ BOOL searchAssemblyDirectory;
+ BOOL hasDllImportSearchPathFlags = GetDllImportSearchPathFlags(pMD, &dllImportSearchPathFlags, &searchAssemblyDirectory);
+ dllImportSearchPathFlags |= searchAssemblyDirectory ? DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY : 0;
+
Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
+ NATIVE_LIBRARY_HANDLE handle = NULL;
GCX_COOP();
@@ -6491,87 +6519,6 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaCallback(NDirectMethodDesc *
return handle;
}
-// Return the AssemblyLoadContext for an assembly
-INT_PTR GetManagedAssemblyLoadContext(Assembly* pAssembly)
-{
- STANDARD_VM_CONTRACT;
-
- PTR_ICLRPrivBinder pBindingContext = pAssembly->GetManifestFile()->GetBindingContext();
- if (pBindingContext == NULL)
- {
- // GetBindingContext() returns NULL for System.Private.CoreLib
- return NULL;
- }
-
- UINT_PTR assemblyBinderID = 0;
- IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID));
-
- AppDomain *pDomain = GetAppDomain();
- ICLRPrivBinder *pCurrentBinder = reinterpret_cast(assemblyBinderID);
-
-#ifdef FEATURE_COMINTEROP
- if (AreSameBinderInstance(pCurrentBinder, pDomain->GetWinRtBinder()))
- {
- // No ALC associated handle with WinRT Binders.
- return NULL;
- }
-#endif // FEATURE_COMINTEROP
-
- // The code here deals with two implementations of ICLRPrivBinder interface:
- // - CLRPrivBinderCoreCLR for the TPA binder in the default ALC, and
- // - CLRPrivBinderAssemblyLoadContext for custom ALCs.
- // in order obtain the associated ALC handle.
- INT_PTR ptrManagedAssemblyLoadContext = AreSameBinderInstance(pCurrentBinder, pDomain->GetTPABinderContext())
- ? ((CLRPrivBinderCoreCLR *)pCurrentBinder)->GetManagedAssemblyLoadContext()
- : ((CLRPrivBinderAssemblyLoadContext *)pCurrentBinder)->GetManagedAssemblyLoadContext();
-
- return ptrManagedAssemblyLoadContext;
-}
-
-// static
-NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaEvent(NDirectMethodDesc * pMD, PCWSTR wszLibName)
-{
- STANDARD_VM_CONTRACT;
-
- NATIVE_LIBRARY_HANDLE hmod = NULL;
- Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
- INT_PTR ptrManagedAssemblyLoadContext = GetManagedAssemblyLoadContext(pAssembly);
-
- if (ptrManagedAssemblyLoadContext == NULL)
- {
- return NULL;
- }
-
- GCX_COOP();
-
- struct {
- STRINGREF DllName;
- OBJECTREF AssemblyRef;
- } gc = { NULL, NULL };
-
- GCPROTECT_BEGIN(gc);
-
- gc.DllName = StringObject::NewString(wszLibName);
- gc.AssemblyRef = pAssembly->GetExposedObject();
-
- // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.ResolveUnmanagedDllUsingEvent method
- // While ResolveUnmanagedDllUsingEvent() could compute the AssemblyLoadContext using the AssemblyRef
- // argument, it will involve another pInvoke to the runtime. So AssemblyLoadContext is passed in
- // as an additional argument.
- PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUNMANAGEDDLLUSINGEVENT);
- DECLARE_ARGHOLDER_ARRAY(args, 3);
- args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(gc.DllName);
- args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.AssemblyRef);
- args[ARGNUM_2] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext);
-
- // Make the call
- CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args);
-
- GCPROTECT_END();
-
- return hmod;
-}
-
// Try to load the module alongside the assembly where the PInvoke was declared.
NATIVE_LIBRARY_HANDLE NDirect::LoadFromPInvokeAssemblyDirectory(Assembly *pAssembly, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker)
{
@@ -6842,7 +6789,7 @@ HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracke
if ( !name || !*name )
return NULL;
- PREFIX_ASSUME( name != NULL );
+ PREFIX_ASSUME( name != NULL );
MAKE_WIDEPTR_FROMUTF8( wszLibName, name );
ModuleHandleHolder hmod = LoadLibraryModuleViaCallback(pMD, wszLibName);
diff --git a/src/vm/dllimport.h b/src/vm/dllimport.h
index 1203803b9afd..f4943ff8a0a8 100644
--- a/src/vm/dllimport.h
+++ b/src/vm/dllimport.h
@@ -76,7 +76,7 @@ class NDirect
static LPVOID NDirectGetEntryPoint(NDirectMethodDesc *pMD, HINSTANCE hMod);
static NATIVE_LIBRARY_HANDLE LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError);
static NATIVE_LIBRARY_HANDLE LoadLibraryByName(LPCWSTR name, Assembly *callingAssembly,
- BOOL hasDllImportSearchPathFlag, DWORD dllImportSearchPathFlag,
+ BOOL hasDllImportSearchPathFlags, DWORD dllImportSearchPathFlags,
BOOL throwOnError);
static HINSTANCE LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracker *pErrorTracker);
static void FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle);
@@ -122,12 +122,13 @@ class NDirect
private:
NDirect() {LIMITED_METHOD_CONTRACT;}; // prevent "new"'s on this class
- static NATIVE_LIBRARY_HANDLE LoadFromNativeDllSearchDirectories(AppDomain* pDomain, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker);
+ static NATIVE_LIBRARY_HANDLE LoadFromNativeDllSearchDirectories(LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker);
static NATIVE_LIBRARY_HANDLE LoadFromPInvokeAssemblyDirectory(Assembly *pAssembly, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker);
static NATIVE_LIBRARY_HANDLE LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, LPCWSTR wszLibName);
static NATIVE_LIBRARY_HANDLE LoadLibraryModuleViaEvent(NDirectMethodDesc * pMD, LPCWSTR wszLibName);
+ static NATIVE_LIBRARY_HANDLE LoadLibraryModuleViaCallback(NDirectMethodDesc * pMD, LPCWSTR wszLibName);
static NATIVE_LIBRARY_HANDLE LoadLibraryModuleBySearch(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker, const wchar_t* wszLibName);
- static NATIVE_LIBRARY_HANDLE LoadLibraryModuleBySearch(Assembly *callingAssembly, BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlag, LoadLibErrorTracker * pErrorTracker, const wchar_t* wszLibName);
+ static NATIVE_LIBRARY_HANDLE LoadLibraryModuleBySearch(Assembly *callingAssembly, BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlags, LoadLibErrorTracker * pErrorTracker, const wchar_t* wszLibName);
#if !defined(FEATURE_PAL)
// Indicates if the OS supports the new secure LoadLibraryEx flags introduced in KB2533623
diff --git a/src/vm/interoputil.cpp b/src/vm/interoputil.cpp
index a21e68b27095..6854edd15773 100644
--- a/src/vm/interoputil.cpp
+++ b/src/vm/interoputil.cpp
@@ -898,8 +898,8 @@ void FillExceptionData(
//---------------------------------------------------------------------------
//returns true if pImport has DefaultDllImportSearchPathsAttribute
-//if true, also returns dllImportSearchPathFlag and searchAssemblyDirectory values.
-BOOL GetDefaultDllImportSearchPathsAttributeValue(IMDInternalImport *pImport, mdToken token, DWORD * pDllImportSearchPathFlag)
+//if true, also returns dllImportSearchPathFlags and searchAssemblyDirectory values.
+BOOL GetDefaultDllImportSearchPathsAttributeValue(IMDInternalImport *pImport, mdToken token, DWORD * pDllImportSearchPathFlags)
{
CONTRACTL
{
@@ -929,7 +929,7 @@ BOOL GetDefaultDllImportSearchPathsAttributeValue(IMDInternalImport *pImport, md
args[0].InitEnum(SERIALIZATION_TYPE_U4, (ULONG)0);
ParseKnownCaArgs(ca, args, lengthof(args));
- *pDllImportSearchPathFlag = args[0].val.u4;
+ *pDllImportSearchPathFlags = args[0].val.u4;
return TRUE;
}
diff --git a/src/vm/interoputil.h b/src/vm/interoputil.h
index 91f6828c4950..770568a93671 100644
--- a/src/vm/interoputil.h
+++ b/src/vm/interoputil.h
@@ -137,7 +137,7 @@ void FillExceptionData(
//---------------------------------------------------------------------------
//returns true if pImport has DefaultDllImportSearchPathsAttribute
-//if true, also returns dllImportSearchPathFlag and searchAssemblyDirectory values.
+//if true, also returns dllImportSearchPathFlags and searchAssemblyDirectory values.
BOOL GetDefaultDllImportSearchPathsAttributeValue(IMDInternalImport *pImport, mdToken token, DWORD * pDlImportSearchPathFlag);
//---------------------------------------------------------------------------
diff --git a/src/vm/metasig.h b/src/vm/metasig.h
index cfa844012eea..cbac116bd3d3 100644
--- a/src/vm/metasig.h
+++ b/src/vm/metasig.h
@@ -552,6 +552,7 @@ DEFINE_METASIG_T(IM(RefGuid_OutIntPtr_RetCustomQueryInterfaceResult, r(g(GUID))
DEFINE_METASIG_T(SM(IntPtr_AssemblyName_RetAssemblyBase, I C(ASSEMBLY_NAME), C(ASSEMBLYBASE)))
DEFINE_METASIG_T(SM(Str_AssemblyBase_IntPtr_RetIntPtr, s C(ASSEMBLYBASE) I, I))
+DEFINE_METASIG_T(SM(Str_AssemblyBase_Bool_UInt_RetIntPtr, s C(ASSEMBLYBASE) F K, I))
// ThreadPool
DEFINE_METASIG(SM(Obj_Bool_RetVoid, j F, v))
diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h
index a1341dd70f68..730355f4354f 100644
--- a/src/vm/mscorlib.h
+++ b/src/vm/mscorlib.h
@@ -487,6 +487,8 @@ DEFINE_METHOD(MARSHAL, GET_DELEGATE_FOR_FUNCTION_POINTER, GetDelega
DEFINE_METHOD(MARSHAL, ALLOC_CO_TASK_MEM, AllocCoTaskMem, SM_Int_RetIntPtr)
DEFINE_FIELD(MARSHAL, SYSTEM_MAX_DBCS_CHAR_SIZE, SystemMaxDBCSCharSize)
+DEFINE_CLASS(NATIVELIBRARY, Interop, NativeLibrary)
+DEFINE_METHOD(NATIVELIBRARY, LOADLIBRARYCALLBACKSTUB, LoadLibraryCallbackStub, SM_Str_AssemblyBase_Bool_UInt_RetIntPtr)
DEFINE_CLASS(MEMBER, Reflection, MemberInfo)
diff --git a/tests/src/Interop/CMakeLists.txt b/tests/src/Interop/CMakeLists.txt
index e46e78a8ff47..1af7157f0761 100644
--- a/tests/src/Interop/CMakeLists.txt
+++ b/tests/src/Interop/CMakeLists.txt
@@ -57,6 +57,7 @@ add_subdirectory(MarshalAPI/FunctionPointer)
add_subdirectory(MarshalAPI/IUnknown)
add_subdirectory(NativeLibrary)
add_subdirectory(NativeLibraryResolveEvent)
+add_subdirectory(NativeLibraryResolveCallBack)
add_subdirectory(SizeConst)
add_subdirectory(DllImportAttribute/ExeFile)
add_subdirectory(DllImportAttribute/FileNameContainDot)
diff --git a/tests/src/Interop/NativeLibraryResolveCallBack/CMakeLists.txt b/tests/src/Interop/NativeLibraryResolveCallBack/CMakeLists.txt
new file mode 100644
index 000000000000..b3f3409e3e0f
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveCallBack/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required (VERSION 2.6)
+project (ResolveLib)
+include_directories(${INC_PLATFORM_DIR})
+set(SOURCES ResolveLib.cpp)
+
+# add the executable
+add_library (ResolveLib SHARED ${SOURCES})
+target_link_libraries(ResolveLib ${LINK_LIBRARIES_ADDITIONAL})
+
+# add the install targets
+install (TARGETS ResolveLib DESTINATION bin)
+
+
diff --git a/tests/src/Interop/NativeLibraryResolveCallBack/CallBackTests.cs b/tests/src/Interop/NativeLibraryResolveCallBack/CallBackTests.cs
new file mode 100644
index 000000000000..4eb8accb34fb
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveCallBack/CallBackTests.cs
@@ -0,0 +1,86 @@
+// 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.
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using TestLibrary;
+
+using Console = Internal.Console;
+
+[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)]
+public class CallBackTests
+{
+ public static int Main()
+ {
+ try
+ {
+ Assembly assembly = Assembly.GetExecutingAssembly();
+
+ DllImportResolver resolver =
+ (string libraryName, Assembly asm, DllImportSearchPath? dllImportSearchPath) =>
+ {
+ if (!dllImportSearchPath.HasValue || dllImportSearchPath.Value != DllImportSearchPath.System32)
+ {
+ Console.WriteLine($"Unexpected dllImportSearchPath: {dllImportSearchPath.ToString()}");
+ throw new ArgumentException();
+ }
+
+ return NativeLibrary.Load("ResolveLib", asm, dllImportSearchPath);
+ };
+
+ DllImportResolver anotherResolver =
+ (string libraryName, Assembly asm, DllImportSearchPath? dllImportSearchPath) =>
+ IntPtr.Zero;
+
+ try
+ {
+ NativeLibrary.SetDllImportResolver(null, resolver);
+
+ Console.WriteLine("Expected exception not thrown");
+ return 101;
+ }
+ catch (ArgumentNullException e) { }
+
+ try
+ {
+ NativeLibrary.SetDllImportResolver(assembly, null);
+
+ Console.WriteLine("Expected exception not thrown");
+ return 102;
+ }
+ catch (ArgumentNullException e) { }
+
+ // Set a resolver callback
+ NativeLibrary.SetDllImportResolver(assembly, resolver);
+
+ try
+ {
+ // Try to set another resolver on the same assembly.
+ NativeLibrary.SetDllImportResolver(assembly, anotherResolver);
+
+ Console.WriteLine("Expected Exception not thrown");
+ return 103;
+ }
+ catch (InvalidOperationException e) { }
+
+ if (NativeSum(10, 10) != 20)
+ {
+ Console.WriteLine("Unexpected ReturnValue from NativeSum()");
+ return 104;
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Unexpected exception: {e.ToString()} {e.Message}");
+ return 105;
+ }
+
+ return 100;
+ }
+
+ [DllImport("NativeLib")]
+ [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
+ static extern int NativeSum(int arg1, int arg2);
+}
diff --git a/tests/src/Interop/NativeLibraryResolveCallBack/CallBackTests.csproj b/tests/src/Interop/NativeLibraryResolveCallBack/CallBackTests.csproj
new file mode 100644
index 000000000000..d899e8ded7c9
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveCallBack/CallBackTests.csproj
@@ -0,0 +1,39 @@
+
+
+
+
+ Debug
+ AnyCPU
+ CallBackTests
+ 2.0
+ {F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}
+ Exe
+ {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ ..\..\
+ true
+ true
+
+
+
+
+
+
+
+
+ False
+
+
+
+
+
+
+
+
+
+
+ {c8c0dc74-fac4-45b1-81fe-70c4808366e0}
+ CoreCLRTestLibrary
+
+
+
+
diff --git a/tests/src/Interop/NativeLibraryResolveCallBack/ResolveLib.cpp b/tests/src/Interop/NativeLibraryResolveCallBack/ResolveLib.cpp
new file mode 100644
index 000000000000..597b549a9976
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveCallBack/ResolveLib.cpp
@@ -0,0 +1,10 @@
+// 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.
+#include
+#include
+
+extern "C" DLL_EXPORT int NativeSum(int a, int b)
+{
+ return a + b;
+}