diff options
author | Gaurav Khanna (CLR) <gaurav.khanna@microsoft.com> | 2016-01-15 15:06:01 -0800 |
---|---|---|
committer | Gaurav Khanna <gkhanna@microsoft.com> | 2016-01-21 07:26:51 -0800 |
commit | 7e1a3b532dc2fb2eab2d41994c971759243def19 (patch) | |
tree | f85fba1b7b911ee580768aa8eb40d771a61c0ec1 | |
parent | 17cf0eae0414c01c33af403cc54bb37cf3b84016 (diff) | |
download | coreclr-7e1a3b532dc2fb2eab2d41994c971759243def19.tar.gz coreclr-7e1a3b532dc2fb2eab2d41994c971759243def19.tar.bz2 coreclr-7e1a3b532dc2fb2eab2d41994c971759243def19.zip |
Add support for AssemblyResolution when an assembly is not found in a given Load Context.
1) Expose AssemblyResolve event off AssemblyLoadContext
2) Invoke the event if the abstract Load implementation does not return an assembly.
-rw-r--r-- | src/binder/assemblybinder.cpp | 12 | ||||
-rw-r--r-- | src/binder/clrprivbinderassemblyloadcontext.cpp | 2 | ||||
-rw-r--r-- | src/binder/clrprivbindercoreclr.cpp | 36 | ||||
-rw-r--r-- | src/binder/inc/assemblybinder.hpp | 2 | ||||
-rw-r--r-- | src/binder/inc/clrprivbindercoreclr.h | 4 | ||||
-rw-r--r-- | src/mscorlib/model.xml | 1 | ||||
-rw-r--r-- | src/mscorlib/src/System/Runtime/Loader/AssemblyLoadContext.cs | 38 | ||||
-rw-r--r-- | src/vm/appdomain.cpp | 7 | ||||
-rw-r--r-- | src/vm/assemblynative.cpp | 4 |
9 files changed, 85 insertions, 21 deletions
diff --git a/src/binder/assemblybinder.cpp b/src/binder/assemblybinder.cpp index 46f393b95e..139a66cbe8 100644 --- a/src/binder/assemblybinder.cpp +++ b/src/binder/assemblybinder.cpp @@ -56,7 +56,7 @@ extern BOOL RuntimeIsLegacyNetCF(DWORD adid); #include "clrprivbindercoreclr.h" #include "clrprivbinderassemblyloadcontext.h" // Helper function in the VM, invoked by the Binder, to invoke the host assembly resolver -extern HRESULT RuntimeInvokeHostAssemblyResolver(CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly); +extern HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly); // Helper to check if we have a host assembly resolver set extern BOOL RuntimeCanUseAppPathAssemblyResolver(DWORD adid); @@ -1856,7 +1856,7 @@ namespace BINDER_SPACE #endif //CROSSGEN_COMPILE #if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) -HRESULT AssemblyBinder::BindUsingHostAssemblyResolver (/* in */ CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin, +HRESULT AssemblyBinder::BindUsingHostAssemblyResolver (/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin, /* in */ AssemblyName *pAssemblyName, /* in */ IAssemblyName *pIAssemblyName, /* out */ Assembly **ppAssembly) @@ -1864,15 +1864,11 @@ HRESULT AssemblyBinder::BindUsingHostAssemblyResolver (/* in */ CLRPrivBinderAss HRESULT hr = E_FAIL; BINDER_LOG_ENTER(W("AssemblyBinder::BindUsingHostAssemblyResolver")); - _ASSERTE(pLoadContextToBindWithin != NULL); - - // Get the application context within which the assembly will be bound and loaded - ApplicationContext *pApplicationContext = pLoadContextToBindWithin->GetAppContext(); - _ASSERTE(pApplicationContext != NULL); + _ASSERTE(pManagedAssemblyLoadContextToBindWithin != NULL); // Call into the VM to use the HostAssemblyResolver and load the assembly ICLRPrivAssembly *pLoadedAssembly = NULL; - hr = RuntimeInvokeHostAssemblyResolver(pLoadContextToBindWithin, pIAssemblyName, &pLoadedAssembly); + hr = RuntimeInvokeHostAssemblyResolver(pManagedAssemblyLoadContextToBindWithin, pIAssemblyName, &pLoadedAssembly); if (SUCCEEDED(hr)) { _ASSERTE(pLoadedAssembly != NULL); diff --git a/src/binder/clrprivbinderassemblyloadcontext.cpp b/src/binder/clrprivbinderassemblyloadcontext.cpp index f2ed7e1f6d..344801a081 100644 --- a/src/binder/clrprivbinderassemblyloadcontext.cpp +++ b/src/binder/clrprivbinderassemblyloadcontext.cpp @@ -83,7 +83,7 @@ HRESULT CLRPrivBinderAssemblyLoadContext::BindAssemblyByName(IAssemblyName * // Thus, if default binder has been overridden, then invoke it in an attempt to perform the binding for it make the call // of what to do next. The host-overridden binder can either fail the bind or return reference to an existing assembly // that has been loaded. - hr = AssemblyBinder::BindUsingHostAssemblyResolver(this, pAssemblyName, pIAssemblyName, &pCoreCLRFoundAssembly); + hr = AssemblyBinder::BindUsingHostAssemblyResolver(GetManagedAssemblyLoadContext(), pAssemblyName, pIAssemblyName, &pCoreCLRFoundAssembly); if (SUCCEEDED(hr)) { // We maybe returned an assembly that was bound to a different AssemblyLoadContext instance. diff --git a/src/binder/clrprivbindercoreclr.cpp b/src/binder/clrprivbindercoreclr.cpp index 238c5225cf..4958875bf5 100644 --- a/src/binder/clrprivbindercoreclr.cpp +++ b/src/binder/clrprivbindercoreclr.cpp @@ -62,8 +62,42 @@ HRESULT CLRPrivBinderCoreCLR::BindAssemblyByName(IAssemblyName *pIAssemblyNa IF_FAIL_GO(pAssemblyName->Init(pIAssemblyName)); hr = BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly, false /* excludeAppPaths */); + +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || + (hr == FUSION_E_APP_DOMAIN_LOCKED) || (hr == FUSION_E_REF_DEF_MISMATCH)) + { + // If we are here, one of the following is possible: + // + // 1) The assembly has not been found in the current binder's application context (i.e. it has not already been loaded), OR + // 2) An assembly with the same simple name was already loaded in the context of the current binder but we ran into a Ref/Def + // mismatch (either due to version difference or strong-name difference). + // + // Thus, if default binder has been overridden, then invoke it in an attempt to perform the binding for it make the call + // of what to do next. The host-overridden binder can either fail the bind or return reference to an existing assembly + // that has been loaded. + + // Attempt to resolve the assembly via managed TPA ALC instance if one exists + INT_PTR pManagedAssemblyLoadContext = GetManagedAssemblyLoadContext(); + if (pManagedAssemblyLoadContext != NULL) + { + hr = AssemblyBinder::BindUsingHostAssemblyResolver(pManagedAssemblyLoadContext, pAssemblyName, pIAssemblyName, &pCoreCLRFoundAssembly); + if (SUCCEEDED(hr)) + { + // We maybe returned an assembly that was bound to a different AssemblyLoadContext instance. + // In such a case, we will not overwrite the binding context (which would be wrong since it would not + // be present in the cache of the current binding context). + if (pCoreCLRFoundAssembly->GetBinder() == NULL) + { + pCoreCLRFoundAssembly->SetBinder(this); + } + } + } + } +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + IF_FAIL_GO(hr); - + *ppAssembly = pCoreCLRFoundAssembly.Extract(); Exit:; diff --git a/src/binder/inc/assemblybinder.hpp b/src/binder/inc/assemblybinder.hpp index 4ca0c4f18c..bfe851d8e9 100644 --- a/src/binder/inc/assemblybinder.hpp +++ b/src/binder/inc/assemblybinder.hpp @@ -76,7 +76,7 @@ namespace BINDER_SPACE /* in */ LPCTSTR szMDAssemblyPath = NULL); #if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) - static HRESULT BindUsingHostAssemblyResolver (/* in */ CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin, + static HRESULT BindUsingHostAssemblyResolver (/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin, /* in */ AssemblyName *pAssemblyName, /* in */ IAssemblyName *pIAssemblyName, /* out */ Assembly **ppAssembly); diff --git a/src/binder/inc/clrprivbindercoreclr.h b/src/binder/inc/clrprivbindercoreclr.h index 4d98c9b55a..ce545561f2 100644 --- a/src/binder/inc/clrprivbindercoreclr.h +++ b/src/binder/inc/clrprivbindercoreclr.h @@ -79,12 +79,12 @@ public: BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly, bool excludeAppPaths); - INT_PTR GetManagedTPABinderInstance() + INT_PTR GetManagedAssemblyLoadContext() { return m_ptrManagedAssemblyLoadContext; } - void SetManagedTPABinderInstance(INT_PTR ptrManagedTPABinderInstance) + void SetManagedAssemblyLoadContext(INT_PTR ptrManagedTPABinderInstance) { _ASSERTE(m_ptrManagedAssemblyLoadContext == NULL); diff --git a/src/mscorlib/model.xml b/src/mscorlib/model.xml index 1628cc24fa..8397d355e4 100644 --- a/src/mscorlib/model.xml +++ b/src/mscorlib/model.xml @@ -1023,6 +1023,7 @@ <Member Name="get_Default" /> <Member Name="SetProfileOptimizationRoot(System.String)" /> <Member Name="StartProfileOptimization(System.String)" /> + <Member MemberType="Event" Name="Resolving" /> </Type> <Type Name="System.Reflection.Metadata.AssemblyExtensions"> <Member Name="TryGetRawMetadata(System.Reflection.Assembly,System.Byte*@,System.Int32@)"/> diff --git a/src/mscorlib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/mscorlib/src/System/Runtime/Loader/AssemblyLoadContext.cs index c3cdd810e1..c44365e1ff 100644 --- a/src/mscorlib/src/System/Runtime/Loader/AssemblyLoadContext.cs +++ b/src/mscorlib/src/System/Runtime/Loader/AssemblyLoadContext.cs @@ -64,6 +64,9 @@ namespace System.Runtime.Loader GCHandle gchALC = GCHandle.Alloc(this); IntPtr ptrALC = GCHandle.ToIntPtr(gchALC); m_pNativeAssemblyLoadContext = InitializeAssemblyLoadContext(ptrALC, fRepresentsTPALoadContext); + + // Initialize the resolve event handler to be null by default + Resolving = null; } [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] @@ -177,6 +180,29 @@ namespace System.Runtime.Loader return context.LoadFromAssemblyName(assemblyName); } + private Assembly GetFirstResolvedAssembly(AssemblyName assemblyName) + { + Assembly resolvedAssembly = null; + + Func<AssemblyLoadContext, AssemblyName, Assembly> assemblyResolveHandler = Resolving; + + if (assemblyResolveHandler != null) + { + // Loop through the event subscribers and return the first non-null Assembly instance + Delegate [] arrSubscribers = assemblyResolveHandler.GetInvocationList(); + for(int i = 0; i < arrSubscribers.Length; i++) + { + resolvedAssembly = ((Func<AssemblyLoadContext, AssemblyName, Assembly>)arrSubscribers[i])(this, assemblyName); + if (resolvedAssembly != null) + { + break; + } + } + } + + return resolvedAssembly; + } + public Assembly LoadFromAssemblyName(AssemblyName assemblyName) { // AssemblyName is mutable. Cache the expected name before anybody gets a chance to modify it. @@ -185,6 +211,12 @@ namespace System.Runtime.Loader Assembly assembly = Load(assemblyName); if (assembly == null) { + // Invoke the AssemblyResolve event callbacks if wired up + assembly = GetFirstResolvedAssembly(assemblyName); + } + + if (assembly == null) + { throw new FileNotFoundException(Environment.GetResourceString("IO.FileLoad"), requestedSimpleName); } @@ -355,6 +387,8 @@ namespace System.Runtime.Loader #endif // FEATURE_MULTICOREJI } + public event Func<AssemblyLoadContext, AssemblyName, Assembly> Resolving; + // Contains the reference to VM's representation of the AssemblyLoadContext private IntPtr m_pNativeAssemblyLoadContext; @@ -374,7 +408,9 @@ namespace System.Runtime.Loader [System.Security.SecuritySafeCritical] protected override Assembly Load(AssemblyName assemblyName) { - return Assembly.Load(assemblyName); + // We were loading an assembly into TPA ALC that was not found on TPA list. As a result we are here. + // Returning null will result in the AssemblyResolve event subscribers to be invoked to help resolve the assembly. + return null; } } } diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp index 997363e7e9..dd7406c916 100644 --- a/src/vm/appdomain.cpp +++ b/src/vm/appdomain.cpp @@ -14227,7 +14227,7 @@ BOOL RuntimeCanUseAppPathAssemblyResolver(DWORD adid) } // Returns S_OK if the assembly was successfully loaded -HRESULT RuntimeInvokeHostAssemblyResolver(CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly) +HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly) { CONTRACTL { @@ -14256,9 +14256,6 @@ HRESULT RuntimeInvokeHostAssemblyResolver(CLRPrivBinderAssemblyLoadContext *pLoa GCPROTECT_BEGIN(_gcRefs); - // Get the pointer to the managed assembly load context - INT_PTR ptrManagedAssemblyLoadContext = pLoadContextToBindWithin->GetManagedAssemblyLoadContext(); - // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.Resolve method. // // First, initialize an assembly spec for the requested assembly @@ -14280,7 +14277,7 @@ HRESULT RuntimeInvokeHostAssemblyResolver(CLRPrivBinderAssemblyLoadContext *pLoa // Setup the arguments for the call ARG_SLOT args[2] = { - PtrToArgSlot(ptrManagedAssemblyLoadContext), // IntPtr for managed assembly load context instance + PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance }; diff --git a/src/vm/assemblynative.cpp b/src/vm/assemblynative.cpp index 70bccccf76..c96923ded6 100644 --- a/src/vm/assemblynative.cpp +++ b/src/vm/assemblynative.cpp @@ -2533,14 +2533,14 @@ INT_PTR QCALLTYPE AssemblyNative::InitializeAssemblyLoadContext(INT_PTR ptrManag { // We are initializing the managed instance of Assembly Load Context that would represent the TPA binder. // First, confirm we do not have an existing managed ALC attached to the TPA binder. - INT_PTR ptrTPAAssemblyLoadContext = pTPABinderContext->GetManagedTPABinderInstance(); + INT_PTR ptrTPAAssemblyLoadContext = pTPABinderContext->GetManagedAssemblyLoadContext(); if ((ptrTPAAssemblyLoadContext != NULL) && (ptrTPAAssemblyLoadContext != ptrManagedAssemblyLoadContext)) { COMPlusThrow(kInvalidOperationException, IDS_HOST_ASSEMBLY_RESOLVER_INCOMPATIBLE_TPA_BINDING_CONTEXT); } // Attach the managed TPA binding context with the native one. - pTPABinderContext->SetManagedTPABinderInstance(ptrManagedAssemblyLoadContext); + pTPABinderContext->SetManagedAssemblyLoadContext(ptrManagedAssemblyLoadContext); ptrNativeAssemblyLoadContext = reinterpret_cast<INT_PTR>(pTPABinderContext); } |