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

Commit 2d2e766

Browse files
committed
Enable RefEmitted assemblies to have Fallback LoadContext notion
1 parent 4c5d72f commit 2d2e766

File tree

9 files changed

+200
-36
lines changed

9 files changed

+200
-36
lines changed

src/dlls/mscorrc/resource.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,3 +946,5 @@
946946
#endif // FEATURE_HOST_ASSEMBLY_RESOLVER
947947

948948
#define IDS_NATIVE_IMAGE_CANNOT_BE_LOADED_MULTIPLE_TIMES 0x263a
949+
950+

src/mscorlib/src/System/Runtime/Loader/AssemblyLoadContext.cs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -401,19 +401,26 @@ public static AssemblyLoadContext GetLoadContext(Assembly assembly)
401401
}
402402

403403
AssemblyLoadContext loadContextForAssembly = null;
404-
IntPtr ptrAssemblyLoadContext = GetLoadContextForAssembly((RuntimeAssembly)assembly);
405-
if (ptrAssemblyLoadContext == IntPtr.Zero)
406-
{
407-
// If the load context is returned null, then the assembly was bound using the TPA binder
408-
// and we shall return reference to the active "Default" binder - which could be the TPA binder
409-
// or an overridden CLRPrivBinderAssemblyLoadContext instance.
410-
loadContextForAssembly = AssemblyLoadContext.Default;
411-
}
412-
else
404+
405+
RuntimeAssembly rtAsm = assembly as RuntimeAssembly;
406+
407+
// We only support looking up load context for runtime assemblies.
408+
if (rtAsm != null)
413409
{
414-
loadContextForAssembly = (AssemblyLoadContext)(GCHandle.FromIntPtr(ptrAssemblyLoadContext).Target);
410+
IntPtr ptrAssemblyLoadContext = GetLoadContextForAssembly(rtAsm);
411+
if (ptrAssemblyLoadContext == IntPtr.Zero)
412+
{
413+
// If the load context is returned null, then the assembly was bound using the TPA binder
414+
// and we shall return reference to the active "Default" binder - which could be the TPA binder
415+
// or an overridden CLRPrivBinderAssemblyLoadContext instance.
416+
loadContextForAssembly = AssemblyLoadContext.Default;
417+
}
418+
else
419+
{
420+
loadContextForAssembly = (AssemblyLoadContext)(GCHandle.FromIntPtr(ptrAssemblyLoadContext).Target);
421+
}
415422
}
416-
423+
417424
return loadContextForAssembly;
418425
}
419426

src/vm/assembly.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,55 @@ Assembly *Assembly::CreateDynamic(AppDomain *pDomain, CreateDynamicAssemblyArgs
756756
name, &assemData, dwFlags,
757757
&ma));
758758
pFile = PEAssembly::Create(pCallerAssembly->GetManifestFile(), pAssemblyEmit, args->access & ASSEMBLY_ACCESS_REFLECTION_ONLY);
759+
760+
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
761+
// Dynamically created modules (aka RefEmit assemblies) do not have a LoadContext associated with them since they are not bound
762+
// using an actual binder. As a result, we will assume the same binding/loadcontext information for the dynamic assembly as its
763+
// caller/creator to ensure that any assembly loads triggered by the dynamic assembly are resolved using the intended load context.
764+
//
765+
// If the creator assembly has a HostAssembly associated with it, then use it for binding. Otherwise, ithe creator is dynamic
766+
// and will have a fallback load context binder associated with it.
767+
ICLRPrivBinder *pFallbackLoadContextBinder = nullptr;
768+
769+
// There is always a manifest file - wehther working with static or dynamic assemblies.
770+
PEFile *pCallerAssemblyManifestFile = pCallerAssembly->GetManifestFile();
771+
_ASSERTE(pCallerAssemblyManifestFile != NULL);
772+
773+
if (!pCallerAssemblyManifestFile->IsDynamic())
774+
{
775+
// Static assemblies with do not have fallback load context
776+
_ASSERTE(pCallerAssemblyManifestFile->GetFallbackLoadContextBinder() == nullptr);
777+
778+
if (pCallerAssemblyManifestFile->IsSystem())
779+
{
780+
// CoreLibrary is always bound to TPA binder
781+
pFallbackLoadContextBinder = pDomain->GetTPABinderContext();
782+
}
783+
else
784+
{
785+
// Fetch the binder from the host assembly
786+
PTR_ICLRPrivAssembly pCallerAssemblyHostAssembly = pCallerAssemblyManifestFile->GetHostAssembly();
787+
_ASSERTE(pCallerAssemblyHostAssembly != nullptr);
788+
789+
UINT_PTR assemblyBinderID = 0;
790+
IfFailThrow(pCallerAssemblyHostAssembly->GetBinderID(&assemblyBinderID));
791+
pFallbackLoadContextBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
792+
}
793+
}
794+
else
795+
{
796+
// Creator assembly is dynamic too, so use its fallback load context for the one
797+
// we are creating.
798+
pFallbackLoadContextBinder = pCallerAssemblyManifestFile->GetFallbackLoadContextBinder();
799+
}
800+
801+
// At this point, we should have a fallback load context binder to work with
802+
_ASSERTE(pFallbackLoadContextBinder != nullptr);
803+
804+
// Set it as the fallback load context binder for the dynamic assembly being created
805+
pFile->SetFallbackLoadContextBinder(pFallbackLoadContextBinder);
806+
#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
807+
759808
}
760809

