summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSwaroop Sridhar <Swaroop.Sridhar@microsoft.com>2019-01-12 22:25:25 -0800
committerGitHub <noreply@github.com>2019-01-12 22:25:25 -0800
commit8b7d300c164971f573d8186e78204597a679c7d8 (patch)
tree4bae0726ff3cb5b97c43ad127b8b41a117e2c06f
parent908891ce9df8d94f28ec6a53a012d39d00c8e65d (diff)
downloadcoreclr-8b7d300c164971f573d8186e78204597a679c7d8.tar.gz
coreclr-8b7d300c164971f573d8186e78204597a679c7d8.tar.bz2
coreclr-8b7d300c164971f573d8186e78204597a679c7d8.zip
Add NativeLibrary Resolve Event (#21929)
This change adds the Native library resolving event, to be raised as the last attempt to resolve a native DLL in an AssemblyLoadContext. With this change, the DllImport resolution sequence is as follows (stopping at any step with successful resolution): * If the invoking-assembly is not in the default load context, call AssemblyLoadContext.LoadUnmanagedDll() * Run the default load logic, try loading from: * AppDomain cache * NATIVE_DLL_SEARCH_DIRECTORIES * Invoking-assembly directory, System32, etc. based on DllImportSearchPaths * Raise the ResolvingUnmanagedDll event API Review: https://github.com/dotnet/corefx/issues/32850 The ResolveEventTests triggered a pre-existing bug in the exception handling code (#21964). Disabling the test on ARM64 Windows until the issue is fixed.
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs50
-rw-r--r--src/vm/dllimport.cpp126
-rw-r--r--src/vm/dllimport.h3
-rw-r--r--src/vm/metasig.h1
-rw-r--r--src/vm/mscorlib.h1
-rw-r--r--tests/issues.targets6
-rw-r--r--tests/src/Interop/CMakeLists.txt1
-rw-r--r--tests/src/Interop/NativeLibraryResolveEvent/CMakeLists.txt13
-rw-r--r--tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.cs118
-rw-r--r--tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.csproj36
-rw-r--r--tests/src/Interop/NativeLibraryResolveEvent/ResolvedLib.cpp12
-rw-r--r--tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.cs16
-rw-r--r--tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.csproj33
-rw-r--r--tests/src/Interop/ReadMe.md4
14 files changed, 391 insertions, 29 deletions
diff --git a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs
index 7ec6cf4c29..fdb27d95f6 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs
@@ -78,6 +78,7 @@ namespace System.Runtime.Loader
m_pNativeAssemblyLoadContext = InitializeAssemblyLoadContext(thisHandlePtr, fRepresentsTPALoadContext, isCollectible);
// Initialize event handlers to be null by default
+ ResolvingUnmanagedDll = null;
Resolving = null;
Unloading = null;
@@ -304,12 +305,12 @@ namespace System.Runtime.Loader
resolvedAssembly = handler(this, assemblyName);
if (resolvedAssembly != null)
{
- break;
+ return resolvedAssembly;
}
}
}
- return resolvedAssembly;
+ return null;
}
private Assembly ValidateAssemblyNameWithSimpleName(Assembly assembly, string requestedSimpleName)
@@ -413,6 +414,36 @@ namespace System.Runtime.Loader
return context.LoadUnmanagedDll(unmanagedDllName);
}
+ // This method is invoked by the VM to resolve a native library using the ResolvingUnmanagedDll event
+ // after trying all other means of resolution.
+ private static IntPtr ResolveUnmanagedDllUsingEvent(string unmanagedDllName, Assembly assembly, IntPtr gchManagedAssemblyLoadContext)
+ {
+ AssemblyLoadContext context = (AssemblyLoadContext)(GCHandle.FromIntPtr(gchManagedAssemblyLoadContext).Target);
+ return context.GetResolvedUnmanagedDll(assembly, unmanagedDllName);
+ }
+
+ private IntPtr GetResolvedUnmanagedDll(Assembly assembly, string unmanagedDllName)
+ {
+ IntPtr resolvedDll = IntPtr.Zero;
+
+ Func<Assembly, string, IntPtr> dllResolveHandler = ResolvingUnmanagedDll;
+
+ if (dllResolveHandler != null)
+ {
+ // Loop through the event subscribers and return the first non-null native library handle
+ foreach (Func<Assembly, string, IntPtr> handler in dllResolveHandler.GetInvocationList())
+ {
+ resolvedDll = handler(assembly, unmanagedDllName);
+ if (resolvedDll != IntPtr.Zero)
+ {
+ return resolvedDll;
+ }
+ }
+ }
+
+ return IntPtr.Zero;
+ }
+
public static AssemblyLoadContext Default
{
get
@@ -509,7 +540,22 @@ namespace System.Runtime.Loader
}
}
+ // Event handler for resolving native libraries.
+ // This event is raised if the native library could not be resolved via
+ // the default resolution logic [including AssemblyLoadContext.LoadUnmanagedDll()]
+ //
+ // Inputs: Invoking assembly, and library name to resolve
+ // Returns: A handle to the loaded native library
+ public event Func<Assembly, string, IntPtr> ResolvingUnmanagedDll;
+
+ // Event handler for resolving managed assemblies.
+ // This event is raised if the managed assembly could not be resolved via
+ // the default resolution logic [including AssemblyLoadContext.Load()]
+ //
+ // Inputs: The AssemblyLoadContext and AssemblyName to be loaded
+ // Returns: The Loaded assembly object.
public event Func<AssemblyLoadContext, AssemblyName, Assembly> Resolving;
+
public event Action<AssemblyLoadContext> Unloading;
// Contains the reference to VM's representation of the AssemblyLoadContext
diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp
index 8c86882006..37cfda9775 100644
--- a/src/vm/dllimport.cpp
+++ b/src/vm/dllimport.cpp
@@ -6275,7 +6275,7 @@ INT_PTR NDirect::GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR sy
}
// static
-NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, AppDomain* pDomain, PCWSTR wszLibName)
+NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, PCWSTR wszLibName)
{
STANDARD_VM_CONTRACT;
//Dynamic Pinvoke Support:
@@ -6290,7 +6290,8 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD,
}
#endif
- LPVOID hmod = NULL;
+ NATIVE_LIBRARY_HANDLE hmod = NULL;
+ AppDomain* pDomain = GetAppDomain();
CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext();
Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
@@ -6349,11 +6350,92 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD,
args[ARGNUM_1] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext);
// Make the call
- CALL_MANAGED_METHOD(hmod,LPVOID,args);
+ CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args);
+
+ GCPROTECT_END();
+
+ return hmod;
+}
+
+// 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<ICLRPrivBinder *>(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 (NATIVE_LIBRARY_HANDLE)hmod;
+ return hmod;
}
// Try to load the module alongside the assembly where the PInvoke was declared.
@@ -6633,15 +6715,13 @@ HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracke
AppDomain* pDomain = GetAppDomain();
// AssemblyLoadContext is not supported in AppX mode and thus,
- // we should not perform PInvoke resolution via it when operating in
- // AppX mode.
+ // we should not perform PInvoke resolution via it when operating in AppX mode.
if (!AppX::IsAppXProcess())
{
- hmod = LoadLibraryModuleViaHost(pMD, pDomain, wszLibName);
+ hmod = LoadLibraryModuleViaHost(pMD, wszLibName);
if (hmod != NULL)
{
#ifdef FEATURE_PAL
- // Register the system library handle with PAL and get a PAL library handle
hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
#endif // FEATURE_PAL
return hmod.Extract();
@@ -6654,36 +6734,30 @@ HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracke
return hmod.Extract();
}
-#ifdef FEATURE_PAL
- // In the PAL version of CoreCLR, the CLR module itself exports the functionality
- // that the Windows version obtains from kernel32 and friends. In order to avoid
- // picking up the wrong instance, we perform this redirection first.
- // This is also true for CoreSystem builds, where mscorlib p/invokes are forwarded through coreclr
- // itself so we can control CoreSystem library/API name re-mapping from one central location.
- if (SString::_wcsicmp(wszLibName, MAIN_CLR_MODULE_NAME_W) == 0)
+ hmod = LoadLibraryModuleBySearch(pMD, pErrorTracker, wszLibName);
+ if (hmod != NULL)
{
- hmod = GetCLRModule();
- }
+#ifdef FEATURE_PAL
+ hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
#endif // FEATURE_PAL
- if (hmod == NULL)
+ // If we have a handle add it to the cache.
+ pDomain->AddUnmanagedImageToCache(wszLibName, hmod);
+ return hmod.Extract();
+ }
+
+ if (!AppX::IsAppXProcess())
{
- hmod = LoadLibraryModuleBySearch(pMD, pErrorTracker, wszLibName);
+ hmod = LoadLibraryModuleViaEvent(pMD, wszLibName);
if (hmod != NULL)
{
#ifdef FEATURE_PAL
- // Register the system library handle with PAL and get a PAL library handle
hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
#endif // FEATURE_PAL
+ return hmod.Extract();
}
}
- if (hmod != NULL)
- {
- // If we have a handle add it to the cache.
- pDomain->AddUnmanagedImageToCache(wszLibName, hmod);
- }
-
return hmod.Extract();
}
diff --git a/src/vm/dllimport.h b/src/vm/dllimport.h
index 2f25e59570..1203803b9a 100644
--- a/src/vm/dllimport.h
+++ b/src/vm/dllimport.h
@@ -124,7 +124,8 @@ private:
static NATIVE_LIBRARY_HANDLE LoadFromNativeDllSearchDirectories(AppDomain* pDomain, 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, AppDomain* pDomain, const wchar_t* wszLibName);
+ static NATIVE_LIBRARY_HANDLE LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, LPCWSTR wszLibName);
+ static NATIVE_LIBRARY_HANDLE LoadLibraryModuleViaEvent(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);
diff --git a/src/vm/metasig.h b/src/vm/metasig.h
index 154c91570c..cfa844012e 100644
--- a/src/vm/metasig.h
+++ b/src/vm/metasig.h
@@ -551,6 +551,7 @@ DEFINE_METASIG_T(IM(RefGuid_OutIntPtr_RetCustomQueryInterfaceResult, r(g(GUID))
#endif //FEATURE_COMINTEROP
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))
// ThreadPool
DEFINE_METASIG(SM(Obj_Bool_RetVoid, j F, v))
diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h
index 62205d39bc..a1341dd70f 100644
--- a/src/vm/mscorlib.h
+++ b/src/vm/mscorlib.h
@@ -864,6 +864,7 @@ DEFINE_METHOD(FIRSTCHANCE_EVENTARGS, CTOR, .ctor,
DEFINE_CLASS(ASSEMBLYLOADCONTEXT, Loader, AssemblyLoadContext)
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVE, Resolve, SM_IntPtr_AssemblyName_RetAssemblyBase)
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVEUNMANAGEDDLL, ResolveUnmanagedDll, SM_Str_IntPtr_RetIntPtr)
+DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVEUNMANAGEDDLLUSINGEVENT, ResolveUnmanagedDllUsingEvent, SM_Str_AssemblyBase_IntPtr_RetIntPtr)
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVEUSINGEVENT, ResolveUsingResolvingEvent, SM_IntPtr_AssemblyName_RetAssemblyBase)
DEFINE_FIELD(ASSEMBLYLOADCONTEXT, ASSEMBLY_LOAD, AssemblyLoad)
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, ON_ASSEMBLY_LOAD, OnAssemblyLoad, SM_Assembly_RetVoid)
diff --git a/tests/issues.targets b/tests/issues.targets
index 11043ffd2a..fd1d96558f 100644
--- a/tests/issues.targets
+++ b/tests/issues.targets
@@ -388,6 +388,9 @@
<ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl/*">
<Issue>9565</Issue>
</ExcludeList>
+ <ExcludeList Include="$(XunitTestBinBase)/Interop/NativeLibraryResolveEvent/ResolveEventTests/*">
+ <Issue>21964</Issue>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/Regressions/coreclr/16064/methodimpl/*">
<Issue>9565</Issue>
</ExcludeList>
@@ -819,6 +822,9 @@
<ExcludeList Include="$(XunitTestBinBase)/Interop/NativeLibrary/NativeLibraryTests/*">
<Issue>Issue building native components for the test. Since tests are currently built on Windows, the native components end up at Core_Root instead of Test directory, which is not suitable for the test.</Issue>
</ExcludeList>
+ <ExcludeList Include="$(XunitTestBinBase)/Interop/MarshalAPI/ResolveEvent/ResolveEventTests/*">
+ <Issue>Issue building native components for the test. Since tests are currently built on Windows, the native components end up at Core_Root instead of Test directory, which is not suitable for the test.</Issue>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/CoreMangLib/cti/system/byte/ByteToString3/*">
<Issue>needs triage</Issue>
</ExcludeList>
diff --git a/tests/src/Interop/CMakeLists.txt b/tests/src/Interop/CMakeLists.txt
index 9784ea1c95..e46e78a8ff 100644
--- a/tests/src/Interop/CMakeLists.txt
+++ b/tests/src/Interop/CMakeLists.txt
@@ -56,6 +56,7 @@ add_subdirectory(StringMarshalling/VBByRefStr)
add_subdirectory(MarshalAPI/FunctionPointer)
add_subdirectory(MarshalAPI/IUnknown)
add_subdirectory(NativeLibrary)
+add_subdirectory(NativeLibraryResolveEvent)
add_subdirectory(SizeConst)
add_subdirectory(DllImportAttribute/ExeFile)
add_subdirectory(DllImportAttribute/FileNameContainDot)
diff --git a/tests/src/Interop/NativeLibraryResolveEvent/CMakeLists.txt b/tests/src/Interop/NativeLibraryResolveEvent/CMakeLists.txt
new file mode 100644
index 0000000000..20935806ca
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveEvent/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required (VERSION 2.6)
+project (ResolvedLib)
+include_directories(${INC_PLATFORM_DIR})
+set(SOURCES ResolvedLib.cpp)
+
+# add the executable
+add_library (ResolvedLib SHARED ${SOURCES})
+target_link_libraries(ResolvedLib ${LINK_LIBRARIES_ADDITIONAL})
+
+# add the install targets
+install (TARGETS ResolvedLib DESTINATION bin)
+
+
diff --git a/tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.cs b/tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.cs
new file mode 100644
index 0000000000..245bfc6529
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.cs
@@ -0,0 +1,118 @@
+// 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.Runtime.Loader;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+using Console = Internal.Console;
+
+public class ALC : AssemblyLoadContext
+{
+ protected override Assembly Load(AssemblyName assemblyName)
+ {
+ return Assembly.Load(assemblyName);
+ }
+}
+
+public class ResolveEventTests
+{
+ static int HandlerTracker = 1;
+
+ public static int Main()
+ {
+ // Events on the Default Load Context
+
+ try
+ {
+ AssemblyLoadContext.Default.ResolvingUnmanagedDll += HandlerFail;
+ NativeSum(10, 10);
+ }
+ catch (DllNotFoundException e)
+ {
+ if (HandlerTracker != 0)
+ {
+ Console.WriteLine("Event Handlers not called as expected");
+ return 101;
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Unexpected exception: {e.Message}");
+ return 102;
+ }
+
+ try
+ {
+ AssemblyLoadContext.Default.ResolvingUnmanagedDll += HandlerPass;
+
+ if(NativeSum(10, 10) != 20)
+ {
+ Console.WriteLine("Unexpected ReturnValue from NativeSum()");
+ return 103;
+ }
+ if (HandlerTracker != 0)
+ {
+ Console.WriteLine("Event Handlers not called as expected");
+ return 104;
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Unexpected exception: {e.Message}");
+ return 105;
+ }
+
+ // Events on a Custom Load Context
+
+ try
+ {
+ string currentDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ string testAsmDir = Path.Combine(currentDir, "..", "TestAsm", "TestAsm");
+
+ ALC alc = new ALC();
+ alc.ResolvingUnmanagedDll += HandlerPass;
+
+ var assembly = alc.LoadFromAssemblyPath(Path.Combine(testAsmDir, "TestAsm.dll"));
+ var type = assembly.GetType("TestAsm");
+ var method = type.GetMethod("Sum");
+ int value = (int)method.Invoke(null, new object[] { 10, 10 });
+
+ if(value != 20)
+ {
+ Console.WriteLine("Unexpected ReturnValue from TestAsm.Sum()");
+ return 106;
+ }
+ if (HandlerTracker != 1)
+ {
+ Console.WriteLine("Event Handlers not called as expected");
+ return 107;
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Unexpected exception: {e.Message}");
+ return 108;
+ }
+
+ return 100;
+ }
+
+ public static IntPtr HandlerFail(Assembly assembly, string libraryName)
+ {
+ HandlerTracker--;
+ return IntPtr.Zero;
+ }
+
+ public static IntPtr HandlerPass(Assembly assembly, string libraryName)
+ {
+ HandlerTracker++;
+ return NativeLibrary.Load("ResolvedLib", assembly, null);
+ }
+
+ [DllImport("NativeLib")]
+ static extern int NativeSum(int arg1, int arg2);
+
+}
diff --git a/tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.csproj b/tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.csproj
new file mode 100644
index 0000000000..d912938457
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.csproj
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>ResolveEventTests</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <ReferenceSystemPrivateCoreLib>true</ReferenceSystemPrivateCoreLib>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="*.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="CMakeLists.txt" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/NativeLibraryResolveEvent/ResolvedLib.cpp b/tests/src/Interop/NativeLibraryResolveEvent/ResolvedLib.cpp
new file mode 100644
index 0000000000..87ae50ed58
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveEvent/ResolvedLib.cpp
@@ -0,0 +1,12 @@
+// 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 <stdio.h>
+#include <xplatform.h>
+
+extern "C" DLL_EXPORT int NativeSum(int a, int b)
+{
+ return a + b;
+}
+
+
diff --git a/tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.cs b/tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.cs
new file mode 100644
index 0000000000..1517c94f6e
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.cs
@@ -0,0 +1,16 @@
+// 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.Runtime.InteropServices;
+
+public class TestAsm
+{
+ public static int Sum(int a, int b)
+ {
+ return NativeSum(a, b);
+ }
+
+ [DllImport("NativeLib")]
+ static extern int NativeSum(int arg1, int arg2);
+}
diff --git a/tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.csproj b/tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.csproj
new file mode 100644
index 0000000000..e8f3e3af21
--- /dev/null
+++ b/tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.csproj
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>TestAsm</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <ReferenceSystemPrivateCoreLib>true</ReferenceSystemPrivateCoreLib>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="*.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/ReadMe.md b/tests/src/Interop/ReadMe.md
index 9bd1d79313..97e4202580 100644
--- a/tests/src/Interop/ReadMe.md
+++ b/tests/src/Interop/ReadMe.md
@@ -57,6 +57,10 @@ Testing P/Invoke has two aspects:
The Marshal API surface area testing is traditionally done via unit testing and far better suited in the [CoreFX](https://github.com/dotnet/corefx/tree/master/src/System.Runtime.InteropServices/tests) repo. Cases where testing the API surface area requires native tests assets will be performed in the [CoreCLR](https://github.com/dotnet/coreclr/tree/master/tests/src/Interop) repo.
+### NativeLibrary
+
+This series has unit tests corresponding to `System.Runtime.NativeLibrary` APIs and related events in `System.Runtime.Loader.AssemblyLoadContext`.
+
## Common Task steps
### Adding new native project