summaryrefslogtreecommitdiff
path: root/src/vm/assemblyspec.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/assemblyspec.cpp')
-rw-r--r--src/vm/assemblyspec.cpp2473
1 files changed, 2473 insertions, 0 deletions
diff --git a/src/vm/assemblyspec.cpp b/src/vm/assemblyspec.cpp
new file mode 100644
index 0000000000..2b4b1fb480
--- /dev/null
+++ b/src/vm/assemblyspec.cpp
@@ -0,0 +1,2473 @@
+// 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.
+
+/*============================================================
+**
+** Header: AssemblySpec.cpp
+**
+** Purpose: Implements Assembly binding class
+**
+**
+
+
+**
+===========================================================*/
+
+#include "common.h"
+
+#include <stdlib.h>
+
+#ifdef FEATURE_FUSION
+#include "actasm.h"
+#include "appctx.h"
+#endif
+#include "assemblyspec.hpp"
+#include "security.h"
+#include "eeconfig.h"
+#include "strongname.h"
+#include "strongnameholders.h"
+#ifdef FEATURE_FUSION
+#include "assemblysink.h"
+#include "dbglog.h"
+#include "bindinglog.hpp"
+#include "assemblyfilehash.h"
+#endif
+#include "mdaassistants.h"
+#include "eventtrace.h"
+
+#ifdef FEATURE_COMINTEROP
+#include "clrprivbinderutil.h"
+#include "winrthelpers.h"
+#endif
+
+#ifdef _DEBUG
+// This debug-only wrapper for LookupAssembly is solely for the use of postconditions and
+// assertions. The problem is that the real LookupAssembly can throw an OOM
+// simply because it can't allocate scratch space. For the sake of asserting,
+// we can treat those as successful lookups.
+BOOL UnsafeVerifyLookupAssembly(AssemblySpecBindingCache *pCache, AssemblySpec *pSpec, DomainAssembly *pComparator)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ BOOL result = FALSE;
+
+ EX_TRY
+ {
+ SCAN_IGNORE_FAULT; // Won't go away: This wrapper exists precisely to turn an OOM here into something our postconditions can deal with.
+ result = (pComparator == pCache->LookupAssembly(pSpec));
+ }
+ EX_CATCH
+ {
+ Exception *ex = GET_EXCEPTION();
+
+ result = ex->IsTransient();
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+
+ return result;
+
+}
+#endif
+
+#ifdef _DEBUG
+// This debug-only wrapper for LookupFile is solely for the use of postconditions and
+// assertions. The problem is that the real LookupFile can throw an OOM
+// simply because it can't allocate scratch space. For the sake of asserting,
+// we can treat those as successful lookups.
+BOOL UnsafeVerifyLookupFile(AssemblySpecBindingCache *pCache, AssemblySpec *pSpec, PEAssembly *pComparator)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ BOOL result = FALSE;
+
+ EX_TRY
+ {
+ SCAN_IGNORE_FAULT; // Won't go away: This wrapper exists precisely to turn an OOM here into something our postconditions can deal with.
+ result = pCache->LookupFile(pSpec)->Equals(pComparator);
+ }
+ EX_CATCH
+ {
+ Exception *ex = GET_EXCEPTION();
+
+ result = ex->IsTransient();
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+
+ return result;
+
+}
+
+#endif
+
+#ifdef _DEBUG
+
+// This debug-only wrapper for Contains is solely for the use of postconditions and
+// assertions. The problem is that the real Contains can throw an OOM
+// simply because it can't allocate scratch space. For the sake of asserting,
+// we can treat those as successful lookups.
+BOOL UnsafeContains(AssemblySpecBindingCache *pCache, AssemblySpec *pSpec)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ BOOL result = FALSE;
+
+ EX_TRY
+ {
+ SCAN_IGNORE_FAULT; // Won't go away: This wrapper exists precisely to turn an OOM here into something our postconditions can deal with.
+ result = pCache->Contains(pSpec);
+ }
+ EX_CATCH
+ {
+ Exception *ex = GET_EXCEPTION();
+
+ result = ex->IsTransient();
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+
+ return result;
+
+}
+#endif
+
+
+
+AssemblySpecHash::~AssemblySpecHash()
+{
+ CONTRACTL
+ {
+ DESTRUCTOR_CHECK;
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ PtrHashMap::PtrIterator i = m_map.begin();
+ while (!i.end())
+ {
+ AssemblySpec *s = (AssemblySpec*) i.GetValue();
+ if (m_pHeap != NULL)
+ s->~AssemblySpec();
+ else
+ delete s;
+
+ ++i;
+ }
+}
+
+// Check assembly name for invalid characters
+// Return value:
+// TRUE: If no invalid characters were found, or if the assembly name isn't set
+// FALSE: If invalid characters were found
+// This is needed to prevent security loopholes with ':', '/' and '\' in the assembly name
+BOOL AssemblySpec::IsValidAssemblyName()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ }
+ CONTRACTL_END;
+
+ if (GetName())
+ {
+ SString ssAssemblyName(SString::Utf8, GetName());
+ for (SString::Iterator i = ssAssemblyName.Begin(); i[0] != W('\0'); i++) {
+ switch (i[0]) {
+ case W(':'):
+ case W('\\'):
+ case W('/'):
+ return FALSE;
+
+ default:
+ break;
+ }
+ }
+ }
+ return TRUE;
+}
+
+HRESULT AssemblySpec::InitializeSpecInternal(mdToken kAssemblyToken,
+ IMDInternalImport *pImport,
+ DomainAssembly *pStaticParent,
+ BOOL fIntrospectionOnly,
+ BOOL fAllowAllocation)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ if (fAllowAllocation) {GC_TRIGGERS;} else {GC_NOTRIGGER;};
+ if (fAllowAllocation) {INJECT_FAULT(COMPlusThrowOM());} else {FORBID_FAULT;};
+ NOTHROW;
+ MODE_ANY;
+ PRECONDITION(pImport->IsValidToken(kAssemblyToken));
+ PRECONDITION(TypeFromToken(kAssemblyToken) == mdtAssembly
+ || TypeFromToken(kAssemblyToken) == mdtAssemblyRef);
+ PRECONDITION(pStaticParent == NULL || !(pStaticParent->IsIntrospectionOnly() && !fIntrospectionOnly)); //Something's wrong if an introspection assembly loads an assembly for execution.
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ // We also did this check as a precondition as we should have prevented this structurally - but just
+ // in case, make sure retail stops us from proceeding further.
+ if (pStaticParent != NULL && pStaticParent->IsIntrospectionOnly() && !fIntrospectionOnly)
+ {
+ EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
+ }
+
+ // Normalize this boolean as it tends to be used for comparisons
+ m_fIntrospectionOnly = !!fIntrospectionOnly;
+
+ IfFailThrow(BaseAssemblySpec::Init(kAssemblyToken,pImport));
+
+ if (IsContentType_WindowsRuntime())
+ {
+ if (!fAllowAllocation)
+ { // We don't support this because we must be able to allocate in order to
+ // extract embedded type names for the native image scenario. Currently,
+ // the only caller of this method with fAllowAllocation == FALSE is
+ // Module::GetAssemblyIfLoaded, and since this method will only check the
+ // assembly spec cache, and since we can't cache WinRT assemblies, this
+ // limitation should have no negative impact.
+ IfFailThrow(E_FAIL);
+ }
+
+ // Extract embedded content, if present (currently used for embedded WinRT type names).
+ ParseEncodedName();
+ }
+
+ // For static binds, we cannot reference a weakly named assembly from a strong named one.
+ // (Note that this constraint doesn't apply to dynamic binds which is why this check is
+ // not farther down the stack.)
+ if (pStaticParent != NULL)
+ {
+ // We dont validate this for CoreCLR as there is no good use-case for this scenario.
+#if !defined(FEATURE_CORECLR)
+ // It is OK for signed assemblies to reference WinRT assemblies (.winmd files) that are not signed
+ if (!IsContentType_WindowsRuntime() && pStaticParent->GetFile()->IsStrongNamed() && !IsStrongNamed())
+ {
+ ThrowHR(FUSION_E_PRIVATE_ASM_DISALLOWED);
+ }
+#endif // !defined(FEATURE_CORECLR)
+
+ SetParentAssembly(pStaticParent);
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+
+ return hr;
+} // AssemblySpec::InitializeSpecInternal
+
+#ifdef FEATURE_FUSION
+void AssemblySpec::InitializeSpec(IAssemblyName *pName,
+ DomainAssembly *pStaticParent /*=NULL*/ ,
+ BOOL fIntrospectionOnly /*=FALSE*/ )
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ GC_TRIGGERS;
+ THROWS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // Normalize this boolean as it tends to be used for comparisons
+ m_fIntrospectionOnly = !!fIntrospectionOnly;
+ IfFailThrow(Init(pName));
+
+ // For static binds, we cannot reference a strongly named assembly from a weakly named one.
+ // (Note that this constraint doesn't apply to dynamic binds which is why this check is
+ // not farther down the stack.)
+
+ if (pStaticParent != NULL) {
+ if (pStaticParent->GetFile()->IsStrongNamed() && !IsStrongNamed())
+ {
+ EEFileLoadException::Throw(this, FUSION_E_PRIVATE_ASM_DISALLOWED);
+ }
+ SetParentAssembly(pStaticParent);
+ }
+
+ // Extract embedded WinRT name, if present.
+ ParseEncodedName();
+}
+#endif //FEATURE_FUSION
+
+#ifdef FEATURE_MIXEDMODE
+void AssemblySpec::InitializeSpec(HMODULE hMod,
+ BOOL fIntrospectionOnly /*=FALSE*/)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ GC_TRIGGERS;
+ THROWS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // Normalize this boolean as it tends to be used for comparisons
+ m_fIntrospectionOnly = !!fIntrospectionOnly;
+
+ PEDecoder pe(hMod);
+
+ if (!pe.CheckILFormat())
+ {
+ StackSString path;
+ PEImage::GetPathFromDll(hMod, path);
+ EEFileLoadException::Throw(path, COR_E_BADIMAGEFORMAT);
+ }
+
+ COUNT_T size;
+ const void *data = pe.GetMetadata(&size);
+ SafeComHolder<IMDInternalImport> pImport;
+ IfFailThrow(GetMetaDataInternalInterface((void *) data, size, ofRead,
+ IID_IMDInternalImport,
+ (void **) &pImport));
+
+ mdAssembly a;
+ if (FAILED(pImport->GetAssemblyFromScope(&a)))
+ ThrowHR(COR_E_ASSEMBLYEXPECTED);
+
+ InitializeSpec(a, pImport, NULL, fIntrospectionOnly);
+}
+#endif //FEATURE_MIXEDMODE
+
+void AssemblySpec::InitializeSpec(PEAssembly * pFile)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pFile));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+ ReleaseHolder<IMDInternalImport> pImport(pFile->GetMDImportWithRef());
+ mdAssembly a;
+ IfFailThrow(pImport->GetAssemblyFromScope(&a));
+
+ InitializeSpec(a, pImport, NULL, pFile->IsIntrospectionOnly());
+
+#ifdef FEATURE_COMINTEROP
+ if (IsContentType_WindowsRuntime())
+ {
+ LPCSTR szNamespace;
+ LPCSTR szTypeName;
+ SString ssFakeNameSpaceAllocationBuffer;
+ IfFailThrow(::GetFirstWinRTTypeDef(pImport, &szNamespace, &szTypeName, pFile->GetPath(), &ssFakeNameSpaceAllocationBuffer));
+
+ SetWindowsRuntimeType(szNamespace, szTypeName);
+
+ // pFile is not guaranteed to stay around (it might be unloaded with the AppDomain), we have to copy the type name
+ CloneFields(WINRT_TYPE_NAME_OWNED);
+ }
+#endif //FEATURE_COMINTEROP
+
+#if defined(FEATURE_CORECLR)
+ // Set the binding context for the AssemblySpec
+ ICLRPrivBinder* pCurrentBinder = GetBindingContext();
+ ICLRPrivBinder* pExpectedBinder = pFile->GetBindingContext();
+ if (pCurrentBinder == NULL)
+ {
+ // We should aways having the binding context in the PEAssembly. The only exception to this are the following:
+ //
+ // 1) when we are here during EEStartup and loading mscorlib.dll.
+ // 2) We are dealing with dynamic assemblies
+ _ASSERTE((pExpectedBinder != NULL) || pFile->IsSystem() || pFile->IsDynamic());
+ SetBindingContext(pExpectedBinder);
+ }
+#endif // defined(FEATURE_CORECLR)
+}
+
+#ifndef CROSSGEN_COMPILE
+
+// This uses thread storage to allocate space. Please use Checkpoint and release it.
+#ifdef FEATURE_FUSION
+HRESULT AssemblySpec::InitializeSpec(StackingAllocator* alloc, ASSEMBLYNAMEREF* pName,
+ BOOL fParse /*=TRUE*/, BOOL fIntrospectionOnly /*=FALSE*/)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(alloc));
+ PRECONDITION(CheckPointer(pName));
+ PRECONDITION(IsProtectedByGCFrame(pName));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // Simple name
+ if ((*pName)->GetSimpleName() != NULL) {
+ WCHAR* pString;
+ int iString;
+ ((STRINGREF) (*pName)->GetSimpleName())->RefInterpretGetStringValuesDangerousForGC(&pString, &iString);
+ DWORD lgth = WszWideCharToMultiByte(CP_UTF8, 0, pString, iString, NULL, 0, NULL, NULL);
+ if (lgth + 1 < lgth)
+ ThrowHR(E_INVALIDARG);
+ LPSTR lpName = (LPSTR) alloc->Alloc(S_UINT32(lgth) + S_UINT32(1));
+ WszWideCharToMultiByte(CP_UTF8, 0, pString, iString,
+ lpName, lgth+1, NULL, NULL);
+ lpName[lgth] = '\0';
+ m_pAssemblyName = lpName;
+ }
+
+ if (fParse) {
+ HRESULT hr = ParseName();
+ // Sometimes Fusion flags invalid characters in the name, sometimes it doesn't
+ // depending on where the invalid characters are
+ // We want to Raise the assembly resolve event on all invalid characters
+ // but calling ParseName before checking for invalid characters gives Fusion a chance to
+ // parse the rest of the name (to get a public key token, etc.)
+ if ((hr == FUSION_E_INVALID_NAME) || (!IsValidAssemblyName())) {
+ // This is the only case where we do not throw on an error
+ // We don't want to throw so as to give the caller a chance to call RaiseAssemblyResolveEvent
+ // The only caller that cares is System.Reflection.Assembly.InternalLoad which calls us through
+ // AssemblyNameNative::Init
+ return FUSION_E_INVALID_NAME;
+ }
+ else
+ IfFailThrow(hr);
+ }
+ else {
+ // Flags
+ m_dwFlags = (*pName)->GetFlags();
+
+ // Version
+ VERSIONREF version = (VERSIONREF) (*pName)->GetVersion();
+ if(version == NULL) {
+ m_context.usMajorVersion = (USHORT)-1;
+ m_context.usMinorVersion = (USHORT)-1;
+ m_context.usBuildNumber = (USHORT)-1;
+ m_context.usRevisionNumber = (USHORT)-1;
+ }
+ else {
+ m_context.usMajorVersion = (USHORT)version->GetMajor();
+ m_context.usMinorVersion = (USHORT)version->GetMinor();
+ m_context.usBuildNumber = (USHORT)version->GetBuild();
+ m_context.usRevisionNumber = (USHORT)version->GetRevision();
+ }
+
+ m_context.szLocale = 0;
+
+ if ((*pName)->GetCultureInfo() != NULL)
+ {
+ struct _gc {
+ OBJECTREF cultureinfo;
+ STRINGREF pString;
+ } gc;
+
+ gc.cultureinfo = (*pName)->GetCultureInfo();
+ gc.pString = NULL;
+
+ GCPROTECT_BEGIN(gc);
+
+ MethodDescCallSite getName(METHOD__CULTURE_INFO__GET_NAME, &gc.cultureinfo);
+
+ ARG_SLOT args[] = {
+ ObjToArgSlot(gc.cultureinfo)
+ };
+ gc.pString = getName.Call_RetSTRINGREF(args);
+ if (gc.pString != NULL) {
+ WCHAR* pString;
+ int iString;
+ gc.pString->RefInterpretGetStringValuesDangerousForGC(&pString, &iString);
+ DWORD lgth = WszWideCharToMultiByte(CP_UTF8, 0, pString, iString, NULL, 0, NULL, NULL);
+ LPSTR lpLocale = (LPSTR) alloc->Alloc(S_UINT32(lgth) + S_UINT32(1));
+ WszWideCharToMultiByte(CP_UTF8, 0, pString, iString,
+ lpLocale, lgth+1, NULL, NULL);
+ lpLocale[lgth] = '\0';
+ m_context.szLocale = lpLocale;
+ }
+ GCPROTECT_END();
+ }
+
+ // Strong name
+ // Note that we prefer to take a public key token if present,
+ // even if flags indicate a full public key
+ if ((*pName)->GetPublicKeyToken() != NULL) {
+ m_dwFlags &= ~afPublicKey;
+ PBYTE pArray = NULL;
+ pArray = (*pName)->GetPublicKeyToken()->GetDirectPointerToNonObjectElements();
+ m_cbPublicKeyOrToken = (*pName)->GetPublicKeyToken()->GetNumComponents();
+ m_pbPublicKeyOrToken = new (alloc) BYTE[m_cbPublicKeyOrToken];
+ memcpy(m_pbPublicKeyOrToken, pArray, m_cbPublicKeyOrToken);
+ }
+ else if ((*pName)->GetPublicKey() != NULL) {
+ m_dwFlags |= afPublicKey;
+ PBYTE pArray = NULL;
+ pArray = (*pName)->GetPublicKey()->GetDirectPointerToNonObjectElements();
+ m_cbPublicKeyOrToken = (*pName)->GetPublicKey()->GetNumComponents();
+ m_pbPublicKeyOrToken = new (alloc) BYTE[m_cbPublicKeyOrToken];
+ memcpy(m_pbPublicKeyOrToken, pArray, m_cbPublicKeyOrToken);
+ }
+ }
+
+ // Hash for control
+ // <TODO>@TODO cts, can we use unsafe in this case!!!</TODO>
+ if ((*pName)->GetHashForControl() != NULL)
+ SetHashForControl((*pName)->GetHashForControl()->GetDataPtr(),
+ (*pName)->GetHashForControl()->GetNumComponents(),
+ (*pName)->GetHashAlgorithmForControl());
+
+ // Normalize this boolean as it tends to be used for comparisons
+ m_fIntrospectionOnly = !!fIntrospectionOnly;
+
+ // Extract embedded WinRT name, if present.
+ ParseEncodedName();
+
+ return S_OK;
+}
+
+#else // FEATURE_FUSION
+HRESULT AssemblySpec::InitializeSpec(StackingAllocator* alloc, ASSEMBLYNAMEREF* pName,
+ BOOL fParse /*=TRUE*/, BOOL fIntrospectionOnly /*=FALSE*/)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(alloc));
+ PRECONDITION(CheckPointer(pName));
+ PRECONDITION(IsProtectedByGCFrame(pName));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // Simple name
+ if ((*pName)->GetSimpleName() != NULL) {
+ WCHAR* pString;
+ int iString;
+ ((STRINGREF) (*pName)->GetSimpleName())->RefInterpretGetStringValuesDangerousForGC(&pString, &iString);
+ DWORD lgth = WszWideCharToMultiByte(CP_UTF8, 0, pString, iString, NULL, 0, NULL, NULL);
+ if (lgth + 1 < lgth)
+ ThrowHR(E_INVALIDARG);
+ LPSTR lpName = (LPSTR) alloc->Alloc(S_UINT32(lgth) + S_UINT32(1));
+ WszWideCharToMultiByte(CP_UTF8, 0, pString, iString,
+ lpName, lgth+1, NULL, NULL);
+ lpName[lgth] = '\0';
+ // Calling Init here will trash the cached lpName in AssemblySpec, but lpName is still needed by ParseName
+ // call below.
+ SetName(lpName);
+ }
+ else
+ {
+ // Ensure we always have an assembly simple name.
+ LPSTR lpName = (LPSTR) alloc->Alloc(S_UINT32(1));
+ lpName[0] = '\0';
+ SetName(lpName);
+ }
+
+ if (fParse) {
+ HRESULT hr = ParseName();
+ // Sometimes Fusion flags invalid characters in the name, sometimes it doesn't
+ // depending on where the invalid characters are
+ // We want to Raise the assembly resolve event on all invalid characters
+ // but calling ParseName before checking for invalid characters gives Fusion a chance to
+ // parse the rest of the name (to get a public key token, etc.)
+ if ((hr == FUSION_E_INVALID_NAME) || (!IsValidAssemblyName())) {
+ // This is the only case where we do not throw on an error
+ // We don't want to throw so as to give the caller a chance to call RaiseAssemblyResolveEvent
+ // The only caller that cares is System.Reflection.Assembly.InternalLoad which calls us through
+ // AssemblyNameNative::Init
+ return FUSION_E_INVALID_NAME;
+ }
+ else
+ IfFailThrow(hr);
+ }
+ else {
+ AssemblyMetaDataInternal asmInfo;
+ // Flags
+ DWORD dwFlags = (*pName)->GetFlags();
+
+ // Version
+ VERSIONREF version = (VERSIONREF) (*pName)->GetVersion();
+ if(version == NULL) {
+ asmInfo.usMajorVersion = (USHORT)-1;
+ asmInfo.usMinorVersion = (USHORT)-1;
+ asmInfo.usBuildNumber = (USHORT)-1;
+ asmInfo.usRevisionNumber = (USHORT)-1;
+ }
+ else {
+ asmInfo.usMajorVersion = (USHORT)version->GetMajor();
+ asmInfo.usMinorVersion = (USHORT)version->GetMinor();
+ asmInfo.usBuildNumber = (USHORT)version->GetBuild();
+ asmInfo.usRevisionNumber = (USHORT)version->GetRevision();
+ }
+
+ asmInfo.szLocale = 0;
+ asmInfo.ulOS = 0;
+ asmInfo.rOS = 0;
+ asmInfo.ulProcessor = 0;
+ asmInfo.rProcessor = 0;
+
+ if ((*pName)->GetCultureInfo() != NULL)
+ {
+ struct _gc {
+ OBJECTREF cultureinfo;
+ STRINGREF pString;
+ } gc;
+
+ gc.cultureinfo = (*pName)->GetCultureInfo();
+ gc.pString = NULL;
+
+ GCPROTECT_BEGIN(gc);
+
+ MethodDescCallSite getName(METHOD__CULTURE_INFO__GET_NAME, &gc.cultureinfo);
+
+ ARG_SLOT args[] = {
+ ObjToArgSlot(gc.cultureinfo)
+ };
+ gc.pString = getName.Call_RetSTRINGREF(args);
+ if (gc.pString != NULL) {
+ WCHAR* pString;
+ int iString;
+ gc.pString->RefInterpretGetStringValuesDangerousForGC(&pString, &iString);
+ DWORD lgth = WszWideCharToMultiByte(CP_UTF8, 0, pString, iString, NULL, 0, NULL, NULL);
+ S_UINT32 lengthWillNull = S_UINT32(lgth) + S_UINT32(1);
+ LPSTR lpLocale = (LPSTR) alloc->Alloc(lengthWillNull);
+ if (lengthWillNull.IsOverflow())
+ {
+ COMPlusThrowHR(COR_E_OVERFLOW);
+ }
+ WszWideCharToMultiByte(CP_UTF8, 0, pString, iString,
+ lpLocale, lengthWillNull.Value(), NULL, NULL);
+ lpLocale[lgth] = '\0';
+ asmInfo.szLocale = lpLocale;
+ }
+ GCPROTECT_END();
+ }
+
+ // Strong name
+ DWORD cbPublicKeyOrToken=0;
+ BYTE* pbPublicKeyOrToken=NULL;
+ // Note that we prefer to take a public key token if present,
+ // even if flags indicate a full public key
+ if ((*pName)->GetPublicKeyToken() != NULL) {
+ dwFlags &= ~afPublicKey;
+ PBYTE pArray = NULL;
+ pArray = (*pName)->GetPublicKeyToken()->GetDirectPointerToNonObjectElements();
+ cbPublicKeyOrToken = (*pName)->GetPublicKeyToken()->GetNumComponents();
+ pbPublicKeyOrToken = pArray;
+ }
+ else if ((*pName)->GetPublicKey() != NULL) {
+ dwFlags |= afPublicKey;
+ PBYTE pArray = NULL;
+ pArray = (*pName)->GetPublicKey()->GetDirectPointerToNonObjectElements();
+ cbPublicKeyOrToken = (*pName)->GetPublicKey()->GetNumComponents();
+ pbPublicKeyOrToken = pArray;
+ }
+ BaseAssemblySpec::Init(GetName(),&asmInfo,pbPublicKeyOrToken,cbPublicKeyOrToken,dwFlags);
+ }
+
+ CloneFieldsToStackingAllocator(alloc);
+
+ // Hash for control
+ // <TODO>@TODO cts, can we use unsafe in this case!!!</TODO>
+ if ((*pName)->GetHashForControl() != NULL)
+ SetHashForControl((*pName)->GetHashForControl()->GetDataPtr(),
+ (*pName)->GetHashForControl()->GetNumComponents(),
+ (*pName)->GetHashAlgorithmForControl());
+
+ // Normalize this boolean as it tends to be used for comparisons
+ m_fIntrospectionOnly = !!fIntrospectionOnly;
+
+ // Extract embedded WinRT name, if present.
+ ParseEncodedName();
+
+ return S_OK;
+}
+#endif // FEATURE_FUSION
+
+void AssemblySpec::AssemblyNameInit(ASSEMBLYNAMEREF* pAsmName, PEImage* pImageInfo)
+{
+ CONTRACTL
+ {
+ THROWS;
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ PRECONDITION(IsProtectedByGCFrame (pAsmName));
+ }
+ CONTRACTL_END;
+
+ struct _gc {
+ OBJECTREF CultureInfo;
+ STRINGREF Locale;
+ OBJECTREF Version;
+ U1ARRAYREF PublicKeyOrToken;
+ STRINGREF Name;
+ STRINGREF CodeBase;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ GCPROTECT_BEGIN(gc);
+
+ if ((m_context.usMajorVersion != (USHORT) -1) &&
+ (m_context.usMinorVersion != (USHORT) -1)) {
+
+ MethodTable* pVersion = MscorlibBinder::GetClass(CLASS__VERSION);
+
+ // version
+ gc.Version = AllocateObject(pVersion);
+
+
+ MethodDescCallSite ctorMethod(METHOD__VERSION__CTOR);
+
+ ARG_SLOT VersionArgs[5] =
+ {
+ ObjToArgSlot(gc.Version),
+ (ARG_SLOT) m_context.usMajorVersion,
+ (ARG_SLOT) m_context.usMinorVersion,
+ (ARG_SLOT) m_context.usBuildNumber,
+ (ARG_SLOT) m_context.usRevisionNumber,
+ };
+ ctorMethod.Call(VersionArgs);
+ }
+
+ // cultureinfo
+ if (m_context.szLocale) {
+
+ MethodTable* pCI = MscorlibBinder::GetClass(CLASS__CULTURE_INFO);
+ gc.CultureInfo = AllocateObject(pCI);
+
+ gc.Locale = StringObject::NewString(m_context.szLocale);
+
+ MethodDescCallSite strCtor(METHOD__CULTURE_INFO__STR_CTOR);
+
+ ARG_SLOT args[2] =
+ {
+ ObjToArgSlot(gc.CultureInfo),
+ ObjToArgSlot(gc.Locale)
+ };
+
+ strCtor.Call(args);
+ }
+
+
+ // public key or token byte array
+ if (m_pbPublicKeyOrToken)
+ Security::CopyEncodingToByteArray((BYTE*) m_pbPublicKeyOrToken,
+ m_cbPublicKeyOrToken,
+ (OBJECTREF*) &gc.PublicKeyOrToken);
+
+ // simple name
+ if(GetName())
+ gc.Name = StringObject::NewString(GetName());
+
+ if (GetCodeBase())
+ gc.CodeBase = StringObject::NewString(GetCodeBase());
+
+ BOOL fPublicKey = m_dwFlags & afPublicKey;
+
+ ULONG hashAlgId=0;
+ if (pImageInfo != NULL)
+ {
+ if(!pImageInfo->GetMDImport()->IsValidToken(TokenFromRid(1, mdtAssembly)))
+ {
+ ThrowHR(COR_E_BADIMAGEFORMAT);
+ }
+ IfFailThrow(pImageInfo->GetMDImport()->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, &hashAlgId, NULL, NULL, NULL));
+ }
+
+ MethodDescCallSite init(METHOD__ASSEMBLY_NAME__INIT);
+
+ ARG_SLOT MethodArgs[] =
+ {
+ ObjToArgSlot(*pAsmName),
+ ObjToArgSlot(gc.Name),
+ fPublicKey ? ObjToArgSlot(gc.PublicKeyOrToken) :
+ (ARG_SLOT) NULL, // public key
+ fPublicKey ? (ARG_SLOT) NULL :
+ ObjToArgSlot(gc.PublicKeyOrToken), // public key token
+ ObjToArgSlot(gc.Version),
+ ObjToArgSlot(gc.CultureInfo),
+ (ARG_SLOT) hashAlgId,
+ (ARG_SLOT) 1, // AssemblyVersionCompatibility.SameMachine
+ ObjToArgSlot(gc.CodeBase),
+ (ARG_SLOT) m_dwFlags,
+ (ARG_SLOT) NULL // key pair
+ };
+
+ init.Call(MethodArgs);
+
+ // Only set the processor architecture if we're looking at a newer binary that has
+ // that information in the PE, and we're not looking at a reference assembly.
+ if(pImageInfo && !pImageInfo->HasV1Metadata() && !pImageInfo->IsReferenceAssembly())
+ {
+ DWORD dwMachine, dwKind;
+
+ pImageInfo->GetPEKindAndMachine(&dwMachine,&dwKind);
+
+ MethodDescCallSite setPA(METHOD__ASSEMBLY_NAME__SET_PROC_ARCH_INDEX);
+
+ ARG_SLOT PAMethodArgs[] = {
+ ObjToArgSlot(*pAsmName),
+ (ARG_SLOT)dwMachine,
+ (ARG_SLOT)dwKind
+ };
+
+ setPA.Call(PAMethodArgs);
+ }
+
+ GCPROTECT_END();
+}
+
+// This uses thread storage to allocate space. Please use Checkpoint and release it.
+void AssemblySpec::SetCodeBase(StackingAllocator* alloc, STRINGREF *pCodeBase)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pCodeBase));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // Codebase
+ if (pCodeBase != NULL && *pCodeBase != NULL) {
+ WCHAR* pString;
+ int iString;
+ (*pCodeBase)->RefInterpretGetStringValuesDangerousForGC(&pString, &iString);
+
+ DWORD dwCodeBase = (DWORD) iString+1;
+ m_wszCodeBase = new (alloc) WCHAR[dwCodeBase];
+ memcpy((void*)m_wszCodeBase, pString, dwCodeBase * sizeof(WCHAR));
+ }
+}
+
+#endif // CROSSGEN_COMPILE
+
+#ifdef FEATURE_FUSION
+
+/* static */
+void AssemblySpec::DemandFileIOPermission(PEAssembly *pFile)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pFile));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // should have already checked permission if the codebase is set
+ if (!GetCodeBase()) {
+
+ if (pFile->IsBindingCodeBase()) {
+ if (pFile->IsSourceDownloadCache()) {
+ StackSString check;
+ pFile->GetCodeBase(check);
+
+ DemandFileIOPermission(check, FALSE, FILE_WEBPERM);
+ }
+ else
+ DemandFileIOPermission(pFile->GetPath(), TRUE, FILE_READANDPATHDISC);
+ }
+ }
+}
+
+STDAPI RuntimeCheckLocationAccess(LPCWSTR wszLocation)
+{
+
+ if (GetThread()==NULL)
+ return S_FALSE;
+
+ CONTRACTL
+ {
+ NOTHROW;
+ MODE_ANY;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(wszLocation));
+ }
+ CONTRACTL_END;
+ OVERRIDE_LOAD_LEVEL_LIMIT(FILE_ACTIVE);
+ HRESULT hr=S_OK;
+ DWORD dwDemand = 0;
+
+ if (SString::_wcsnicmp(wszLocation, W("file"), 4))
+ dwDemand = AssemblySpec::FILE_WEBPERM;
+ else
+ dwDemand = AssemblySpec::FILE_READANDPATHDISC;
+
+ EX_TRY
+ {
+ AssemblySpec::DemandFileIOPermission(wszLocation,
+ FALSE,
+ dwDemand);
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+
+}
+
+/* static */
+void AssemblySpec::DemandFileIOPermission(LPCWSTR wszCodeBase,
+ BOOL fHavePath,
+ DWORD dwDemandFlag)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(wszCodeBase));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ GCX_COOP();
+
+ MethodDescCallSite demandPermission(METHOD__ASSEMBLY__DEMAND_PERMISSION);
+
+ STRINGREF codeBase = NULL;
+ GCPROTECT_BEGIN(codeBase);
+
+ codeBase = StringObject::NewString(wszCodeBase);
+ ARG_SLOT args[3] =
+ {
+ ObjToArgSlot(codeBase),
+ BoolToArgSlot(fHavePath),
+ dwDemandFlag
+ };
+ demandPermission.Call(args);
+ GCPROTECT_END();
+}
+
+BOOL AssemblySpec::FindAssemblyFile(AppDomain* pAppDomain, BOOL fThrowOnFileNotFound,
+ IAssembly** ppIAssembly, IHostAssembly **ppIHostAssembly, IBindResult** ppNativeFusionAssembly,
+ IFusionBindLog** ppFusionLog, HRESULT *pHRBindResult, StackCrawlMark *pCallerStackMark /* = NULL */,
+ AssemblyLoadSecurity *pLoadSecurity /* = NULL */)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pAppDomain));
+ PRECONDITION(CheckPointer(pHRBindResult));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ GCX_PREEMP();
+
+ IApplicationContext *pFusionContext = pAppDomain->GetFusionContext();
+
+ AssemblySink* pSink = pAppDomain->AllocateAssemblySink(this);
+ SafeComHolderPreemp<IAssemblyBindSink> sinkholder(pSink);
+
+ BOOL fSuppressSecurityChecks = pLoadSecurity != NULL && pLoadSecurity->m_fSuppressSecurityChecks;
+
+ if (!GetCodeBase() && !fSuppressSecurityChecks)
+ pSink->RequireCodebaseSecurityCheck();
+
+ BOOL fIsWellKnown = FALSE;
+ HRESULT hr = S_OK;
+
+ IfFailGo(AssemblySpec::LoadAssembly(pFusionContext,
+ pSink,
+ ppIAssembly,
+ ppIHostAssembly,
+ ppNativeFusionAssembly,
+ IsIntrospectionOnly(),
+ fSuppressSecurityChecks));
+
+ // Host should have already done appropriate permission demand
+ if (!(*ppIHostAssembly)) {
+ DWORD dwLocation;
+ IfFailGo((*ppIAssembly)->GetAssemblyLocation(&dwLocation));
+
+ fIsWellKnown = (dwLocation == ASMLOC_UNKNOWN);
+
+ // check if it was cached, where a codebase had originally loaded it
+ if (pSink->DoCodebaseSecurityCheck() &&
+ !fSuppressSecurityChecks &&
+ (dwLocation & ASMLOC_CODEBASE_HINT)) {
+ if ((dwLocation & ASMLOC_LOCATION_MASK) == ASMLOC_DOWNLOAD_CACHE) {
+ StackSString codeBase;
+ SafeComHolderPreemp<IAssemblyName> pNameDef;
+
+ // <TODO>We could be caching the IAssemblyName and codebase</TODO>
+ IfFailGo((*ppIAssembly)->GetAssemblyNameDef(&pNameDef));
+
+ FusionBind::GetAssemblyNameStringProperty(pNameDef, ASM_NAME_CODEBASE_URL, codeBase);
+
+ DemandFileIOPermission(codeBase, FALSE, FILE_WEBPERM);
+ }
+ else if ((dwLocation & ASMLOC_LOCATION_MASK) != ASMLOC_GAC) {
+ StackSString path;
+ FusionBind::GetAssemblyManifestModulePath((*ppIAssembly), path);
+
+ DemandFileIOPermission(path, TRUE, FILE_READANDPATHDISC);
+ }
+ }
+
+ // Verify control hash
+ if (m_HashForControl.GetSize() > 0) {
+ StackSString path;
+
+ FusionBind::GetAssemblyManifestModulePath((*ppIAssembly), path);
+
+ AssemblyFileHash fileHash;
+ IfFailGo(fileHash.SetFileName(path));
+ IfFailGo(fileHash.CalculateHash(m_dwHashAlg));
+
+ if (!m_HashForControl.Equals(fileHash.GetHash(), fileHash.GetHashSize()))
+ IfFailGo(FUSION_E_REF_DEF_MISMATCH);
+ }
+ }
+
+#ifdef MDA_SUPPORTED
+ MdaLoadFromContext* pProbe = MDA_GET_ASSISTANT(LoadFromContext);
+ if (pProbe) {
+ pProbe->NowLoading(ppIAssembly, pCallerStackMark);
+ }
+#endif
+
+ *ppFusionLog = pSink->m_pFusionLog;
+ if (*ppFusionLog)
+ (*ppFusionLog)->AddRef();
+ return fIsWellKnown;
+
+ ErrExit:
+ {
+
+ *pHRBindResult = hr;
+
+ if (fThrowOnFileNotFound || (!Assembly::FileNotFound(hr)))
+ EEFileLoadException::Throw(this, pSink->m_pFusionLog, hr);
+ }
+
+ return FALSE;
+}
+#endif // FEATURE_FUSION
+
+void AssemblySpec::MatchRetargetedPublicKeys(Assembly *pAssembly)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pAssembly));
+ }
+ CONTRACTL_END;
+#ifdef FEATURE_FUSION
+ GCX_PREEMP();
+
+ // Manually apply fusion policy to obtain retargeted public key
+ SafeComHolderPreemp<IAssemblyName> pRequestedAssemblyName(NULL);
+ SafeComHolderPreemp<IAssemblyName> pPostPolicyAssemblyName(NULL);
+ IfFailThrow(CreateFusionName(&pRequestedAssemblyName));
+ HRESULT hr = PreBindAssembly(GetAppDomain()->GetFusionContext(),
+ pRequestedAssemblyName,
+ NULL, // pAsmParent
+ &pPostPolicyAssemblyName,
+ NULL // pvReserved
+ );
+ if (SUCCEEDED(hr)
+ || (FAILED(hr) && (hr == FUSION_E_REF_DEF_MISMATCH))) {
+ IAssemblyName *pResultAssemblyName = pAssembly->GetFusionAssemblyName();
+ if (pResultAssemblyName
+ && pPostPolicyAssemblyName
+ && pResultAssemblyName->IsEqual(pPostPolicyAssemblyName, ASM_CMPF_PUBLIC_KEY_TOKEN) == S_OK)
+ return;
+ }
+#endif // FEATURE_FUSION
+ ThrowHR(FUSION_E_REF_DEF_MISMATCH);
+}
+
+
+// Check if the supplied assembly's public key matches up with the one in the Spec, if any
+// Throws an appropriate exception in case of a mismatch
+void AssemblySpec::MatchPublicKeys(Assembly *pAssembly)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ // Check that the public keys are the same as in the AR.
+ if (IsStrongNamed()) {
+
+ const void *pbPublicKey;
+ DWORD cbPublicKey;
+ pbPublicKey = pAssembly->GetPublicKey(&cbPublicKey);
+ if (cbPublicKey == 0)
+ ThrowHR(FUSION_E_PRIVATE_ASM_DISALLOWED);
+
+ if (m_dwFlags & afPublicKey) {
+ if ((m_cbPublicKeyOrToken != cbPublicKey) ||
+ memcmp(m_pbPublicKeyOrToken, pbPublicKey, m_cbPublicKeyOrToken))
+ return MatchRetargetedPublicKeys(pAssembly);
+ }
+
+ // Ref has a token
+ else {
+ StrongNameBufferHolder<BYTE> pbStrongNameToken;
+ DWORD cbStrongNameToken;
+
+ if (!StrongNameTokenFromPublicKey((BYTE*) pbPublicKey,
+ cbPublicKey,
+ &pbStrongNameToken,
+ &cbStrongNameToken))
+ ThrowHR(StrongNameErrorInfo());
+ if ((m_cbPublicKeyOrToken != cbStrongNameToken) ||
+ memcmp(m_pbPublicKeyOrToken,
+ pbStrongNameToken,
+ cbStrongNameToken)) {
+ return MatchRetargetedPublicKeys(pAssembly);
+ }
+ }
+ }
+}
+
+
+PEAssembly *AssemblySpec::ResolveAssemblyFile(AppDomain *pDomain, BOOL fPreBind)
+{
+ CONTRACT(PEAssembly *)
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ // No assembly resolve on codebase binds
+ if (GetName() == NULL)
+ RETURN NULL;
+
+ Assembly *pAssembly = pDomain->RaiseAssemblyResolveEvent(this, IsIntrospectionOnly(), fPreBind);
+
+ if (pAssembly != NULL) {
+#ifdef FEATURE_FUSION
+ if (!IsIntrospectionOnly() && IsLoggingNeeded()) {
+ BinderLogging::BindingLog::CacheResultOfAssemblyResolveEvent(pDomain->GetFusionContext(), GetParentLoadContext(), pAssembly);
+ }
+#endif
+ PEAssembly *pFile = pAssembly->GetManifestFile();
+ pFile->AddRef();
+
+ RETURN pFile;
+ }
+
+ RETURN NULL;
+}
+
+
+Assembly *AssemblySpec::LoadAssembly(FileLoadLevel targetLevel, AssemblyLoadSecurity *pLoadSecurity, BOOL fThrowOnFileNotFound, BOOL fRaisePrebindEvents, StackCrawlMark *pCallerStackMark)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ DomainAssembly * pDomainAssembly = LoadDomainAssembly(targetLevel, pLoadSecurity, fThrowOnFileNotFound, fRaisePrebindEvents, pCallerStackMark);
+ if (pDomainAssembly == NULL) {
+ _ASSERTE(!fThrowOnFileNotFound);
+ return NULL;
+ }
+ return pDomainAssembly->GetAssembly();
+}
+
+#if defined(FEATURE_CORECLR)
+// Returns a BOOL indicating if the two Binder references point to the same
+// binder instance.
+BOOL AreSameBinderInstance(ICLRPrivBinder *pBinderA, ICLRPrivBinder *pBinderB)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ BOOL fIsSameInstance = FALSE;
+
+ if ((pBinderA != NULL) && (pBinderB != NULL))
+ {
+ // Get the ID for the first binder
+ UINT_PTR binderIDA = 0, binderIDB = 0;
+ HRESULT hr = pBinderA->GetBinderID(&binderIDA);
+ if (SUCCEEDED(hr))
+ {
+ // Get the ID for the second binder
+ hr = pBinderB->GetBinderID(&binderIDB);
+ if (SUCCEEDED(hr))
+ {
+ fIsSameInstance = (binderIDA == binderIDB);
+ }
+ }
+ }
+
+ return fIsSameInstance;
+}
+#endif // defined(FEATURE_CORECLR)
+
+ICLRPrivBinder* AssemblySpec::GetBindingContextFromParentAssembly(AppDomain *pDomain)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(pDomain != NULL);
+ }
+ CONTRACTL_END;
+
+ ICLRPrivBinder *pParentAssemblyBinder = NULL;
+ DomainAssembly *pParentDomainAssembly = GetParentAssembly();
+
+ if(pParentDomainAssembly != NULL)
+ {
+ // Get the PEAssembly associated with the parent's domain assembly
+ PEAssembly *pParentPEAssembly = pParentDomainAssembly->GetFile();
+
+ // ICLRPrivAssembly implements ICLRPrivBinder and thus, "is a" binder in a manner of semantics.
+ pParentAssemblyBinder = pParentPEAssembly->GetBindingContext();
+ }
+
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+ if (GetPreferFallbackLoadContextBinder())
+ {
+ // If we have been asked to use the fallback load context binder (currently only supported for AssemblyLoadContext.LoadFromAssemblyName),
+ // then pretend we do not have any binder yet available.
+ _ASSERTE(GetFallbackLoadContextBinderForRequestingAssembly() != NULL);
+ 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) Entrypoint assembly
+ // 3) RefEmitted assembly
+ // 4) AssemblyLoadContext.LoadFromAssemblyName
+ //
+ // For (1) and (2), 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 (3) and (4), fetch the fallback load context binder reference.
+
+ pParentAssemblyBinder = GetFallbackLoadContextBinderForRequestingAssembly();
+ }
+
+ if (pParentAssemblyBinder != NULL)
+ {
+ 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 defined(FEATURE_COMINTEROP)
+ if (!IsContentType_WindowsRuntime() && (pParentAssemblyBinder != NULL))
+ {
+ CLRPrivBinderWinRT *pWinRTBinder = pDomain->GetWinRtBinder();
+ if (AreSameBinderInstance(pWinRTBinder, pParentAssemblyBinder))
+ {
+ // We could be here when a non-WinRT assembly load is triggerred by a winmd (e.g. System.Runtime being loaded due to
+ // types being referenced from Windows.Foundation.Winmd).
+ //
+ // If the AssemblySpec does not correspond to WinRT type but our parent assembly binder is a WinRT binder,
+ // then such an assembly will not be found by the binder. In such a case, we reset our binder reference.
+ pParentAssemblyBinder = NULL;
+ }
+ }
+#endif // defined(FEATURE_COMINTEROP)
+
+ if (!pParentAssemblyBinder)
+ {
+ // 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 CoreLibrary and thus, the default binding context should be
+ // used as the parent assembly binder.
+ pParentAssemblyBinder = static_cast<ICLRPrivBinder*>(pDomain->GetFusionContext());
+ }
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+
+ return pParentAssemblyBinder;
+}
+
+DomainAssembly *AssemblySpec::LoadDomainAssembly(FileLoadLevel targetLevel,
+ AssemblyLoadSecurity *pLoadSecurity,
+ BOOL fThrowOnFileNotFound,
+ BOOL fRaisePrebindEvents,
+ StackCrawlMark *pCallerStackMark)
+{
+ CONTRACT(DomainAssembly *)
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ POSTCONDITION((!fThrowOnFileNotFound && CheckPointer(RETVAL, NULL_OK))
+ || CheckPointer(RETVAL));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ ETWOnStartup (LoaderCatchCall_V1, LoaderCatchCallEnd_V1);
+ AppDomain* pDomain = GetAppDomain();
+
+#ifndef FEATURE_CORECLR
+ // Event Tracing for Windows is used to log data for performance and functional testing purposes.
+ // The events in this function are used to help measure the performance of assembly loading as a whole for dynamic loads.
+
+ // Special-purpose holder structure to ensure the LoaderPhaseEnd ETW event is fired when returning from function.
+ struct ETWLoaderPhaseHolder
+ {
+ StackSString ETWCodeBase, ETWAssemblyName;
+
+ DWORD _dwAppDomainId;
+ BOOL initialized;
+
+ ETWLoaderPhaseHolder()
+ : _dwAppDomainId(ETWAppDomainIdNotAvailable)
+ , initialized(FALSE)
+ { }
+
+ void Init(DWORD dwAppDomainId, LPCWSTR wszCodeBase, LPCSTR szAssemblyName)
+ {
+ _dwAppDomainId = dwAppDomainId;
+
+ EX_TRY
+ {
+ if (wszCodeBase != NULL)
+ {
+ ETWCodeBase.Append(wszCodeBase);
+ ETWCodeBase.Normalize(); // Ensures that the later cast to LPCWSTR does not throw.
+ }
+ }
+ EX_CATCH
+ {
+ ETWCodeBase.Clear();
+ }
+ EX_END_CATCH(RethrowTransientExceptions)
+
+ EX_TRY
+ {
+ if (szAssemblyName != NULL)
+ {
+ ETWAssemblyName.AppendUTF8(szAssemblyName);
+ ETWAssemblyName.Normalize(); // Ensures that the later cast to LPCWSTR does not throw.
+ }
+ }
+ EX_CATCH
+ {
+ ETWAssemblyName.Clear();
+ }
+ EX_END_CATCH(RethrowTransientExceptions)
+
+ FireEtwLoaderPhaseStart(_dwAppDomainId, ETWLoadContextNotAvailable, ETWFieldUnused, ETWLoaderDynamicLoad, ETWCodeBase.IsEmpty() ? NULL : (LPCWSTR)ETWCodeBase, ETWAssemblyName.IsEmpty() ? NULL : (LPCWSTR)ETWAssemblyName, GetClrInstanceId());
+
+ initialized = TRUE;
+ }
+
+ ~ETWLoaderPhaseHolder()
+ {
+ if (initialized)
+ {
+ FireEtwLoaderPhaseEnd(_dwAppDomainId, ETWLoadContextNotAvailable, ETWFieldUnused, ETWLoaderDynamicLoad, ETWCodeBase.IsEmpty() ? NULL : (LPCWSTR)ETWCodeBase, ETWAssemblyName.IsEmpty() ? NULL : (LPCWSTR)ETWAssemblyName, GetClrInstanceId());
+ }
+ }
+ };
+
+ ETWLoaderPhaseHolder loaderPhaseHolder;
+ if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_PRIVATEBINDING_KEYWORD)) {
+#ifdef FEATURE_FUSION
+ loaderPhaseHolder.Init(pDomain->GetId().m_dwId, m_wszCodeBase, m_pAssemblyName);
+#else
+ loaderPhaseHolder.Init(pDomain->GetId().m_dwId, NULL, NULL);
+#endif
+ }
+#endif // FEATURE_CORECLR
+
+ DomainAssembly *pAssembly = nullptr;
+
+ ICLRPrivBinder * pBinder = GetHostBinder();
+
+ // If no binder was explicitly set, check if parent assembly has a binder.
+ if (pBinder == nullptr)
+ {
+ pBinder = GetBindingContextFromParentAssembly(pDomain);
+ }
+
+#ifdef FEATURE_APPX_BINDER
+ // If no explicit or parent binder, check domain.
+ if (pBinder == nullptr && AppX::IsAppXProcess())
+ {
+ pBinder = pDomain->GetCurrentLoadContextHostBinder();
+ }
+#endif
+
+ if (pBinder != nullptr)
+ {
+ ReleaseHolder<ICLRPrivAssembly> pPrivAssembly;
+ HRESULT hrCachedResult;
+ if (SUCCEEDED(pBinder->FindAssemblyBySpec(GetAppDomain(), this, &hrCachedResult, &pPrivAssembly)) &&
+ SUCCEEDED(hrCachedResult))
+ {
+ pAssembly = pDomain->FindAssembly(pPrivAssembly);
+ }
+ }
+
+ if ((pAssembly == nullptr) && CanUseWithBindingCache())
+ {
+ pAssembly = pDomain->FindCachedAssembly(this);
+ }
+
+ if (pAssembly)
+ {
+ pDomain->LoadDomainFile(pAssembly, targetLevel);
+ RETURN pAssembly;
+ }
+
+#ifdef FEATURE_REFLECTION_ONLY_LOAD
+ if (IsIntrospectionOnly() && (GetCodeBase() == NULL))
+ {
+ SafeComHolder<IAssemblyName> pIAssemblyName;
+ IfFailThrow(CreateFusionName(&pIAssemblyName));
+
+ // Note: We do not support introspection-only collectible assemblies (yet)
+ AppDomain::AssemblyIterator i = pDomain->IterateAssembliesEx(
+ (AssemblyIterationFlags)(kIncludeLoaded | kIncludeIntrospection | kExcludeCollectible));
+ CollectibleAssemblyHolder<DomainAssembly *> pCachedDomainAssembly;
+
+ while (i.Next(pCachedDomainAssembly.This()))
+ {
+ _ASSERTE(!pCachedDomainAssembly->IsCollectible());
+ IAssemblyName * pCachedAssemblyName = pCachedDomainAssembly->GetAssembly()->GetFusionAssemblyName();
+ if (S_OK == pCachedAssemblyName->IsEqual(pIAssemblyName, ASM_CMPF_IL_ALL))
+ {
+ RETURN pCachedDomainAssembly;
+ }
+ }
+ }
+#endif // FEATURE_REFLECTION_ONLY_LOAD
+
+ PEAssemblyHolder pFile(pDomain->BindAssemblySpec(this, fThrowOnFileNotFound, fRaisePrebindEvents, pCallerStackMark, pLoadSecurity));
+ if (pFile == NULL)
+ RETURN NULL;
+
+ pAssembly = pDomain->LoadDomainAssembly(this, pFile, targetLevel, pLoadSecurity);
+
+ RETURN pAssembly;
+}
+
+/* static */
+Assembly *AssemblySpec::LoadAssembly(LPCSTR pSimpleName,
+ AssemblyMetaDataInternal* pContext,
+ const BYTE * pbPublicKeyOrToken,
+ DWORD cbPublicKeyOrToken,
+ DWORD dwFlags)
+{
+ CONTRACT(Assembly *)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pSimpleName));
+ POSTCONDITION(CheckPointer(RETVAL));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ AssemblySpec spec;
+ IfFailThrow(spec.Init(pSimpleName, pContext,
+ pbPublicKeyOrToken, cbPublicKeyOrToken, dwFlags));
+
+ RETURN spec.LoadAssembly(FILE_LOADED);
+}
+
+/* static */
+Assembly *AssemblySpec::LoadAssembly(LPCWSTR pFilePath)
+{
+ CONTRACT(Assembly *)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pFilePath));
+ POSTCONDITION(CheckPointer(RETVAL));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ AssemblySpec spec;
+ spec.SetCodeBase(pFilePath);
+ RETURN spec.LoadAssembly(FILE_LOADED);
+}
+
+#ifndef FEATURE_FUSION
+HRESULT AssemblySpec::CheckFriendAssemblyName()
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Version, Culture, Architecture, and publickeytoken are not permitted
+ if ((m_context.usMajorVersion != (USHORT) -1) ||
+ (m_context.szLocale != NULL) ||
+ (IsAfPA_Specified(m_dwFlags)) ||
+ (IsStrongNamed() && !HasPublicKey()))
+ {
+ return META_E_CA_BAD_FRIENDS_ARGS;
+ }
+ else
+ {
+ return S_OK;
+ }
+}
+#endif //FEATURE_FUSION
+
+HRESULT AssemblySpec::EmitToken(
+ IMetaDataAssemblyEmit *pEmit,
+ mdAssemblyRef *pToken,
+ BOOL fUsePublicKeyToken, /*=TRUE*/
+ BOOL fMustBeBindable /*=FALSE*/)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ MODE_ANY;
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ SmallStackSString ssName;
+ fMustBeBindable ? GetEncodedName(ssName) : GetName(ssName);
+
+ ASSEMBLYMETADATA AMD;
+
+ AMD.usMajorVersion = m_context.usMajorVersion;
+ AMD.usMinorVersion = m_context.usMinorVersion;
+ AMD.usBuildNumber = m_context.usBuildNumber;
+ AMD.usRevisionNumber = m_context.usRevisionNumber;
+
+ if (m_context.szLocale) {
+ AMD.cbLocale = MultiByteToWideChar(CP_UTF8, 0, m_context.szLocale, -1, NULL, 0);
+ if(AMD.cbLocale==0)
+ IfFailGo(HRESULT_FROM_GetLastError());
+ AMD.szLocale = (LPWSTR) alloca(AMD.cbLocale * sizeof(WCHAR) );
+ if(MultiByteToWideChar(CP_UTF8, 0, m_context.szLocale, -1, AMD.szLocale, AMD.cbLocale)==0)
+ IfFailGo(HRESULT_FROM_GetLastError());
+ }
+ else {
+ AMD.cbLocale = 0;
+ AMD.szLocale = NULL;
+ }
+
+ // If we've been asked to emit a public key token in the reference but we've
+ // been given a public key then we need to generate the token now.
+ if (m_cbPublicKeyOrToken && fUsePublicKeyToken && IsAfPublicKey(m_dwFlags)) {
+ StrongNameBufferHolder<BYTE> pbPublicKeyToken;
+ DWORD cbPublicKeyToken;
+ if (!StrongNameTokenFromPublicKey(m_pbPublicKeyOrToken,
+ m_cbPublicKeyOrToken,
+ &pbPublicKeyToken,
+ &cbPublicKeyToken)) {
+ IfFailGo(StrongNameErrorInfo());
+ }
+
+ hr = pEmit->DefineAssemblyRef(pbPublicKeyToken,
+ cbPublicKeyToken,
+ ssName.GetUnicode(),
+ &AMD,
+ NULL,
+ 0,
+ m_dwFlags & ~afPublicKey,
+ pToken);
+ }
+ else {
+ hr = pEmit->DefineAssemblyRef(m_pbPublicKeyOrToken,
+ m_cbPublicKeyOrToken,
+ ssName.GetUnicode(),
+ &AMD,
+ NULL,
+ 0,
+ m_dwFlags,
+ pToken);
+ }
+
+ hr = S_OK;
+ ErrExit:
+ ;
+ }
+ EX_CATCH_HRESULT(hr);
+
+ return hr;
+}
+
+//===========================================================================================
+// Constructs an AssemblySpec for the given IAssemblyName. Recognizes IAssemblyName objects
+// that were built from WinRT AssemblySpec objects, extracts the encoded type name, and sets
+// the type namespace and class name properties appropriately.
+
+void AssemblySpec::ParseEncodedName()
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ } CONTRACTL_END
+
+#ifdef FEATURE_COMINTEROP
+ if (IsContentType_WindowsRuntime())
+ {
+ StackSString ssEncodedName(SString::Utf8, m_pAssemblyName);
+ ssEncodedName.Normalize();
+
+ SString::Iterator itBang = ssEncodedName.Begin();
+ if (ssEncodedName.Find(itBang, SL(W("!"))))
+ {
+ StackSString ssAssemblyName(ssEncodedName, ssEncodedName.Begin(), itBang - ssEncodedName.Begin());
+ StackSString ssTypeName(ssEncodedName, ++itBang, ssEncodedName.End() - itBang);
+ SetName(ssAssemblyName);
+ SetWindowsRuntimeType(ssTypeName);
+ }
+ }
+#endif
+}
+
+void AssemblySpec::SetWindowsRuntimeType(
+ LPCUTF8 szNamespace,
+ LPCUTF8 szClassName)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+#ifdef FEATURE_COMINTEROP
+ // Release already allocated string
+ if (m_ownedFlags & WINRT_TYPE_NAME_OWNED)
+ {
+ if (m_szWinRtTypeNamespace != nullptr)
+ delete [] m_szWinRtTypeNamespace;
+ if (m_szWinRtTypeClassName != nullptr)
+ delete [] m_szWinRtTypeClassName;
+ }
+ m_szWinRtTypeNamespace = szNamespace;
+ m_szWinRtTypeClassName = szClassName;
+
+ m_ownedFlags &= ~WINRT_TYPE_NAME_OWNED;
+#else
+ // Classic (non-phone) CoreCLR does not support WinRT interop; this should never be called with a non-empty type name
+ _ASSERTE((szNamespace == NULL) && (szClassName == NULL));
+#endif
+}
+
+void AssemblySpec::SetWindowsRuntimeType(
+ SString const & _ssTypeName)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Release already allocated string
+ if (m_ownedFlags & WINRT_TYPE_NAME_OWNED)
+ {
+ if (m_szWinRtTypeNamespace != nullptr)
+ delete[] m_szWinRtTypeNamespace;
+ if (m_szWinRtTypeClassName != nullptr)
+ delete[] m_szWinRtTypeClassName;
+ m_ownedFlags &= ~WINRT_TYPE_NAME_OWNED;
+ }
+
+ SString ssTypeName;
+ _ssTypeName.ConvertToUTF8(ssTypeName);
+
+ LPUTF8 szTypeName = (LPUTF8)ssTypeName.GetUTF8NoConvert();
+ ns::SplitInline(szTypeName, m_szWinRtTypeNamespace, m_szWinRtTypeClassName);
+ m_ownedFlags &= ~WINRT_TYPE_NAME_OWNED;
+ // Make a copy of the type name strings
+ CloneFields(WINRT_TYPE_NAME_OWNED);
+}
+
+
+AssemblySpecBindingCache::AssemblySpecBindingCache()
+{
+ LIMITED_METHOD_CONTRACT;
+}
+
+AssemblySpecBindingCache::~AssemblySpecBindingCache()
+{
+ CONTRACTL
+ {
+ DESTRUCTOR_CHECK;
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ Clear();
+}
+
+void AssemblySpecBindingCache::Clear()
+{
+ CONTRACTL
+ {
+ DESTRUCTOR_CHECK;
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ PtrHashMap::PtrIterator i = m_map.begin();
+ while (!i.end())
+ {
+ AssemblyBinding *b = (AssemblyBinding*) i.GetValue();
+ if (m_pHeap == NULL)
+ delete b;
+ else
+ b->~AssemblyBinding();
+
+ ++i;
+ }
+
+ m_map.Clear();
+}
+
+void AssemblySpecBindingCache::OnAppDomainUnload()
+{
+ CONTRACTL
+ {
+ DESTRUCTOR_CHECK;
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ PtrHashMap::PtrIterator i = m_map.begin();
+ while (!i.end())
+ {
+ AssemblyBinding *b = (AssemblyBinding*) i.GetValue();
+ b->OnAppDomainUnload();
+
+ ++i;
+ }
+}
+
+void AssemblySpecBindingCache::Init(CrstBase *pCrst, LoaderHeap *pHeap)
+{
+ WRAPPER_NO_CONTRACT;
+
+ LockOwner lock = {pCrst, IsOwnerOfCrst};
+ m_map.Init(INITIAL_ASM_SPEC_HASH_SIZE, CompareSpecs, TRUE, &lock);
+ m_pHeap = pHeap;
+}
+
+#if defined(FEATURE_CORECLR)
+AssemblySpecBindingCache::AssemblyBinding* AssemblySpecBindingCache::GetAssemblyBindingEntryForAssemblySpec(AssemblySpec* pSpec, BOOL fThrow)
+{
+ CONTRACTL
+ {
+ if (fThrow)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ else
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ MODE_ANY;
+ PRECONDITION(pSpec != NULL);
+ }
+ CONTRACTL_END;
+
+ AssemblyBinding* pEntry = (AssemblyBinding *) INVALIDENTRY;
+ UPTR key = (UPTR)pSpec->Hash();
+
+ // On CoreCLR, we will use the BinderID as the key
+ ICLRPrivBinder *pBinderContextForLookup = NULL;
+ AppDomain *pSpecDomain = pSpec->GetAppDomain();
+ bool fGetBindingContextFromParent = true;
+
+ // Check if the AssemblySpec already has specified its binding context. This will be set for assemblies that are
+ // attempted to be explicitly bound using AssemblyLoadContext LoadFrom* methods.
+ pBinderContextForLookup = pSpec->GetBindingContext();
+ if (pBinderContextForLookup != NULL)
+ {
+ // We are working with the actual binding context in which the assembly was expected to be loaded.
+ // Thus, we dont need to get it from the parent assembly.
+ fGetBindingContextFromParent = false;
+ }
+
+ if (fGetBindingContextFromParent)
+ {
+ // MScorlib does not have a binding context associated with it and its lookup will only be done
+ // using its AssemblySpec hash.
+ if (!pSpec->IsAssemblySpecForMscorlib())
+ {
+ pBinderContextForLookup = pSpec->GetBindingContextFromParentAssembly(pSpecDomain);
+ pSpec->SetBindingContext(pBinderContextForLookup);
+ }
+ }
+
+ UPTR lookupKey = key;
+ if (pBinderContextForLookup)
+ {
+ UINT_PTR binderID = 0;
+ HRESULT hr = pBinderContextForLookup->GetBinderID(&binderID);
+ _ASSERTE(SUCCEEDED(hr));
+ lookupKey = key^binderID;
+ }
+
+ pEntry = (AssemblyBinding *) m_map.LookupValue(lookupKey, pSpec);
+
+ // Reset the binding context if one was originally never present in the AssemblySpec and we didnt find any entry
+ // in the cache.
+ if (fGetBindingContextFromParent)
+ {
+ if (pEntry == (AssemblyBinding *) INVALIDENTRY)
+ {
+ pSpec->SetBindingContext(NULL);
+ }
+ }
+
+ return pEntry;
+}
+#endif // defined(FEATURE_CORECLR)
+
+BOOL AssemblySpecBindingCache::Contains(AssemblySpec *pSpec)
+{
+ WRAPPER_NO_CONTRACT;
+
+#if !defined(FEATURE_CORECLR)
+ DWORD key = pSpec->Hash();
+ AssemblyBinding *entry = (AssemblyBinding *) m_map.LookupValue(key, pSpec);
+ return (entry != (AssemblyBinding *) INVALIDENTRY);
+#else // defined(FEATURE_CORECLR)
+ return (GetAssemblyBindingEntryForAssemblySpec(pSpec, TRUE) != (AssemblyBinding *) INVALIDENTRY);
+#endif // !defined(FEATURE_CORECLR)
+}
+
+DomainAssembly *AssemblySpecBindingCache::LookupAssembly(AssemblySpec *pSpec,
+ BOOL fThrow /*=TRUE*/)
+{
+ CONTRACT(DomainAssembly *)
+ {
+ INSTANCE_CHECK;
+ if (fThrow) {
+ GC_TRIGGERS;
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ else {
+ GC_NOTRIGGER;
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ AssemblyBinding *entry = (AssemblyBinding *) INVALIDENTRY;
+
+#if !defined(FEATURE_CORECLR)
+ DWORD key = pSpec->Hash();
+ entry = (AssemblyBinding *) m_map.LookupValue(key, pSpec);
+#else // defined(FEATURE_CORECLR)
+ entry = GetAssemblyBindingEntryForAssemblySpec(pSpec, fThrow);
+#endif // !defined(FEATURE_CORECLR)
+
+ if (entry == (AssemblyBinding *) INVALIDENTRY)
+ RETURN NULL;
+ else
+ {
+ if ((entry->GetAssembly() == NULL) && fThrow)
+ {
+ // May be either unloaded, or an exception occurred.
+ entry->ThrowIfError();
+ }
+
+ RETURN entry->GetAssembly();
+ }
+}
+
+PEAssembly *AssemblySpecBindingCache::LookupFile(AssemblySpec *pSpec, BOOL fThrow /*=TRUE*/)
+{
+ CONTRACT(PEAssembly *)
+ {
+ INSTANCE_CHECK;
+ if (fThrow) {
+ GC_TRIGGERS;
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ else {
+ GC_NOTRIGGER;
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ AssemblyBinding *entry = (AssemblyBinding *) INVALIDENTRY;
+
+#if !defined(FEATURE_CORECLR)
+ DWORD key = pSpec->Hash();
+ entry = (AssemblyBinding *) m_map.LookupValue(key, pSpec);
+#else // defined(FEATURE_CORECLR)
+ entry = GetAssemblyBindingEntryForAssemblySpec(pSpec, fThrow);
+#endif // !defined(FEATURE_CORECLR)
+
+ if (entry == (AssemblyBinding *) INVALIDENTRY)
+ RETURN NULL;
+ else
+ {
+ if (fThrow && (entry->GetFile() == NULL))
+ {
+ CONSISTENCY_CHECK(entry->IsError());
+ entry->ThrowIfError();
+ }
+
+ RETURN entry->GetFile();
+ }
+}
+
+
+class AssemblyBindingHolder
+{
+public:
+ AssemblyBindingHolder()
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_entry = NULL;
+ m_pHeap = NULL;
+ }
+
+ AssemblySpecBindingCache::AssemblyBinding *CreateAssemblyBinding(LoaderHeap *pHeap)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+ m_pHeap = pHeap;
+ if (pHeap)
+ {
+ m_entry = new (m_amTracker.Track(pHeap->AllocMem(S_SIZE_T(sizeof(AssemblySpecBindingCache::AssemblyBinding))))) AssemblySpecBindingCache::AssemblyBinding;
+ }
+ else
+ {
+ m_entry = new AssemblySpecBindingCache::AssemblyBinding;
+ }
+ return m_entry;
+ }
+
+ ~AssemblyBindingHolder()
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ if (m_entry)
+ {
+ if (m_pHeap)
+ {
+ // just call destructor - m_amTracker will delete the memory for m_entry itself.
+ m_entry->~AssemblyBinding();
+ }
+ else
+ {
+ delete m_entry;
+ }
+ }
+ }
+
+ void SuppressRelease()
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_entry = NULL;
+ m_pHeap = NULL;
+ m_amTracker.SuppressRelease();
+ }
+
+ AllocMemTracker *GetPamTracker()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return &m_amTracker;
+ }
+
+
+
+private:
+ AssemblySpecBindingCache::AssemblyBinding *m_entry;
+ LoaderHeap *m_pHeap;
+ AllocMemTracker m_amTracker;
+};
+
+// NOTE ABOUT STATE OF CACHE ENTRIES:
+//
+// A cache entry can be in one of 4 states:
+// 1. Empty (no entry)
+// 2. File (a PEAssembly has been bound, but not yet an Assembly)
+// 3. Assembly (Both a PEAssembly & Assembly are available.)
+// 4. Error (an error has occurred)
+//
+// The legal state transitions are:
+// 1 -> any
+// 2 -> 3
+// 2 -> 4
+
+
+BOOL AssemblySpecBindingCache::StoreAssembly(AssemblySpec *pSpec, DomainAssembly *pAssembly)
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ // Host binder based assembly spec's cannot currently be safely inserted into caches.
+ PRECONDITION(pSpec->GetHostBinder() == nullptr);
+ POSTCONDITION(UnsafeContains(this, pSpec));
+ POSTCONDITION(UnsafeVerifyLookupAssembly(this, pSpec, pAssembly));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ UPTR key = (UPTR)pSpec->Hash();
+
+#if defined(FEATURE_CORECLR)
+ // On CoreCLR, we will use the BinderID as the key
+ ICLRPrivBinder* pBinderContextForLookup = pAssembly->GetFile()->GetBindingContext();
+ _ASSERTE(pBinderContextForLookup || pAssembly->GetFile()->IsSystem());
+ if (pBinderContextForLookup)
+ {
+ UINT_PTR binderID = 0;
+ HRESULT hr = pBinderContextForLookup->GetBinderID(&binderID);
+ _ASSERTE(SUCCEEDED(hr));
+ key = key^binderID;
+
+ if (!pSpec->GetBindingContext())
+ {
+ pSpec->SetBindingContext(pBinderContextForLookup);
+ }
+ }
+#endif // defined(FEATURE_CORECLR)
+
+ AssemblyBinding *entry = (AssemblyBinding *) m_map.LookupValue(key, pSpec);
+
+ if (entry == (AssemblyBinding *) INVALIDENTRY)
+ {
+ AssemblyBindingHolder abHolder;
+ entry = abHolder.CreateAssemblyBinding(m_pHeap);
+
+ entry->Init(pSpec,pAssembly->GetFile(),pAssembly,NULL,m_pHeap, abHolder.GetPamTracker());
+
+ m_map.InsertValue(key, entry);
+
+ abHolder.SuppressRelease();
+
+ STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StoreFile (StoreAssembly): Add cached entry (%p) with PEFile %p",entry,pAssembly->GetFile());
+ RETURN TRUE;
+ }
+ else
+ {
+ if (!entry->IsError())
+ {
+ if (entry->GetAssembly() != NULL)
+ {
+ // OK if this is a duplicate
+ if (entry->GetAssembly() == pAssembly)
+ RETURN TRUE;
+ }
+ else
+ {
+ // OK if we have have a matching PEAssembly
+ if (entry->GetFile() != NULL
+ && pAssembly->GetFile()->Equals(entry->GetFile()))
+ {
+ entry->SetAssembly(pAssembly);
+ RETURN TRUE;
+ }
+ }
+ }
+
+ // Invalid cache transition (see above note about state transitions)
+ RETURN FALSE;
+ }
+}
+
+// Note that this routine may be called outside a lock, so may be racing with another thread.
+// Returns TRUE if add was successful - if FALSE is returned, caller should honor current
+// cached value to ensure consistency.
+
+BOOL AssemblySpecBindingCache::StoreFile(AssemblySpec *pSpec, PEAssembly *pFile)
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ // Host binder based assembly spec's cannot currently be safely inserted into caches.
+ PRECONDITION(pSpec->GetHostBinder() == nullptr);
+ POSTCONDITION((!RETVAL) || (UnsafeContains(this, pSpec) && UnsafeVerifyLookupFile(this, pSpec, pFile)));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ UPTR key = (UPTR)pSpec->Hash();
+
+#if defined(FEATURE_CORECLR)
+ // On CoreCLR, we will use the BinderID as the key
+ ICLRPrivBinder* pBinderContextForLookup = pFile->GetBindingContext();
+ _ASSERTE(pBinderContextForLookup || pFile->IsSystem());
+ if (pBinderContextForLookup)
+ {
+ UINT_PTR binderID = 0;
+ HRESULT hr = pBinderContextForLookup->GetBinderID(&binderID);
+ _ASSERTE(SUCCEEDED(hr));
+ key = key^binderID;
+
+ if (!pSpec->GetBindingContext())
+ {
+ pSpec->SetBindingContext(pBinderContextForLookup);
+ }
+ }
+#endif // defined(FEATURE_CORECLR)
+
+ AssemblyBinding *entry = (AssemblyBinding *) m_map.LookupValue(key, pSpec);
+
+ if (entry == (AssemblyBinding *) INVALIDENTRY)
+ {
+ AssemblyBindingHolder abHolder;
+ entry = abHolder.CreateAssemblyBinding(m_pHeap);
+
+ entry->Init(pSpec,pFile,NULL,NULL,m_pHeap, abHolder.GetPamTracker());
+
+ m_map.InsertValue(key, entry);
+ abHolder.SuppressRelease();
+
+ STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StoreFile: Add cached entry (%p) with PEFile %p\n", entry, pFile);
+
+ RETURN TRUE;
+ }
+ else
+ {
+ if (!entry->IsError())
+ {
+ // OK if this is a duplicate
+ if (entry->GetFile() != NULL
+ && pFile->Equals(entry->GetFile()))
+ RETURN TRUE;
+ }
+ else
+ if (entry->IsPostBindError())
+ {
+ // Another thread has reported what's going to happen later.
+ entry->ThrowIfError();
+
+ }
+ STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"Incompatible cached entry found (%p) when adding PEFile %p\n", entry, pFile);
+ // Invalid cache transition (see above note about state transitions)
+ RETURN FALSE;
+ }
+}
+
+BOOL AssemblySpecBindingCache::StoreException(AssemblySpec *pSpec, Exception* pEx)
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ // Host binder based assembly spec's cannot currently be safely inserted into caches.
+ PRECONDITION(pSpec->GetHostBinder() == nullptr);
+ DISABLED(POSTCONDITION(UnsafeContains(this, pSpec))); //<TODO>@todo: Getting violations here - StoreExceptions could happen anywhere so this is possibly too aggressive.</TODO>
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ UPTR key = (UPTR)pSpec->Hash();
+
+#if !defined(FEATURE_CORECLR)
+ AssemblyBinding *entry = (AssemblyBinding *) m_map.LookupValue(key, pSpec);
+#else // defined(FEATURE_CORECLR)
+ AssemblyBinding *entry = GetAssemblyBindingEntryForAssemblySpec(pSpec, TRUE);
+ if (entry == (AssemblyBinding *) INVALIDENTRY)
+ {
+ // TODO: Merge this with the failure lookup in the binder
+ //
+ // Since no entry was found for this assembly in any binding context, save the failure
+ // in the TPABinder context
+ ICLRPrivBinder* pBinderToSaveException = NULL;
+ pBinderToSaveException = pSpec->GetBindingContext();
+ if (pBinderToSaveException == NULL)
+ {
+ if (!pSpec->IsAssemblySpecForMscorlib())
+ {
+ pBinderToSaveException = pSpec->GetBindingContextFromParentAssembly(pSpec->GetAppDomain());
+ UINT_PTR binderID = 0;
+ HRESULT hr = pBinderToSaveException->GetBinderID(&binderID);
+ _ASSERTE(SUCCEEDED(hr));
+ key = key^binderID;
+ }
+ }
+ }
+#endif // defined(FEATURE_CORECLR)
+
+ if (entry == (AssemblyBinding *) INVALIDENTRY) {
+ AssemblyBindingHolder abHolder;
+ entry = abHolder.CreateAssemblyBinding(m_pHeap);
+
+ entry->Init(pSpec,NULL,NULL,pEx,m_pHeap, abHolder.GetPamTracker());
+
+ m_map.InsertValue(key, entry);
+ abHolder.SuppressRelease();
+
+ STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StoreFile (StoreException): Add cached entry (%p) with exception %p",entry,pEx);
+ RETURN TRUE;
+ }
+ else
+ {
+ // OK if this is a duplicate
+ if (entry->IsError())
+ {
+ if (entry->GetHR() == pEx->GetHR())
+ RETURN TRUE;
+ }
+ else
+ {
+ // OK to transition to error if we don't have an Assembly yet
+ if (entry->GetAssembly() == NULL)
+ {
+ entry->InitException(pEx);
+ RETURN TRUE;
+ }
+ }
+
+ // Invalid cache transition (see above note about state transitions)
+ RETURN FALSE;
+ }
+}
+
+/* static */
+BOOL AssemblySpecHash::CompareSpecs(UPTR u1, UPTR u2)
+{
+ // the same...
+ WRAPPER_NO_CONTRACT;
+ return AssemblySpecBindingCache::CompareSpecs(u1,u2);
+}
+
+
+
+
+/* static */
+BOOL AssemblySpecBindingCache::CompareSpecs(UPTR u1, UPTR u2)
+{
+ WRAPPER_NO_CONTRACT;
+ AssemblySpec *a1 = (AssemblySpec *) (u1 << 1);
+ AssemblySpec *a2 = (AssemblySpec *) u2;
+
+#if defined(FEATURE_APPX_BINDER)
+ _ASSERTE(a1->GetAppDomain() == a2->GetAppDomain());
+ if (a1->GetAppDomain()->HasLoadContextHostBinder())
+ return (CLRPrivBinderUtil::CompareHostBinderSpecs(a1,a2));
+#endif
+
+ if ((!a1->CompareEx(a2)) ||
+ (a1->IsIntrospectionOnly() != a2->IsIntrospectionOnly()))
+ return FALSE;
+ return TRUE;
+}
+
+
+
+/* static */
+BOOL DomainAssemblyCache::CompareBindingSpec(UPTR spec1, UPTR spec2)
+{
+ WRAPPER_NO_CONTRACT;
+
+ AssemblySpec* pSpec1 = (AssemblySpec*) (spec1 << 1);
+ AssemblyEntry* pEntry2 = (AssemblyEntry*) spec2;
+
+#if defined(FEATURE_FUSION)
+ AssemblySpec* pSpec2 = &pEntry2->spec;
+ _ASSERTE(pSpec1->GetAppDomain() == pSpec2->GetAppDomain());
+ if (pSpec1->GetAppDomain()->HasLoadContextHostBinder())
+ return (CLRPrivBinderUtil::CompareHostBinderSpecs(pSpec1,pSpec2));
+#endif
+
+
+ if ((!pSpec1->CompareEx(&pEntry2->spec)) ||
+ (pSpec1->IsIntrospectionOnly() != pEntry2->spec.IsIntrospectionOnly()))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+DomainAssemblyCache::AssemblyEntry* DomainAssemblyCache::LookupEntry(AssemblySpec* pSpec)
+{
+ CONTRACT (DomainAssemblyCache::AssemblyEntry*)
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END
+
+ DWORD hashValue = pSpec->Hash();
+
+ LPVOID pResult = m_Table.LookupValue(hashValue, pSpec);
+ if(pResult == (LPVOID) INVALIDENTRY)
+ RETURN NULL;
+ else
+ RETURN (AssemblyEntry*) pResult;
+}
+
+VOID DomainAssemblyCache::InsertEntry(AssemblySpec* pSpec, LPVOID pData1, LPVOID pData2/*=NULL*/)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+ LPVOID ptr = LookupEntry(pSpec);
+ if(ptr == NULL) {
+
+ BaseDomain::CacheLockHolder lh(m_pDomain);
+
+ ptr = LookupEntry(pSpec);
+ if(ptr == NULL) {
+ AllocMemTracker amTracker;
+ AllocMemTracker *pamTracker = &amTracker;
+
+ AssemblyEntry* pEntry = (AssemblyEntry*) pamTracker->Track( m_pDomain->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(AssemblyEntry))) );
+ new (&pEntry->spec) AssemblySpec ();
+
+ pEntry->spec.CopyFrom(pSpec);
+ pEntry->spec.CloneFieldsToLoaderHeap(AssemblySpec::ALL_OWNED, m_pDomain->GetLowFrequencyHeap(), pamTracker);
+ pEntry->pData[0] = pData1;
+ pEntry->pData[1] = pData2;
+ DWORD hashValue = pEntry->Hash();
+ m_Table.InsertValue(hashValue, pEntry);
+
+ pamTracker->SuppressRelease();
+ }
+ // lh goes out of scope here
+ }
+#ifdef _DEBUG
+ else {
+ _ASSERTE(pData1 == ((AssemblyEntry*) ptr)->pData[0]);
+ _ASSERTE(pData2 == ((AssemblyEntry*) ptr)->pData[1]);
+ }
+#endif
+
+}
+
+#ifdef FEATURE_FUSION
+
+IAssembly * AssemblySpec::GetParentIAssembly()
+{
+ LIMITED_METHOD_CONTRACT;
+ if(m_pParentAssembly)
+ return m_pParentAssembly->GetFile()->GetFusionAssembly();
+
+ return NULL;
+}
+
+LPCVOID AssemblySpec::GetParentAssemblyPtr()
+{
+ LIMITED_METHOD_CONTRACT;
+ if(m_pParentAssembly)
+ {
+ if (m_pParentAssembly->GetFile()->HasHostAssembly())
+ return m_pParentAssembly->GetFile()->GetHostAssembly();
+ else
+ return m_pParentAssembly->GetFile()->GetFusionAssembly();
+ }
+ return NULL;
+}
+
+#endif //FEATURE_FUSION
+
+
+
+DomainAssembly * AssemblySpec::GetParentAssembly()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_pParentAssembly;
+}