761810
AssemblyLoadSecurity loadSecurity;

src/vm/assemblynative.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ FCIMPL9(Object*, AssemblyNative::Load, AssemblyNameBaseObject* assemblyNameUNSAF
179179
CheckPointHolder cph(pThread->m_MarshalAlloc.GetCheckpoint()); //hold checkpoint for autorelease
180180

181181
DomainAssembly * pParentAssembly = NULL;
182+
Assembly * pRefAssembly = NULL;
182183

183184
if(gc.assemblyName->GetSimpleName() == NULL)
184185
{
@@ -194,7 +195,6 @@ FCIMPL9(Object*, AssemblyNative::Load, AssemblyNameBaseObject* assemblyNameUNSAF
194195
gc.codeBase = NULL;
195196

196197
// Compute parent assembly
197-
Assembly * pRefAssembly;
198198
if (gc.requestingAssembly == NULL)
199199
{
200200
pRefAssembly = SystemDomain::GetCallersAssembly(stackMark);
@@ -241,7 +241,17 @@ FCIMPL9(Object*, AssemblyNative::Load, AssemblyNameBaseObject* assemblyNameUNSAF
241241

242242
if (pParentAssembly != NULL)
243243
spec.SetParentAssembly(pParentAssembly);
244-
244+
245+
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
246+
// If the requesting assembly has Fallback LoadContext binder available,
247+
// then set it up in the AssemblySpec.
248+
if (pRefAssembly != NULL)
249+
{
250+
PEFile *pRefAssemblyManifestFile = pRefAssembly->GetManifestFile();
251+
spec.SetFallbackLoadContextBinderForRequestingAssembly(pRefAssemblyManifestFile->GetFallbackLoadContextBinder());
252+
}
253+
#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
254+
245255
AssemblyLoadSecurity loadSecurity;
246256
loadSecurity.m_pAdditionalEvidence = &gc.security;
247257
loadSecurity.m_fCheckLoadFromRemoteSource = !!(gc.codeBase != NULL);
@@ -2552,20 +2562,26 @@ INT_PTR QCALLTYPE AssemblyNative::GetLoadContextForAssembly(QCall::AssemblyHandl
25522562
{
25532563
// Get the binding context for the assembly.
25542564
//
2565+
ICLRPrivBinder *pOpaqueBinder = nullptr;
2566+
AppDomain *pCurDomain = AppDomain::GetCurrentDomain();
2567+
CLRPrivBinderCoreCLR *pTPABinder = pCurDomain->GetTPABinderContext();
2568+
2569+
25552570
// GetBindingContext returns a ICLRPrivAssembly which can be used to get access to the
25562571
// actual ICLRPrivBinder instance in which the assembly was loaded.
25572572
PTR_ICLRPrivBinder pBindingContext = pPEAssembly->GetBindingContext();
25582573
UINT_PTR assemblyBinderID = 0;
25592574
IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID));
25602575

2561-
AppDomain *pCurDomain = AppDomain::GetCurrentDomain();
2562-
CLRPrivBinderCoreCLR *pTPABinder = pCurDomain->GetTPABinderContext();
2563-
25642576
// If the assembly was bound using the TPA binder,
25652577
// then we will return the reference to "Default" binder from the managed implementation when this QCall returns.
25662578
//
25672579
// See earlier comment about "Default" binder for additional context.
2568-
ICLRPrivBinder *pOpaqueBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
2580+
pOpaqueBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
2581+
2582+
// We should have a load context binder at this point.
2583+
_ASSERTE(pOpaqueBinder != nullptr);
2584+
25692585
if (!AreSameBinderInstance(pTPABinder, pOpaqueBinder))
25702586
{
25712587
// Only CLRPrivBinderAssemblyLoadContext instance contains the reference to its

src/vm/assemblyspec.cpp

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,26 +1256,40 @@ ICLRPrivBinder* AssemblySpec::GetBindingContextFromParentAssembly(AppDomain *pDo
12561256

12571257
// ICLRPrivAssembly implements ICLRPrivBinder and thus, "is a" binder in a manner of semantics.
12581258
pParentAssemblyBinder = pParentPEAssembly->GetBindingContext();
1259-
1259+
}
1260+
12601261
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
1261-
if (pParentAssemblyBinder != NULL)
1262+
if (pParentAssemblyBinder == NULL)
1263+
{
1264+
// If the parent assembly binder is not available, then we maybe dealing with one of the following
1265+
// assembly scenarios:
1266+
//
1267+
// 1) Domain Neutral assembly
1268+
// 2) RefEmitted assembly
1269+
// 3) Entrypoint assembly
1270+
//
1271+
// For (1) and (3), we will need to bind against the DefaultContext binder (aka TPA Binder). This happens
1272+
// below if we do not find the parent assembly binder.
1273+
//
1274+
// For (2), check if we have the fallback load context binder for the requesting dynamic assembly available.
1275+
1276+
pParentAssemblyBinder = GetFallbackLoadContextBinderForRequestingAssembly();
1277+
}
1278+
1279+
if (pParentAssemblyBinder != NULL)
1280+
{
1281+
CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext();
1282+
if (AreSameBinderInstance(pTPABinder, pParentAssemblyBinder))
12621283
{
1263-
CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext();
1264-
if (AreSameBinderInstance(pTPABinder, pParentAssemblyBinder))
1265-
{
1266-
// If the parent assembly is a platform (TPA) assembly, then its binding context will always be the TPABinder context. In
1267-
// such case, we will return the default context for binding to allow the bind to go
1268-
// via the custom binder context, if it was overridden. If it was not overridden, then we will get the expected
1269-
// TPABinder context anyways.
1270-
//
1271-
// Get the reference to the default binding context (this could be the TPABinder context or custom AssemblyLoadContext)
1272-
pParentAssemblyBinder = static_cast<ICLRPrivBinder*>(pDomain->GetFusionContext());
1273-
}
1284+
// If the parent assembly is a platform (TPA) assembly, then its binding context will always be the TPABinder context. In
1285+
// such case, we will return the default context for binding to allow the bind to go
1286+
// via the custom binder context, if it was overridden. If it was not overridden, then we will get the expected
1287+
// TPABinder context anyways.
1288+
//
1289+
// Get the reference to the default binding context (this could be the TPABinder context or custom AssemblyLoadContext)
1290+
pParentAssemblyBinder = static_cast<ICLRPrivBinder*>(pDomain->GetFusionContext());
12741291
}
1275-
#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
12761292
}
1277-
1278-
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
12791293

12801294
#if defined(FEATURE_COMINTEROP)
12811295
if (!IsContentType_WindowsRuntime() && (pParentAssemblyBinder != NULL))
@@ -1295,10 +1309,10 @@ ICLRPrivBinder* AssemblySpec::GetBindingContextFromParentAssembly(AppDomain *pDo
12951309

12961310
if (!pParentAssemblyBinder)
12971311
{
1298-
// We can be here when loading assemblies via the host (e.g. ICLRRuntimeHost2::ExecuteAssembly) or when attempting
1299-
// to load assemblies via custom AssemblyLoadContext implementation.
1312+
// We can be here when loading assemblies via the host (e.g. ICLRRuntimeHost2::ExecuteAssembly) or dealing with assemblies
1313+
// whose parent is a domain neutral assembly (see comment above for details).
13001314
//
1301-
// In such a case, the parent assembly (semantically) is mscorlib and thus, the default binding context should be
1315+
// In such a case, the parent assembly (semantically) is CoreLibrary and thus, the default binding context should be
13021316
// used as the parent assembly binder.
13031317
pParentAssemblyBinder = static_cast<ICLRPrivBinder*>(pDomain->GetFusionContext());
13041318
}

src/vm/assemblyspec.hpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ class AssemblySpec : public BaseAssemblySpec
4242
DWORD m_dwHashAlg;
4343
DomainAssembly *m_pParentAssembly;
4444

45+
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
46+
// Contains the reference to the fallback load context associated with RefEmitted assembly requesting the load of another assembly (static or dynamic)
47+
ICLRPrivBinder *m_pFallbackLoadContextBinder;
48+
#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
49+
4550
BOOL IsValidAssemblyName();
4651

4752
HRESULT InitializeSpecInternal(mdToken kAssemblyRefOrDef,
@@ -67,13 +72,23 @@ class AssemblySpec : public BaseAssemblySpec
6772
{
6873
LIMITED_METHOD_CONTRACT;
6974
m_pParentAssembly = NULL;
75+
76+
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
77+
m_pFallbackLoadContextBinder = NULL;
78+
#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
79+
7080
}
7181
#endif //!DACCESS_COMPILE
7282

7383
AssemblySpec(AppDomain *pAppDomain) : m_pAppDomain(pAppDomain)
7484
{
7585
LIMITED_METHOD_CONTRACT
7686
m_pParentAssembly = NULL;
87+
88+
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
89+
m_pFallbackLoadContextBinder = NULL;
90+
#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
91+
7792
}
7893

7994
#ifdef FEATURE_FUSION
@@ -158,6 +173,22 @@ class AssemblySpec : public BaseAssemblySpec
158173
#endif
159174
}
160175

176+
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
177+
void SetFallbackLoadContextBinderForRequestingAssembly(ICLRPrivBinder *pFallbackLoadContextBinder)
178+
{
179+
LIMITED_METHOD_CONTRACT;
180+
181+
m_pFallbackLoadContextBinder = pFallbackLoadContextBinder;
182+
}
183+
184+
ICLRPrivBinder* GetFallbackLoadContextBinderForRequestingAssembly()
185+
{
186+
LIMITED_METHOD_CONTRACT;
187+
188+
return m_pFallbackLoadContextBinder;
189+
}
190+
#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
191+
161192
// Note that this method does not clone the fields!
162193
void CopyFrom(AssemblySpec* pSource)
163194
{
@@ -173,6 +204,12 @@ class AssemblySpec : public BaseAssemblySpec
173204

174205
SetIntrospectionOnly(pSource->IsIntrospectionOnly());
175206
SetParentAssembly(pSource->GetParentAssembly());
207+
208+
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
209+
// Copy the details of the fallback load context binder
210+
SetFallbackLoadContextBinderForRequestingAssembly(pSource->GetFallbackLoadContextBinderForRequestingAssembly());
211+
#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
212+
176213
m_HashForControl = pSource->m_HashForControl;
177214
m_dwHashAlg = pSource->m_dwHashAlg;
178215
}

src/vm/pefile.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ PEFile::PEFile(PEImage *identity, BOOL fCheckAuthenticodeSignature/*=TRUE*/) :
9999
,m_securityManagerLock(CrstPEFileSecurityManager)
100100
#endif // FEATURE_CAS_POLICY
101101
,m_pHostAssembly(nullptr)
102+
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
103+
,m_pFallbackLoadContextBinder(nullptr)
104+
#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
102105
{
103106
CONTRACTL
104107
{

src/vm/pefile.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,17 @@ class PEFile
659659
protected:
660660
PTR_ICLRPrivAssembly m_pHostAssembly;
661661

662+
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
663+
// For certain assemblies, we do not have m_pHostAssembly since they are not bound using an actual binder.
664+
// An example is Ref-Emitted assemblies. Thus, when such assemblies trigger load of their dependencies,
665+
// we need to ensure they are loaded in appropriate load context.
666+
//
667+
// To enable this, we maintain a concept of "Fallback LoadContext", which will be set to the Binder of the
668+
// assembly that created the dynamic assembly. If the creator assembly is dynamic itself, then its fallback
669+
// load context would be propagated to the assembly being dynamically generated.
670+
ICLRPrivBinder *m_pFallbackLoadContextBinder;
671+
#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
672+
662673
protected:
663674

664675
friend class CLRPrivBinderFusion;
@@ -684,6 +695,21 @@ class PEFile
684695

685696
bool CanUseWithBindingCache()
686697
{ LIMITED_METHOD_CONTRACT; return !HasHostAssembly(); }
698+
699+
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
700+
void SetFallbackLoadContextBinder(ICLRPrivBinder *pFallbackLoadContextBinder)
701+
{
702+
LIMITED_METHOD_CONTRACT;
703+
m_pFallbackLoadContextBinder = pFallbackLoadContextBinder;
704+
}
705+
706+
ICLRPrivBinder *GetFallbackLoadContextBinder()
707+
{
708+
LIMITED_METHOD_CONTRACT;
709+
710+
return m_pFallbackLoadContextBinder;
711+
}
712+
#endif //defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
687713
}; // class PEFile
688714

689715

src/vm/typeparse.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1902,6 +1902,16 @@ DomainAssembly * LoadDomainAssembly(
19021902
spec.SetParentAssembly(pRequestingAssembly->GetDomainAssembly());
19031903
}
19041904

1905+
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
1906+
// If the requesting assembly has Fallback LoadContext binder available,
1907+
// then set it up in the AssemblySpec.
1908+
if (pRequestingAssembly != NULL)
1909+
{
1910+
PEFile *pRequestingAssemblyManifestFile = pRequestingAssembly->GetManifestFile();
1911+
spec.SetFallbackLoadContextBinderForRequestingAssembly(pRequestingAssemblyManifestFile->GetFallbackLoadContextBinder());
1912+
}
1913+
#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
1914+
19051915
if (bThrowIfNotFound)
19061916
{
19071917
pDomainAssembly = spec.LoadDomainAssembly(FILE_LOADED);

0 commit comments

Comments
 (0)