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

Add NativeLibrary Resolve Event #21929

Merged
merged 1 commit into from
Jan 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ internal AssemblyLoadContext(bool fRepresentsTPALoadContext, bool isCollectible)
m_pNativeAssemblyLoadContext = InitializeAssemblyLoadContext(thisHandlePtr, fRepresentsTPALoadContext, isCollectible);

// Initialize event handlers to be null by default
ResolvingUnmanagedDll = null;
Resolving = null;
Unloading = null;

Expand Down Expand Up @@ -304,12 +305,12 @@ private Assembly GetFirstResolvedAssembly(AssemblyName assemblyName)
resolvedAssembly = handler(this, assemblyName);
if (resolvedAssembly != null)
{
break;
return resolvedAssembly;
}
}
}

return resolvedAssembly;
return null;
}

private Assembly ValidateAssemblyNameWithSimpleName(Assembly assembly, string requestedSimpleName)
Expand Down Expand Up @@ -413,6 +414,36 @@ private static IntPtr ResolveUnmanagedDll(string unmanagedDllName, IntPtr gchMan
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)
{
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down Expand Up @@ -509,7 +540,22 @@ internal static void OnProcessExit()
}
}

// 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
Expand Down
126 changes: 100 additions & 26 deletions src/vm/dllimport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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();

Expand Down Expand Up @@ -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;
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved

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);
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
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.
Expand Down Expand Up @@ -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();
Expand All @@ -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();
}

Expand Down
3 changes: 2 additions & 1 deletion src/vm/dllimport.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ class NDirect

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);

Expand Down
1 change: 1 addition & 0 deletions src/vm/metasig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
1 change: 1 addition & 0 deletions src/vm/mscorlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions tests/issues.targets
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions tests/src/Interop/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 13 additions & 0 deletions tests/src/Interop/NativeLibraryResolveEvent/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)


Loading