diff options
Diffstat (limited to 'src/vm/clrprivbinderfusion.cpp')
-rw-r--r-- | src/vm/clrprivbinderfusion.cpp | 819 |
1 files changed, 819 insertions, 0 deletions
diff --git a/src/vm/clrprivbinderfusion.cpp b/src/vm/clrprivbinderfusion.cpp new file mode 100644 index 0000000000..d31774f7b2 --- /dev/null +++ b/src/vm/clrprivbinderfusion.cpp @@ -0,0 +1,819 @@ +// 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. + + + +#include "common.h" // precompiled header + +#ifndef DACCESS_COMPILE + +//===================================================================================================================== +#include "assemblyspec.hpp" +#include "corhdr.h" +#include "domainfile.h" +#include "fusion.h" +#include "policy.h" +#include "sstring.h" +#include "stackingallocator.h" +#include "threads.h" +#include "clrprivbinderfusion.h" +#include "clrprivbinderutil.h" +#include "fusionlogging.h" + +using namespace CLRPrivBinderUtil; + +//================================================================================================= +#define STDMETHOD_NOTIMPL(...) \ + STDMETHOD(__VA_ARGS__) \ + { \ + WRAPPER_NO_CONTRACT; \ + _ASSERTE_MSG(false, "Method not implemented."); \ + return E_NOTIMPL; \ + } + +//================================================================================================= +static HRESULT PropagateOutStringArgument( + __in LPCSTR pszValue, + __out_ecount_opt(*pcchArg) LPWSTR pwzArg, + __in DWORD cchArg, + __out DWORD * pcchArg) +{ + LIMITED_METHOD_CONTRACT; + + VALIDATE_PTR_RET(pszValue); + VALIDATE_CONDITION((pwzArg == nullptr || cchArg > 0), return E_INVALIDARG); + + HRESULT hr = S_OK; + + if (pwzArg != nullptr) + { + DWORD cchWritten = WszMultiByteToWideChar( + CP_UTF8, 0 /*flags*/, pszValue, -1, pwzArg, cchArg); + + if (cchWritten == 0) + { + hr = HRESULT_FROM_GetLastError(); + } + else if (pcchArg != nullptr) + { + *pcchArg = cchWritten; + } + } + + if (pcchArg != nullptr && (pwzArg == nullptr || hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))) + { + *pcchArg = WszMultiByteToWideChar( + CP_UTF8, 0 /*flags*/, pszValue, -1, nullptr, 0); + + if (*pcchArg == 0) + { + hr = HRESULT_FROM_GetLastError(); + } + } + + return hr; +} + +//================================================================================================= +// This is needed to allow calls to IsAnyFrameworkAssembly in GC_NOTRIGGER/NO_FAULT regions (i.e., +// GC stack walking). CAssemblyName (which implements IAssemblyName in most other uses) allocates +// during construction and so cannot be used in this scenario. + +class AssemblySpecAsIAssemblyName + : public IAssemblyName +{ +public: + AssemblySpecAsIAssemblyName( + AssemblySpec * pSpec) + : m_pSpec(pSpec) + { LIMITED_METHOD_CONTRACT; } + + //============================================================================================= + // IUnknown methods + + // Not used by IsAnyFrameworkAssembly + STDMETHOD_(ULONG, AddRef()) + { + WRAPPER_NO_CONTRACT; + _ASSERTE_MSG(false, "Method not implemented."); + return E_NOTIMPL; + } + + // Not used by IsAnyFrameworkAssembly + STDMETHOD_(ULONG, Release()) + { + WRAPPER_NO_CONTRACT; + _ASSERTE_MSG(false, "Method not implemented."); + return E_NOTIMPL; + } + + // Not used by IsAnyFrameworkAssembly + STDMETHOD_NOTIMPL(QueryInterface( + REFIID riid, + void **ppvObject)); + + //============================================================================================= + // IAssemblyName methods + + STDMETHOD_NOTIMPL(SetProperty( + DWORD PropertyId, + void const * pvProperty, + DWORD cbProperty)); + +#define ASSURE_SUFFICIENT_BUFFER(SRCSIZE) \ + do { \ + if ((pvProperty == nullptr) || (*pcbProperty < SRCSIZE)) { \ + *pcbProperty = SRCSIZE; \ + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); \ + } \ + } while (false) + + STDMETHOD(GetProperty)( + DWORD PropertyId, + LPVOID pvProperty, + LPDWORD pcbProperty) + { + LIMITED_METHOD_CONTRACT; + + VALIDATE_PTR_RET(pcbProperty); + VALIDATE_CONDITION((pvProperty == nullptr) == (*pcbProperty == 0), return E_INVALIDARG); + + HRESULT hr = S_OK; + + switch (PropertyId) + { + case ASM_NAME_NAME: + return PropagateOutStringArgument(m_pSpec->GetName(), (LPWSTR) pvProperty, + *pcbProperty / sizeof(WCHAR), pcbProperty); + + case ASM_NAME_MAJOR_VERSION: + ASSURE_SUFFICIENT_BUFFER(sizeof(USHORT)); + *reinterpret_cast<USHORT*>(pvProperty) = m_pSpec->GetContext()->usMajorVersion; + *pcbProperty = sizeof(USHORT); + return S_OK; + + case ASM_NAME_MINOR_VERSION: + ASSURE_SUFFICIENT_BUFFER(sizeof(USHORT)); + *reinterpret_cast<USHORT*>(pvProperty) = m_pSpec->GetContext()->usMinorVersion; + *pcbProperty = sizeof(USHORT); + return S_OK; + + case ASM_NAME_BUILD_NUMBER: + ASSURE_SUFFICIENT_BUFFER(sizeof(USHORT)); + *reinterpret_cast<USHORT*>(pvProperty) = m_pSpec->GetContext()->usBuildNumber; + *pcbProperty = sizeof(USHORT); + return S_OK; + + case ASM_NAME_REVISION_NUMBER: + ASSURE_SUFFICIENT_BUFFER(sizeof(USHORT)); + *reinterpret_cast<USHORT*>(pvProperty) = m_pSpec->GetContext()->usRevisionNumber; + *pcbProperty = sizeof(USHORT); + return S_OK; + + case ASM_NAME_CULTURE: + if (m_pSpec->GetContext()->szLocale == nullptr) + { + return FUSION_E_INVALID_NAME; + } + return PropagateOutStringArgument(m_pSpec->GetContext()->szLocale, (LPWSTR) pvProperty, + *pcbProperty / sizeof(WCHAR), pcbProperty); + + case ASM_NAME_PUBLIC_KEY_TOKEN: + { + if (!m_pSpec->HasPublicKeyToken()) + { + return FUSION_E_INVALID_NAME; + } + + PBYTE pbSN; + DWORD cbSN; + m_pSpec->GetPublicKeyToken(&pbSN, &cbSN); + ASSURE_SUFFICIENT_BUFFER(cbSN); + memcpy_s(pvProperty, *pcbProperty, pbSN, cbSN); + *pcbProperty = cbSN; + } + return S_OK; + + case ASM_NAME_RETARGET: + ASSURE_SUFFICIENT_BUFFER(sizeof(BOOL)); + *reinterpret_cast<BOOL*>(pvProperty) = m_pSpec->IsRetargetable(); + *pcbProperty = sizeof(BOOL); + return S_OK; + + default: + _ASSERTE_MSG(false, "Unexpected property requested."); + return E_INVALIDARG; + } + } + +#undef ASSURE_SUFFICIENT_BUFFER + + // Not used by IsAnyFrameworkAssembly + STDMETHOD_NOTIMPL(Finalize()); + + // Not used by IsAnyFrameworkAssembly + STDMETHOD_NOTIMPL(GetDisplayName( + __out_ecount_opt(*pccDisplayName) LPOLESTR szDisplayName, + __inout LPDWORD pccDisplayName, + DWORD dwDisplayFlags)); + + // Not used by IsAnyFrameworkAssembly + STDMETHOD_NOTIMPL(Reserved( + REFIID refIID, + IUnknown *pUnkReserved1, + IUnknown *pUnkReserved2, + LPCOLESTR szReserved, + LONGLONG llReserved, + LPVOID pvReserved, + DWORD cbReserved, + LPVOID *ppReserved)); + + + STDMETHOD(GetName)( + __inout LPDWORD lpcwBuffer, + __out_ecount_opt(*lpcwBuffer) WCHAR *pwzName) + { + LIMITED_METHOD_CONTRACT; + + VALIDATE_PTR_RET(lpcwBuffer); + return PropagateOutStringArgument( + m_pSpec->GetName(), pwzName, *lpcwBuffer, lpcwBuffer); + } + + STDMETHOD(GetVersion)( + LPDWORD pdwVersionHi, + LPDWORD pdwVersionLow) + { + LIMITED_METHOD_CONTRACT; + + HRESULT hr = S_OK; + + VALIDATE_PTR_RET(pdwVersionHi); + VALIDATE_PTR_RET(pdwVersionLow); + + AssemblyMetaDataInternal * pAMDI = m_pSpec->GetContext(); + + *pdwVersionHi = MAKELONG(pAMDI->usMinorVersion, pAMDI->usMajorVersion); + *pdwVersionLow = MAKELONG(pAMDI->usRevisionNumber, pAMDI->usBuildNumber); + + return S_OK; + } + + + // Exists exclusively to support fusion's IsSystem helper, which compares against 'mscorlib'. + STDMETHOD(IsEqual)( + IAssemblyName *pName, + DWORD dwCmpFlags) + { + LIMITED_METHOD_CONTRACT; + + HRESULT hr = S_OK; + + VALIDATE_PTR_RET(pName); + + // This function is here just to support checks against the name 'mscorlib'. + if ((dwCmpFlags & ASM_CMPF_NAME) != ASM_CMPF_NAME) + { + return E_NOTIMPL; + } + + DWORD cchName1 = 0; + WCHAR wzName1[_MAX_PATH]; + IfFailRet(pName->GetName(&cchName1, wzName1)); + _ASSERTE(SString::_wcsicmp(wzName1, W("mscorlib")) == 0); + + WCHAR wzName2[_MAX_PATH]; + DWORD cchName2 = WszMultiByteToWideChar( + CP_UTF8, 0 /*flags*/, m_pSpec->GetName(), -1, wzName2, (int) (sizeof(wzName2) / sizeof(wzName2[0]))); + + if (0 == cchName2) + { + _ASSERTE(HRESULT_FROM_GetLastError() != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); + return HRESULT_FROM_GetLastError(); + } + + if (cchName1 != cchName2) + { + return S_FALSE; + } + + return SString::_wcsnicmp(wzName1, wzName2, cchName1) == 0 + ? S_OK + : S_FALSE; + } + + STDMETHOD_NOTIMPL(Clone( + IAssemblyName **pName)); + +private: + AssemblySpec * m_pSpec; +}; + +//===================================================================================================================== +HRESULT CLRPrivBinderFusion::FindFusionAssemblyBySpec( + LPVOID pvAppDomain, + LPVOID pvAssemblySpec, + BindingScope kBindingScope, + HRESULT * pResult, + ICLRPrivAssembly ** ppAssembly) +{ + LIMITED_METHOD_CONTRACT;; + HRESULT hr = S_OK; + + AppDomain* pAppDomain = reinterpret_cast<AppDomain*>(pvAppDomain); + AssemblySpec* pAssemblySpec = reinterpret_cast<AssemblySpec*>(pvAssemblySpec); + VALIDATE_PTR_RET(pAppDomain); + VALIDATE_PTR_RET(pAssemblySpec); + VALIDATE_PTR_RET(pResult); + VALIDATE_PTR_RET(ppAssembly); + + if (pAssemblySpec->IsContentType_WindowsRuntime()) + { + return CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT; + } + + BOOL fIsSupportedInAppX; + { + AssemblySpecAsIAssemblyName asName(pAssemblySpec); + + if (Fusion::Util::IsAnyFrameworkAssembly(&asName, &fIsSupportedInAppX) != S_OK) + { // Not a framework assembly identity. + IfFailRet(CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT); + } + } + + if (kBindingScope == kBindingScope_FrameworkSubset) + { // We should allow only some framework assemblies to load + + // DevMode has to allow all FX assemblies, not just a subset - see code:PreBind for more info + { + // Disabling for now, as it causes too many violations. + //CONTRACT_VIOLATION(GCViolation | FaultViolation | ModeViolation); + //_ASSERTE(!AppX::IsAppXDesignMode()); + } + + if (!fIsSupportedInAppX) + { // Assembly is blocked for AppX, fail the load + *pResult = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + *ppAssembly = nullptr; + return S_OK; + } + } + + return FindAssemblyBySpec(pvAppDomain, pvAssemblySpec, pResult, ppAssembly); +} + +//===================================================================================================================== +static +PEAssembly * FindCachedFile(AppDomain * pDomain, AssemblySpec * pSpec) +{ + // Look for cached bind result. Prefer a cached DomainAssembly, as it takes priority over a + // cached PEAssembly (which can be different from the one associated with the DomainAssembly). + DomainAssembly * pDomainAssembly = pDomain->FindCachedAssembly(pSpec, FALSE); + return (pDomainAssembly != nullptr) + ? (pDomainAssembly->GetFile()) + : (pDomain->FindCachedFile(pSpec, FALSE)); +} + +//===================================================================================================================== +// There is no need to create a separate binding record, since we can always just look in the AppDomain's +// AssemblySpecBindingCache for an answer (which is precisely what this function does). + +HRESULT CLRPrivBinderFusion::FindAssemblyBySpec( + LPVOID pvAppDomain, + LPVOID pvAssemblySpec, + HRESULT * pResult, + ICLRPrivAssembly ** ppAssembly) +{ + LIMITED_METHOD_CONTRACT;; + HRESULT hr = S_OK; + + AppDomain* pAppDomain = reinterpret_cast<AppDomain*>(pvAppDomain); + AssemblySpec* pAssemblySpec = reinterpret_cast<AssemblySpec*>(pvAssemblySpec); + VALIDATE_PTR_RET(pAppDomain); + VALIDATE_PTR_RET(pAssemblySpec); + VALIDATE_PTR_RET(pResult); + VALIDATE_PTR_RET(ppAssembly); + + // For the Architecture property, canonicalize peMSIL to peNone (which are considered equivalent), + // to ensure consistent lookups in the AssemblySpecBindingCache for the CLRPrivBinderFusion binder. + if (pAssemblySpec->GetPEKIND() == peMSIL) + { + pAssemblySpec->SetPEKIND(peNone); + } + + PEAssembly * pPEAssembly = FindCachedFile(pAppDomain, pAssemblySpec); + if (pPEAssembly == nullptr) + { + return E_FAIL; + } + + // Could be racing with another thread that has just added the PEAssembly to the binding cache + // but not yet allocated and assigned a host assembly. + if (!pPEAssembly->HasHostAssembly()) + { + return E_FAIL; + } + + *pResult = S_OK; + *ppAssembly = clr::SafeAddRef(pPEAssembly->GetHostAssembly()); + + return S_OK; +} + +//===================================================================================================================== +HRESULT CLRPrivBinderFusion::BindAssemblyByNameWorker( + IAssemblyName * pAssemblyName, + ICLRPrivAssembly ** ppAssembly) +{ + STANDARD_VM_CONTRACT; + PRECONDITION(CheckPointer(pAssemblyName)); + PRECONDITION(CheckPointer(ppAssembly)); + + HRESULT hr = S_OK; + + AppDomain * pCurDomain = AppDomain::GetCurrentDomain(); + if (pCurDomain == nullptr) + ThrowHR(E_UNEXPECTED); + + AssemblySpec prePolicySpec; + AssemblySpec postPolicySpec; + + prePolicySpec.InitializeSpec(pAssemblyName); + + // For the Architecture property, canonicalize peMSIL to peNone (which are considered equivalent), + // to ensure consistent lookups in the AssemblySpecBindingCache for the CLRPrivBinderFusion binder. + if (prePolicySpec.GetPEKIND() == peMSIL) + { + prePolicySpec.SetPEKIND(peNone); + } + + AssemblySpec * pBindSpec = &prePolicySpec; + PEAssemblyHolder pPEAssembly = clr::SafeAddRef(FindCachedFile(pCurDomain, pBindSpec)); + + if (pPEAssembly == nullptr) + { + // Early on in domain setup there may not be a fusion context, so skip ApplyPolicy then. + _ASSERTE(pCurDomain->GetFusionContext() != nullptr || prePolicySpec.IsMscorlib()); + if (pCurDomain->GetFusionContext() != nullptr) + { + ReleaseHolder<IAssemblyName> pPolicyAssemblyName; + DWORD dwPolicyApplied = 0; + ApplyPolicy(pAssemblyName, pCurDomain->GetFusionContext(), nullptr, &pPolicyAssemblyName, nullptr, nullptr, &dwPolicyApplied); + + if (dwPolicyApplied != 0) + { + postPolicySpec.InitializeSpec(pPolicyAssemblyName); + pBindSpec = &postPolicySpec; + pPEAssembly = clr::SafeAddRef(FindCachedFile(pCurDomain, pBindSpec)); + } + } + + if (pPEAssembly == nullptr) + { + // Trigger a load. + pPEAssembly = pCurDomain->BindAssemblySpec( + pBindSpec, // AssemblySpec + TRUE, // ThrowOnFileNotFound + FALSE, // RaisePrebindEvents + nullptr, // CallerStackMark + nullptr, // AssemblyLoadSecurity + FALSE); // fUseHostBinderIfAvailable - to avoid infinite recursion + _ASSERTE(FindCachedFile(pCurDomain, pBindSpec) == pPEAssembly || pBindSpec->IsMscorlib()); + } + + // If a post-policy spec was used, add the pre-policy spec to the binding cache + // so that it can be found by FindAssemblyBySpec. + if (&prePolicySpec != pBindSpec) + { + // Failure to add simply means someone else beat us to it. In that case + // the FindCachedFile call below (after catch block) will update result + // to the cached value. + INDEBUG(BOOL fRes =) pCurDomain->AddFileToCache(&prePolicySpec, pPEAssembly, TRUE /* fAllowFailure */); + _ASSERTE(!fRes || prePolicySpec.IsMscorlib() || FindCachedFile(pCurDomain, &prePolicySpec) == pPEAssembly); + } + + // Ensure that the assembly is discoverable through a consistent assembly name (the assembly def name of the assembly) + AssemblySpec specAssemblyDef; + specAssemblyDef.InitializeSpec(pPEAssembly); + + // It is expected that all assemlbies found here will be unified assemblies, and therefore have a public key. + _ASSERTE(specAssemblyDef.IsStrongNamed()); + + // Convert public key into the format that matches the garaunteed cache in the AssemblySpecBindingCache ... see the extended logic + // in Module::GetAssemblyIfLoaded. + if (specAssemblyDef.IsStrongNamed() && specAssemblyDef.HasPublicKey()) + { + specAssemblyDef.ConvertPublicKeyToToken(); + } + pCurDomain->AddFileToCache(&specAssemblyDef, pPEAssembly, TRUE); + } + + if (!pPEAssembly->HasHostAssembly()) + { + // This can happen if we just loaded the PEAssembly with BindAssemblySpec above, or if the PEAssembly + // Was not loaded through this binder. (NGEN Case) + + // Note: There can be multiple PEAssembly objects for the same file, however we have to create unique + // CLRPrivAssemblyFusion object, otherwise code:AppDomain::FindAssembly will not recognize the duplicates which + // will lead to creation of multiple code:DomainAssembly objects for the same file in the same AppDomain. + + InlineSString<128> ssPEAssemblyName; + FusionBind::GetAssemblyNameDisplayName(pPEAssembly->GetFusionAssemblyName(), ssPEAssemblyName, ASM_DISPLAYF_FULL); + NewHolder<CLRPrivAssemblyFusion> pAssemblyObj = new CLRPrivAssemblyFusion(ssPEAssemblyName.GetUnicode(), this); + + { + CrstHolder lock(&m_SetHostAssemblyLock); + if (!pPEAssembly->HasHostAssembly()) + { + // Add the host assembly to the PEAssembly. + pPEAssembly->SetHostAssembly(pAssemblyObj.Extract()); + } + } + } + + // Trigger a load so that a DomainAssembly is associated with the ICLRPrivAssembly created above. + pPEAssembly = clr::SafeAddRef(pCurDomain->LoadDomainAssembly(pBindSpec, pPEAssembly, FILE_LOADED)->GetFile()); + + _ASSERTE(pPEAssembly != nullptr); + _ASSERTE(pPEAssembly->HasHostAssembly()); + _ASSERTE(pCurDomain->FindAssembly(pPEAssembly->GetHostAssembly()) != nullptr); + + fusion::logging::LogMessage(0, ID_FUSLOG_BINDING_STATUS_FOUND, pPEAssembly->GetPath().GetUnicode()); + + *ppAssembly = clr::SafeAddRef(pPEAssembly->GetHostAssembly()); + + return hr; +} + +//===================================================================================================================== +void CLRPrivBinderFusion::BindMscorlib( + PEAssembly * pPEAssembly) +{ + STANDARD_VM_CONTRACT; + +#ifdef _DEBUG + NewArrayHolder<WCHAR> dbg_wszAssemblySimpleName; + _ASSERTE(SUCCEEDED(fusion::util::GetProperty(pPEAssembly->GetFusionAssemblyName(), ASM_NAME_NAME, &dbg_wszAssemblySimpleName))); + + _ASSERTE(wcscmp(dbg_wszAssemblySimpleName, W("mscorlib")) == 0); +#endif //_DEBUG + + NewHolder<CLRPrivAssemblyFusion> pPrivAssembly = new CLRPrivAssemblyFusion(W("mscorlib"), this); + + pPEAssembly->SetHostAssembly(pPrivAssembly.Extract()); +} + +//===================================================================================================================== +HRESULT CLRPrivBinderFusion::BindFusionAssemblyByName( + IAssemblyName * pAssemblyName, + BindingScope kBindingScope, + ICLRPrivAssembly ** ppAssembly) +{ + STANDARD_VM_CONTRACT; + HRESULT hr = S_OK; + + fusion::logging::StatusScope logStatus(0, ID_FUSLOG_BINDING_STATUS_FRAMEWORK, &hr); + + DWORD dwContentType = AssemblyContentType_Default; + IfFailRet(fusion::util::GetProperty(pAssemblyName, ASM_NAME_CONTENT_TYPE, &dwContentType)); + if ((hr == S_OK) && (dwContentType != AssemblyContentType_Default)) + { // Not a NetFX content type. + IfFailRet(CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT); + } + + BOOL fIsSupportedInAppX; + if (Fusion::Util::IsAnyFrameworkAssembly(pAssemblyName, &fIsSupportedInAppX) != S_OK) + { // Not a framework assembly identity. + IfFailRet(CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT); + } + if (kBindingScope == kBindingScope_FrameworkSubset) + { // We should allow only some framework assemblies to load + + // DevMode has to allow all FX assemblies, not just a subset - see code:PreBind for more info + _ASSERTE(!AppX::IsAppXDesignMode()); + + if (!fIsSupportedInAppX) + { // Assembly is blocked for AppX, fail the load + fusion::logging::LogMessage(0, ID_FUSLOG_BINDING_STATUS_FX_ASSEMBLY_BLOCKED); + + IfFailRet(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + } + } + + return (hr = BindAssemblyByNameWorker(pAssemblyName, ppAssembly)); +} + +//===================================================================================================================== +// Implements code:ICLRPrivBinder::BindAssemblyByName +HRESULT CLRPrivBinderFusion::BindAssemblyByName( + IAssemblyName * pAssemblyName, + ICLRPrivAssembly ** ppAssembly) +{ + WRAPPER_NO_CONTRACT; + return BindAssemblyByNameWorker( + pAssemblyName, + ppAssembly); +} + +//===================================================================================================================== +// Implements code:ICLRPrivBinder::GetBinderID +HRESULT CLRPrivBinderFusion::GetBinderID( + UINT_PTR *pBinderId) +{ + LIMITED_METHOD_CONTRACT; + + *pBinderId = (UINT_PTR)this; + return S_OK; +} + +//===================================================================================================================== +// Implements code:IBindContext::PreBind +HRESULT CLRPrivBinderFusion::PreBind( + IAssemblyName * pIAssemblyName, + DWORD dwPreBindFlags, + IBindResult ** ppIBindResult) +{ + STANDARD_BIND_CONTRACT; + PRECONDITION(CheckPointer(pIAssemblyName)); + PRECONDITION(CheckPointer(ppIBindResult)); + + HRESULT hr = S_OK; + + BOOL fIsSupportedInAppX; + if (Fusion::Util::IsAnyFrameworkAssembly(pIAssemblyName, &fIsSupportedInAppX) != S_OK) + { // Not a framework assembly identity. + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + EX_TRY + { + // Create new IL binding scope. + fusion::logging::BindingScope defaultScope(pIAssemblyName, FUSION_BIND_LOG_CATEGORY_DEFAULT); + + // Ideally the caller would give us arg kBindingContext like in code:BindFusionAssemblyByName, so we can give the same answer. + // That is not easy, so we will make the decision here: + // - DevMode will allow all FX assemblies (that covers designer binding context scenario for designers that need to + // load WPF with ngen images for perf reasons). + // We know that the real bind via code:BindFusionAssemblyByName will succeed for the assemblies (because we are in DevMode). + // - Normal mode (non-DevMode) we will allow only subset of FX assemblies. + // It implies that designer binding context (used by debuggers) will not use ngen images for blocked FX assemblies + // (transitively). That is acceptable performance trade-off. + if (!AppX::IsAppXDesignMode()) + { // We should allow only some framework assemblies to load + if (!fIsSupportedInAppX) + { // Assembly is blocked for AppX, fail the load + fusion::logging::LogMessage(0, ID_FUSLOG_BINDING_STATUS_FX_ASSEMBLY_BLOCKED); + + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + } + + if (SUCCEEDED(hr)) + { + AppDomain * pDomain = AppDomain::GetCurrentDomain(); + ReleaseHolder<IBindContext> pIBindContext; + if (SUCCEEDED(hr = GetBindContextFromApplicationContext(pDomain->CreateFusionContext(), &pIBindContext))) + { + hr = pIBindContext->PreBind(pIAssemblyName, dwPreBindFlags, ppIBindResult); + } + } + } + EX_CATCH_HRESULT(hr); + + return hr; +} + +//===================================================================================================================== +HRESULT CLRPrivBinderFusion::PreBindFusionAssemblyByName( + IAssemblyName *pIAssemblyName, + DWORD dwPreBindFlags, + IBindResult **ppIBindResult) +{ + STANDARD_VM_CONTRACT; + HRESULT hr = S_OK; + + DWORD dwContentType = AssemblyContentType_Default; + IfFailRet(fusion::util::GetProperty(pIAssemblyName, ASM_NAME_CONTENT_TYPE, &dwContentType)); + if ((hr == S_OK) && (dwContentType != AssemblyContentType_Default)) + { // Not a NetFX content type. + IfFailRet(CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT); + } + + IfFailRet(PreBind(pIAssemblyName, dwPreBindFlags, ppIBindResult)); + _ASSERTE(*ppIBindResult != nullptr); + + if (*ppIBindResult == nullptr) + IfFailRet(E_UNEXPECTED); + + return S_OK; +} + +//===================================================================================================================== +// Implements code:IBindContext::IsDefaultContext +HRESULT CLRPrivBinderFusion::IsDefaultContext() +{ + STANDARD_BIND_CONTRACT; + return S_OK; +} + +//===================================================================================================================== +CLRPrivBinderFusion::~CLRPrivBinderFusion() +{ + WRAPPER_NO_CONTRACT; +} + +//===================================================================================================================== +CLRPrivAssemblyFusion::CLRPrivAssemblyFusion( + LPCWSTR wszName, + CLRPrivBinderFusion * pBinder) + : m_pBinder(clr::SafeAddRef(pBinder)), + m_wszName(DuplicateStringThrowing(wszName)) +{ + STANDARD_VM_CONTRACT; +} + +//===================================================================================================================== +LPCWSTR CLRPrivAssemblyFusion::GetName() const +{ + LIMITED_METHOD_CONTRACT; + + return m_wszName; +} + +//===================================================================================================================== +// Implements code:IUnknown::Release +ULONG CLRPrivAssemblyFusion::Release() +{ + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_CAN_TAKE_LOCK; + _ASSERTE(m_cRef > 0); + + ULONG cRef = InterlockedDecrement(&m_cRef); + + if (cRef == 0) + { + delete this; + } + + return cRef; +} + +//===================================================================================================================== +// Implements code:ICLRPrivBinder::BindAssemblyByName +HRESULT CLRPrivAssemblyFusion::BindAssemblyByName( + IAssemblyName * pAssemblyName, + ICLRPrivAssembly ** ppAssembly) +{ + WRAPPER_NO_CONTRACT; + return m_pBinder->BindAssemblyByName( + pAssemblyName, + ppAssembly); +} + +//===================================================================================================================== +// Implements code:ICLRPrivBinder::GetBinderID +HRESULT CLRPrivAssemblyFusion::GetBinderID( + UINT_PTR *pBinderId) +{ + LIMITED_METHOD_CONTRACT; + + *pBinderId = reinterpret_cast<UINT_PTR>(m_pBinder.GetValue()); + return S_OK; +} + +//===================================================================================================================== +// Implements code:ICLRPrivAssembly::IsShareable +HRESULT CLRPrivAssemblyFusion::IsShareable( + BOOL * pbIsShareable) +{ + LIMITED_METHOD_CONTRACT; + *pbIsShareable = TRUE; // These things are only used in the AppX scenario, where all fusion assemblies are unified, shareable assemblies. + return S_OK; +} + +//===================================================================================================================== +// Implements code:ICLRPrivAssembly::GetAvailableImageTypes +HRESULT CLRPrivAssemblyFusion::GetAvailableImageTypes( + LPDWORD pdwImageTypes) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"CLRPrivAssemblyFusion::GetAvailableImageTypes"); + return E_NOTIMPL; +} + +//===================================================================================================================== +// Implements code:ICLRPrivAssembly::GetImageResource +HRESULT CLRPrivAssemblyFusion::GetImageResource( + DWORD dwImageType, + DWORD *pdwImageType, + ICLRPrivResource ** ppIResource) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(!"CLRPrivAssemblyFusion::GetImageResource"); + return E_NOTIMPL; +} + +#endif // !DACCESS_COMPILE + |