Enable RefEmitted assemblies to have Fallback LoadContext notion
authorGaurav Khanna <gkhanna@microsoft.com>
Tue, 26 Jul 2016 16:24:38 +0000 (09:24 -0700)
committerGaurav Khanna <gkhanna@microsoft.com>
Thu, 28 Jul 2016 18:16:40 +0000 (11:16 -0700)
src/dlls/mscorrc/resource.h
src/mscorlib/src/System/Runtime/Loader/AssemblyLoadContext.cs
src/vm/assembly.cpp
src/vm/assemblynative.cpp
src/vm/assemblyspec.cpp
src/vm/assemblyspec.hpp
src/vm/pefile.cpp
src/vm/pefile.h
src/vm/typeparse.cpp

index 759cb54b30286d5b558fe1d90f4736ee950c3602..1391a21545648c2371637a01641bd88a257dae87 100644 (file)
 #endif // FEATURE_HOST_ASSEMBLY_RESOLVER
 
 #define IDS_NATIVE_IMAGE_CANNOT_BE_LOADED_MULTIPLE_TIMES                               0x263a
+
+
index 527fbe4d6ce8dc97cbfcd598ab13d18aad79d4cc..c4872e363b7ce8f1bc355a5952722efab674bb06 100644 (file)
@@ -401,19 +401,26 @@ namespace System.Runtime.Loader
             }
             
             AssemblyLoadContext loadContextForAssembly = null;
-            IntPtr ptrAssemblyLoadContext = GetLoadContextForAssembly((RuntimeAssembly)assembly);
-            if (ptrAssemblyLoadContext == IntPtr.Zero)
-            {
-                // If the load context is returned null, then the assembly was bound using the TPA binder
-                // and we shall return reference to the active "Default" binder - which could be the TPA binder
-                // or an overridden CLRPrivBinderAssemblyLoadContext instance.
-                loadContextForAssembly = AssemblyLoadContext.Default;
-            }
-            else
+
+            RuntimeAssembly rtAsm = assembly as RuntimeAssembly;
+            
+            // We only support looking up load context for runtime assemblies.
+            if (rtAsm != null)
             {
-                loadContextForAssembly = (AssemblyLoadContext)(GCHandle.FromIntPtr(ptrAssemblyLoadContext).Target);
+                IntPtr ptrAssemblyLoadContext = GetLoadContextForAssembly(rtAsm);
+                if (ptrAssemblyLoadContext == IntPtr.Zero)
+                {
+                    // If the load context is returned null, then the assembly was bound using the TPA binder
+                    // and we shall return reference to the active "Default" binder - which could be the TPA binder
+                    // or an overridden CLRPrivBinderAssemblyLoadContext instance.
+                    loadContextForAssembly = AssemblyLoadContext.Default;
+                }
+                else
+                {
+                    loadContextForAssembly = (AssemblyLoadContext)(GCHandle.FromIntPtr(ptrAssemblyLoadContext).Target);
+                }
             }
-            
+
             return loadContextForAssembly;
         }
         
index 06effd72401fcbf4eff63f374d2eaf1d34d76ba4..481bceaf557d7a3cef62f843ab17977c23fa863f 100644 (file)
@@ -756,6 +756,55 @@ Assembly *Assembly::CreateDynamic(AppDomain *pDomain, CreateDynamicAssemblyArgs
                                                    name, &assemData, dwFlags,
                                                    &ma));
         pFile = PEAssembly::Create(pCallerAssembly->GetManifestFile(), pAssemblyEmit, args->access & ASSEMBLY_ACCESS_REFLECTION_ONLY);
+
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+        // Dynamically created modules (aka RefEmit assemblies) do not have a LoadContext associated with them since they are not bound
+        // using an actual binder. As a result, we will assume the same binding/loadcontext information for the dynamic assembly as its
+        // caller/creator to ensure that any assembly loads triggered by the dynamic assembly are resolved using the intended load context.
+        //
+        // If the creator assembly has a HostAssembly associated with it, then use it for binding. Otherwise, ithe creator is dynamic
+        // and will have a fallback load context binder associated with it.
+        ICLRPrivBinder *pFallbackLoadContextBinder = nullptr;
+        
+        // There is always a manifest file - wehther working with static or dynamic assemblies.
+        PEFile *pCallerAssemblyManifestFile = pCallerAssembly->GetManifestFile();
+        _ASSERTE(pCallerAssemblyManifestFile != NULL);
+
+        if (!pCallerAssemblyManifestFile->IsDynamic())
+        {
+            // Static assemblies with do not have fallback load context
+            _ASSERTE(pCallerAssemblyManifestFile->GetFallbackLoadContextBinder() == nullptr);
+
+            if (pCallerAssemblyManifestFile->IsSystem())
+            {
+                // CoreLibrary is always bound to TPA binder
+                pFallbackLoadContextBinder = pDomain->GetTPABinderContext();
+            }
+            else
+            {
+                // Fetch the binder from the host assembly
+                PTR_ICLRPrivAssembly pCallerAssemblyHostAssembly = pCallerAssemblyManifestFile->GetHostAssembly();
+                _ASSERTE(pCallerAssemblyHostAssembly != nullptr);
+
+                UINT_PTR assemblyBinderID = 0;
+                IfFailThrow(pCallerAssemblyHostAssembly->GetBinderID(&assemblyBinderID));
+                pFallbackLoadContextBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
+            }
+        }
+        else
+        {
+            // Creator assembly is dynamic too, so use its fallback load context for the one
+            // we are creating.
+            pFallbackLoadContextBinder = pCallerAssemblyManifestFile->GetFallbackLoadContextBinder(); 
+        }
+
+        // At this point, we should have a fallback load context binder to work with
+        _ASSERTE(pFallbackLoadContextBinder != nullptr);
+
+        // Set it as the fallback load context binder for the dynamic assembly being created
+        pFile->SetFallbackLoadContextBinder(pFallbackLoadContextBinder);
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+
     }            
     
     AssemblyLoadSecurity loadSecurity;
index c8251711ad981c3eca33c90f72d765000e70edda..29e0a451f0e749bd7f620c018e11b3eeda5f8c1c 100644 (file)
@@ -179,6 +179,7 @@ FCIMPL9(Object*, AssemblyNative::Load, AssemblyNameBaseObject* assemblyNameUNSAF
     CheckPointHolder cph(pThread->m_MarshalAlloc.GetCheckpoint()); //hold checkpoint for autorelease
 
     DomainAssembly * pParentAssembly = NULL;
+    Assembly * pRefAssembly = NULL;
 
     if(gc.assemblyName->GetSimpleName() == NULL)
     {
@@ -194,7 +195,6 @@ FCIMPL9(Object*, AssemblyNative::Load, AssemblyNameBaseObject* assemblyNameUNSAF
             gc.codeBase = NULL;
 
         // Compute parent assembly
-        Assembly * pRefAssembly;
         if (gc.requestingAssembly == NULL)
         {
             pRefAssembly = SystemDomain::GetCallersAssembly(stackMark);
@@ -241,7 +241,17 @@ FCIMPL9(Object*, AssemblyNative::Load, AssemblyNameBaseObject* assemblyNameUNSAF
 
     if (pParentAssembly != NULL)
         spec.SetParentAssembly(pParentAssembly);
-    
+
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)    
+    // If the requesting assembly has Fallback LoadContext binder available,
+    // then set it up in the AssemblySpec.
+    if (pRefAssembly != NULL)
+    {
+        PEFile *pRefAssemblyManifestFile = pRefAssembly->GetManifestFile();
+        spec.SetFallbackLoadContextBinderForRequestingAssembly(pRefAssemblyManifestFile->GetFallbackLoadContextBinder());
+    }
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+
     AssemblyLoadSecurity loadSecurity;
     loadSecurity.m_pAdditionalEvidence = &gc.security;
     loadSecurity.m_fCheckLoadFromRemoteSource = !!(gc.codeBase != NULL);
@@ -2552,20 +2562,26 @@ INT_PTR QCALLTYPE AssemblyNative::GetLoadContextForAssembly(QCall::AssemblyHandl
     {
         // Get the binding context for the assembly.
         //
+        ICLRPrivBinder *pOpaqueBinder = nullptr;
+        AppDomain *pCurDomain = AppDomain::GetCurrentDomain();
+        CLRPrivBinderCoreCLR *pTPABinder = pCurDomain->GetTPABinderContext();
+
+        
         // GetBindingContext returns a ICLRPrivAssembly which can be used to get access to the
         // actual ICLRPrivBinder instance in which the assembly was loaded.
         PTR_ICLRPrivBinder pBindingContext = pPEAssembly->GetBindingContext();
         UINT_PTR assemblyBinderID = 0;
         IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID));
 
-        AppDomain *pCurDomain = AppDomain::GetCurrentDomain();
-        CLRPrivBinderCoreCLR *pTPABinder = pCurDomain->GetTPABinderContext();
-        
         // If the assembly was bound using the TPA binder,
         // then we will return the reference to "Default" binder from the managed implementation when this QCall returns.
         //
         // See earlier comment about "Default" binder for additional context.
-        ICLRPrivBinder *pOpaqueBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
+        pOpaqueBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
+        
+        // We should have a load context binder at this point.
+        _ASSERTE(pOpaqueBinder != nullptr);
+
         if (!AreSameBinderInstance(pTPABinder, pOpaqueBinder))
         {
             // Only CLRPrivBinderAssemblyLoadContext instance contains the reference to its
index 5baf588cb2158a82e902ebcff8e6b4361c29fe4c..3bd6d388618d4413dec53afb10e24b2e0f6cdc9b 100644 (file)
@@ -1256,26 +1256,40 @@ ICLRPrivBinder* AssemblySpec::GetBindingContextFromParentAssembly(AppDomain *pDo
         
         // ICLRPrivAssembly implements ICLRPrivBinder and thus, "is a" binder in a manner of semantics.
         pParentAssemblyBinder = pParentPEAssembly->GetBindingContext();
-        
+    }
+
 #if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
-        if (pParentAssemblyBinder != NULL)
+    if (pParentAssemblyBinder == NULL)
+    {
+        // If the parent assembly binder is not available, then we maybe dealing with one of the following
+        // assembly scenarios:
+        //
+        // 1) Domain Neutral assembly
+        // 2) RefEmitted assembly
+        // 3) Entrypoint assembly
+        //
+        // For (1) and (3), we will need to bind against the DefaultContext binder (aka TPA Binder). This happens
+        // below if we do not find the parent assembly binder.
+        //
+        // For (2), check if we have the fallback load context binder for the requesting dynamic assembly available.
+        
+        pParentAssemblyBinder = GetFallbackLoadContextBinderForRequestingAssembly();
+    }
+
+    if (pParentAssemblyBinder != NULL)
+    {
+        CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext();
+        if (AreSameBinderInstance(pTPABinder, pParentAssemblyBinder))
         {
-            CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext();
-            if (AreSameBinderInstance(pTPABinder, pParentAssemblyBinder))
-            {
-                // If the parent assembly is a platform (TPA) assembly, then its binding context will always be the TPABinder context. In 
-                // such case, we will return the default context for binding to allow the bind to go
-                // via the custom binder context, if it was overridden. If it was not overridden, then we will get the expected
-                // TPABinder context anyways.
-                //
-                // Get the reference to the default binding context (this could be the TPABinder context or custom AssemblyLoadContext)
-                pParentAssemblyBinder = static_cast<ICLRPrivBinder*>(pDomain->GetFusionContext());
-            }
+            // If the parent assembly is a platform (TPA) assembly, then its binding context will always be the TPABinder context. In 
+            // such case, we will return the default context for binding to allow the bind to go
+            // via the custom binder context, if it was overridden. If it was not overridden, then we will get the expected
+            // TPABinder context anyways.
+            //
+            // Get the reference to the default binding context (this could be the TPABinder context or custom AssemblyLoadContext)
+            pParentAssemblyBinder = static_cast<ICLRPrivBinder*>(pDomain->GetFusionContext());
         }
-#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
     }
-       
-#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
 
 #if defined(FEATURE_COMINTEROP)
     if (!IsContentType_WindowsRuntime() && (pParentAssemblyBinder != NULL))
@@ -1295,10 +1309,10 @@ ICLRPrivBinder* AssemblySpec::GetBindingContextFromParentAssembly(AppDomain *pDo
     
     if (!pParentAssemblyBinder)
     {
-        // We can be here when loading assemblies via the host (e.g. ICLRRuntimeHost2::ExecuteAssembly) or when attempting
-        // to load assemblies via custom AssemblyLoadContext implementation. 
+        // We can be here when loading assemblies via the host (e.g. ICLRRuntimeHost2::ExecuteAssembly) or dealing with assemblies
+        // whose parent is a domain neutral assembly (see comment above for details).
         //
-        // In such a case, the parent assembly (semantically) is mscorlib and thus, the default binding context should be 
+        // In such a case, the parent assembly (semantically) is CoreLibrary and thus, the default binding context should be 
         // used as the parent assembly binder.
         pParentAssemblyBinder = static_cast<ICLRPrivBinder*>(pDomain->GetFusionContext());
     }
index 4a7130bd28ae9e37d41b721728a887445d4a4ba8..84e67dfcc21cfd653ea1b041e23550ab944cbe0c 100644 (file)
@@ -42,6 +42,11 @@ class AssemblySpec  : public BaseAssemblySpec
     DWORD            m_dwHashAlg;
     DomainAssembly  *m_pParentAssembly;
 
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+    // Contains the reference to the fallback load context associated with RefEmitted assembly requesting the load of another assembly (static or dynamic)
+    ICLRPrivBinder *m_pFallbackLoadContextBinder;
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+
     BOOL IsValidAssemblyName();
     
     HRESULT InitializeSpecInternal(mdToken kAssemblyRefOrDef, 
@@ -67,6 +72,11 @@ class AssemblySpec  : public BaseAssemblySpec
     {
         LIMITED_METHOD_CONTRACT;
         m_pParentAssembly = NULL;
+
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+        m_pFallbackLoadContextBinder = NULL;        
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+
     }
 #endif //!DACCESS_COMPILE
 
@@ -74,6 +84,11 @@ class AssemblySpec  : public BaseAssemblySpec
     { 
         LIMITED_METHOD_CONTRACT
         m_pParentAssembly = NULL;
+
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+        m_pFallbackLoadContextBinder = NULL;        
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+
     }
 
 #ifdef FEATURE_FUSION
@@ -158,6 +173,22 @@ class AssemblySpec  : public BaseAssemblySpec
 #endif
     }
 
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+    void SetFallbackLoadContextBinderForRequestingAssembly(ICLRPrivBinder *pFallbackLoadContextBinder)
+    {
+       LIMITED_METHOD_CONTRACT;
+
+        m_pFallbackLoadContextBinder = pFallbackLoadContextBinder;
+    }
+
+    ICLRPrivBinder* GetFallbackLoadContextBinderForRequestingAssembly()
+    {
+        LIMITED_METHOD_CONTRACT;
+
+        return m_pFallbackLoadContextBinder;
+    }
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+
     // Note that this method does not clone the fields!
     void CopyFrom(AssemblySpec* pSource)
     {
@@ -173,6 +204,12 @@ class AssemblySpec  : public BaseAssemblySpec
 
         SetIntrospectionOnly(pSource->IsIntrospectionOnly());
         SetParentAssembly(pSource->GetParentAssembly());
+
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+        // Copy the details of the fallback load context binder
+        SetFallbackLoadContextBinderForRequestingAssembly(pSource->GetFallbackLoadContextBinderForRequestingAssembly());
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+
         m_HashForControl = pSource->m_HashForControl;
         m_dwHashAlg = pSource->m_dwHashAlg;
     }
index 440281e9535c767d8672d304d84b76c36933134b..86785e2417022e4cd6731d809b7238300ae53b5a 100644 (file)
@@ -99,6 +99,9 @@ PEFile::PEFile(PEImage *identity, BOOL fCheckAuthenticodeSignature/*=TRUE*/) :
     ,m_securityManagerLock(CrstPEFileSecurityManager)
 #endif // FEATURE_CAS_POLICY
     ,m_pHostAssembly(nullptr)
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+    ,m_pFallbackLoadContextBinder(nullptr)
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
 {
     CONTRACTL
     {
index 5cefd5d2bddd71012621b72c8f4861c5eaff89fc..413d08c18571b29e9ecb3d19548a2592c98004a7 100644 (file)
@@ -659,6 +659,17 @@ public:
 protected:
     PTR_ICLRPrivAssembly m_pHostAssembly;
 
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+    // For certain assemblies, we do not have m_pHostAssembly since they are not bound using an actual binder.
+    // An example is Ref-Emitted assemblies. Thus, when such assemblies trigger load of their dependencies, 
+    // we need to ensure they are loaded in appropriate load context.
+    //
+    // To enable this, we maintain a concept of "Fallback LoadContext", which will be set to the Binder of the
+    // assembly that created the dynamic assembly. If the creator assembly is dynamic itself, then its fallback
+    // load context would be propagated to the assembly being dynamically generated.
+    ICLRPrivBinder *m_pFallbackLoadContextBinder;
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+
 protected:
 
     friend class CLRPrivBinderFusion;
@@ -684,6 +695,21 @@ public:
 
     bool CanUseWithBindingCache()
     { LIMITED_METHOD_CONTRACT; return !HasHostAssembly(); }
+
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+    void SetFallbackLoadContextBinder(ICLRPrivBinder *pFallbackLoadContextBinder)
+    { 
+        LIMITED_METHOD_CONTRACT; 
+        m_pFallbackLoadContextBinder = pFallbackLoadContextBinder; 
+    }
+
+    ICLRPrivBinder *GetFallbackLoadContextBinder()
+    {
+        LIMITED_METHOD_CONTRACT;
+
+        return m_pFallbackLoadContextBinder;
+    }
+#endif //defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
 };  // class PEFile
 
 
index 0b601f36ce1bf2a89ec2bc8870c285b973094a48..356cb784232944a1281fed340025fde7aa737834 100644 (file)
@@ -1902,6 +1902,16 @@ DomainAssembly * LoadDomainAssembly(
         spec.SetParentAssembly(pRequestingAssembly->GetDomainAssembly());
     }
     
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)    
+    // If the requesting assembly has Fallback LoadContext binder available,
+    // then set it up in the AssemblySpec.
+    if (pRequestingAssembly != NULL)
+    {
+        PEFile *pRequestingAssemblyManifestFile = pRequestingAssembly->GetManifestFile();
+        spec.SetFallbackLoadContextBinderForRequestingAssembly(pRequestingAssemblyManifestFile->GetFallbackLoadContextBinder());
+    }
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+
     if (bThrowIfNotFound)
     {
         pDomainAssembly = spec.LoadDomainAssembly(FILE_LOADED);