diff options
author | Tomáš Rylek <trylek@microsoft.com> | 2020-12-08 05:19:44 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-08 05:19:44 +0300 |
commit | 69e114c1abf91241a0eeecf1ecceab4711b8aa62 (patch) | |
tree | b81a0b35748f5e598412bcc504335cdbd322cd43 /src/coreclr/binder | |
parent | 0ec07945a9759a72a689edbb01e69b232e26e05a (diff) |
December infra rollout - remove duplicated 'src' from coreclr subrepo (src/coreclr/src becomes src/coreclr) (#44973)
* Move src/coreclr/src/Directory.Build.targets to src/coreclr
Merge src/coreclr/src/CMakeLists.txt into src/coreclr/CMakeLists.txt
* Mechanical move of src/coreclr/src to src/coreclr
* Scripts adjustments to reflect the changed paths
Diffstat (limited to 'src/coreclr/binder')
50 files changed, 9274 insertions, 0 deletions
diff --git a/src/coreclr/binder/CMakeLists.txt b/src/coreclr/binder/CMakeLists.txt new file mode 100644 index 00000000000..208f1214dd0 --- /dev/null +++ b/src/coreclr/binder/CMakeLists.txt @@ -0,0 +1,99 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) +include_directories(BEFORE "../vm/${ARCH_SOURCES_DIR}") +include_directories(BEFORE "../vm") +include_directories(BEFORE "inc") + +set(BINDER_COMMON_SOURCES + applicationcontext.cpp + assembly.cpp + assemblybinder.cpp + assemblyidentitycache.cpp + assemblyname.cpp + bindertracing.cpp + clrprivbindercoreclr.cpp + coreclrbindercommon.cpp + failurecache.cpp + fusionassemblyname.cpp + stringlexer.cpp + textualidentityparser.cpp + utils.cpp + variables.cpp +) + +set(BINDER_COMMON_HEADERS + inc/applicationcontext.hpp + inc/applicationcontext.inl + inc/assembly.hpp + inc/assembly.inl + inc/assemblybinder.hpp + inc/assemblyentry.hpp + inc/assemblyhashtraits.hpp + inc/assemblyidentity.hpp + inc/assemblyidentitycache.hpp + inc/assemblyname.hpp + inc/assemblyname.inl + inc/assemblyversion.hpp + inc/assemblyversion.inl + inc/bindertypes.hpp + inc/bindertracing.h + inc/bindresult.hpp + inc/bindresult.inl + inc/clrprivbindercoreclr.h + inc/coreclrbindercommon.h + inc/failurecache.hpp + inc/failurecachehashtraits.hpp + inc/fusionassemblyname.hpp + inc/fusionhelpers.hpp + inc/loadcontext.hpp + inc/loadcontext.inl + inc/stringlexer.hpp + inc/stringlexer.inl + inc/textualidentityparser.hpp + inc/utils.hpp + inc/variables.hpp +) + +set(BINDER_SOURCES + ${BINDER_COMMON_SOURCES} + activitytracker.cpp + clrprivbinderassemblyloadcontext.cpp +) + +set(BINDER_HEADERS + ${BINDER_COMMON_HEADERS} + inc/activitytracker.h + inc/clrprivbinderassemblyloadcontext.h + inc/contextentry.hpp +) + +set(BINDER_CROSSGEN_SOURCES + ${BINDER_COMMON_SOURCES} +) + +set(BINDER_CROSSGEN_HEADERS + ${BINDER_COMMON_HEADERS} +) + +if (CLR_CMAKE_TARGET_WIN32) + list(APPEND BINDER_SOURCES ${BINDER_HEADERS}) + list(APPEND BINDER_CROSSGEN_SOURCES ${BINDER_CROSSGEN_HEADERS}) +endif(CLR_CMAKE_TARGET_WIN32) + +convert_to_absolute_path(BINDER_SOURCES ${BINDER_SOURCES}) +convert_to_absolute_path(BINDER_CROSSGEN_SOURCES ${BINDER_CROSSGEN_SOURCES}) + +add_library_clr(v3binder_obj + OBJECT + ${BINDER_SOURCES} +) +add_dependencies(v3binder_obj eventing_headers) +add_library(v3binder INTERFACE) +target_sources(v3binder INTERFACE $<TARGET_OBJECTS:v3binder_obj>) + +add_library_clr(v3binder_crossgen + STATIC + ${BINDER_CROSSGEN_SOURCES} +) +add_dependencies(v3binder_crossgen eventing_headers) +set_target_properties(v3binder_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) + diff --git a/src/coreclr/binder/activitytracker.cpp b/src/coreclr/binder/activitytracker.cpp new file mode 100644 index 00000000000..b7287d3afa9 --- /dev/null +++ b/src/coreclr/binder/activitytracker.cpp @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// activitytracker.cpp +// + + +// +// Helpers for interaction with the managed ActivityTracker +// +// ============================================================ + +#include "common.h" +#include "activitytracker.h" + +void ActivityTracker::Start(/*out*/ GUID *activityId, /*out*/ GUID *relatedActivityId) +{ + GCX_COOP(); + + OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); + + PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__START_ASSEMBLY_LOAD); + DECLARE_ARGHOLDER_ARRAY(args, 2); + args[ARGNUM_0] = PTR_TO_ARGHOLDER(activityId); + args[ARGNUM_1] = PTR_TO_ARGHOLDER(relatedActivityId); + + CALL_MANAGED_METHOD_NORET(args) +} + +void ActivityTracker::Stop(/*out*/ GUID *activityId) +{ + GCX_COOP(); + + OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); + + PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__STOP_ASSEMBLY_LOAD); + DECLARE_ARGHOLDER_ARRAY(args, 1); + args[ARGNUM_0] = PTR_TO_ARGHOLDER(activityId); + + CALL_MANAGED_METHOD_NORET(args) +} diff --git a/src/coreclr/binder/applicationcontext.cpp b/src/coreclr/binder/applicationcontext.cpp new file mode 100644 index 00000000000..f865b424049 --- /dev/null +++ b/src/coreclr/binder/applicationcontext.cpp @@ -0,0 +1,336 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// ApplicationContext.cpp +// + + +// +// Implements the ApplicationContext class +// +// ============================================================ + +#include "applicationcontext.hpp" +#include "stringarraylist.h" +#include "loadcontext.hpp" +#include "failurecache.hpp" +#include "assemblyidentitycache.hpp" +#include "utils.hpp" +#include "variables.hpp" +#include "ex.h" +#include "clr/fs/path.h" +using namespace clr::fs; + +namespace BINDER_SPACE +{ + STDMETHODIMP ApplicationContext::QueryInterface(REFIID riid, + void **ppv) + { + HRESULT hr = S_OK; + + if (ppv == NULL) + { + hr = E_POINTER; + } + else + { + if (IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppv = static_cast<IUnknown *>(this); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + } + + return hr; + } + + STDMETHODIMP_(ULONG) ApplicationContext::AddRef() + { + return InterlockedIncrement(&m_cRef); + } + + STDMETHODIMP_(ULONG) ApplicationContext::Release() + { + ULONG ulRef = InterlockedDecrement(&m_cRef); + + if (ulRef == 0) + { + delete this; + } + + return ulRef; + } + + ApplicationContext::ApplicationContext() + { + m_cRef = 1; + m_dwAppDomainId = 0; + m_pExecutionContext = NULL; + m_pFailureCache = NULL; + m_contextCS = NULL; + m_pTrustedPlatformAssemblyMap = nullptr; + m_binderID = 0; + } + + ApplicationContext::~ApplicationContext() + { + SAFE_RELEASE(m_pExecutionContext); + SAFE_DELETE(m_pFailureCache); + + if (m_contextCS != NULL) + { + ClrDeleteCriticalSection(m_contextCS); + } + + if (m_pTrustedPlatformAssemblyMap != nullptr) + { + delete m_pTrustedPlatformAssemblyMap; + } + } + + HRESULT ApplicationContext::Init(UINT_PTR binderID) + { + HRESULT hr = S_OK; + + ReleaseHolder<ExecutionContext> pExecutionContext; + + FailureCache *pFailureCache = NULL; + + // Allocate context objects + SAFE_NEW(pExecutionContext, ExecutionContext); + + SAFE_NEW(pFailureCache, FailureCache); + + m_contextCS = ClrCreateCriticalSection( + CrstFusionAppCtx, + CRST_REENTRANCY); + if (!m_contextCS) + { + SAFE_DELETE(pFailureCache); + hr = E_OUTOFMEMORY; + } + else + { + m_pExecutionContext = pExecutionContext.Extract(); + + m_pFailureCache = pFailureCache; + } + + m_binderID = binderID; + Exit: + return hr; + } + + HRESULT ApplicationContext::SetupBindingPaths(SString &sTrustedPlatformAssemblies, + SString &sPlatformResourceRoots, + SString &sAppPaths, + SString &sAppNiPaths, + BOOL fAcquireLock) + { + HRESULT hr = S_OK; + +#ifndef CROSSGEN_COMPILE + CRITSEC_Holder contextLock(fAcquireLock ? GetCriticalSectionCookie() : NULL); +#endif + if (m_pTrustedPlatformAssemblyMap != nullptr) + { + GO_WITH_HRESULT(S_OK); + } + + // + // Parse TrustedPlatformAssemblies + // + m_pTrustedPlatformAssemblyMap = new SimpleNameToFileNameMap(); + + sTrustedPlatformAssemblies.Normalize(); + + for (SString::Iterator i = sTrustedPlatformAssemblies.Begin(); i != sTrustedPlatformAssemblies.End(); ) + { + SString fileName; + SString simpleName; + bool isNativeImage = false; + HRESULT pathResult = S_OK; + IF_FAIL_GO(pathResult = GetNextTPAPath(sTrustedPlatformAssemblies, i, /*dllOnly*/ false, fileName, simpleName, isNativeImage)); + if (pathResult == S_FALSE) + { + break; + } + + const SimpleNameToFileNameMapEntry *pExistingEntry = m_pTrustedPlatformAssemblyMap->LookupPtr(simpleName.GetUnicode()); + + if (pExistingEntry != nullptr) + { + // + // We want to store only the first entry matching a simple name we encounter. + // The exception is if we first store an IL reference and later in the string + // we encounter a native image. Since we don't touch IL in the presence of + // native images, we replace the IL entry with the NI. + // + if ((pExistingEntry->m_wszILFileName != nullptr && !isNativeImage) || + (pExistingEntry->m_wszNIFileName != nullptr && isNativeImage)) + { + continue; + } + } + + LPWSTR wszSimpleName = nullptr; + if (pExistingEntry == nullptr) + { + wszSimpleName = new WCHAR[simpleName.GetCount() + 1]; + if (wszSimpleName == nullptr) + { + GO_WITH_HRESULT(E_OUTOFMEMORY); + } + wcscpy_s(wszSimpleName, simpleName.GetCount() + 1, simpleName.GetUnicode()); + } + else + { + wszSimpleName = pExistingEntry->m_wszSimpleName; + } + + LPWSTR wszFileName = new WCHAR[fileName.GetCount() + 1]; + if (wszFileName == nullptr) + { + GO_WITH_HRESULT(E_OUTOFMEMORY); + } + wcscpy_s(wszFileName, fileName.GetCount() + 1, fileName.GetUnicode()); + + SimpleNameToFileNameMapEntry mapEntry; + mapEntry.m_wszSimpleName = wszSimpleName; + if (isNativeImage) + { + mapEntry.m_wszNIFileName = wszFileName; + mapEntry.m_wszILFileName = pExistingEntry == nullptr ? nullptr : pExistingEntry->m_wszILFileName; + } + else + { + mapEntry.m_wszILFileName = wszFileName; + mapEntry.m_wszNIFileName = pExistingEntry == nullptr ? nullptr : pExistingEntry->m_wszNIFileName; + } + + m_pTrustedPlatformAssemblyMap->AddOrReplace(mapEntry); + } + + // + // Parse PlatformResourceRoots + // + sPlatformResourceRoots.Normalize(); + for (SString::Iterator i = sPlatformResourceRoots.Begin(); i != sPlatformResourceRoots.End(); ) + { + SString pathName; + HRESULT pathResult = S_OK; + + IF_FAIL_GO(pathResult = GetNextPath(sPlatformResourceRoots, i, pathName)); + if (pathResult == S_FALSE) + { + break; + } + +#ifndef CROSSGEN_COMPILE + if (Path::IsRelative(pathName)) + { + GO_WITH_HRESULT(E_INVALIDARG); + } +#endif + + m_platformResourceRoots.Append(pathName); + } + + // + // Parse AppPaths + // + sAppPaths.Normalize(); + for (SString::Iterator i = sAppPaths.Begin(); i != sAppPaths.End(); ) + { + SString pathName; + HRESULT pathResult = S_OK; + + IF_FAIL_GO(pathResult = GetNextPath(sAppPaths, i, pathName)); + if (pathResult == S_FALSE) + { + break; + } + +#ifndef CROSSGEN_COMPILE + if (Path::IsRelative(pathName)) + { + GO_WITH_HRESULT(E_INVALIDARG); + } +#endif + + m_appPaths.Append(pathName); + } + + // + // Parse AppNiPaths + // + sAppNiPaths.Normalize(); + for (SString::Iterator i = sAppNiPaths.Begin(); i != sAppNiPaths.End(); ) + { + SString pathName; + HRESULT pathResult = S_OK; + + IF_FAIL_GO(pathResult = GetNextPath(sAppNiPaths, i, pathName)); + if (pathResult == S_FALSE) + { + break; + } + +#ifndef CROSSGEN_COMPILE + if (Path::IsRelative(pathName)) + { + GO_WITH_HRESULT(E_INVALIDARG); + } +#endif + + m_appNiPaths.Append(pathName); + } + + Exit: + return hr; + } + + HRESULT ApplicationContext::GetAssemblyIdentity(LPCSTR szTextualIdentity, + AssemblyIdentityUTF8 **ppAssemblyIdentity) + { + HRESULT hr = S_OK; + + _ASSERTE(szTextualIdentity != NULL); + _ASSERTE(ppAssemblyIdentity != NULL); + + CRITSEC_Holder contextLock(GetCriticalSectionCookie()); + + AssemblyIdentityUTF8 *pAssemblyIdentity = m_assemblyIdentityCache.Lookup(szTextualIdentity); + if (pAssemblyIdentity == NULL) + { + NewHolder<AssemblyIdentityUTF8> pNewAssemblyIdentity; + SString sTextualIdentity; + + SAFE_NEW(pNewAssemblyIdentity, AssemblyIdentityUTF8); + sTextualIdentity.SetUTF8(szTextualIdentity); + + IF_FAIL_GO(TextualIdentityParser::Parse(sTextualIdentity, pNewAssemblyIdentity)); + IF_FAIL_GO(m_assemblyIdentityCache.Add(szTextualIdentity, pNewAssemblyIdentity)); + + pNewAssemblyIdentity->PopulateUTF8Fields(); + + pAssemblyIdentity = pNewAssemblyIdentity.Extract(); + } + + *ppAssemblyIdentity = pAssemblyIdentity; + + Exit: + return hr; + } + + bool ApplicationContext::IsTpaListProvided() + { + return m_pTrustedPlatformAssemblyMap != nullptr; + } +}; diff --git a/src/coreclr/binder/assembly.cpp b/src/coreclr/binder/assembly.cpp new file mode 100644 index 00000000000..d4948c4d1ae --- /dev/null +++ b/src/coreclr/binder/assembly.cpp @@ -0,0 +1,213 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// Assembly.cpp +// + + +// +// Implements the Assembly class +// +// ============================================================ +#include "common.h" +#include "assembly.hpp" +#include "utils.hpp" + +namespace BINDER_SPACE +{ + namespace + { + BOOL IsPlatformArchitecture(PEKIND kArchitecture) + { + return ((kArchitecture != peMSIL) && (kArchitecture != peNone)); + } + }; + + STDMETHODIMP Assembly::QueryInterface(REFIID riid, + void **ppv) + { + HRESULT hr = S_OK; + + if (ppv == NULL) + { + hr = E_POINTER; + } + else + { + if (IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppv = static_cast<IUnknown *>(this); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + } + + return hr; + } + + STDMETHODIMP_(ULONG) Assembly::AddRef() + { + return InterlockedIncrement(&m_cRef); + } + + STDMETHODIMP_(ULONG) Assembly::Release() + { + ULONG ulRef = InterlockedDecrement(&m_cRef); + + if (ulRef == 0) + { + delete this; + } + + return ulRef; + } + + Assembly::Assembly() + { + m_cRef = 1; + m_pPEImage = NULL; + m_pNativePEImage = NULL; + m_pAssemblyName = NULL; + m_pMDImport = NULL; + m_dwAssemblyFlags = FLAG_NONE; + m_pBinder = NULL; + } + + Assembly::~Assembly() + { + if (m_pPEImage != NULL) + { + BinderReleasePEImage(m_pPEImage); + m_pPEImage = NULL; + } + +#ifdef FEATURE_PREJIT + if (m_pNativePEImage != NULL) + { + BinderReleasePEImage(m_pNativePEImage); + m_pNativePEImage = NULL; + } +#endif + + SAFE_RELEASE(m_pAssemblyName); + SAFE_RELEASE(m_pMDImport); + } + + HRESULT Assembly::Init(IMDInternalImport *pIMetaDataAssemblyImport, + PEKIND PeKind, + PEImage *pPEImage, + PEImage *pNativePEImage, + SString &assemblyPath, + BOOL fIsInGAC) + { + HRESULT hr = S_OK; + + ReleaseHolder<AssemblyName> pAssemblyName; + SAFE_NEW(pAssemblyName, AssemblyName); + + // Get assembly name def from meta data import and store it for later refs access + IF_FAIL_GO(pAssemblyName->Init(pIMetaDataAssemblyImport, PeKind)); + SetMDImport(pIMetaDataAssemblyImport); + if (!fIsInGAC) + { + GetPath().Set(assemblyPath); + } + + // Safe architecture for validation + PEKIND kAssemblyArchitecture; + kAssemblyArchitecture = pAssemblyName->GetArchitecture(); + SetIsInGAC(fIsInGAC); + SetPEImage(pPEImage); + SetNativePEImage(pNativePEImage); + pAssemblyName->SetIsDefinition(TRUE); + + // Now take ownership of assembly names + SetAssemblyName(pAssemblyName.Extract(), FALSE /* fAddRef */); + + // Finally validate architecture + if (!IsValidArchitecture(kAssemblyArchitecture)) + { + // Assembly image can't be executed on this platform + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT)); + } + + Exit: + return hr; + } + + HRESULT Assembly::GetMVID(GUID *pMVID) + { + // Zero init the GUID incase we fail + ZeroMemory(pMVID, sizeof(GUID)); + + return m_pMDImport->GetScopeProps(NULL, pMVID); + } + + /* static */ + PEKIND Assembly::GetSystemArchitecture() + { +#if defined(TARGET_X86) + return peI386; +#elif defined(TARGET_AMD64) + return peAMD64; +#elif defined(TARGET_ARM) + return peARM; +#elif defined(TARGET_ARM64) + return peARM64; +#else + PORTABILITY_ASSERT("Assembly::GetSystemArchitecture"); +#endif + } + + /* static */ + BOOL Assembly::IsValidArchitecture(PEKIND kArchitecture) + { + if (!IsPlatformArchitecture(kArchitecture)) + return TRUE; + + return (kArchitecture == GetSystemArchitecture()); + } + + // -------------------------------------------------------------------- + // ICLRPrivAssembly methods + // -------------------------------------------------------------------- + LPCWSTR Assembly::GetSimpleName() + { + AssemblyName *pAsmName = GetAssemblyName(); + return (pAsmName == nullptr ? nullptr : (LPCWSTR)pAsmName->GetSimpleName()); + } + + HRESULT Assembly::BindAssemblyByName(IAssemblyName * pIAssemblyName, ICLRPrivAssembly ** ppAssembly) + { + return (m_pBinder == NULL) ? E_FAIL : m_pBinder->BindAssemblyByName(pIAssemblyName, ppAssembly); + } + + HRESULT Assembly::GetBinderID(UINT_PTR *pBinderId) + { + return (m_pBinder == NULL) ? E_FAIL : m_pBinder->GetBinderID(pBinderId); + } + + HRESULT Assembly::GetLoaderAllocator(LPVOID* pLoaderAllocator) + { + return (m_pBinder == NULL) ? E_FAIL : m_pBinder->GetLoaderAllocator(pLoaderAllocator); + } + + HRESULT Assembly::GetAvailableImageTypes( + LPDWORD pdwImageTypes) + { + HRESULT hr = E_FAIL; + + if(pdwImageTypes == nullptr) + return E_INVALIDARG; + + *pdwImageTypes = ASSEMBLY_IMAGE_TYPE_ASSEMBLY; + + return S_OK; + } +} + diff --git a/src/coreclr/binder/assemblybinder.cpp b/src/coreclr/binder/assemblybinder.cpp new file mode 100644 index 00000000000..aa29465af82 --- /dev/null +++ b/src/coreclr/binder/assemblybinder.cpp @@ -0,0 +1,1579 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// AssemblyBinder.cpp +// + + +// +// Implements the AssemblyBinder class +// +// ============================================================ + +#include "assemblybinder.hpp" +#include "assemblyname.hpp" +#include "assembly.hpp" +#include "applicationcontext.hpp" +#include "bindertracing.h" +#include "loadcontext.hpp" +#include "bindresult.inl" +#include "failurecache.hpp" +#include "utils.hpp" +#include "variables.hpp" +#include "stringarraylist.h" +#include "configuration.h" + +#define APP_DOMAIN_LOCKED_UNLOCKED 0x02 +#define APP_DOMAIN_LOCKED_CONTEXT 0x04 + +#ifndef IMAGE_FILE_MACHINE_ARM64 +#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian +#endif + +BOOL IsCompilationProcess(); + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) +#include "clrprivbindercoreclr.h" +#include "clrprivbinderassemblyloadcontext.h" +// Helper function in the VM, invoked by the Binder, to invoke the host assembly resolver +extern HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, + IAssemblyName *pIAssemblyName, CLRPrivBinderCoreCLR *pTPABinder, + BINDER_SPACE::AssemblyName *pAssemblyName, ICLRPrivAssembly **ppLoadedAssembly); + +#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + +namespace BINDER_SPACE +{ + namespace + { + // + // This defines the assembly equivalence relation + // + bool IsCompatibleAssemblyVersion(/* in */ AssemblyName *pRequestedName, + /* in */ AssemblyName *pFoundName) + { + AssemblyVersion *pRequestedVersion = pRequestedName->GetVersion(); + AssemblyVersion *pFoundVersion = pFoundName->GetVersion(); + + if (!pRequestedVersion->HasMajor()) + { + // An unspecified requested version component matches any value for the same component in the found version, + // regardless of lesser-order version components + return true; + } + if (!pFoundVersion->HasMajor() || pRequestedVersion->GetMajor() > pFoundVersion->GetMajor()) + { + // - A specific requested version component does not match an unspecified value for the same component in + // the found version, regardless of lesser-order version components + // - Or, the requested version is greater than the found version + return false; + } + if (pRequestedVersion->GetMajor() < pFoundVersion->GetMajor()) + { + // The requested version is less than the found version + return true; + } + + if (!pRequestedVersion->HasMinor()) + { + return true; + } + if (!pFoundVersion->HasMinor() || pRequestedVersion->GetMinor() > pFoundVersion->GetMinor()) + { + return false; + } + if (pRequestedVersion->GetMinor() < pFoundVersion->GetMinor()) + { + return true; + } + + if (!pRequestedVersion->HasBuild()) + { + return true; + } + if (!pFoundVersion->HasBuild() || pRequestedVersion->GetBuild() > pFoundVersion->GetBuild()) + { + return false; + } + if (pRequestedVersion->GetBuild() < pFoundVersion->GetBuild()) + { + return true; + } + + if (!pRequestedVersion->HasRevision()) + { + return true; + } + if (!pFoundVersion->HasRevision() || pRequestedVersion->GetRevision() > pFoundVersion->GetRevision()) + { + return false; + } + return true; + } + + HRESULT URLToFullPath(PathString &assemblyPath) + { + HRESULT hr = S_OK; + + SString::Iterator pos = assemblyPath.Begin(); + if (assemblyPath.MatchCaseInsensitive(pos, g_BinderVariables->httpURLPrefix)) + { + // HTTP downloads are unsupported + hr = FUSION_E_CODE_DOWNLOAD_DISABLED; + } + else + { + SString fullAssemblyPath; + WCHAR *pwzFullAssemblyPath = fullAssemblyPath.OpenUnicodeBuffer(MAX_LONGPATH); + DWORD dwCCFullAssemblyPath = MAX_LONGPATH + 1; // SString allocates extra byte for null. + + MutateUrlToPath(assemblyPath); + + dwCCFullAssemblyPath = WszGetFullPathName(assemblyPath.GetUnicode(), + dwCCFullAssemblyPath, + pwzFullAssemblyPath, + NULL); + if (dwCCFullAssemblyPath > MAX_LONGPATH) + { + fullAssemblyPath.CloseBuffer(); + pwzFullAssemblyPath = fullAssemblyPath.OpenUnicodeBuffer(dwCCFullAssemblyPath - 1); + dwCCFullAssemblyPath = WszGetFullPathName(assemblyPath.GetUnicode(), + dwCCFullAssemblyPath, + pwzFullAssemblyPath, + NULL); + } + fullAssemblyPath.CloseBuffer(dwCCFullAssemblyPath); + + if (dwCCFullAssemblyPath == 0) + { + hr = HRESULT_FROM_GetLastError(); + } + else + { + assemblyPath.Set(fullAssemblyPath); + } + } + + return hr; + } + +#ifndef CROSSGEN_COMPILE + HRESULT CreateImageAssembly(IMDInternalImport *pIMetaDataAssemblyImport, + PEKIND PeKind, + PEImage *pPEImage, + PEImage *pNativePEImage, + BindResult *pBindResult) + { + HRESULT hr = S_OK; + ReleaseHolder<Assembly> pAssembly; + PathString asesmblyPath; + + SAFE_NEW(pAssembly, Assembly); + IF_FAIL_GO(pAssembly->Init(pIMetaDataAssemblyImport, + PeKind, + pPEImage, + pNativePEImage, + asesmblyPath, + FALSE /* fIsInGAC */)); + + pBindResult->SetResult(pAssembly); + pBindResult->SetIsFirstRequest(TRUE); + + Exit: + return hr; + } +#endif // !CROSSGEN_COMPILE + }; + + /* static */ + HRESULT AssemblyBinder::Startup() + { + STATIC_CONTRACT_NOTHROW; + + HRESULT hr = S_OK; + + // This should only be called once + _ASSERTE(g_BinderVariables == NULL); + g_BinderVariables = new Variables(); + IF_FAIL_GO(g_BinderVariables->Init()); + + Exit: + return hr; + } + + + HRESULT AssemblyBinder::TranslatePEToArchitectureType(DWORD *pdwPAFlags, PEKIND *PeKind) + { + HRESULT hr = S_OK; + + _ASSERTE(pdwPAFlags != NULL); + _ASSERTE(PeKind != NULL); + + CorPEKind CLRPeKind = (CorPEKind) pdwPAFlags[0]; + DWORD dwImageType = pdwPAFlags[1]; + + *PeKind = peNone; + + if(CLRPeKind == peNot) + { + // Not a PE. Shouldn't ever get here. + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT)); + } + else + { + if ((CLRPeKind & peILonly) && !(CLRPeKind & pe32Plus) && + !(CLRPeKind & pe32BitRequired) && dwImageType == IMAGE_FILE_MACHINE_I386) + { + // Processor-agnostic (MSIL) + *PeKind = peMSIL; + } + else if (CLRPeKind & pe32Plus) + { + // 64-bit + if (CLRPeKind & pe32BitRequired) + { + // Invalid + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT)); + } + + // Regardless of whether ILONLY is set or not, the architecture + // is the machine type. + if(dwImageType == IMAGE_FILE_MACHINE_ARM64) + *PeKind = peARM64; + else if (dwImageType == IMAGE_FILE_MACHINE_AMD64) + *PeKind = peAMD64; + else + { + // We don't support other architectures + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT)); + } + } + else + { + // 32-bit, non-agnostic + if(dwImageType == IMAGE_FILE_MACHINE_I386) + *PeKind = peI386; + else if(dwImageType == IMAGE_FILE_MACHINE_ARMNT) + *PeKind = peARM; + else + { + // Not supported + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT)); + } + } + } + + Exit: + return hr; + } + + // See code:BINDER_SPACE::AssemblyBinder::GetAssembly for info on fNgenExplicitBind + // and fExplicitBindToNativeImage, and see code:CEECompileInfo::LoadAssemblyByPath + // for an example of how they're used. + HRESULT AssemblyBinder::BindAssembly(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* in */ LPCWSTR szCodeBase, + /* in */ PEAssembly *pParentAssembly, + /* in */ BOOL fNgenExplicitBind, + /* in */ BOOL fExplicitBindToNativeImage, + /* in */ bool excludeAppPaths, + /* out */ Assembly **ppAssembly) + { + HRESULT hr = S_OK; + LONG kContextVersion = 0; + BindResult bindResult; + + // Tracing happens outside the binder lock to avoid calling into managed code within the lock + BinderTracing::ResolutionAttemptedOperation tracer{pAssemblyName, pApplicationContext->GetBinderID(), 0 /*managedALC*/, hr}; + +#ifndef CROSSGEN_COMPILE + Retry: + { + // Lock the binding application context + CRITSEC_Holder contextLock(pApplicationContext->GetCriticalSectionCookie()); +#endif + + if (szCodeBase == NULL) + { + IF_FAIL_GO(BindByName(pApplicationContext, + pAssemblyName, + false, // skipFailureCaching + false, // skipVersionCompatibilityCheck + excludeAppPaths, + &bindResult)); + } + else + { + PathString assemblyPath(szCodeBase); + + // Convert URL to full path and block HTTP downloads + IF_FAIL_GO(URLToFullPath(assemblyPath)); + BOOL fDoNgenExplicitBind = fNgenExplicitBind; + + // Only use explicit ngen binding in the new coreclr path-based binding model + if (!pApplicationContext->IsTpaListProvided()) + { + fDoNgenExplicitBind = FALSE; + } + + IF_FAIL_GO(BindWhereRef(pApplicationContext, + assemblyPath, + fDoNgenExplicitBind, + fExplicitBindToNativeImage, + excludeAppPaths, + &bindResult)); + } + + // Remember the post-bind version + kContextVersion = pApplicationContext->GetVersion(); + +#ifndef CROSSGEN_COMPILE + } // lock(pApplicationContext) +#endif + + Exit: + tracer.TraceBindResult(bindResult); + + if (bindResult.HaveResult()) + { +#ifndef CROSSGEN_COMPILE + BindResult hostBindResult; + + hr = RegisterAndGetHostChosen(pApplicationContext, + kContextVersion, + &bindResult, + &hostBindResult); + + if (hr == S_FALSE) + { + // Another bind interfered. We need to retry the entire bind. + // This by design loops as long as needed because by construction we eventually + // will succeed or fail the bind. + bindResult.Reset(); + goto Retry; + } + else if (hr == S_OK) + { + *ppAssembly = hostBindResult.GetAsAssembly(TRUE /* fAddRef */); + } +#else // CROSSGEN_COMPILE + + *ppAssembly = bindResult.GetAsAssembly(TRUE /* fAddRef */); + +#endif // CROSSGEN_COMPILE + } + + return hr; + } + + /* static */ + HRESULT AssemblyBinder::BindToSystem(SString &systemDirectory, + Assembly **ppSystemAssembly, + bool fBindToNativeImage) + { + // Indirect check that binder was initialized. + _ASSERTE(g_BinderVariables != NULL); + + HRESULT hr = S_OK; + + _ASSERTE(ppSystemAssembly != NULL); + + ReleaseHolder<Assembly> pSystemAssembly; + + // At run-time, System.Private.CoreLib.dll is expected to be the NI image. + // System.Private.CoreLib.dll is expected to be found at one of the following locations: + // * Non-single-file app: In systemDirectory, beside coreclr.dll + // * Framework-dependent single-file app: In systemDirectory, beside coreclr.dll + // * Self-contained single-file app: Within the single-file bundle. + // + // CoreLib path (sCoreLib): + // * Absolute path when looking for a file on disk + // * Bundle-relative path when looking within the single-file bundle. + + StackSString sCoreLibName(CoreLibName_IL_W); + StackSString sCoreLib; + BinderTracing::PathSource pathSource = BinderTracing::PathSource::Bundle; + BundleFileLocation bundleFileLocation = Bundle::ProbeAppBundle(sCoreLibName, /*pathIsBundleRelative */ true); + if (!bundleFileLocation.IsValid()) + { + pathSource = BinderTracing::PathSource::ApplicationAssemblies; + } + sCoreLib.Set(systemDirectory); + CombinePath(sCoreLib, sCoreLibName, sCoreLib); + + hr = AssemblyBinder::GetAssembly(sCoreLib, + TRUE /* fIsInGAC */, + fBindToNativeImage, + &pSystemAssembly, + NULL /* szMDAssemblyPath */, + bundleFileLocation); + BinderTracing::PathProbed(sCoreLib, pathSource, hr); + + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + // Try to find corelib in the TPA + StackSString sCoreLibSimpleName(CoreLibName_W); + StackSString sTrustedPlatformAssemblies = Configuration::GetKnobStringValue(W("TRUSTED_PLATFORM_ASSEMBLIES")); + sTrustedPlatformAssemblies.Normalize(); + + bool found = false; + for (SString::Iterator i = sTrustedPlatformAssemblies.Begin(); i != sTrustedPlatformAssemblies.End(); ) + { + SString fileName; + SString simpleName; + bool isNativeImage = false; + HRESULT pathResult = S_OK; + IF_FAIL_GO(pathResult = GetNextTPAPath(sTrustedPlatformAssemblies, i, /*dllOnly*/ true, fileName, simpleName, isNativeImage)); + if (pathResult == S_FALSE) + { + break; + } + + if (simpleName.EqualsCaseInsensitive(sCoreLibSimpleName)) + { + sCoreLib = fileName; + found = true; + break; + } + } + + if (!found) + { + GO_WITH_HRESULT(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + } + + hr = AssemblyBinder::GetAssembly(sCoreLib, + TRUE /* fIsInGAC */, + fBindToNativeImage, + &pSystemAssembly, + NULL /* szMDAssemblyPath */, + bundleFileLocation); + + BinderTracing::PathProbed(sCoreLib, BinderTracing::PathSource::ApplicationAssemblies, hr); + } + + IF_FAIL_GO(hr); + + *ppSystemAssembly = pSystemAssembly.Extract(); + + Exit: + return hr; + } + + + /* static */ + HRESULT AssemblyBinder::BindToSystemSatellite(SString& systemDirectory, + SString& simpleName, + SString& cultureName, + Assembly** ppSystemAssembly) + { + // Indirect check that binder was initialized. + _ASSERTE(g_BinderVariables != NULL); + + HRESULT hr = S_OK; + + _ASSERTE(ppSystemAssembly != NULL); + + // Satellite assembly's relative path + StackSString relativePath; + + // append culture name + if (!cultureName.IsEmpty()) + { + CombinePath(relativePath, cultureName, relativePath); + } + + // append satellite assembly's simple name + CombinePath(relativePath, simpleName, relativePath); + + // append extension + relativePath.Append(W(".dll")); + + // Satellite assembly's path: + // * Absolute path when looking for a file on disk + // * Bundle-relative path when looking within the single-file bundle. + StackSString sCoreLibSatellite; + + BinderTracing::PathSource pathSource = BinderTracing::PathSource::Bundle; + BundleFileLocation bundleFileLocation = Bundle::ProbeAppBundle(relativePath, /*pathIsBundleRelative */ true); + if (!bundleFileLocation.IsValid()) + { + sCoreLibSatellite.Set(systemDirectory); + pathSource = BinderTracing::PathSource::ApplicationAssemblies; + } + CombinePath(sCoreLibSatellite, relativePath, sCoreLibSatellite); + + ReleaseHolder<Assembly> pSystemAssembly; + IF_FAIL_GO(AssemblyBinder::GetAssembly(sCoreLibSatellite, + TRUE /* fIsInGAC */, + FALSE /* fExplicitBindToNativeImage */, + &pSystemAssembly, + NULL /* szMDAssemblyPath */, + bundleFileLocation)); + BinderTracing::PathProbed(sCoreLibSatellite, pathSource, hr); + + *ppSystemAssembly = pSystemAssembly.Extract(); + + Exit: + return hr; + } + + /* static */ + HRESULT AssemblyBinder::BindByName(ApplicationContext *pApplicationContext, + AssemblyName *pAssemblyName, + bool skipFailureCaching, + bool skipVersionCompatibilityCheck, + bool excludeAppPaths, + BindResult *pBindResult) + { + HRESULT hr = S_OK; + PathString assemblyDisplayName; + + // Look for already cached binding failure (ignore PA, every PA will lock the context) + pAssemblyName->GetDisplayName(assemblyDisplayName, + AssemblyName::INCLUDE_VERSION); + + hr = pApplicationContext->GetFailureCache()->Lookup(assemblyDisplayName); + if (FAILED(hr)) + { + if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) && skipFailureCaching) + { + // Ignore pre-existing transient bind error (re-bind will succeed) + pApplicationContext->GetFailureCache()->Remove(assemblyDisplayName); + } + + goto LogExit; + } + else if (hr == S_FALSE) + { + // workaround: Special case for byte arrays. Rerun the bind to create binding log. + pAssemblyName->SetIsDefinition(TRUE); + hr = S_OK; + } + + if (!Assembly::IsValidArchitecture(pAssemblyName->GetArchitecture())) + { + // Assembly reference contains wrong architecture + IF_FAIL_GO(FUSION_E_INVALID_NAME); + } + + IF_FAIL_GO(BindLocked(pApplicationContext, + pAssemblyName, + skipVersionCompatibilityCheck, + excludeAppPaths, + pBindResult)); + + if (!pBindResult->HaveResult()) + { + // Behavior rules are clueless now + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + } + + Exit: + if (FAILED(hr)) + { + if (skipFailureCaching) + { + if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + // Cache non-transient bind error for byte-array + hr = S_FALSE; + } + else + { + // Ignore transient bind error (re-bind will succeed) + goto LogExit; + } + } + + hr = pApplicationContext->AddToFailureCache(assemblyDisplayName, hr); + } + + LogExit: + return hr; + } + + /* static */ + // See code:BINDER_SPACE::AssemblyBinder::GetAssembly for info on fNgenExplicitBind + // and fExplicitBindToNativeImage, and see code:CEECompileInfo::LoadAssemblyByPath + // for an example of how they're used. + HRESULT AssemblyBinder::BindWhereRef(ApplicationContext *pApplicationContext, + PathString &assemblyPath, + BOOL fNgenExplicitBind, + BOOL fExplicitBindToNativeImage, + bool excludeAppPaths, + BindResult *pBindResult) + { + HRESULT hr = S_OK; + + ReleaseHolder<Assembly> pAssembly; + BindResult lockedBindResult; + + // Look for already cached binding failure + hr = pApplicationContext->GetFailureCache()->Lookup(assemblyPath); + if (FAILED(hr)) + { + goto LogExit; + } + + // If we return this assembly, then it is guaranteed to be not in GAC + // Design decision. For now, keep the V2 model of Fusion being oblivious of the strong name. + // Security team did not see any security concern with interpreting the version information. + IF_FAIL_GO(GetAssembly(assemblyPath, + FALSE /* fIsInGAC */, + + // Pass through caller's intent of whether to bind to the + // NI using an explicit path to the NI that was + // specified. Generally only NGEN PDB generation has + // this TRUE. + fExplicitBindToNativeImage, + &pAssembly, + NULL /* szMDAssemblyPath */, + Bundle::ProbeAppBundle(assemblyPath))); + + AssemblyName *pAssemblyName; + pAssemblyName = pAssembly->GetAssemblyName(); + + if (!fNgenExplicitBind) + { + IF_FAIL_GO(BindLocked(pApplicationContext, + pAssemblyName, + false, // skipVersionCompatibilityCheck + excludeAppPaths, + &lockedBindResult)); + if (lockedBindResult.HaveResult()) + { + pBindResult->SetResult(&lockedBindResult); + GO_WITH_HRESULT(S_OK); + } + } + + hr = S_OK; + pBindResult->SetResult(pAssembly); + + Exit: + + if (FAILED(hr)) + { + // Always cache binding failures + hr = pApplicationContext->AddToFailureCache(assemblyPath, hr); + } + + LogExit: + return hr; + } + + /* static */ + HRESULT AssemblyBinder::BindLocked(ApplicationContext *pApplicationContext, + AssemblyName *pAssemblyName, + bool skipVersionCompatibilityCheck, + bool excludeAppPaths, + BindResult *pBindResult) + { + HRESULT hr = S_OK; + + bool isTpaListProvided = pApplicationContext->IsTpaListProvided(); +#ifndef CROSSGEN_COMPILE + ContextEntry *pContextEntry = NULL; + hr = FindInExecutionContext(pApplicationContext, pAssemblyName, &pContextEntry); + + // Add the attempt to the bind result on failure / not found. On success, it will be added after the version check. + if (FAILED(hr) || pContextEntry == NULL) + pBindResult->SetAttemptResult(hr, pContextEntry); + + IF_FAIL_GO(hr); + if (pContextEntry != NULL) + { + if (!skipVersionCompatibilityCheck) + { + // Can't give higher version than already bound + bool isCompatible = IsCompatibleAssemblyVersion(pAssemblyName, pContextEntry->GetAssemblyName()); + hr = isCompatible ? S_OK : FUSION_E_APP_DOMAIN_LOCKED; + pBindResult->SetAttemptResult(hr, pContextEntry); + + // TPA binder returns FUSION_E_REF_DEF_MISMATCH for incompatible version + if (hr == FUSION_E_APP_DOMAIN_LOCKED && isTpaListProvided) + hr = FUSION_E_REF_DEF_MISMATCH; + } + else + { + pBindResult->SetAttemptResult(hr, pContextEntry); + } + + IF_FAIL_GO(hr); + + pBindResult->SetResult(pContextEntry); + } + else +#endif // !CROSSGEN_COMPILE + if (isTpaListProvided) + { + // BindByTpaList handles setting attempt results on the bind result + hr = BindByTpaList(pApplicationContext, + pAssemblyName, + excludeAppPaths, + pBindResult); + if (SUCCEEDED(hr) && pBindResult->HaveResult()) + { + bool isCompatible = IsCompatibleAssemblyVersion(pAssemblyName, pBindResult->GetAssemblyName()); + hr = isCompatible ? S_OK : FUSION_E_APP_DOMAIN_LOCKED; + pBindResult->SetAttemptResult(hr, pBindResult->GetAsAssembly()); + + // TPA binder returns FUSION_E_REF_DEF_MISMATCH for incompatible version + if (hr == FUSION_E_APP_DOMAIN_LOCKED && isTpaListProvided) + hr = FUSION_E_REF_DEF_MISMATCH; + } + + if (FAILED(hr)) + { + pBindResult->SetNoResult(); + } + IF_FAIL_GO(hr); + } + Exit: + return hr; + } + +#ifndef CROSSGEN_COMPILE + /* static */ + HRESULT AssemblyBinder::FindInExecutionContext(ApplicationContext *pApplicationContext, + AssemblyName *pAssemblyName, + ContextEntry **ppContextEntry) + { + HRESULT hr = S_OK; + + _ASSERTE(pApplicationContext != NULL); + _ASSERTE(pAssemblyName != NULL); + _ASSERTE(ppContextEntry != NULL); + + ExecutionContext *pExecutionContext = pApplicationContext->GetExecutionContext(); + ContextEntry *pContextEntry = pExecutionContext->Lookup(pAssemblyName); + + // Set any found context entry. It is up to the caller to check the returned HRESULT + // for errors due to validation + *ppContextEntry = pContextEntry; + if (pContextEntry != NULL) + { + AssemblyName *pContextName = pContextEntry->GetAssemblyName(); + if (pAssemblyName->GetIsDefinition() && + (pContextName->GetArchitecture() != pAssemblyName->GetArchitecture())) + { + return FUSION_E_APP_DOMAIN_LOCKED; + } + } + + return pContextEntry != NULL ? S_OK : S_FALSE; + } + +#endif //CROSSGEN_COMPILE + + // + // Tests whether a candidate assembly's name matches the requested. + // This does not do a version check. The binder applies version policy + // further up the stack once it gets a successful bind. + // + BOOL TestCandidateRefMatchesDef(AssemblyName *pRequestedAssemblyName, + AssemblyName *pBoundAssemblyName, + BOOL tpaListAssembly) + { + DWORD dwIncludeFlags = AssemblyName::INCLUDE_DEFAULT; + + if (!tpaListAssembly) + { + SString &culture = pRequestedAssemblyName->GetCulture(); + if (culture.IsEmpty() || culture.EqualsCaseInsensitive(g_BinderVariables->cultureNeutral)) + { + dwIncludeFlags |= AssemblyName::EXCLUDE_CULTURE; + } + } + + if (pRequestedAssemblyName->GetArchitecture() != peNone) + { + dwIncludeFlags |= AssemblyName::INCLUDE_ARCHITECTURE; + } + + return pBoundAssemblyName->Equals(pRequestedAssemblyName, dwIncludeFlags); + } + + namespace + { + HRESULT BindSatelliteResourceFromBundle( + AssemblyName* pRequestedAssemblyName, + SString &relativePath, + BindResult* pBindResult) + { + HRESULT hr = S_OK; + + BundleFileLocation bundleFileLocation = Bundle::ProbeAppBundle(relativePath, /* pathIsBundleRelative */ true); + if (!bundleFileLocation.IsValid()) + { + return hr; + } + + ReleaseHolder<Assembly> pAssembly; + hr = AssemblyBinder::GetAssembly(relativePath, + FALSE /* fIsInGAC */, + FALSE /* fExplicitBindToNativeImage */, + &pAssembly, + NULL, // szMDAssemblyPath + bundleFileLocation); + + BinderTracing::PathProbed(relativePath, BinderTracing::PathSource::Bundle, hr); + + // Missing files are okay and expected when probing + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + return S_OK; + } + + pBindResult->SetAttemptResult(hr, pAssembly); + if (FAILED(hr)) + return hr; + + AssemblyName* pBoundAssemblyName = pAssembly->GetAssemblyName(); + if (TestCandidateRefMatchesDef(pRequestedAssemblyName, pBoundAssemblyName, false /*tpaListAssembly*/)) + { + pBindResult->SetResult(pAssembly); + hr = S_OK; + } + else + { + hr = FUSION_E_REF_DEF_MISMATCH; + } + + pBindResult->SetAttemptResult(hr, pAssembly); + return hr; + } + + HRESULT BindSatelliteResourceByProbingPaths( + const StringArrayList *pResourceRoots, + AssemblyName *pRequestedAssemblyName, + SString &relativePath, + BindResult *pBindResult, + BinderTracing::PathSource pathSource) + { + HRESULT hr = S_OK; + + for (UINT i = 0; i < pResourceRoots->GetCount(); i++) + { + ReleaseHolder<Assembly> pAssembly; + SString &wszBindingPath = (*pResourceRoots)[i]; + SString fileName(wszBindingPath); + CombinePath(fileName, relativePath, fileName); + + hr = AssemblyBinder::GetAssembly(fileName, + FALSE /* fIsInGAC */, + FALSE /* fExplicitBindToNativeImage */, + &pAssembly); + BinderTracing::PathProbed(fileName, pathSource, hr); + + // Missing files are okay and expected when probing + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + continue; + } + + pBindResult->SetAttemptResult(hr, pAssembly); + if (FAILED(hr)) + return hr; + + AssemblyName *pBoundAssemblyName = pAssembly->GetAssemblyName(); + if (TestCandidateRefMatchesDef(pRequestedAssemblyName, pBoundAssemblyName, false /*tpaListAssembly*/)) + { + pBindResult->SetResult(pAssembly); + hr = S_OK; + } + else + { + hr = FUSION_E_REF_DEF_MISMATCH; + } + + pBindResult->SetAttemptResult(hr, pAssembly); + return hr; + } + + // Up-stack expects S_OK when we don't find any candidate assemblies and no fatal error occurred (ie, no S_FALSE) + return S_OK; + } + + HRESULT BindSatelliteResource( + ApplicationContext* pApplicationContext, + AssemblyName* pRequestedAssemblyName, + BindResult* pBindResult) + { + // Satellite resource probing strategy is to look: + // * First within the single-file bundle + // * Then under each of the Platform Resource Roots + // * Then under each of the App Paths. + // + // During each search, if we find a platform resource file with matching file name, but whose ref-def didn't match, + // fall back to application resource lookup to handle case where a user creates resources with the same + // names as platform ones. + + HRESULT hr = S_OK; + SString& simpleNameRef = pRequestedAssemblyName->GetSimpleName(); + SString& cultureRef = pRequestedAssemblyName->GetCulture(); + + _ASSERTE(!cultureRef.IsEmpty() && !cultureRef.EqualsCaseInsensitive(g_BinderVariables->cultureNeutral)); + + ReleaseHolder<Assembly> pAssembly; + SString fileName; + CombinePath(fileName, cultureRef, fileName); + CombinePath(fileName, simpleNameRef, fileName); + fileName.Append(W(".dll")); + + hr = BindSatelliteResourceFromBundle(pRequestedAssemblyName, fileName, pBindResult); + + if (pBindResult->HaveResult() || (FAILED(hr) && hr != FUSION_E_CONFIGURATION_ERROR)) + { + return hr; + } + + hr = BindSatelliteResourceByProbingPaths(pApplicationContext->GetPlatformResourceRoots(), + pRequestedAssemblyName, + fileName, + pBindResult, + BinderTracing::PathSource::PlatformResourceRoots); + + if (pBindResult->HaveResult() || (FAILED(hr) && hr != FUSION_E_CONFIGURATION_ERROR)) + { + return hr; + } + + hr = BindSatelliteResourceByProbingPaths(pApplicationContext->GetAppPaths(), + pRequestedAssemblyName, + fileName, + pBindResult, + BinderTracing::PathSource::AppPaths); + + return hr; + } + + HRESULT BindAssemblyByProbingPaths( + const StringArrayList *pBindingPaths, + AssemblyName *pRequestedAssemblyName, + bool useNativeImages, + Assembly **ppAssembly) + { + SString &simpleName = pRequestedAssemblyName->GetSimpleName(); + BinderTracing::PathSource pathSource = useNativeImages ? BinderTracing::PathSource::AppNativeImagePaths : BinderTracing::PathSource::AppPaths; + // Loop through the binding paths looking for a matching assembly + for (DWORD i = 0; i < pBindingPaths->GetCount(); i++) + { + HRESULT hr; + ReleaseHolder<Assembly> pAssembly; + LPCWSTR wszBindingPath = (*pBindingPaths)[i]; + + PathString fileNameWithoutExtension(wszBindingPath); + CombinePath(fileNameWithoutExtension, simpleName, fileNameWithoutExtension); + + // Look for a matching dll first + PathString fileName(fileNameWithoutExtension); + fileName.Append(useNativeImages ? W(".ni.dll") : W(".dll")); + hr = AssemblyBinder::GetAssembly(fileName, + FALSE, // fIsInGAC + useNativeImages, // fExplicitBindToNativeImage + &pAssembly); + BinderTracing::PathProbed(fileName, pathSource, hr); + + if (FAILED(hr)) + { + fileName.Set(fileNameWithoutExtension); + fileName.Append(useNativeImages ? W(".ni.exe") : W(".exe")); + hr = AssemblyBinder::GetAssembly(fileName, + FALSE, // fIsInGAC + useNativeImages, // fExplicitBindToNativeImage + &pAssembly); + BinderTracing::PathProbed(fileName, pathSource, hr); + } + + // Since we're probing, file not founds are ok and we should just try another + // probing path + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + continue; + } + + // Set any found assembly. It is up to the caller to check the returned HRESULT for errors due to validation + *ppAssembly = pAssembly.Extract(); + if (FAILED(hr)) + return hr; + + // We found a candidate. + // + // Below this point, we either establish that the ref-def matches, or + // we fail the bind. + + // Compare requested AssemblyName with that from the candidate assembly + if (!TestCandidateRefMatchesDef(pRequestedAssemblyName, pAssembly->GetAssemblyName(), false /*tpaListAssembly*/)) + return FUSION_E_REF_DEF_MISMATCH; + + return S_OK; + } + + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + } + + /* + * BindByTpaList is the entry-point for the custom binding algorithm in CoreCLR. + * + * The search for assemblies will proceed in the following order: + * + * If this application is a single-file bundle, the meta-data contained in the bundle + * will be probed to find the requested assembly. If the assembly is not found, + * The list of platform assemblies (TPAs) are considered next. + * + * Platform assemblies are specified as a list of files. This list is the only set of + * assemblies that we will load as platform. They can be specified as IL or NIs. + * + * Resources for platform assemblies are located by probing starting at the Platform Resource Roots, + * a set of folders configured by the host. + * + * If a requested assembly identity cannot be found in the TPA list or the resource roots, + * it is considered an application assembly. We probe for application assemblies in one of two + * sets of paths: the AppNiPaths, a list of paths containing native images, and the AppPaths, a + * list of paths containing IL files and satellite resource folders. + * + */ + /* static */ + HRESULT AssemblyBinder::BindByTpaList(ApplicationContext *pApplicationContext, + AssemblyName *pRequestedAssemblyName, + bool excludeAppPaths, + BindResult *pBindResult) + { + HRESULT hr = S_OK; + + SString &culture = pRequestedAssemblyName->GetCulture(); + bool fPartialMatchOnTpa = false; + + if (!culture.IsEmpty() && !culture.EqualsCaseInsensitive(g_BinderVariables->cultureNeutral)) + { + IF_FAIL_GO(BindSatelliteResource(pApplicationContext, pRequestedAssemblyName, pBindResult)); + } + else + { + ReleaseHolder<Assembly> pTPAAssembly; + SString& simpleName = pRequestedAssemblyName->GetSimpleName(); + + // Is assembly in the bundle? + // Single-file bundle contents take precedence over TPA. + // The list of bundled assemblies is contained in the bundle manifest, and NOT in the TPA. + // Therefore the bundle is first probed using the assembly's simple name. + // If found, the assembly is loaded from the bundle. + if (Bundle::AppIsBundle()) + { + // Search Assembly.ni.dll, then Assembly.dll + // The Assembly.ni.dll paths are rare, and intended for supporting managed C++ R2R assemblies. + SString candidates[] = { W(".ni.dll"), W(".dll") }; + + // Loop through the binding paths looking for a matching assembly + for (int i = 0; i < 2; i++) + { + SString assemblyFileName(simpleName); + assemblyFileName.Append(candidates[i]); + + SString assemblyFilePath(Bundle::AppBundle->BasePath()); + assemblyFilePath.Append(assemblyFileName); + + BundleFileLocation bundleFileLocation = Bundle::ProbeAppBundle(assemblyFileName, /* pathIsBundleRelative */ true); + if (bundleFileLocation.IsValid()) + { + hr = GetAssembly(assemblyFilePath, + TRUE, // fIsInGAC + FALSE, // fExplicitBindToNativeImage + &pTPAAssembly, + NULL, // szMDAssemblyPath + bundleFileLocation); + + BinderTracing::PathProbed(assemblyFilePath, BinderTracing::PathSource::Bundle, hr); + + if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + // Any other error is fatal + IF_FAIL_GO(hr); + + if (TestCandidateRefMatchesDef(pRequestedAssemblyName, pTPAAssembly->GetAssemblyName(), true /*tpaListAssembly*/)) + { + // We have found the requested assembly match in the bundle with validation of the full-qualified name. + // Bind to it. + pBindResult->SetResult(pTPAAssembly); + GO_WITH_HRESULT(S_OK); + } + } + } + } + } + + // Is assembly on TPA list? + SimpleNameToFileNameMap * tpaMap = pApplicationContext->GetTpaList(); + const SimpleNameToFileNameMapEntry *pTpaEntry = tpaMap->LookupPtr(simpleName.GetUnicode()); + if (pTpaEntry != nullptr) + { + if (pTpaEntry->m_wszNIFileName != nullptr) + { + SString fileName(pTpaEntry->m_wszNIFileName); + + hr = GetAssembly(fileName, + TRUE, // fIsInGAC + TRUE, // fExplicitBindToNativeImage + &pTPAAssembly); + BinderTracing::PathProbed(fileName, BinderTracing::PathSource::ApplicationAssemblies, hr); + } + else + { + _ASSERTE(pTpaEntry->m_wszILFileName != nullptr); + SString fileName(pTpaEntry->m_wszILFileName); + + hr = GetAssembly(fileName, + TRUE, // fIsInGAC + FALSE, // fExplicitBindToNativeImage + &pTPAAssembly); + BinderTracing::PathProbed(fileName, BinderTracing::PathSource::ApplicationAssemblies, hr); + } + + pBindResult->SetAttemptResult(hr, pTPAAssembly); + + // On file not found, simply fall back to app path probing + if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + // Any other error is fatal + IF_FAIL_GO(hr); + + if (TestCandidateRefMatchesDef(pRequestedAssemblyName, pTPAAssembly->GetAssemblyName(), true /*tpaListAssembly*/)) + { + // We have found the requested assembly match on TPA with validation of the full-qualified name. Bind to it. + pBindResult->SetResult(pTPAAssembly); + pBindResult->SetAttemptResult(S_OK, pTPAAssembly); + GO_WITH_HRESULT(S_OK); + } + else + { + // We found the assembly on TPA but it didn't match the RequestedAssembly assembly-name. In this case, lets proceed to see if we find the requested + // assembly in the App paths. + pBindResult->SetAttemptResult(FUSION_E_REF_DEF_MISMATCH, pTPAAssembly); + fPartialMatchOnTpa = true; + } + } + + // We either didn't find a candidate, or the ref-def failed. Either way; fall back to app path probing. + } + + if (!excludeAppPaths) + { + // Probe AppNiPaths first, then AppPaths + ReleaseHolder<Assembly> pAssembly; + hr = BindAssemblyByProbingPaths(pApplicationContext->GetAppNiPaths(), + pRequestedAssemblyName, + true, // useNativeImages + &pAssembly); + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + hr = BindAssemblyByProbingPaths(pApplicationContext->GetAppPaths(), + pRequestedAssemblyName, + false, // useNativeImages + &pAssembly); + } + + pBindResult->SetAttemptResult(hr, pAssembly); + if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + IF_FAIL_GO(hr); + + // At this point, we have found an assembly with the expected name in the App paths. If this was also found on TPA, + // make sure that the app assembly has the same fullname (excluding version) as the TPA version. If it does, then + // we should bind to the TPA assembly. If it does not, then bind to the app assembly since it has a different fullname than the + // TPA assembly. + if (fPartialMatchOnTpa) + { + if (TestCandidateRefMatchesDef(pAssembly->GetAssemblyName(), pTPAAssembly->GetAssemblyName(), true /*tpaListAssembly*/)) + { + // Fullname (SimpleName+Culture+PKT) matched for TPA and app assembly - so bind to TPA instance. + pBindResult->SetResult(pTPAAssembly); + pBindResult->SetAttemptResult(hr, pTPAAssembly); + GO_WITH_HRESULT(S_OK); + } + else + { + // Fullname (SimpleName+Culture+PKT) did not match for TPA and app assembly - so bind to app instance. + pBindResult->SetResult(pAssembly); + GO_WITH_HRESULT(S_OK); + } + } + else + { + // We didn't see this assembly on TPA - so simply bind to the app instance. + pBindResult->SetResult(pAssembly); + GO_WITH_HRESULT(S_OK); + } + } + } + } + + // Couldn't find a matching assembly in any of the probing paths + // Return S_FALSE here. BindByName will interpret a successful HRESULT + // and lack of BindResult as a failure to find a matching assembly. + hr = S_FALSE; + + Exit: + return hr; + } + + /* static */ + HRESULT AssemblyBinder::GetAssembly(SString &assemblyPath, + BOOL fIsInGAC, + + // When binding to the native image, should we + // assume assemblyPath explicitly specifies that + // NI? (If not, infer the path to the NI + // implicitly.) + BOOL fExplicitBindToNativeImage, + + Assembly **ppAssembly, + + // If assemblyPath refers to a native image without metadata, + // szMDAssemblyPath gives the alternative file to get metadata. + LPCTSTR szMDAssemblyPath, + BundleFileLocation bundleFileLocation) + { + HRESULT hr = S_OK; + + _ASSERTE(ppAssembly != NULL); + + ReleaseHolder<Assembly> pAssembly; + ReleaseHolder<IMDInternalImport> pIMetaDataAssemblyImport; + DWORD dwPAFlags[2]; + PEKIND PeKind = peNone; + PEImage *pPEImage = NULL; + PEImage *pNativePEImage = NULL; + + // Allocate assembly object + SAFE_NEW(pAssembly, Assembly); + + // Obtain assembly meta data + { + LPCTSTR szAssemblyPath = const_cast<LPCTSTR>(assemblyPath.GetUnicode()); + + hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, &pNativePEImage, fExplicitBindToNativeImage, bundleFileLocation); + IF_FAIL_GO(hr); + + // If we found a native image, it might be an MSIL assembly masquerading as an native image + // as a fallback mechanism for when the Triton tool chain wasn't able to generate a native image. + // In that case it will not have a native header, so just treat it like the MSIL assembly it is. + if (pNativePEImage) + { + BOOL hasHeader = TRUE; + IF_FAIL_GO(BinderHasNativeHeader(pNativePEImage, &hasHeader)); + if (!hasHeader) + { + BinderReleasePEImage(pPEImage); + BinderReleasePEImage(pNativePEImage); + + hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, &pNativePEImage, false, bundleFileLocation); + IF_FAIL_GO(hr); + } + } + + if (pNativePEImage) + hr = BinderAcquireImport(pNativePEImage, &pIMetaDataAssemblyImport, dwPAFlags, TRUE); + else + hr = BinderAcquireImport(pPEImage, &pIMetaDataAssemblyImport, dwPAFlags, FALSE); + + IF_FAIL_GO(hr); + + if (pIMetaDataAssemblyImport == NULL && pNativePEImage != NULL) + { + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + } + + IF_FAIL_GO(TranslatePEToArchitectureType(dwPAFlags, &PeKind)); + } + + // Initialize assembly object + IF_FAIL_GO(pAssembly->Init(pIMetaDataAssemblyImport, + PeKind, + pPEImage, + pNativePEImage, + assemblyPath, + fIsInGAC)); + + // We're done + *ppAssembly = pAssembly.Extract(); + + Exit: + + BinderReleasePEImage(pPEImage); + BinderReleasePEImage(pNativePEImage); + + // Normalize file not found + if ((FAILED(hr)) && IsFileNotFound(hr)) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + return hr; + } + +#ifndef CROSSGEN_COMPILE + + /* static */ + HRESULT AssemblyBinder::Register(ApplicationContext *pApplicationContext, + BindResult *pBindResult) + { + HRESULT hr = S_OK; + + _ASSERTE(!pBindResult->GetIsContextBound()); + + pApplicationContext->IncrementVersion(); + + // Register the bindResult in the ExecutionContext only if we dont have it already. + // This method is invoked under a lock (by its caller), so we are thread safe. + ContextEntry *pContextEntry = NULL; + hr = FindInExecutionContext(pApplicationContext, pBindResult->GetAssemblyName(), &pContextEntry); + if (SUCCEEDED(hr)) + { + if (pContextEntry == NULL) + { + ExecutionContext *pExecutionContext = pApplicationContext->GetExecutionContext(); + IF_FAIL_GO(pExecutionContext->Register(pBindResult)); + } + else + { + // Update the BindResult with the contents of the ContextEntry we found + pBindResult->SetResult(pContextEntry); + } + } + + Exit: + return hr; + } + + /* static */ + HRESULT AssemblyBinder::RegisterAndGetHostChosen(ApplicationContext *pApplicationContext, + LONG kContextVersion, + BindResult *pBindResult, + BindResult *pHostBindResult) + { + HRESULT hr = S_OK; + + _ASSERTE(pBindResult != NULL); + _ASSERTE(pBindResult->HaveResult()); + _ASSERTE(pHostBindResult != NULL); + + if (!pBindResult->GetIsContextBound()) + { + pHostBindResult->SetResult(pBindResult); + + { + // Lock the application context + CRITSEC_Holder contextLock(pApplicationContext->GetCriticalSectionCookie()); + + // Only perform costly validation if other binds succeded before us + if (kContextVersion != pApplicationContext->GetVersion()) + { + IF_FAIL_GO(AssemblyBinder::OtherBindInterfered(pApplicationContext, + pBindResult)); + + if (hr == S_FALSE) + { + // Another bind interfered + GO_WITH_HRESULT(hr); + } + } + + // No bind interfered, we can now register + IF_FAIL_GO(Register(pApplicationContext, + pHostBindResult)); + } + } + else + { + // No work required. Return the input + pHostBindResult->SetResult(pBindResult); + } + + Exit: + return hr; + } + + /* static */ + HRESULT AssemblyBinder::OtherBindInterfered(ApplicationContext *pApplicationContext, + BindResult *pBindResult) + { + HRESULT hr = S_FALSE; + AssemblyName *pAssemblyName = pBindResult->GetAssemblyName(); + PathString assemblyDisplayName; + + _ASSERTE(pAssemblyName != NULL); + + // Look for already cached binding failure (ignore PA, every PA will lock the context) + pAssemblyName->GetDisplayName(assemblyDisplayName, AssemblyName::INCLUDE_VERSION); + hr = pApplicationContext->GetFailureCache()->Lookup(assemblyDisplayName); + + if (hr == S_OK) + { + ContextEntry *pContextEntry = NULL; + + hr = FindInExecutionContext(pApplicationContext, pAssemblyName, &pContextEntry); + + if (SUCCEEDED(hr) && (pContextEntry == NULL)) + { + // We can accept this bind in the domain + GO_WITH_HRESULT(S_OK); + } + } + + // Some other bind interfered + GO_WITH_HRESULT(S_FALSE); + + Exit: + return hr; + } + +#endif //CROSSGEN_COMPILE + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) +HRESULT AssemblyBinder::BindUsingHostAssemblyResolver(/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin, + /* in */ AssemblyName *pAssemblyName, + /* in */ IAssemblyName *pIAssemblyName, + /* in */ CLRPrivBinderCoreCLR *pTPABinder, + /* out */ Assembly **ppAssembly) +{ + HRESULT hr = E_FAIL; + + _ASSERTE(pManagedAssemblyLoadContextToBindWithin != NULL); + + // RuntimeInvokeHostAssemblyResolver will perform steps 2-4 of CLRPrivBinderAssemblyLoadContext::BindAssemblyByName. + ICLRPrivAssembly *pLoadedAssembly = NULL; + hr = RuntimeInvokeHostAssemblyResolver(pManagedAssemblyLoadContextToBindWithin, pIAssemblyName, + pTPABinder, pAssemblyName, &pLoadedAssembly); + if (SUCCEEDED(hr)) + { + _ASSERTE(pLoadedAssembly != NULL); + *ppAssembly = static_cast<Assembly *>(pLoadedAssembly); + } + + return hr; +} + +/* static */ +HRESULT AssemblyBinder::BindUsingPEImage(/* in */ ApplicationContext *pApplicationContext, + /* in */ BINDER_SPACE::AssemblyName *pAssemblyName, + /* in */ PEImage *pPEImage, + /* in */ PEKIND peKind, + /* in */ IMDInternalImport *pIMetaDataAssemblyImport, + /* [retval] [out] */ Assembly **ppAssembly) +{ + HRESULT hr = E_FAIL; + + // Indirect check that binder was initialized. + _ASSERTE(g_BinderVariables != NULL); + + LONG kContextVersion = 0; + BindResult bindResult; + + // Prepare binding data + *ppAssembly = NULL; + + // Tracing happens outside the binder lock to avoid calling into managed code within the lock + BinderTracing::ResolutionAttemptedOperation tracer{pAssemblyName, pApplicationContext->GetBinderID(), 0 /*managedALC*/, hr}; + + // Attempt the actual bind (eventually more than once) +Retry: + bool mvidMismatch = false; + { + // Lock the application context + CRITSEC_Holder contextLock(pApplicationContext->GetCriticalSectionCookie()); + + // Attempt uncached bind and register stream if possible + // We skip version compatibility check - so assemblies with same simple name will be reported + // as a successful bind. Below we compare MVIDs in that case instead (which is a more precise equality check). + hr = BindByName(pApplicationContext, + pAssemblyName, + true, // skipFailureCaching + true, // skipVersionCompatibilityCheck + false, // excludeAppPaths + &bindResult); + + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + IF_FAIL_GO(CreateImageAssembly(pIMetaDataAssemblyImport, + peKind, + pPEImage, + NULL, + &bindResult)); + } + else if (hr == S_OK) + { + if (bindResult.HaveResult()) + { + // Attempt was made to load an assembly that has the same name as a previously loaded one. Since same name + // does not imply the same assembly, we will need to check the MVID to confirm it is the same assembly as being + // requested. + // + GUID incomingMVID; + ZeroMemory(&incomingMVID, sizeof(GUID)); + + // If we cannot get MVID, then err on side of caution and fail the + // load. + IF_FAIL_GO(pIMetaDataAssemblyImport->GetScopeProps(NULL, &incomingMVID)); + + GUID boundMVID; + ZeroMemory(&boundMVID, sizeof(GUID)); + + // If we cannot get MVID, then err on side of caution and fail the + // load. + IF_FAIL_GO(bindResult.GetAsAssembly()->GetMVID(&boundMVID)); + + mvidMismatch = incomingMVID != boundMVID; + if (mvidMismatch) + { + // MVIDs do not match, so fail the load. + IF_FAIL_GO(COR_E_FILELOAD); + } + + // MVIDs match - request came in for the same assembly that was previously loaded. + // Let it through... + } + } + + // Remember the post-bind version of the context + kContextVersion = pApplicationContext->GetVersion(); + + } // lock(pApplicationContext) + + if (bindResult.HaveResult()) + { + BindResult hostBindResult; + + // This has to happen outside the binder lock as it can cause new binds + IF_FAIL_GO(RegisterAndGetHostChosen(pApplicationContext, + kContextVersion, + &bindResult, + &hostBindResult)); + + if (hr == S_FALSE) + { + tracer.TraceBindResult(bindResult); + + // Another bind interfered. We need to retry entire bind. + // This by design loops as long as needed because by construction we eventually + // will succeed or fail the bind. + bindResult.Reset(); + goto Retry; + } + else if (hr == S_OK) + { + *ppAssembly = hostBindResult.GetAsAssembly(TRUE /* fAddRef */); + } + } + +Exit: + tracer.TraceBindResult(bindResult, mvidMismatch); + return hr; +} +#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) +}; + + diff --git a/src/coreclr/binder/assemblyidentitycache.cpp b/src/coreclr/binder/assemblyidentitycache.cpp new file mode 100644 index 00000000000..bff89d1ad29 --- /dev/null +++ b/src/coreclr/binder/assemblyidentitycache.cpp @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// AssemblyIdentityCache.cpp +// + + +// +// Implements the AssemblyIdentityCache class +// +// ============================================================ + +#include "assemblyidentitycache.hpp" + +namespace BINDER_SPACE +{ + AssemblyIdentityCache::AssemblyIdentityCache() : SHash<AssemblyIdentityHashTraits>::SHash() + { + // Nothing to do here + } + + AssemblyIdentityCache::~AssemblyIdentityCache() + { + // Delete entries and contents array + for (Hash::Iterator i = Hash::Begin(), end = Hash::End(); i != end; i++) + { + const AssemblyIdentityCacheEntry *pAssemblyIdentityCacheEntry = *i; + delete pAssemblyIdentityCacheEntry; + } + RemoveAll(); + } + + HRESULT AssemblyIdentityCache::Add(LPCSTR szTextualIdentity, + AssemblyIdentityUTF8 *pAssemblyIdentity) + { + HRESULT hr = S_OK; + + NewHolder<AssemblyIdentityCacheEntry> pAssemblyIdentityCacheEntry; + SAFE_NEW(pAssemblyIdentityCacheEntry, AssemblyIdentityCacheEntry); + + pAssemblyIdentityCacheEntry->SetTextualIdentity(szTextualIdentity); + pAssemblyIdentityCacheEntry->SetAssemblyIdentity(pAssemblyIdentity); + + Hash::Add(pAssemblyIdentityCacheEntry); + pAssemblyIdentityCacheEntry.SuppressRelease(); + + Exit: + return hr; + } + + AssemblyIdentityUTF8 *AssemblyIdentityCache::Lookup(LPCSTR szTextualIdentity) + { + AssemblyIdentityUTF8 *pAssemblyIdentity = NULL; + AssemblyIdentityCacheEntry *pAssemblyIdentityCacheEntry = Hash::Lookup(szTextualIdentity); + + if (pAssemblyIdentityCacheEntry != NULL) + { + pAssemblyIdentity = pAssemblyIdentityCacheEntry->GetAssemblyIdentity(); + } + + return pAssemblyIdentity; + } +}; diff --git a/src/coreclr/binder/assemblyname.cpp b/src/coreclr/binder/assemblyname.cpp new file mode 100644 index 00000000000..71e70283c0b --- /dev/null +++ b/src/coreclr/binder/assemblyname.cpp @@ -0,0 +1,498 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// AssemblyName.cpp +// + + +// +// Implements the AssemblyName class +// +// ============================================================ + +#include "assemblyname.hpp" +#include "assembly.hpp" +#include "utils.hpp" +#include "variables.hpp" + +#include "fusionassemblyname.hpp" + +#include "textualidentityparser.hpp" + +#include "corpriv.h" + +#include "ex.h" + +namespace BINDER_SPACE +{ + AssemblyName::AssemblyName() + { + m_cRef = 1; + m_dwNameFlags = NAME_FLAG_NONE; + // Default values present in every assembly name + SetHave(AssemblyIdentity::IDENTITY_FLAG_CULTURE | + AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL); + } + + AssemblyName::~AssemblyName() + { + // Nothing to do here + } + + HRESULT AssemblyName::Init(IMDInternalImport *pIMetaDataAssemblyImport, + PEKIND PeKind, + mdAssemblyRef mdar /* = 0 */, + BOOL fIsDefinition /* = TRUE */) + { + HRESULT hr = S_OK; + mdAssembly mda = 0; + AssemblyMetaDataInternal amd = {0}; + CONST VOID *pvPublicKeyToken = NULL; + DWORD dwPublicKeyToken = 0; + LPCSTR pAssemblyName = NULL; + DWORD dwRefOrDefFlags = 0; + DWORD dwHashAlgId = 0; + + if (fIsDefinition) + { + // Get the assembly token + IF_FAIL_GO(pIMetaDataAssemblyImport->GetAssemblyFromScope(&mda)); + } + + // Get name and metadata + if (fIsDefinition) + { + IF_FAIL_GO(pIMetaDataAssemblyImport->GetAssemblyProps( + mda, // [IN] The Assembly for which to get the properties. + &pvPublicKeyToken, // [OUT] Pointer to the PublicKeyToken blob. + &dwPublicKeyToken, // [OUT] Count of bytes in the PublicKeyToken Blob. + &dwHashAlgId, // [OUT] Hash Algorithm. + &pAssemblyName, // [OUT] Name. + &amd, // [OUT] Assembly MetaData. + &dwRefOrDefFlags // [OUT] Flags. + )); + } + else + { + IF_FAIL_GO(pIMetaDataAssemblyImport->GetAssemblyRefProps( + mdar, // [IN] The Assembly for which to get the properties. + &pvPublicKeyToken, // [OUT] Pointer to the PublicKeyToken blob. + &dwPublicKeyToken, // [OUT] Count of bytes in the PublicKeyToken Blob. + &pAssemblyName, // [OUT] Name. + &amd, // [OUT] Assembly MetaData. + NULL, // [OUT] Hash blob. + NULL, // [OUT] Count of bytes in hash blob. + &dwRefOrDefFlags // [OUT] Flags. + )); + } + + { + StackSString culture; + culture.SetUTF8(amd.szLocale); + culture.Normalize(); + + SString::CIterator itr = culture.Begin(); + if (culture.Find(itr, L';')) + { + culture = SString(culture, culture.Begin(), itr-1); + } + + SetCulture(culture); + } + + { + StackSString assemblyName; + assemblyName.SetUTF8(pAssemblyName); + assemblyName.Normalize(); + + COUNT_T assemblyNameLength = assemblyName.GetCount(); + if (assemblyNameLength == 0 || assemblyNameLength >= MAX_PATH_FNAME) + { + IF_FAIL_GO(FUSION_E_INVALID_NAME); + } + + SetSimpleName(assemblyName); + } + + // See if the assembly[def] is retargetable (ie, for a generic assembly). + if (IsAfRetargetable(dwRefOrDefFlags)) + { + SetIsRetargetable(TRUE); + } + + // Set ContentType + if (IsAfContentType_Default(dwRefOrDefFlags)) + { + SetContentType(AssemblyContentType_Default); + } + else + { + IF_FAIL_GO(FUSION_E_INVALID_NAME); + } + + // Set the assembly version + { + AssemblyVersion *pAssemblyVersion = GetVersion(); + + pAssemblyVersion->SetFeatureVersion(amd.usMajorVersion, amd.usMinorVersion); + pAssemblyVersion->SetServiceVersion(amd.usBuildNumber, amd.usRevisionNumber); + SetHave(AssemblyIdentity::IDENTITY_FLAG_VERSION); + } + + // Set public key and/or public key token (if we have it) + if (pvPublicKeyToken && dwPublicKeyToken) + { + SBuffer publicKeyOrTokenBLOB((const BYTE *) pvPublicKeyToken, dwPublicKeyToken); + + if (IsAfPublicKey(dwRefOrDefFlags)) + { + SBuffer publicKeyTokenBLOB; + + IF_FAIL_GO(GetTokenFromPublicKey(publicKeyOrTokenBLOB, publicKeyTokenBLOB)); + GetPublicKeyTokenBLOB().Set(publicKeyTokenBLOB); + } + else + { + GetPublicKeyTokenBLOB().Set(publicKeyOrTokenBLOB); + } + + SetHave(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN); + } + + SetArchitecture(PeKind); + + Exit: + return hr; + } + + HRESULT AssemblyName::Init(SString &assemblyDisplayName) + { + return TextualIdentityParser::Parse(assemblyDisplayName, this); + } + + HRESULT AssemblyName::Init(IAssemblyName *pIAssemblyName) + { + HRESULT hr = S_OK; + + _ASSERTE(pIAssemblyName != NULL); + + EX_TRY + { + { + // Set the simpleName + StackSString simpleName; + hr = fusion::util::GetSimpleName(pIAssemblyName, simpleName); + IF_FAIL_GO(hr); + SetSimpleName(simpleName); + SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_SIMPLE_NAME); + } + + // Display version + DWORD dwVersionParts[4] = {0,0,0,0}; + DWORD cbVersionSize = sizeof(dwVersionParts[0]); + hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_MAJOR_VERSION, static_cast<PVOID>(&dwVersionParts[0]), &cbVersionSize); + IF_FAIL_GO(hr); + if ((hr == S_OK) && (cbVersionSize != 0)) + { + // Property is present - loop to get the individual version details + for(DWORD i = 0; i < 4; i++) + { + cbVersionSize = sizeof(dwVersionParts[i]); + hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_MAJOR_VERSION+i, static_cast<PVOID>(&dwVersionParts[i]), &cbVersionSize); + IF_FAIL_GO(hr); + } + + m_version.SetFeatureVersion(dwVersionParts[0], dwVersionParts[1]); + m_version.SetServiceVersion(dwVersionParts[2], dwVersionParts[3]); + SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_VERSION); + } + + { + // Display culture + StackSString culture; + hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_CULTURE, culture); + IF_FAIL_GO(hr); + if (hr == S_OK) + { + SetCulture(culture); + SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CULTURE); + } + } + + { + // Display public key token + NewArrayHolder<BYTE> pPublicKeyToken; + DWORD cbPublicKeyToken = 0; + hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_PUBLIC_KEY_TOKEN, static_cast<PBYTE*>(&pPublicKeyToken), &cbPublicKeyToken); + IF_FAIL_GO(hr); + if ((hr == S_OK) && (cbPublicKeyToken != 0)) + { + m_publicKeyOrTokenBLOB.Set(pPublicKeyToken, cbPublicKeyToken); + SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN); + } + else + { + SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL); + } + } + + // Display processor architecture + DWORD peKind = 0; + DWORD cbPeKind = sizeof(peKind); + hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_ARCHITECTURE, static_cast<PVOID>(&peKind), &cbPeKind); + IF_FAIL_GO(hr); + if ((hr == S_OK) && (cbPeKind != 0)) + { + PEKIND PeKind = (PEKIND)peKind; + if (PeKind != peNone) + { + SetArchitecture(PeKind); + SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE); + } + } + + // Display retarget flag + BOOL fRetarget = FALSE; + DWORD cbRetarget = sizeof(fRetarget); + hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_RETARGET, static_cast<PVOID>(&fRetarget), &cbRetarget); + IF_FAIL_GO(hr); + if ((hr == S_OK) && (cbRetarget != 0)) + { + if (fRetarget) + { + SetIsRetargetable(fRetarget); + SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE); + } + } + + // Display content type + DWORD dwContentType = AssemblyContentType_Default; + DWORD cbContentType = sizeof(dwContentType); + hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_CONTENT_TYPE, static_cast<PVOID>(&dwContentType), &cbContentType); + IF_FAIL_GO(hr); + if ((hr == S_OK) && (cbContentType != 0)) + { + if (dwContentType != AssemblyContentType_Default) + { + SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE); + SetContentType((AssemblyContentType)dwContentType); + } + } + + { + // Display custom flag. Dont set it if it is not present since that will end up adding the "Custom=null" attribute + // in the displayname of the assembly that maybe generated using this AssemblyName instance. This could create conflict when + // the displayname is generated from the assembly directly as that will not have a "Custom" field set. + NewArrayHolder<BYTE> pCustomBLOB; + DWORD cbCustomBLOB = 0; + hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_CUSTOM, static_cast<PBYTE*>(&pCustomBLOB), &cbCustomBLOB); + IF_FAIL_GO(hr); + if ((hr == S_OK) && (cbCustomBLOB != 0)) + { + m_customBLOB.Set(pCustomBLOB, cbCustomBLOB); + SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CUSTOM); + } + } + } + EX_CATCH_HRESULT(hr); +Exit: + return hr; + } + + ULONG AssemblyName::AddRef() + { + return InterlockedIncrement(&m_cRef); + } + + ULONG AssemblyName::Release() + { + ULONG ulRef = InterlockedDecrement(&m_cRef); + if (ulRef == 0) + { + delete this; + } + return ulRef; + } + + BOOL AssemblyName::IsCoreLib() + { + // TODO: Is this simple comparison enough? + return EqualsCaseInsensitive(GetSimpleName(), g_BinderVariables->corelib); + } + + ULONG AssemblyName::Hash(DWORD dwIncludeFlags) + { + DWORD dwHash = 0; + DWORD dwUseIdentityFlags = m_dwIdentityFlags; + + // Prune unwanted name parts + if ((dwIncludeFlags & INCLUDE_VERSION) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_VERSION; + } + if ((dwIncludeFlags & INCLUDE_ARCHITECTURE) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE; + } + if ((dwIncludeFlags & INCLUDE_RETARGETABLE) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE; + } + if ((dwIncludeFlags & INCLUDE_CONTENT_TYPE) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE; + } + if ((dwIncludeFlags & INCLUDE_PUBLIC_KEY_TOKEN) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY; + dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN; + } + if ((dwIncludeFlags & EXCLUDE_CULTURE) != 0) + { + dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_CULTURE; + } + + dwHash ^= static_cast<DWORD>(HashCaseInsensitive(GetSimpleName())); + dwHash = _rotl(dwHash, 4); + + if (AssemblyIdentity::Have(dwUseIdentityFlags, + AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY) || + AssemblyIdentity::Have(dwUseIdentityFlags, + AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN)) + { + const BYTE *pbPublicKeyOrToken = GetPublicKeyTokenBLOB(); + DWORD dwcbPublicKeyOrToken = GetPublicKeyTokenBLOB().GetSize(); + + _ASSERTE(pbPublicKeyOrToken != NULL); + + dwHash ^= HashBytes(pbPublicKeyOrToken, dwcbPublicKeyOrToken); + dwHash = _rotl(dwHash, 4); + } + + if (AssemblyIdentity::Have(dwUseIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_VERSION)) + { + AssemblyVersion *pAssemblyVersion = GetVersion(); + + dwHash ^= pAssemblyVersion->GetMajor(); + dwHash = _rotl(dwHash, 8); + dwHash ^= pAssemblyVersion->GetMinor(); + dwHash = _rotl(dwHash, 8); + dwHash ^= pAssemblyVersion->GetBuild(); + dwHash = _rotl(dwHash, 8); + dwHash ^= pAssemblyVersion->GetRevision(); + dwHash = _rotl(dwHash, 8); + } + + if (AssemblyIdentity::Have(dwUseIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_CULTURE)) + { + dwHash ^= static_cast<DWORD>(HashCaseInsensitive(GetNormalizedCulture())); + dwHash = _rotl(dwHash, 4); + } + + if (AssemblyIdentity::Have(dwUseIdentityFlags, + AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE)) + { + dwHash ^= 1; + dwHash = _rotl(dwHash, 4); + } + + if (AssemblyIdentity::Have(dwUseIdentityFlags, + AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE)) + { + dwHash ^= static_cast<DWORD>(GetArchitecture()); + dwHash = _rotl(dwHash, 4); + } + + if (AssemblyIdentity::Have(dwUseIdentityFlags, + AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE)) + { + dwHash ^= static_cast<DWORD>(GetContentType()); + dwHash = _rotl(dwHash, 4); + } + + return static_cast<ULONG>(dwHash); + } + + BOOL AssemblyName::Equals(AssemblyName *pAssemblyName, + DWORD dwIncludeFlags) + { + BOOL fEquals = FALSE; + + if (GetContentType() == AssemblyContentType_WindowsRuntime) + { // Assembly is meaningless for WinRT, all assemblies form one joint type namespace + return (GetContentType() == pAssemblyName->GetContentType()); + } + + if (EqualsCaseInsensitive(GetSimpleName(), pAssemblyName->GetSimpleName()) && + (GetContentType() == pAssemblyName->GetContentType())) + { + fEquals = TRUE; + + if ((dwIncludeFlags & EXCLUDE_CULTURE) == 0) + { + fEquals = EqualsCaseInsensitive(GetNormalizedCulture(), pAssemblyName->GetNormalizedCulture()); + } + + if (fEquals && (dwIncludeFlags & INCLUDE_PUBLIC_KEY_TOKEN) != 0) + { + fEquals = (GetPublicKeyTokenBLOB().Equals(pAssemblyName->GetPublicKeyTokenBLOB())); + } + + if (fEquals && ((dwIncludeFlags & INCLUDE_ARCHITECTURE) != 0)) + { + fEquals = (GetArchitecture() == pAssemblyName->GetArchitecture()); + } + + if (fEquals && ((dwIncludeFlags & INCLUDE_VERSION) != 0)) + { + fEquals = GetVersion()->Equals(pAssemblyName->GetVersion()); + } + + if (fEquals && ((dwIncludeFlags & INCLUDE_RETARGETABLE) != 0)) + { + fEquals = (GetIsRetargetable() == pAssemblyName->GetIsRetargetable()); + } + } + + return fEquals; + } + + void AssemblyName::GetDisplayName(PathString &displayName, + DWORD dwIncludeFlags) + { + DWORD dwUseIdentityFlags = m_dwIdentityFlags; + + // Prune unwanted name parts + if ((dwIncludeFlags & INCLUDE_VERSION) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_VERSION; + } + if ((dwIncludeFlags & INCLUDE_ARCHITECTURE) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE; + } + if ((dwIncludeFlags & INCLUDE_RETARGETABLE) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE; + } + if ((dwIncludeFlags & INCLUDE_CONTENT_TYPE) == 0) + { + dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE; + } + + TextualIdentityParser::ToString(this, dwUseIdentityFlags, displayName); + } + + SString &AssemblyName::GetNormalizedCulture() + { + SString &culture = GetCulture(); + + if (culture.IsEmpty()) + { + culture = g_BinderVariables->cultureNeutral; + } + + return culture; + } +}; // namespace BINDER_SPACE diff --git a/src/coreclr/binder/bindertracing.cpp b/src/coreclr/binder/bindertracing.cpp new file mode 100644 index 00000000000..29ba819ee6c --- /dev/null +++ b/src/coreclr/binder/bindertracing.cpp @@ -0,0 +1,461 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// bindertracing.cpp +// + + +// +// Implements helpers for binder tracing +// +// ============================================================ + +#include "common.h" +#include "bindertracing.h" +#include "bindresult.hpp" + +#include "activitytracker.h" + +#ifdef FEATURE_EVENT_TRACE +#include "eventtracebase.h" +#endif // FEATURE_EVENT_TRACE + +using namespace BINDER_SPACE; + +namespace +{ + void FireAssemblyLoadStart(const BinderTracing::AssemblyBindOperation::BindRequest &request) + { +#ifdef FEATURE_EVENT_TRACE + if (!EventEnabledAssemblyLoadStart()) + return; + + GUID activityId = GUID_NULL; + GUID relatedActivityId = GUID_NULL; + ActivityTracker::Start(&activityId, &relatedActivityId); + + FireEtwAssemblyLoadStart( + GetClrInstanceId(), + request.AssemblyName, + request.AssemblyPath, + request.RequestingAssembly, + request.AssemblyLoadContext, + request.RequestingAssemblyLoadContext, + &activityId, + &relatedActivityId); +#endif // FEATURE_EVENT_TRACE + } + + void FireAssemblyLoadStop(const BinderTracing::AssemblyBindOperation::BindRequest &request, PEAssembly *resultAssembly, bool cached) + { +#ifdef FEATURE_EVENT_TRACE + if (!EventEnabledAssemblyLoadStop()) + return; + + GUID activityId = GUID_NULL; + ActivityTracker::Stop(&activityId); + + SString resultName; + SString resultPath; + bool success = resultAssembly != nullptr; + if (success) + { + resultPath = resultAssembly->GetPath(); + resultAssembly->GetDisplayName(resultName); + } + + FireEtwAssemblyLoadStop( + GetClrInstanceId(), + request.AssemblyName, + request.AssemblyPath, + request.RequestingAssembly, + request.AssemblyLoadContext, + request.RequestingAssemblyLoadContext, + success, + resultName, + resultPath, + cached, + &activityId); +#endif // FEATURE_EVENT_TRACE + } + + void GetAssemblyLoadContextNameFromManagedALC(INT_PTR managedALC, /* out */ SString &alcName) + { + if (managedALC == GetAppDomain()->GetTPABinderContext()->GetManagedAssemblyLoadContext()) + { + alcName.Set(W("Default")); + return; + } + +#ifdef CROSSGEN_COMPILE + alcName.Set(W("Custom")); +#else // CROSSGEN_COMPILE + OBJECTREF *alc = reinterpret_cast<OBJECTREF *>(managedALC); + + GCX_COOP(); + struct _gc { + STRINGREF alcName; + } gc; + ZeroMemory(&gc, sizeof(gc)); + + GCPROTECT_BEGIN(gc); + + PREPARE_VIRTUAL_CALLSITE(METHOD__OBJECT__TO_STRING, *alc); + DECLARE_ARGHOLDER_ARRAY(args, 1); + args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*alc); + CALL_MANAGED_METHOD_RETREF(gc.alcName, STRINGREF, args); + gc.alcName->GetSString(alcName); + + GCPROTECT_END(); +#endif // CROSSGEN_COMPILE + } + + void GetAssemblyLoadContextNameFromBinderID(UINT_PTR binderID, AppDomain *domain, /*out*/ SString &alcName) + { + ICLRPrivBinder *binder = reinterpret_cast<ICLRPrivBinder *>(binderID); + if (AreSameBinderInstance(binder, domain->GetTPABinderContext())) + { + alcName.Set(W("Default")); + } + else + { +#ifdef CROSSGEN_COMPILE + GetAssemblyLoadContextNameFromManagedALC(0, alcName); +#else // CROSSGEN_COMPILE + CLRPrivBinderAssemblyLoadContext *alcBinder = static_cast<CLRPrivBinderAssemblyLoadContext *>(binder); + + GetAssemblyLoadContextNameFromManagedALC(alcBinder->GetManagedAssemblyLoadContext(), alcName); +#endif // CROSSGEN_COMPILE + } + } + + void GetAssemblyLoadContextNameFromBindContext(ICLRPrivBinder *bindContext, AppDomain *domain, /*out*/ SString &alcName) + { + _ASSERTE(bindContext != nullptr); + + UINT_PTR binderID = 0; + HRESULT hr = bindContext->GetBinderID(&binderID); + _ASSERTE(SUCCEEDED(hr)); + if (SUCCEEDED(hr)) + GetAssemblyLoadContextNameFromBinderID(binderID, domain, alcName); + } + + void GetAssemblyLoadContextNameFromSpec(AssemblySpec *spec, /*out*/ SString &alcName) + { + _ASSERTE(spec != nullptr); + + AppDomain *domain = spec->GetAppDomain(); + ICLRPrivBinder* bindContext = spec->GetBindingContext(); + if (bindContext == nullptr) + bindContext = spec->GetBindingContextFromParentAssembly(domain); + + GetAssemblyLoadContextNameFromBindContext(bindContext, domain, alcName); + } + + void PopulateBindRequest(/*inout*/ BinderTracing::AssemblyBindOperation::BindRequest &request) + { + AssemblySpec *spec = request.AssemblySpec; + _ASSERTE(spec != nullptr); + + if (request.AssemblyPath.IsEmpty()) + request.AssemblyPath = spec->GetCodeBase(); + + if (spec->GetName() != nullptr) + spec->GetDisplayName(ASM_DISPLAYF_VERSION | ASM_DISPLAYF_CULTURE | ASM_DISPLAYF_PUBLIC_KEY_TOKEN, request.AssemblyName); + + DomainAssembly *parentAssembly = spec->GetParentAssembly(); + if (parentAssembly != nullptr) + { + PEAssembly *peAssembly = parentAssembly->GetFile(); + _ASSERTE(peAssembly != nullptr); + peAssembly->GetDisplayName(request.RequestingAssembly); + + AppDomain *domain = parentAssembly->GetAppDomain(); + ICLRPrivBinder *bindContext = peAssembly->GetBindingContext(); + if (bindContext == nullptr) + bindContext = domain->GetTPABinderContext(); // System.Private.CoreLib returns null + + GetAssemblyLoadContextNameFromBindContext(bindContext, domain, request.RequestingAssemblyLoadContext); + } + + GetAssemblyLoadContextNameFromSpec(spec, request.AssemblyLoadContext); + } + + const WCHAR *s_assemblyNotFoundMessage = W("Could not locate assembly"); +} + +bool BinderTracing::IsEnabled() +{ +#ifdef FEATURE_EVENT_TRACE + // Just check for the AssemblyLoadStart event being enabled. + return EventEnabledAssemblyLoadStart(); +#endif // FEATURE_EVENT_TRACE + return false; +} + +namespace BinderTracing +{ + AssemblyBindOperation::AssemblyBindOperation(AssemblySpec *assemblySpec, const WCHAR *assemblyPath) + : m_bindRequest { assemblySpec, nullptr, assemblyPath } + , m_populatedBindRequest { false } + , m_checkedIgnoreBind { false } + , m_ignoreBind { false } + , m_resultAssembly { nullptr } + , m_cached { false } + { + _ASSERTE(assemblySpec != nullptr); + + if (!BinderTracing::IsEnabled() || ShouldIgnoreBind()) + return; + + PopulateBindRequest(m_bindRequest); + m_populatedBindRequest = true; + FireAssemblyLoadStart(m_bindRequest); + } + + AssemblyBindOperation::~AssemblyBindOperation() + { + if (BinderTracing::IsEnabled() && !ShouldIgnoreBind()) + { + // Make sure the bind request is populated. Tracing may have been enabled mid-bind. + if (!m_populatedBindRequest) + PopulateBindRequest(m_bindRequest); + + FireAssemblyLoadStop(m_bindRequest, m_resultAssembly, m_cached); + } + + if (m_resultAssembly != nullptr) + m_resultAssembly->Release(); + } + + void AssemblyBindOperation::SetResult(PEAssembly *assembly, bool cached) + { + _ASSERTE(m_resultAssembly == nullptr); + m_resultAssembly = assembly; + if (m_resultAssembly != nullptr) + m_resultAssembly->AddRef(); + + m_cached = cached; + } + + bool AssemblyBindOperation::ShouldIgnoreBind() + { + if (m_checkedIgnoreBind) + return m_ignoreBind; + + // ActivityTracker or EventSource may have triggered the system satellite load. + // Don't track system satellite binding to avoid potential infinite recursion. + m_ignoreBind = m_bindRequest.AssemblySpec->IsCoreLibSatellite(); + m_checkedIgnoreBind = true; + return m_ignoreBind; + } +} + +namespace BinderTracing +{ + ResolutionAttemptedOperation::ResolutionAttemptedOperation(AssemblyName *assemblyName, UINT_PTR binderID, INT_PTR managedALC, const HRESULT& hr) + : m_hr { hr } + , m_stage { Stage::NotYetStarted } + , m_tracingEnabled { BinderTracing::IsEnabled() } + , m_assemblyNameObject { assemblyName } + , m_pFoundAssembly { nullptr } + { + _ASSERTE(binderID != 0 || managedALC != 0); + + if (!m_tracingEnabled) + return; + + // When binding the main assembly (by code base instead of name), the assembly name will be null. In this special case, we just + // leave the assembly name empty. + if (m_assemblyNameObject != nullptr) + m_assemblyNameObject->GetDisplayName(m_assemblyName, AssemblyName::INCLUDE_VERSION | AssemblyName::INCLUDE_PUBLIC_KEY_TOKEN); + + if (managedALC != 0) + { + GetAssemblyLoadContextNameFromManagedALC(managedALC, m_assemblyLoadContextName); + } + else + { + GetAssemblyLoadContextNameFromBinderID(binderID, GetAppDomain(), m_assemblyLoadContextName); + } + } + + // This function simply traces out the two stages represented by the bind result. + // It does not change the stage/assembly of the ResolutionAttemptedOperation class instance. + void ResolutionAttemptedOperation::TraceBindResult(const BindResult &bindResult, bool mvidMismatch) + { + if (!m_tracingEnabled) + return; + + // Use the error message that would be reported in the file load exception + StackSString errorMsg; + if (mvidMismatch) + errorMsg.LoadResource(CCompRC::Error, IDS_HOST_ASSEMBLY_RESOLVER_ASSEMBLY_ALREADY_LOADED_IN_CONTEXT); + + const BindResult::AttemptResult *inContextAttempt = bindResult.GetAttempt(true /*foundInContext*/); + const BindResult::AttemptResult *appAssembliesAttempt = bindResult.GetAttempt(false /*foundInContext*/); + + if (inContextAttempt != nullptr) + { + // If there the attempt HR represents a success, but the tracked HR represents a failure (e.g. from further validation), report the failed HR + bool isLastAttempt = appAssembliesAttempt == nullptr; + TraceStage(Stage::FindInLoadContext, + isLastAttempt && FAILED(m_hr) && SUCCEEDED(inContextAttempt->HResult) ? m_hr : inContextAttempt->HResult, + inContextAttempt->Assembly, + mvidMismatch && isLastAttempt ? errorMsg.GetUnicode() : nullptr); + } + + if (appAssembliesAttempt != nullptr) + TraceStage(Stage::ApplicationAssemblies, FAILED(m_hr) && SUCCEEDED(appAssembliesAttempt->HResult) ? m_hr : appAssembliesAttempt->HResult, appAssembliesAttempt->Assembly, mvidMismatch ? errorMsg.GetUnicode() : nullptr); + } + + void ResolutionAttemptedOperation::TraceStage(Stage stage, HRESULT hr, BINDER_SPACE::Assembly *resultAssembly, const WCHAR *customError) + { + if (!m_tracingEnabled || stage == Stage::NotYetStarted) + return; + + PathString resultAssemblyName; + StackSString resultAssemblyPath; + if (resultAssembly != nullptr) + { + resultAssembly->GetAssemblyName()->GetDisplayName(resultAssemblyName, AssemblyName::INCLUDE_VERSION | AssemblyName::INCLUDE_PUBLIC_KEY_TOKEN); + resultAssemblyPath = resultAssembly->GetPEImage()->GetPath(); + } + + Result result; + StackSString errorMsg; + if (customError != nullptr) + { + errorMsg.Set(customError); + result = Result::Failure; + } + else if (!m_exceptionMessage.IsEmpty()) + { + errorMsg = m_exceptionMessage; + result = Result::Exception; + } + else + { + switch (hr) + { + case S_FALSE: + case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): + static_assert(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == COR_E_FILENOTFOUND, + "COR_E_FILENOTFOUND has sane value"); + + result = Result::AssemblyNotFound; + errorMsg.Set(s_assemblyNotFoundMessage); + break; + + case FUSION_E_APP_DOMAIN_LOCKED: + result = Result::IncompatibleVersion; + + { + errorMsg.Set(W("Requested version")); + if (m_assemblyNameObject != nullptr) + { + const auto &reqVersion = m_assemblyNameObject->GetVersion(); + errorMsg.AppendPrintf(W(" %d.%d.%d.%d"), + reqVersion->GetMajor(), + reqVersion->GetMinor(), + reqVersion->GetBuild(), + reqVersion->GetRevision()); + } + + errorMsg.Append(W(" is incompatible with found version")); + if (resultAssembly != nullptr) + { + const auto &foundVersion = resultAssembly->GetAssemblyName()->GetVersion(); + errorMsg.AppendPrintf(W(" %d.%d.%d.%d"), + foundVersion->GetMajor(), + foundVersion->GetMinor(), + foundVersion->GetBuild(), + foundVersion->GetRevision()); + } + } + break; + + case FUSION_E_REF_DEF_MISMATCH: + result = Result::MismatchedAssemblyName; + errorMsg.Printf(W("Requested assembly name '%s' does not match found assembly name"), m_assemblyName.GetUnicode()); + if (resultAssembly != nullptr) + errorMsg.AppendPrintf(W(" '%s'"), resultAssemblyName.GetUnicode()); + + break; + + default: + if (SUCCEEDED(hr)) + { + result = Result::Success; + _ASSERTE(resultAssembly != nullptr); + // Leave errorMsg empty in this case. + } + else + { + result = Result::Failure; + errorMsg.Printf(W("Resolution failed with HRESULT (%08x)"), m_hr); + } + } + } + + FireEtwResolutionAttempted( + GetClrInstanceId(), + m_assemblyName, + static_cast<uint16_t>(stage), + m_assemblyLoadContextName, + static_cast<uint16_t>(result), + resultAssemblyName, + resultAssemblyPath, + errorMsg); + } + + // static + void ResolutionAttemptedOperation::TraceAppDomainAssemblyResolve(AssemblySpec *spec, PEAssembly *resultAssembly, Exception *exception) + { + if (!BinderTracing::IsEnabled()) + return; + + Result result; + StackSString errorMessage; + StackSString resultAssemblyName; + StackSString resultAssemblyPath; + if (exception != nullptr) + { + exception->GetMessage(errorMessage); + result = Result::Exception; + } + else if (resultAssembly != nullptr) + { + result = Result::Success; + resultAssemblyPath = resultAssembly->GetPath(); + resultAssembly->GetDisplayName(resultAssemblyName); + } + else + { + result = Result::AssemblyNotFound; + errorMessage.Set(s_assemblyNotFoundMessage); + } + + StackSString assemblyName; + spec->GetDisplayName(ASM_DISPLAYF_VERSION | ASM_DISPLAYF_CULTURE | ASM_DISPLAYF_PUBLIC_KEY_TOKEN, assemblyName); + + StackSString alcName; + GetAssemblyLoadContextNameFromSpec(spec, alcName); + + FireEtwResolutionAttempted( + GetClrInstanceId(), + assemblyName, + static_cast<uint16_t>(Stage::AppDomainAssemblyResolveEvent), + alcName, + static_cast<uint16_t>(result), + resultAssemblyName, + resultAssemblyPath, + errorMessage); + } +} + +void BinderTracing::PathProbed(const WCHAR *path, BinderTracing::PathSource source, HRESULT hr) +{ + FireEtwKnownPathProbed(GetClrInstanceId(), path, source, hr); +} diff --git a/src/coreclr/binder/clrprivbinderassemblyloadcontext.cpp b/src/coreclr/binder/clrprivbinderassemblyloadcontext.cpp new file mode 100644 index 00000000000..69d8d833728 --- /dev/null +++ b/src/coreclr/binder/clrprivbinderassemblyloadcontext.cpp @@ -0,0 +1,293 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "common.h" +#include "assemblybinder.hpp" +#include "clrprivbindercoreclr.h" +#include "clrprivbinderassemblyloadcontext.h" + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + +using namespace BINDER_SPACE; + +// ============================================================================ +// CLRPrivBinderAssemblyLoadContext implementation +// ============================================================================ +HRESULT CLRPrivBinderAssemblyLoadContext::BindAssemblyByNameWorker(BINDER_SPACE::AssemblyName *pAssemblyName, + BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly) +{ + VALIDATE_ARG_RET(pAssemblyName != nullptr && ppCoreCLRFoundAssembly != nullptr); + HRESULT hr = S_OK; + +#ifdef _DEBUG + // CoreLib should be bound using BindToSystem + _ASSERTE(!pAssemblyName->IsCoreLib()); +#endif + + // Do we have the assembly already loaded in the context of the current binder? + hr = AssemblyBinder::BindAssembly(&m_appContext, + pAssemblyName, + NULL, + NULL, + FALSE, //fNgenExplicitBind, + FALSE, //fExplicitBindToNativeImage, + false, //excludeAppPaths, + ppCoreCLRFoundAssembly); + if (!FAILED(hr)) + { + _ASSERTE(*ppCoreCLRFoundAssembly != NULL); + (*ppCoreCLRFoundAssembly)->SetBinder(this); + } + + return hr; +} + +HRESULT CLRPrivBinderAssemblyLoadContext::BindAssemblyByName(IAssemblyName *pIAssemblyName, + ICLRPrivAssembly **ppAssembly) +{ + HRESULT hr = S_OK; + VALIDATE_ARG_RET(pIAssemblyName != nullptr && ppAssembly != nullptr); + + _ASSERTE(m_pTPABinder != NULL); + + ReleaseHolder<BINDER_SPACE::Assembly> pCoreCLRFoundAssembly; + ReleaseHolder<AssemblyName> pAssemblyName; + + SAFE_NEW(pAssemblyName, AssemblyName); + IF_FAIL_GO(pAssemblyName->Init(pIAssemblyName)); + + // When LoadContext needs to resolve an assembly reference, it will go through the following lookup order: + // + // 1) Lookup the assembly within the LoadContext itself. If assembly is found, use it. + // 2) Invoke the LoadContext's Load method implementation. If assembly is found, use it. + // 3) Lookup the assembly within TPABinder (except for satellite requests). If assembly is found, use it. + // 4) Invoke the LoadContext's ResolveSatelliteAssembly method (for satellite requests). If assembly is found, use it. + // 5) Invoke the LoadContext's Resolving event. If assembly is found, use it. + // 6) Raise exception. + // + // This approach enables a LoadContext to override assemblies that have been loaded in TPA context by loading + // a different (or even the same!) version. + + { + // Step 1 - Try to find the assembly within the LoadContext. + hr = BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly); + if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || + (hr == FUSION_E_APP_DOMAIN_LOCKED) || (hr == FUSION_E_REF_DEF_MISMATCH)) + { + // If we are here, one of the following is possible: + // + // 1) The assembly has not been found in the current binder's application context (i.e. it has not already been loaded), OR + // 2) An assembly with the same simple name was already loaded in the context of the current binder but we ran into a Ref/Def + // mismatch (either due to version difference or strong-name difference). + // + // Thus, if default binder has been overridden, then invoke it in an attempt to perform the binding for it make the call + // of what to do next. The host-overridden binder can either fail the bind or return reference to an existing assembly + // that has been loaded. + // + hr = AssemblyBinder::BindUsingHostAssemblyResolver(GetManagedAssemblyLoadContext(), pAssemblyName, pIAssemblyName, m_pTPABinder, &pCoreCLRFoundAssembly); + if (SUCCEEDED(hr)) + { + // We maybe returned an assembly that was bound to a different AssemblyLoadContext instance. + // In such a case, we will not overwrite the binding context (which would be wrong since it would not + // be present in the cache of the current binding context). + if (pCoreCLRFoundAssembly->GetBinder() == NULL) + { + pCoreCLRFoundAssembly->SetBinder(this); + } + } + } + } + + IF_FAIL_GO(hr); + + // Extract the assembly reference. + // + // For TPA assemblies that were bound, TPABinder + // would have already set the binder reference for the assembly, so we just need to + // extract the reference now. + *ppAssembly = pCoreCLRFoundAssembly.Extract(); + +Exit:; + + return hr; +} + +HRESULT CLRPrivBinderAssemblyLoadContext::BindUsingPEImage( /* in */ PEImage *pPEImage, + /* in */ BOOL fIsNativeImage, + /* [retval][out] */ ICLRPrivAssembly **ppAssembly) +{ + HRESULT hr = S_OK; + + EX_TRY + { + ReleaseHolder<BINDER_SPACE::Assembly> pCoreCLRFoundAssembly; + ReleaseHolder<BINDER_SPACE::AssemblyName> pAssemblyName; + ReleaseHolder<IMDInternalImport> pIMetaDataAssemblyImport; + + PEKIND PeKind = peNone; + + // Get the Metadata interface + DWORD dwPAFlags[2]; + IF_FAIL_GO(BinderAcquireImport(pPEImage, &pIMetaDataAssemblyImport, dwPAFlags, fIsNativeImage)); + IF_FAIL_GO(AssemblyBinder::TranslatePEToArchitectureType(dwPAFlags, &PeKind)); + + _ASSERTE(pIMetaDataAssemblyImport != NULL); + + // Using the information we just got, initialize the assemblyname + SAFE_NEW(pAssemblyName, AssemblyName); + IF_FAIL_GO(pAssemblyName->Init(pIMetaDataAssemblyImport, PeKind)); + + // Validate architecture + if (!BINDER_SPACE::Assembly::IsValidArchitecture(pAssemblyName->GetArchitecture())) + { + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT)); + } + + // Disallow attempt to bind to the core library. Aside from that, + // the LoadContext can load any assembly (even if it was in a different LoadContext like TPA). + if (pAssemblyName->IsCoreLib()) + { + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + } + + hr = AssemblyBinder::BindUsingPEImage(&m_appContext, pAssemblyName, pPEImage, PeKind, pIMetaDataAssemblyImport, &pCoreCLRFoundAssembly); + if (hr == S_OK) + { + _ASSERTE(pCoreCLRFoundAssembly != NULL); + pCoreCLRFoundAssembly->SetBinder(this); + *ppAssembly = pCoreCLRFoundAssembly.Extract(); + } +Exit:; + } + EX_CATCH_HRESULT(hr); + + return hr; +} + +HRESULT CLRPrivBinderAssemblyLoadContext::GetLoaderAllocator(LPVOID* pLoaderAllocator) +{ + _ASSERTE(pLoaderAllocator != NULL); + if (m_pAssemblyLoaderAllocator == NULL) + { + return E_FAIL; + } + + *pLoaderAllocator = m_pAssemblyLoaderAllocator; + return S_OK; +} + +//============================================================================= +// Creates an instance of the AssemblyLoadContext Binder +// +// This method does not take a lock since it is invoked from the ctor of the +// managed AssemblyLoadContext type. +//============================================================================= +/* static */ +HRESULT CLRPrivBinderAssemblyLoadContext::SetupContext(DWORD dwAppDomainId, + CLRPrivBinderCoreCLR *pTPABinder, + LoaderAllocator* pLoaderAllocator, + void* loaderAllocatorHandle, + UINT_PTR ptrAssemblyLoadContext, + CLRPrivBinderAssemblyLoadContext **ppBindContext) +{ + HRESULT hr = E_FAIL; + EX_TRY + { + if(ppBindContext != NULL) + { + ReleaseHolder<CLRPrivBinderAssemblyLoadContext> pBinder; + + SAFE_NEW(pBinder, CLRPrivBinderAssemblyLoadContext); + UINT_PTR binderId; + pBinder->GetBinderID(&binderId); + hr = pBinder->m_appContext.Init(binderId); + if(SUCCEEDED(hr)) + { + // Save the reference to the AppDomain in which the binder lives + pBinder->m_appContext.SetAppDomainId(dwAppDomainId); + + // Save reference to the TPABinder that is required to be present. + _ASSERTE(pTPABinder != NULL); + pBinder->m_pTPABinder = pTPABinder; + + // Save the reference to the IntPtr for GCHandle for the managed + // AssemblyLoadContext instance + pBinder->m_ptrManagedAssemblyLoadContext = ptrAssemblyLoadContext; + + if (pLoaderAllocator != NULL) + { + // Link to LoaderAllocator, keep a reference to it + VERIFY(pLoaderAllocator->AddReferenceIfAlive()); + } + pBinder->m_pAssemblyLoaderAllocator = pLoaderAllocator; + pBinder->m_loaderAllocatorHandle = loaderAllocatorHandle; + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + if (pLoaderAllocator != NULL) + { + ((AssemblyLoaderAllocator*)pLoaderAllocator)->RegisterBinder(pBinder); + } +#endif + // Return reference to the allocated Binder instance + *ppBindContext = clr::SafeAddRef(pBinder.Extract()); + } + } + } + EX_CATCH_HRESULT(hr); + +Exit: + return hr; +} + +void CLRPrivBinderAssemblyLoadContext::PrepareForLoadContextRelease(INT_PTR ptrManagedStrongAssemblyLoadContext) +{ + CONTRACTL + { + GC_NOTRIGGER; + THROWS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + // Add a strong handle so that the managed assembly load context stays alive until the + // CLRPrivBinderAssemblyLoadContext::ReleaseLoadContext is called. + // We keep the weak handle as well since this method can be running on one thread (e.g. the finalizer one) + // and other thread can be using the weak handle. + m_ptrManagedStrongAssemblyLoadContext = ptrManagedStrongAssemblyLoadContext; + + _ASSERTE(m_pAssemblyLoaderAllocator != NULL); + _ASSERTE(m_loaderAllocatorHandle != NULL); + + // We cannot delete the binder here as it is used indirectly when comparing assemblies with the same binder + // It will be deleted when the LoaderAllocator will be deleted + // But we can release the LoaderAllocator as we are no longer using it here + m_pAssemblyLoaderAllocator->Release(); + m_pAssemblyLoaderAllocator = NULL; + + // Destroy the strong handle to the LoaderAllocator in order to let it reach its finalizer + DestroyHandle(reinterpret_cast<OBJECTHANDLE>(m_loaderAllocatorHandle)); + m_loaderAllocatorHandle = NULL; +} + +CLRPrivBinderAssemblyLoadContext::CLRPrivBinderAssemblyLoadContext() +{ + m_pTPABinder = NULL; + m_ptrManagedStrongAssemblyLoadContext = NULL; +} + +void CLRPrivBinderAssemblyLoadContext::ReleaseLoadContext() +{ + VERIFY(m_ptrManagedAssemblyLoadContext != NULL); + VERIFY(m_ptrManagedStrongAssemblyLoadContext != NULL); + + // This method is called to release the weak and strong handles on the managed AssemblyLoadContext + // once the Unloading event has been fired + OBJECTHANDLE handle = reinterpret_cast<OBJECTHANDLE>(m_ptrManagedAssemblyLoadContext); + DestroyLongWeakHandle(handle); + handle = reinterpret_cast<OBJECTHANDLE>(m_ptrManagedStrongAssemblyLoadContext); + DestroyHandle(handle); + m_ptrManagedAssemblyLoadContext = NULL; +} + +#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + diff --git a/src/coreclr/binder/clrprivbindercoreclr.cpp b/src/coreclr/binder/clrprivbindercoreclr.cpp new file mode 100644 index 00000000000..571e127d7da --- /dev/null +++ b/src/coreclr/binder/clrprivbindercoreclr.cpp @@ -0,0 +1,263 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "common.h" +#include "assemblybinder.hpp" +#include "clrprivbindercoreclr.h" +#include "variables.hpp" + +using namespace BINDER_SPACE; + +//============================================================================= +// Helper functions +//----------------------------------------------------------------------------- + +HRESULT CLRPrivBinderCoreCLR::BindAssemblyByNameWorker(BINDER_SPACE::AssemblyName *pAssemblyName, + BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly, + bool excludeAppPaths) +{ + VALIDATE_ARG_RET(pAssemblyName != nullptr && ppCoreCLRFoundAssembly != nullptr); + HRESULT hr = S_OK; + +#ifdef _DEBUG + // CoreLib should be bound using BindToSystem + _ASSERTE(!pAssemblyName->IsCoreLib()); +#endif + + hr = AssemblyBinder::BindAssembly(&m_appContext, + pAssemblyName, + NULL, + NULL, + FALSE, //fNgenExplicitBind, + FALSE, //fExplicitBindToNativeImage, + excludeAppPaths, + ppCoreCLRFoundAssembly); + if (!FAILED(hr)) + { + (*ppCoreCLRFoundAssembly)->SetBinder(this); + } + + return hr; +} + +// ============================================================================ +// CLRPrivBinderCoreCLR implementation +// ============================================================================ +HRESULT CLRPrivBinderCoreCLR::BindAssemblyByName(IAssemblyName *pIAssemblyName, + ICLRPrivAssembly **ppAssembly) +{ + HRESULT hr = S_OK; + VALIDATE_ARG_RET(pIAssemblyName != nullptr && ppAssembly != nullptr); + + *ppAssembly = nullptr; + + ReleaseHolder<BINDER_SPACE::Assembly> pCoreCLRFoundAssembly; + ReleaseHolder<AssemblyName> pAssemblyName; + + SAFE_NEW(pAssemblyName, AssemblyName); + IF_FAIL_GO(pAssemblyName->Init(pIAssemblyName)); + + hr = BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly, false /* excludeAppPaths */); + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || + (hr == FUSION_E_APP_DOMAIN_LOCKED) || (hr == FUSION_E_REF_DEF_MISMATCH)) + { + // If we are here, one of the following is possible: + // + // 1) The assembly has not been found in the current binder's application context (i.e. it has not already been loaded), OR + // 2) An assembly with the same simple name was already loaded in the context of the current binder but we ran into a Ref/Def + // mismatch (either due to version difference or strong-name difference). + // + // Attempt to resolve the assembly via managed ALC instance. This can either fail the bind or return reference to an existing + // assembly that has been loaded + INT_PTR pManagedAssemblyLoadContext = GetManagedAssemblyLoadContext(); + if (pManagedAssemblyLoadContext == NULL) + { + // For satellite assemblies, the managed ALC has additional resolution logic (defined by the runtime) which + // should be run even if the managed default ALC has not yet been used. (For non-satellite assemblies, any + // additional logic comes through a user-defined event handler which would have initialized the managed ALC, + // so if the managed ALC is not set yet, there is no additional logic to run) + SString &culture = pAssemblyName->GetCulture(); + if (!culture.IsEmpty() && !culture.EqualsCaseInsensitive(g_BinderVariables->cultureNeutral)) + { + // Make sure the managed default ALC is initialized. + GCX_COOP(); + PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__INITIALIZE_DEFAULT_CONTEXT); + DECLARE_ARGHOLDER_ARRAY(args, 0); + CALL_MANAGED_METHOD_NORET(args) + + pManagedAssemblyLoadContext = GetManagedAssemblyLoadContext(); + _ASSERTE(pManagedAssemblyLoadContext != NULL); + } + } + + if (pManagedAssemblyLoadContext != NULL) + { + hr = AssemblyBinder::BindUsingHostAssemblyResolver(pManagedAssemblyLoadContext, pAssemblyName, pIAssemblyName, + NULL, &pCoreCLRFoundAssembly); + if (SUCCEEDED(hr)) + { + // We maybe returned an assembly that was bound to a different AssemblyLoadContext instance. + // In such a case, we will not overwrite the binding context (which would be wrong since it would not + // be present in the cache of the current binding context). + if (pCoreCLRFoundAssembly->GetBinder() == NULL) + { + pCoreCLRFoundAssembly->SetBinder(this); + } + } + } + } +#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + + IF_FAIL_GO(hr); + + *ppAssembly = pCoreCLRFoundAssembly.Extract(); + +Exit:; + + return hr; +} + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) +HRESULT CLRPrivBinderCoreCLR::BindUsingPEImage( /* in */ PEImage *pPEImage, + /* in */ BOOL fIsNativeImage, + /* [retval][out] */ ICLRPrivAssembly **ppAssembly) +{ + HRESULT hr = S_OK; + + EX_TRY + { + ReleaseHolder<BINDER_SPACE::Assembly> pCoreCLRFoundAssembly; + ReleaseHolder<BINDER_SPACE::AssemblyName> pAssemblyName; + ReleaseHolder<IMDInternalImport> pIMetaDataAssemblyImport; + + PEKIND PeKind = peNone; + + // Get the Metadata interface + DWORD dwPAFlags[2]; + IF_FAIL_GO(BinderAcquireImport(pPEImage, &pIMetaDataAssemblyImport, dwPAFlags, fIsNativeImage)); + IF_FAIL_GO(AssemblyBinder::TranslatePEToArchitectureType(dwPAFlags, &PeKind)); + + _ASSERTE(pIMetaDataAssemblyImport != NULL); + + // Using the information we just got, initialize the assemblyname + SAFE_NEW(pAssemblyName, AssemblyName); + IF_FAIL_GO(pAssemblyName->Init(pIMetaDataAssemblyImport, PeKind)); + + // Validate architecture + if (!BINDER_SPACE::Assembly::IsValidArchitecture(pAssemblyName->GetArchitecture())) + { + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT)); + } + + // Easy out for CoreLib + if (pAssemblyName->IsCoreLib()) + { + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + } + + { + // Ensure we are not being asked to bind to a TPA assembly + // + SString& simpleName = pAssemblyName->GetSimpleName(); + SimpleNameToFileNameMap* tpaMap = GetAppContext()->GetTpaList(); + if (tpaMap->LookupPtr(simpleName.GetUnicode()) != NULL) + { + // The simple name of the assembly being requested to be bound was found in the TPA list. + // Now, perform the actual bind to see if the assembly was really in the TPA assembly list or not. + hr = BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly, true /* excludeAppPaths */); + if (SUCCEEDED(hr)) + { + if (pCoreCLRFoundAssembly->GetIsInGAC()) + { + *ppAssembly = pCoreCLRFoundAssembly.Extract(); + goto Exit; + } + } + } + } + + hr = AssemblyBinder::BindUsingPEImage(&m_appContext, pAssemblyName, pPEImage, PeKind, pIMetaDataAssemblyImport, &pCoreCLRFoundAssembly); + if (hr == S_OK) + { + _ASSERTE(pCoreCLRFoundAssembly != NULL); + pCoreCLRFoundAssembly->SetBinder(this); + *ppAssembly = pCoreCLRFoundAssembly.Extract(); + } +Exit:; + } + EX_CATCH_HRESULT(hr); + + return hr; +} +#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + +HRESULT CLRPrivBinderCoreCLR::SetupBindingPaths(SString &sTrustedPlatformAssemblies, + SString &sPlatformResourceRoots, + SString &sAppPaths, + SString &sAppNiPaths) +{ + HRESULT hr = S_OK; + + EX_TRY + { + hr = m_appContext.SetupBindingPaths(sTrustedPlatformAssemblies, sPlatformResourceRoots, sAppPaths, sAppNiPaths, TRUE /* fAcquireLock */); + } + EX_CATCH_HRESULT(hr); + return hr; +} + +// See code:BINDER_SPACE::AssemblyBinder::GetAssembly for info on fNgenExplicitBind +// and fExplicitBindToNativeImage, and see code:CEECompileInfo::LoadAssemblyByPath +// for an example of how they're used. +HRESULT CLRPrivBinderCoreCLR::Bind(SString &assemblyDisplayName, + LPCWSTR wszCodeBase, + PEAssembly *pParentAssembly, + BOOL fNgenExplicitBind, + BOOL fExplicitBindToNativeImage, + ICLRPrivAssembly **ppAssembly) +{ + HRESULT hr = S_OK; + VALIDATE_ARG_RET(ppAssembly != NULL); + + AssemblyName assemblyName; + + ReleaseHolder<AssemblyName> pAssemblyName; + + if (!assemblyDisplayName.IsEmpty()) + { + // AssemblyDisplayName can be empty if wszCodeBase is specified. + SAFE_NEW(pAssemblyName, AssemblyName); + IF_FAIL_GO(pAssemblyName->Init(assemblyDisplayName)); + } + + EX_TRY + { + ReleaseHolder<BINDER_SPACE::Assembly> pAsm; + hr = AssemblyBinder::BindAssembly(&m_appContext, + pAssemblyName, + wszCodeBase, + pParentAssembly, + fNgenExplicitBind, + fExplicitBindToNativeImage, + false, // excludeAppPaths + &pAsm); + if(SUCCEEDED(hr)) + { + _ASSERTE(pAsm != NULL); + pAsm->SetBinder(this); + *ppAssembly = pAsm.Extract(); + } + } + EX_CATCH_HRESULT(hr); + +Exit: + return hr; +} + +HRESULT CLRPrivBinderCoreCLR::GetLoaderAllocator(LPVOID* pLoaderAllocator) +{ + // Not supported by this binder + return E_FAIL; +} diff --git a/src/coreclr/binder/coreclrbindercommon.cpp b/src/coreclr/binder/coreclrbindercommon.cpp new file mode 100644 index 00000000000..13ae1e52a57 --- /dev/null +++ b/src/coreclr/binder/coreclrbindercommon.cpp @@ -0,0 +1,139 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +#include "common.h" +#include "assemblybinder.hpp" +#include "coreclrbindercommon.h" +#include "clrprivbindercoreclr.h" +#include "bundle.h" + +using namespace BINDER_SPACE; + +//============================================================================= +// Init code +//----------------------------------------------------------------------------- +/* static */ +HRESULT CCoreCLRBinderHelper::Init() +{ + STATIC_CONTRACT_NOTHROW; + + return AssemblyBinder::Startup(); +} + +HRESULT CCoreCLRBinderHelper::DefaultBinderSetupContext(DWORD dwAppDomainId,CLRPrivBinderCoreCLR **ppTPABinder) +{ + HRESULT hr = S_OK; + EX_TRY + { + if(ppTPABinder != NULL) + { + ReleaseHolder<CLRPrivBinderCoreCLR> pBinder; + SAFE_NEW(pBinder, CLRPrivBinderCoreCLR); + + BINDER_SPACE::ApplicationContext *pApplicationContext = pBinder->GetAppContext(); + UINT_PTR binderId; + pBinder->GetBinderID(&binderId); + hr = pApplicationContext->Init(binderId); + if(SUCCEEDED(hr)) + { + pApplicationContext->SetAppDomainId(dwAppDomainId); + pBinder->SetManagedAssemblyLoadContext(NULL); + *ppTPABinder = clr::SafeAddRef(pBinder.Extract()); + } + } + } + EX_CATCH_HRESULT(hr); + +Exit: + return hr; +} + +HRESULT CCoreCLRBinderHelper::GetAssemblyIdentity(LPCSTR szTextualIdentity, + BINDER_SPACE::ApplicationContext *pApplicationContext, + NewHolder<AssemblyIdentityUTF8> &assemblyIdentityHolder) +{ + HRESULT hr = S_OK; + VALIDATE_ARG_RET(szTextualIdentity != NULL); + + EX_TRY + { + AssemblyIdentityUTF8 *pAssemblyIdentity = NULL; + if (pApplicationContext != NULL) + { + // This returns a cached copy owned by application context + hr = pApplicationContext->GetAssemblyIdentity(szTextualIdentity, &pAssemblyIdentity); + if(SUCCEEDED(hr)) + { + assemblyIdentityHolder = pAssemblyIdentity; + assemblyIdentityHolder.SuppressRelease(); + } + } + else + { + SString sTextualIdentity; + + sTextualIdentity.SetUTF8(szTextualIdentity); + + // This is a private copy + pAssemblyIdentity = new AssemblyIdentityUTF8(); + hr = TextualIdentityParser::Parse(sTextualIdentity, pAssemblyIdentity); + if(SUCCEEDED(hr)) + { + pAssemblyIdentity->PopulateUTF8Fields(); + assemblyIdentityHolder = pAssemblyIdentity; + } + } + } + EX_CATCH_HRESULT(hr); + + return hr; +} + +//============================================================================= +// Functions that provides binding services beyond the ICLRPrivInterface +//----------------------------------------------------------------------------- + +HRESULT CCoreCLRBinderHelper::BindToSystem(ICLRPrivAssembly **ppSystemAssembly, bool fBindToNativeImage) +{ + HRESULT hr = S_OK; + VALIDATE_ARG_RET(ppSystemAssembly != NULL); + + EX_TRY + { + ReleaseHolder<BINDER_SPACE::Assembly> pAsm; + StackSString systemPath(SystemDomain::System()->SystemDirectory()); + hr = AssemblyBinder::BindToSystem(systemPath, &pAsm, fBindToNativeImage); + if(SUCCEEDED(hr)) + { + _ASSERTE(pAsm != NULL); + *ppSystemAssembly = pAsm.Extract(); + } + } + EX_CATCH_HRESULT(hr); + + return hr; +} + +HRESULT CCoreCLRBinderHelper::BindToSystemSatellite(SString &systemPath, + SString &sSimpleName, + SString &sCultureName, + ICLRPrivAssembly **ppSystemAssembly) +{ + HRESULT hr = S_OK; + VALIDATE_ARG_RET(ppSystemAssembly != NULL && !systemPath.IsEmpty()); + + EX_TRY + { + ReleaseHolder<BINDER_SPACE::Assembly> pAsm; + hr = AssemblyBinder::BindToSystemSatellite(systemPath, sSimpleName, sCultureName, &pAsm); + if(SUCCEEDED(hr)) + { + _ASSERTE(pAsm != NULL); + *ppSystemAssembly = pAsm.Extract(); + } + } + EX_CATCH_HRESULT(hr); + + return hr; +} diff --git a/src/coreclr/binder/failurecache.cpp b/src/coreclr/binder/failurecache.cpp new file mode 100644 index 00000000000..c6e1f286fbb --- /dev/null +++ b/src/coreclr/binder/failurecache.cpp @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// FailureCache.cpp +// + + +// +// Implements the FailureCache class +// +// ============================================================ + +#include "failurecache.hpp" + +namespace BINDER_SPACE +{ + FailureCache::FailureCache() : SHash<FailureCacheHashTraits>::SHash() + { + // Nothing to do here + } + + FailureCache::~FailureCache() + { + // Delete entries and contents array + for (Hash::Iterator i = Hash::Begin(), end = Hash::End(); i != end; i++) + { + const FailureCacheEntry *pFailureCacheEntry = *i; + delete pFailureCacheEntry; + } + RemoveAll(); + } + + HRESULT FailureCache::Add(SString &assemblyNameorPath, + HRESULT hrBindingResult) + { + HRESULT hr = S_OK; + + NewHolder<FailureCacheEntry> pFailureCacheEntry; + SAFE_NEW(pFailureCacheEntry, FailureCacheEntry); + + // No error occurred; report the original error + hr = hrBindingResult; + + pFailureCacheEntry->GetAssemblyNameOrPath().Set(assemblyNameorPath); + pFailureCacheEntry->SetBindingResult(hrBindingResult); + + Hash::Add(pFailureCacheEntry); + pFailureCacheEntry.SuppressRelease(); + + Exit: + return hr; + } + + HRESULT FailureCache::Lookup(SString &assemblyNameorPath) + { + HRESULT hr = S_OK; + FailureCacheEntry *pFailureCachEntry = Hash::Lookup(assemblyNameorPath); + + if (pFailureCachEntry != NULL) + { + hr = pFailureCachEntry->GetBindingResult(); + } + + return hr; + } + + void FailureCache::Remove(SString &assemblyName) + { + FailureCacheEntry *pFailureCachEntry = Hash::Lookup(assemblyName); + + // Hash::Remove does not clean up entries + Hash::Remove(assemblyName); + SAFE_DELETE(pFailureCachEntry); + } +}; diff --git a/src/coreclr/binder/fusionassemblyname.cpp b/src/coreclr/binder/fusionassemblyname.cpp new file mode 100644 index 00000000000..6da1cc3a3b2 --- /dev/null +++ b/src/coreclr/binder/fusionassemblyname.cpp @@ -0,0 +1,714 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// FusionAssemblyName.cpp +// +// Implements the CAssemblyName class +// +// ============================================================ + +#include <windows.h> +#include <winerror.h> +#include "strongnameinternal.h" + +#include "fusionhelpers.hpp" +#include "fusionassemblyname.hpp" + +#include <strsafe.h> +#include "shlwapi.h" + +#include "assemblyidentity.hpp" +#include "textualidentityparser.hpp" + +// --------------------------------------------------------------------------- +// CPropertyArray ctor +// --------------------------------------------------------------------------- +CPropertyArray::CPropertyArray() +{ + _dwSig = 0x504f5250; /* 'PORP' */ + memset(&_rProp, 0, ASM_NAME_MAX_PARAMS * sizeof(FusionProperty)); +} + +// --------------------------------------------------------------------------- +// CPropertyArray dtor +// --------------------------------------------------------------------------- +CPropertyArray::~CPropertyArray() +{ + for (DWORD i = 0; i < ASM_NAME_MAX_PARAMS; i++) + { + if (_rProp[i].cb > sizeof(DWORD)) + { + if (_rProp[i].pv != NULL) + { + FUSION_DELETE_ARRAY((LPBYTE) _rProp[i].pv); + _rProp[i].pv = NULL; + } + } + } +} + +// --------------------------------------------------------------------------- +// CPropertyArray::Set +// --------------------------------------------------------------------------- +HRESULT CPropertyArray::Set(DWORD PropertyId, + LPCVOID pvProperty, DWORD cbProperty) +{ + HRESULT hr = S_OK; + FusionProperty *pItem = NULL; + + pItem = &(_rProp[PropertyId]); + + if (!cbProperty && !pvProperty) + { + if (pItem->cb > sizeof(DWORD)) + { + if (pItem->pv != NULL) + FUSION_DELETE_ARRAY((LPBYTE) pItem->pv); + } + pItem->pv = NULL; + } + else if (cbProperty > sizeof(DWORD)) + { + LPBYTE ptr = NEW(BYTE[cbProperty]); + if (!ptr) + { + hr = E_OUTOFMEMORY; + goto exit; + } + + if (pItem->cb > sizeof(DWORD)) + FUSION_DELETE_ARRAY((LPBYTE) pItem->pv); + + memcpy(ptr, pvProperty, cbProperty); + pItem->pv = ptr; + } + else + { + if (pItem->cb > sizeof(DWORD)) + FUSION_DELETE_ARRAY((LPBYTE) pItem->pv); + + memcpy(&(pItem->pv), pvProperty, cbProperty); + +#ifdef _DEBUG + if (PropertyId == ASM_NAME_ARCHITECTURE) { + PEKIND pe = * ((PEKIND *)pvProperty); + _ASSERTE(pe != peInvalid); + } +#endif + } + pItem->cb = cbProperty; + +exit: + return hr; +} + +// --------------------------------------------------------------------------- +// CPropertyArray::Get +// --------------------------------------------------------------------------- +HRESULT CPropertyArray::Get(DWORD PropertyId, + LPVOID pvProperty, LPDWORD pcbProperty) +{ + HRESULT hr = S_OK; + FusionProperty *pItem; + + _ASSERTE(pcbProperty); + + if (PropertyId >= ASM_NAME_MAX_PARAMS + || (!pvProperty && *pcbProperty)) + { + _ASSERTE(!"Invalid Argument! Passed in NULL buffer with size non-zero!"); + hr = E_INVALIDARG; + goto exit; + } + + pItem = &(_rProp[PropertyId]); + + if (pItem->cb > *pcbProperty) + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + else if (pItem->cb && pvProperty) + memcpy(pvProperty, (pItem->cb > sizeof(DWORD) ? + pItem->pv : (LPBYTE) &(pItem->pv)), pItem->cb); + + *pcbProperty = pItem->cb; + +exit: + return hr; +} + +// --------------------------------------------------------------------------- +// CPropertyArray::operator [] +// Wraps DWORD optimization test. +// --------------------------------------------------------------------------- +FusionProperty CPropertyArray::operator [] (DWORD PropertyId) +{ + FusionProperty prop; + + prop.pv = _rProp[PropertyId].cb > sizeof(DWORD) ? + _rProp[PropertyId].pv : &(_rProp[PropertyId].pv); + + prop.cb = _rProp[PropertyId].cb; + + return prop; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::AddRef +// --------------------------------------------------------------------------- +STDMETHODIMP_(ULONG) +CAssemblyName::AddRef() +{ + return InterlockedIncrement(&_cRef); +} + +// --------------------------------------------------------------------------- +// CAssemblyName::Release +// --------------------------------------------------------------------------- +STDMETHODIMP_(ULONG) +CAssemblyName::Release() +{ + ULONG ulRef = InterlockedDecrement(&_cRef); + if (ulRef == 0) + { + delete this; + } + + return ulRef; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::QueryInterface +// --------------------------------------------------------------------------- +STDMETHODIMP +CAssemblyName::QueryInterface(REFIID riid, void** ppv) +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + if (!ppv) + { + hr = E_POINTER; + goto Exit; + } + + if ( IsEqualIID(riid, IID_IUnknown) + || IsEqualIID(riid, IID_IAssemblyName) + ) + { + *ppv = static_cast<IAssemblyName*> (this); + AddRef(); + hr = S_OK; + goto Exit; + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + goto Exit; + } + + Exit: + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::SetProperty +// --------------------------------------------------------------------------- +STDMETHODIMP +CAssemblyName::SetProperty(DWORD PropertyId, + LPCVOID pvProperty, + DWORD cbProperty) +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + hr = SetPropertyInternal(PropertyId, pvProperty, cbProperty); + + END_ENTRYPOINT_NOTHROW; + return hr; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::GetProperty +// --------------------------------------------------------------------------- +STDMETHODIMP +CAssemblyName::GetProperty(DWORD PropertyId, + LPVOID pvProperty, LPDWORD pcbProperty) +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + // Retrieve the property. + switch(PropertyId) + { + case ASM_NAME_NULL_PUBLIC_KEY_TOKEN: + case ASM_NAME_NULL_PUBLIC_KEY: + { + hr = (_fPublicKeyToken && !_rProp[PropertyId].cb) ? S_OK : S_FALSE; + break; + } + case ASM_NAME_NULL_CUSTOM: + { + hr = (_fCustom && !_rProp[PropertyId].cb) ? S_OK : S_FALSE; + break; + } + default: + { + hr = _rProp.Get(PropertyId, pvProperty, pcbProperty); + break; + } + } + + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::SetPropertyInternal +// --------------------------------------------------------------------------- +HRESULT CAssemblyName::SetPropertyInternal(DWORD PropertyId, + LPCVOID pvProperty, + DWORD cbProperty) +{ + HRESULT hr = S_OK; + LPBYTE pbSN = NULL; + DWORD cbSN = 0; + + if (PropertyId >= ASM_NAME_MAX_PARAMS + || (!pvProperty && cbProperty)) + { + _ASSERTE(!"Invalid Argument! Passed in NULL buffer with size non-zero!"); + hr = E_INVALIDARG; + goto exit; + } + + // <REVISIT_TODO> - make this a switch statement.</REVISIT_TODO> + if (PropertyId == ASM_NAME_MAJOR_VERSION || + PropertyId == ASM_NAME_MINOR_VERSION || + PropertyId == ASM_NAME_BUILD_NUMBER || + PropertyId == ASM_NAME_REVISION_NUMBER) + { + if (cbProperty > sizeof(WORD)) { + hr = E_INVALIDARG; + goto exit; + } + } + + // Check if public key is being set and if so, + // set the public key token if not already set. + if (PropertyId == ASM_NAME_PUBLIC_KEY) + { + // If setting true public key, generate hash. + if (pvProperty && cbProperty) + { + // Generate the public key token from the pk. + if (FAILED(hr = StrongNameTokenFromPublicKey((LPBYTE) pvProperty, cbProperty, &pbSN, &cbSN))) + goto exit; + + // Set the public key token property. + if (FAILED(hr = SetPropertyInternal(ASM_NAME_PUBLIC_KEY_TOKEN, pbSN, cbSN))) + goto exit; + } + // Otherwise expect call to reset property. + else if (!cbProperty) + { + if (FAILED(hr = SetPropertyInternal(ASM_NAME_PUBLIC_KEY_TOKEN, pvProperty, cbProperty))) + goto exit; + } + + } + // Setting NULL public key clears values in public key, + // public key token and sets public key token flag. + else if (PropertyId == ASM_NAME_NULL_PUBLIC_KEY) + { + pvProperty = NULL; + cbProperty = 0; + hr = SetPropertyInternal(ASM_NAME_NULL_PUBLIC_KEY_TOKEN, pvProperty, cbProperty); + goto exit; + } + // Setting or clearing public key token. + else if (PropertyId == ASM_NAME_PUBLIC_KEY_TOKEN) + { + // Defensive: invalid sized public key tokens should be avoided. + if (cbProperty > PUBLIC_KEY_TOKEN_LEN) + { + hr = SetPropertyInternal(ASM_NAME_NULL_PUBLIC_KEY_TOKEN, NULL, 0); + hr = E_INVALIDARG; + goto exit; + } + + if (pvProperty && cbProperty) + _fPublicKeyToken = TRUE; + else if (!cbProperty) + _fPublicKeyToken = FALSE; + } + // Setting NULL public key token clears public key token and + // sets public key token flag. + else if (PropertyId == ASM_NAME_NULL_PUBLIC_KEY_TOKEN) + { + _fPublicKeyToken = TRUE; + pvProperty = NULL; + cbProperty = 0; + PropertyId = ASM_NAME_PUBLIC_KEY_TOKEN; + } + else if (PropertyId == ASM_NAME_CUSTOM) + { + if (pvProperty && cbProperty) + _fCustom = TRUE; + else if (!cbProperty) + _fCustom = FALSE; + } + else if (PropertyId == ASM_NAME_NULL_CUSTOM) + { + _fCustom = TRUE; + pvProperty = NULL; + cbProperty = 0; + PropertyId = ASM_NAME_CUSTOM; + } + + // Setting "neutral" as the culture is the same as "" culture (meaning + // culture-invariant). + else if (PropertyId == ASM_NAME_CULTURE) { + if (pvProperty && !FusionCompareStringI((LPWSTR)pvProperty, W("neutral"))) { + pvProperty = (void *)W(""); + cbProperty = sizeof(W("")); + } + } + + // Set property on array. + hr = _rProp.Set(PropertyId, pvProperty, cbProperty); + +exit: + // Free memory allocated by crypto wrapper. + if (pbSN) { + StrongNameFreeBuffer(pbSN); + } + + return hr; +} + +// --------------------------------------------------------------------------- +// CreateAssemblyNameObject +// --------------------------------------------------------------------------- + +// This is not external for CoreCLR +STDAPI +CreateAssemblyNameObject( + LPASSEMBLYNAME *ppAssemblyName, + LPCOLESTR szAssemblyName) +{ + + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + CAssemblyName *pName = NULL; + + if (!ppAssemblyName) + { + hr = E_INVALIDARG; + goto exit; + } + + pName = NEW(CAssemblyName); + if (!pName) + { + hr = E_OUTOFMEMORY; + goto exit; + } + + hr = pName->Parse((LPWSTR)szAssemblyName); + if (FAILED(hr)) + { + SAFERELEASE(pName); + goto exit; + } + + *ppAssemblyName = pName; + +exit: + END_ENTRYPOINT_NOTHROW; + return hr; +} + +// --------------------------------------------------------------------------- +// CAssemblyName constructor +// --------------------------------------------------------------------------- +CAssemblyName::CAssemblyName() +{ + _dwSig = 0x454d414e; /* 'EMAN' */ + _fPublicKeyToken = FALSE; + _fCustom = TRUE; + _cRef = 1; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::Parse +// --------------------------------------------------------------------------- +HRESULT CAssemblyName::Parse(__in_z LPCWSTR szDisplayName) +{ + HRESULT hr = S_OK; + + if (!(szDisplayName && *szDisplayName)) + { + hr = E_INVALIDARG; + goto exit; + } + + EX_TRY { + BINDER_SPACE::AssemblyIdentity assemblyIdentity; + SString displayName(szDisplayName); + + // Parse the textual identity + hr = BINDER_SPACE::TextualIdentityParser::Parse(displayName, &assemblyIdentity); + if (FAILED(hr)) { + goto exit; + } + + // Set name. + hr = SetProperty(ASM_NAME_NAME, + (LPVOID) assemblyIdentity.m_simpleName.GetUnicode(), + (assemblyIdentity.m_simpleName.GetCount() + 1) * sizeof(WCHAR)); + if (FAILED(hr)) { + goto exit; + } + + // Set version. + if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_VERSION)) { + WORD wVersionPart = 0; + + wVersionPart = (WORD) assemblyIdentity.m_version.GetMajor(); + hr = SetProperty(ASM_NAME_MAJOR_VERSION, &wVersionPart, sizeof(WORD)); + if (FAILED(hr)) { + goto exit; + } + + wVersionPart = (WORD) assemblyIdentity.m_version.GetMinor(); + hr = SetProperty(ASM_NAME_MINOR_VERSION, &wVersionPart, sizeof(WORD)); + if (FAILED(hr)) { + goto exit; + } + + wVersionPart = (WORD) assemblyIdentity.m_version.GetBuild(); + hr = SetProperty(ASM_NAME_BUILD_NUMBER, &wVersionPart, sizeof(WORD)); + if (FAILED(hr)) { + goto exit; + } + + wVersionPart = (WORD) assemblyIdentity.m_version.GetRevision(); + hr = SetProperty(ASM_NAME_REVISION_NUMBER, &wVersionPart, sizeof(WORD)); + if (FAILED(hr)) { + goto exit; + } + } + + // Set culture. + if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CULTURE)) { + hr = SetProperty(ASM_NAME_CULTURE, + (LPVOID) assemblyIdentity.m_cultureOrLanguage.GetUnicode(), + (assemblyIdentity.m_cultureOrLanguage.GetCount()+1) * sizeof(WCHAR)); + if (FAILED(hr)) { + goto exit; + } + } + + // Set public key (token) or NULL flag. + if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY)) { + SBuffer &publicKeyBuffer = assemblyIdentity.m_publicKeyOrTokenBLOB; + const void *pBytes = publicKeyBuffer; + + // This also computes and sets the public key token. + hr = SetProperty(ASM_NAME_PUBLIC_KEY, (void *) pBytes, publicKeyBuffer.GetSize()); + if (FAILED(hr)) { + goto exit; + } + } + else if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN)) { + SBuffer &publicKeyTokenBuffer = assemblyIdentity.m_publicKeyOrTokenBLOB; + const void *pBytes = publicKeyTokenBuffer; + + hr = SetProperty(ASM_NAME_PUBLIC_KEY_TOKEN, + (LPVOID) pBytes, + publicKeyTokenBuffer.GetSize()); + if (FAILED(hr)) { + goto exit; + } + } + else if (assemblyIdentity. + Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL)) { + hr = SetProperty(ASM_NAME_NULL_PUBLIC_KEY_TOKEN, NULL, 0); + if (FAILED(hr)) { + goto exit; + } + } + + // Set architecture. + if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE)) { + PEKIND peKind = assemblyIdentity.m_kProcessorArchitecture; + + hr = SetProperty(ASM_NAME_ARCHITECTURE, (LPVOID) &peKind, sizeof(PEKIND)); + if(FAILED(hr)) { + goto exit; + } + } + + // Set retargetable flag. + if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE)) { + BOOL fRetarget = TRUE; + + if (FAILED(hr = SetProperty(ASM_NAME_RETARGET, &fRetarget, sizeof(BOOL)))) { + goto exit; + } + } + + // Set content type. + if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE)) { + DWORD dwContentType = assemblyIdentity.m_kContentType; + + hr = SetProperty(ASM_NAME_CONTENT_TYPE, &dwContentType, sizeof(dwContentType)); + IfFailGoto(hr, exit); + } + + // Set custom or NULL flag. + if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CUSTOM)) { + SBuffer &customBuffer = assemblyIdentity.m_customBLOB; + const void *pBytes = customBuffer; + + hr = SetProperty(ASM_NAME_CUSTOM, (void *) pBytes, customBuffer.GetSize()); + if (FAILED(hr)) { + goto exit; + } + } + else if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CUSTOM_NULL)) { + hr = SetProperty(ASM_NAME_NULL_CUSTOM, NULL, 0); + if (FAILED(hr)) { + goto exit; + } + } + } + EX_CATCH_HRESULT(hr); + + exit: + return hr; +} + +namespace fusion +{ + namespace util + { + namespace priv + { + inline bool IsNullProperty(DWORD dwProperty) + { + LIMITED_METHOD_CONTRACT; + return dwProperty == ASM_NAME_NULL_PUBLIC_KEY_TOKEN || + dwProperty == ASM_NAME_NULL_PUBLIC_KEY || + dwProperty == ASM_NAME_NULL_CUSTOM; + } + } + + // Non-allocating helper. + HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, PVOID pBuf, DWORD *pcbBuf) + { + LIMITED_METHOD_CONTRACT; + HRESULT hr = S_OK; + + _ASSERTE(pName != nullptr && pcbBuf != nullptr); + if (pName == nullptr || pcbBuf == nullptr) + { + return E_INVALIDARG; + } + + hr = pName->GetProperty(dwProperty, pBuf, pcbBuf); + IfFailRet(hr); + + // Zero-length non-null property means there is no value. + if (hr == S_OK && *pcbBuf == 0 && !priv::IsNullProperty(dwProperty)) + { + hr = S_FALSE; + } + + return hr; + } + + // Allocating helper. + HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, PBYTE * ppBuf, DWORD *pcbBuf) + { + LIMITED_METHOD_CONTRACT; + HRESULT hr = S_OK; + + _ASSERTE(ppBuf != nullptr && (*ppBuf == nullptr || pcbBuf != nullptr)); + if (ppBuf == nullptr || (*ppBuf != nullptr && pcbBuf == nullptr)) + { + return E_INVALIDARG; + } + + DWORD cbBuf = 0; + if (pcbBuf == nullptr) + pcbBuf = &cbBuf; + + hr = GetProperty(pName, dwProperty, *ppBuf, pcbBuf); + + // No provided buffer constitutes a request for one to be allocated. + if (*ppBuf == nullptr) + { + // If it's a null property, allocate a single-byte array to provide consistency. + if (hr == S_OK && priv::IsNullProperty(dwProperty)) + { + *ppBuf = new (nothrow) BYTE[1]; + IfNullRet(*ppBuf); + } + // Great, get the value. + else if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) + { + NewArrayHolder<BYTE> pBuf = new (nothrow) BYTE[*pcbBuf]; + IfNullRet(pBuf); + hr = pName->GetProperty(dwProperty, pBuf, pcbBuf); + IfFailRet(hr); + *ppBuf = pBuf.Extract(); + hr = S_OK; + } + } + + return hr; + } + + HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, SString & ssVal) + { + LIMITED_METHOD_CONTRACT; + HRESULT hr = S_OK; + + _ASSERTE(pName != nullptr); + if (pName == nullptr) + { + return E_INVALIDARG; + } + + DWORD cbSize = 0; + hr = GetProperty(pName, dwProperty, static_cast<PBYTE>(nullptr), &cbSize); + + if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) + { + EX_TRY + { + PWSTR wzNameBuf = ssVal.OpenUnicodeBuffer(cbSize / sizeof(WCHAR) - 1); + hr = GetProperty(pName, dwProperty, reinterpret_cast<PBYTE>(wzNameBuf), &cbSize); + ssVal.CloseBuffer(); + IfFailThrow(hr); + ssVal.Normalize(); + } + EX_CATCH_HRESULT(hr); + IfFailRet(hr); + } + + return hr; + } + } +} + diff --git a/src/coreclr/binder/inc/activitytracker.h b/src/coreclr/binder/inc/activitytracker.h new file mode 100644 index 00000000000..7ff8678a9f3 --- /dev/null +++ b/src/coreclr/binder/inc/activitytracker.h @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// activitytracker.h +// + +#ifndef __ACTIVITY_TRACKER_H__ +#define __ACTIVITY_TRACKER_H__ + +namespace ActivityTracker +{ + void Start(/*out*/ GUID *activityId, /*out*/ GUID *relatedActivityId); + void Stop(/*out*/ GUID *activityId); +}; + +#endif // __ACTIVITY_TRACKER_H__ diff --git a/src/coreclr/binder/inc/applicationcontext.hpp b/src/coreclr/binder/inc/applicationcontext.hpp new file mode 100644 index 00000000000..7bb3b364441 --- /dev/null +++ b/src/coreclr/binder/inc/applicationcontext.hpp @@ -0,0 +1,152 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// ApplicationContext.hpp +// + + +// +// Defines the ApplicationContext class +// +// ============================================================ + +#ifndef __BINDER__APPLICATION_CONTEXT_HPP__ +#define __BINDER__APPLICATION_CONTEXT_HPP__ + +#include "bindertypes.hpp" +#include "failurecache.hpp" +#include "assemblyidentitycache.hpp" +#include "stringarraylist.h" + +namespace BINDER_SPACE +{ + //============================================================================================= + // Data structures for Simple Name -> File Name hash + + // Entry in SHash table that maps namespace to list of files + struct SimpleNameToFileNameMapEntry + { + LPWSTR m_wszSimpleName; + LPWSTR m_wszILFileName; + LPWSTR m_wszNIFileName; + }; + + // SHash traits for Namespace -> FileNameList hash + class SimpleNameToFileNameMapTraits : public NoRemoveSHashTraits< DefaultSHashTraits< SimpleNameToFileNameMapEntry > > + { + public: + typedef PCWSTR key_t; + static const SimpleNameToFileNameMapEntry Null() { SimpleNameToFileNameMapEntry e; e.m_wszSimpleName = nullptr; return e; } + static bool IsNull(const SimpleNameToFileNameMapEntry & e) { return e.m_wszSimpleName == nullptr; } + static key_t GetKey(const SimpleNameToFileNameMapEntry & e) + { + key_t key; + key = e.m_wszSimpleName; + return key; + } + static count_t Hash(const key_t &str) + { + SString ssKey(SString::Literal, str); + return ssKey.HashCaseInsensitive(); + } + static BOOL Equals(const key_t &lhs, const key_t &rhs) { LIMITED_METHOD_CONTRACT; return (SString::_wcsicmp(lhs, rhs) == 0); } + + void OnDestructPerEntryCleanupAction(const SimpleNameToFileNameMapEntry & e) + { + if (e.m_wszILFileName == nullptr && e.m_wszNIFileName == nullptr) + { + // Don't delete simple name here since it's a filename only entry and will be cleaned up + // by the SimpleName -> FileName entry which reuses the same filename pointer. + return; + } + + if (e.m_wszSimpleName != nullptr) + { + delete [] e.m_wszSimpleName; + } + if (e.m_wszILFileName != nullptr) + { + delete [] e.m_wszILFileName; + } + if (e.m_wszNIFileName != nullptr) + { + delete [] e.m_wszNIFileName; + } + } + static const bool s_DestructPerEntryCleanupAction = true; + }; + + typedef SHash<SimpleNameToFileNameMapTraits> SimpleNameToFileNameMap; + + class ApplicationContext + : public IUnknown + { + public: + // IUnknown methods + STDMETHOD(QueryInterface)(REFIID riid, + void **ppv); + STDMETHOD_(ULONG, AddRef)(); + STDMETHOD_(ULONG, Release)(); + + // ApplicationContext methods + ApplicationContext(); + virtual ~ApplicationContext(); + HRESULT Init(UINT_PTR binderID); + + inline SString &GetApplicationName(); + inline DWORD GetAppDomainId(); + inline void SetAppDomainId(DWORD dwAppDomainId); + + HRESULT SetupBindingPaths(/* in */ SString &sTrustedPlatformAssemblies, + /* in */ SString &sPlatformResourceRoots, + /* in */ SString &sAppPaths, + /* in */ SString &sAppNiPaths, + /* in */ BOOL fAcquireLock); + + HRESULT GetAssemblyIdentity(/* in */ LPCSTR szTextualIdentity, + /* in */ AssemblyIdentityUTF8 **ppAssemblyIdentity); + + // Getters/Setter + inline ExecutionContext *GetExecutionContext(); + inline FailureCache *GetFailureCache(); + inline HRESULT AddToFailureCache(SString &assemblyNameOrPath, + HRESULT hrBindResult); + inline StringArrayList *GetAppPaths(); + inline SimpleNameToFileNameMap *GetTpaList(); + inline StringArrayList *GetPlatformResourceRoots(); + inline StringArrayList *GetAppNiPaths(); + + // Using a host-configured Trusted Platform Assembly list + bool IsTpaListProvided(); + inline CRITSEC_COOKIE GetCriticalSectionCookie(); + inline LONG GetVersion(); + inline void IncrementVersion(); + + UINT_PTR GetBinderID() { return m_binderID; } + + protected: + LONG m_cRef; + Volatile<LONG> m_cVersion; + SString m_applicationName; + DWORD m_dwAppDomainId; + ExecutionContext *m_pExecutionContext; + FailureCache *m_pFailureCache; + CRITSEC_COOKIE m_contextCS; + + AssemblyIdentityCache m_assemblyIdentityCache; + + StringArrayList m_platformResourceRoots; + StringArrayList m_appPaths; + StringArrayList m_appNiPaths; + + SimpleNameToFileNameMap * m_pTrustedPlatformAssemblyMap; + + UINT_PTR m_binderID; + }; + +#include "applicationcontext.inl" + +}; + +#endif diff --git a/src/coreclr/binder/inc/applicationcontext.inl b/src/coreclr/binder/inc/applicationcontext.inl new file mode 100644 index 00000000000..4d006d5be6b --- /dev/null +++ b/src/coreclr/binder/inc/applicationcontext.inl @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// ApplicationContext.inl +// + + +// +// Implements inlined methods of ApplicationContext +// +// ============================================================ + +#ifndef __BINDER__APPLICATION_CONTEXT_INL__ +#define __BINDER__APPLICATION_CONTEXT_INL__ + +LONG ApplicationContext::GetVersion() +{ + return m_cVersion; +} + +void ApplicationContext::IncrementVersion() +{ + InterlockedIncrement(&m_cVersion); +} + +SString &ApplicationContext::GetApplicationName() +{ + return m_applicationName; +} + +DWORD ApplicationContext::GetAppDomainId() +{ + return m_dwAppDomainId; +} + +void ApplicationContext::SetAppDomainId(DWORD dwAppDomainId) +{ + m_dwAppDomainId = dwAppDomainId; +} + +ExecutionContext *ApplicationContext::GetExecutionContext() +{ + return m_pExecutionContext; +} + +FailureCache *ApplicationContext::GetFailureCache() +{ + _ASSERTE(m_pFailureCache != NULL); + return m_pFailureCache; +} + +HRESULT ApplicationContext::AddToFailureCache(SString &assemblyNameOrPath, + HRESULT hrBindResult) +{ + HRESULT hr = GetFailureCache()->Add(assemblyNameOrPath, hrBindResult); + IncrementVersion(); + return hr; +} + +StringArrayList *ApplicationContext::GetAppPaths() +{ + return &m_appPaths; +} + +SimpleNameToFileNameMap * ApplicationContext::GetTpaList() +{ + return m_pTrustedPlatformAssemblyMap; +} + +StringArrayList * ApplicationContext::GetPlatformResourceRoots() +{ + return &m_platformResourceRoots; +} + +StringArrayList * ApplicationContext::GetAppNiPaths() +{ + return &m_appNiPaths; +} + +CRITSEC_COOKIE ApplicationContext::GetCriticalSectionCookie() +{ + return m_contextCS; +} + +#endif diff --git a/src/coreclr/binder/inc/assembly.hpp b/src/coreclr/binder/inc/assembly.hpp new file mode 100644 index 00000000000..3f5fb87594e --- /dev/null +++ b/src/coreclr/binder/inc/assembly.hpp @@ -0,0 +1,167 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// Assembly.hpp +// + + +// +// Defines the Assembly class +// +// ============================================================ + +#ifndef __BINDER__ASSEMBLY_HPP__ +#define __BINDER__ASSEMBLY_HPP__ + +#include "bindertypes.hpp" +#include "assemblyname.hpp" + +#include "corpriv.h" +#include "clrprivbinding.h" + +#include "clrprivbindercoreclr.h" + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) +#include "clrprivbinderassemblyloadcontext.h" +#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + +#include "bundle.h" + +STDAPI BinderAcquirePEImage(LPCTSTR szAssemblyPath, + PEImage **ppPEImage, + PEImage **ppNativeImage, + BOOL fExplicitBindToNativeImage, + BundleFileLocation bundleFileLocation); + +STDAPI BinderAcquireImport(PEImage *pPEImage, + IMDInternalImport **pIMetaDataAssemblyImport, + DWORD *pdwPAFlags, + BOOL bNativeImage); + +STDAPI BinderHasNativeHeader(PEImage *pPEImage, + BOOL *result); + +STDAPI BinderReleasePEImage(PEImage *pPEImage); + +STDAPI BinderAddRefPEImage(PEImage *pPEImage); + +namespace BINDER_SPACE +{ + + // An assembly represents a particular set of bits. However we extend this to + // also include whether those bits have precompiled information (NGEN). Thus + // and assembly knows whether it has an NGEN image or not. + // + // This allows us to preferentially use the NGEN image if it is available. + class Assembly + : public ICLRPrivAssembly + { + public: + // -------------------------------------------------------------------- + // IUnknown methods + // -------------------------------------------------------------------- + STDMETHOD(QueryInterface)(REFIID riid, + void ** ppv); + STDMETHOD_(ULONG, AddRef)(); + STDMETHOD_(ULONG, Release)(); + + // -------------------------------------------------------------------- + // ICLRPrivAssembly methods + // -------------------------------------------------------------------- + LPCWSTR GetSimpleName(); + + STDMETHOD(BindAssemblyByName)( + IAssemblyName * pIAssemblyName, + ICLRPrivAssembly ** ppAssembly); + + STDMETHOD(GetAvailableImageTypes)(PDWORD pdwImageTypes); + + STDMETHOD(GetBinderID)(UINT_PTR *pBinderId); + + STDMETHOD(GetLoaderAllocator)(LPVOID* pLoaderAllocator); + + // -------------------------------------------------------------------- + // Assembly methods + // -------------------------------------------------------------------- + Assembly(); + virtual ~Assembly(); + + HRESULT Init(/* in */ IMDInternalImport *pIMetaDataAssemblyImport, + /* in */ PEKIND PeKind, + /* in */ PEImage *pPEImage, + /* in */ PEImage *pPENativeImage, + /* in */ SString &assemblyPath, + /* in */ BOOL fIsInGAC); + + inline AssemblyName *GetAssemblyName(BOOL fAddRef = FALSE); + inline BOOL GetIsInGAC(); + + inline SString &GetPath(); + + inline PEImage *GetPEImage(BOOL fAddRef = FALSE); + inline PEImage *GetNativePEImage(BOOL fAddRef = FALSE); + inline PEImage *GetNativeOrILPEImage(BOOL fAddRef = FALSE); + + HRESULT GetMVID(GUID *pMVID); + + static PEKIND GetSystemArchitecture(); + static BOOL IsValidArchitecture(PEKIND kArchitecture); + + inline ICLRPrivBinder* GetBinder() + { + return m_pBinder; + } + +#ifndef CROSSGEN_COMPILE + protected: +#endif + // Assembly Flags + enum + { + FLAG_NONE = 0x00, + FLAG_IS_IN_GAC = 0x02, + //FLAG_IS_DYNAMIC_BIND = 0x04, + FLAG_IS_BYTE_ARRAY = 0x08, + }; + + inline void SetPEImage(PEImage *pPEImage); + inline void SetNativePEImage(PEImage *pNativePEImage); + + inline void SetAssemblyName(AssemblyName *pAssemblyName, + BOOL fAddRef = TRUE); + inline void SetIsInGAC(BOOL fIsInGAC); + + inline IMDInternalImport *GetMDImport(); + inline void SetMDImport(IMDInternalImport *pMDImport); + + LONG m_cRef; + PEImage *m_pPEImage; + PEImage *m_pNativePEImage; + IMDInternalImport *m_pMDImport; + AssemblyName *m_pAssemblyName; + SString m_assemblyPath; + DWORD m_dwAssemblyFlags; + ICLRPrivBinder *m_pBinder; + + inline void SetBinder(ICLRPrivBinder *pBinder) + { + _ASSERTE(m_pBinder == NULL || m_pBinder == pBinder); + m_pBinder = pBinder; + } + + friend class ::CLRPrivBinderCoreCLR; + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + friend class ::CLRPrivBinderAssemblyLoadContext; +#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + }; + + // This is a fast version which goes around the COM interfaces and directly + // casts the interfaces and does't AddRef + inline BINDER_SPACE::Assembly * GetAssemblyFromPrivAssemblyFast(ICLRPrivAssembly *pPrivAssembly); + +#include "assembly.inl" +}; + +#endif diff --git a/src/coreclr/binder/inc/assembly.inl b/src/coreclr/binder/inc/assembly.inl new file mode 100644 index 00000000000..ca0aa232e90 --- /dev/null +++ b/src/coreclr/binder/inc/assembly.inl @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// Assembly.inl +// + + +// +// Implements the inline methods of Assembly +// +// ============================================================ + +#ifndef __BINDER__ASSEMBLY_INL__ +#define __BINDER__ASSEMBLY_INL__ + +PEImage *Assembly::GetPEImage(BOOL fAddRef /* = FALSE */) +{ + PEImage *pPEImage = m_pPEImage; + + if (fAddRef) + { + BinderAddRefPEImage(pPEImage); + } + + return pPEImage; +} + +PEImage *Assembly::GetNativePEImage(BOOL fAddRef /* = FALSE */) +{ + PEImage *pNativePEImage = m_pNativePEImage; + + if (fAddRef) + { + BinderAddRefPEImage(pNativePEImage); + } + + return pNativePEImage; +} + +PEImage *Assembly::GetNativeOrILPEImage(BOOL fAddRef /* = FALSE */) +{ + PEImage* pPEImage = GetNativePEImage(fAddRef); + if (pPEImage == NULL) + pPEImage = GetPEImage(fAddRef); + return pPEImage; +} + +void Assembly::SetPEImage(PEImage *pPEImage) +{ + BinderAddRefPEImage(pPEImage); + m_pPEImage = pPEImage; +} + +void Assembly::SetNativePEImage(PEImage *pNativePEImage) +{ + BinderAddRefPEImage(pNativePEImage); + m_pNativePEImage = pNativePEImage; +} + +AssemblyName *Assembly::GetAssemblyName(BOOL fAddRef /* = FALSE */) +{ + AssemblyName *pAssemblyName = m_pAssemblyName; + + if (fAddRef && (pAssemblyName != NULL)) + { + pAssemblyName->AddRef(); + } + return pAssemblyName; +} + +void Assembly::SetAssemblyName(AssemblyName *pAssemblyName, + BOOL fAddRef /* = TRUE */) +{ + SAFE_RELEASE(m_pAssemblyName); + + m_pAssemblyName = pAssemblyName; + + if (fAddRef && (pAssemblyName != NULL)) + { + pAssemblyName->AddRef(); + } +} + +BOOL Assembly::GetIsInGAC() +{ + return ((m_dwAssemblyFlags & FLAG_IS_IN_GAC) != 0); +} + +void Assembly::SetIsInGAC(BOOL fIsInGAC) +{ + if (fIsInGAC) + { + m_dwAssemblyFlags |= FLAG_IS_IN_GAC; + } + else + { + m_dwAssemblyFlags &= ~FLAG_IS_IN_GAC; + } +} + +SString &Assembly::GetPath() +{ + return m_assemblyPath; +} + +IMDInternalImport *Assembly::GetMDImport() +{ + return m_pMDImport; +} + +void Assembly::SetMDImport(IMDInternalImport *pMDImport) +{ + SAFE_RELEASE(m_pMDImport); + + m_pMDImport = pMDImport; + m_pMDImport->AddRef(); +} + +BINDER_SPACE::Assembly* GetAssemblyFromPrivAssemblyFast(ICLRPrivAssembly *pPrivAssembly) +{ +#ifdef _DEBUG + if(pPrivAssembly != nullptr) + { + // Ensure the pPrivAssembly we are about to cast is indeed a valid Assembly + DWORD dwImageType = 0; + pPrivAssembly->GetAvailableImageTypes(&dwImageType); + _ASSERTE((dwImageType & ASSEMBLY_IMAGE_TYPE_ASSEMBLY) == ASSEMBLY_IMAGE_TYPE_ASSEMBLY); + } +#endif + return (BINDER_SPACE::Assembly *)pPrivAssembly; +} + +#endif diff --git a/src/coreclr/binder/inc/assemblybinder.hpp b/src/coreclr/binder/inc/assemblybinder.hpp new file mode 100644 index 00000000000..6fc2f2dc457 --- /dev/null +++ b/src/coreclr/binder/inc/assemblybinder.hpp @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// AssemblyBinder.hpp +// + + +// +// Defines the AssemblyBinder class +// +// ============================================================ + +#ifndef __BINDER__ASSEMBLY_BINDER_HPP__ +#define __BINDER__ASSEMBLY_BINDER_HPP__ + +#include "bindertypes.hpp" +#include "bindresult.hpp" +#include "coreclrbindercommon.h" +#include "bundle.h" + +class CLRPrivBinderAssemblyLoadContext; +class CLRPrivBinderCoreCLR; + +namespace BINDER_SPACE +{ + class AssemblyBinder + { + public: + static HRESULT Startup(); + + // See code:BINDER_SPACE::AssemblyBinder::GetAssembly for info on fNgenExplicitBind + // and fExplicitBindToNativeImage, and see code:CEECompileInfo::LoadAssemblyByPath + // for an example of how they're used. + static HRESULT BindAssembly(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* in */ LPCWSTR szCodeBase, + /* in */ PEAssembly *pParentAssembly, + /* in */ BOOL fNgenExplicitBind, + /* in */ BOOL fExplicitBindToNativeImage, + /* in */ bool excludeAppPaths, + /* out */ Assembly **ppAssembly); + + static HRESULT BindToSystem(/* in */ SString &systemDirectory, + /* out */ Assembly **ppSystemAssembly, + /* in */ bool fBindToNativeImage); + + static HRESULT BindToSystemSatellite(/* in */ SString &systemDirectory, + /* in */ SString &simpleName, + /* in */ SString &cultureName, + /* out */ Assembly **ppSystemAssembly); + + static HRESULT GetAssembly(/* in */ SString &assemblyPath, + /* in */ BOOL fIsInGAC, + /* in */ BOOL fExplicitBindToNativeImage, + /* out */ Assembly **ppAssembly, + /* in */ LPCTSTR szMDAssemblyPath = NULL, + /* in */ BundleFileLocation bundleFileLocation = BundleFileLocation::Invalid()); + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + static HRESULT BindUsingHostAssemblyResolver (/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin, + /* in */ AssemblyName *pAssemblyName, + /* in */ IAssemblyName *pIAssemblyName, + /* in */ CLRPrivBinderCoreCLR *pTPABinder, + /* out */ Assembly **ppAssembly); + + static HRESULT BindUsingPEImage(/* in */ ApplicationContext *pApplicationContext, + /* in */ BINDER_SPACE::AssemblyName *pAssemblyName, + /* in */ PEImage *pPEImage, + /* in */ PEKIND peKind, + /* in */ IMDInternalImport *pIMetaDataAssemblyImport, + /* [retval] [out] */ Assembly **ppAssembly); +#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + + static HRESULT TranslatePEToArchitectureType(DWORD *pdwPAFlags, PEKIND *PeKind); + + private: + static HRESULT BindByName(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* in */ bool skipFailureCaching, + /* in */ bool skipVersionCompatibilityCheck, + /* in */ bool excludeAppPaths, + /* out */ BindResult *pBindResult); + + // See code:BINDER_SPACE::AssemblyBinder::GetAssembly for info on fNgenExplicitBind + // and fExplicitBindToNativeImage, and see code:CEECompileInfo::LoadAssemblyByPath + // for an example of how they're used. + static HRESULT BindWhereRef(/* in */ ApplicationContext *pApplicationContext, + /* in */ PathString &assemblyPath, + /* in */ BOOL fNgenExplicitBind, + /* in */ BOOL fExplicitBindToNativeImage, + /* in */ bool excludeAppPaths, + /* out */ BindResult *pBindResult); + + static HRESULT BindLocked(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* in */ bool skipVersionCompatibilityCheck, + /* in */ bool excludeAppPaths, + /* out */ BindResult *pBindResult); + + static HRESULT FindInExecutionContext(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* out */ ContextEntry **ppContextEntry); + + static HRESULT BindByTpaList(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pRequestedAssemblyName, + /* in */ bool excludeAppPaths, + /* out */ BindResult *pBindResult); + + static HRESULT Register(/* in */ ApplicationContext *pApplicationContext, + /* in */ BindResult *pBindResult); + static HRESULT RegisterAndGetHostChosen(/* in */ ApplicationContext *pApplicationContext, + /* in */ LONG kContextVersion, + /* in */ BindResult *pBindResult, + /* out */ BindResult *pHostBindResult); + + static HRESULT OtherBindInterfered(/* in */ ApplicationContext *pApplicationContext, + /* in */ BindResult *pBindResult); + }; +}; + +#endif diff --git a/src/coreclr/binder/inc/assemblyentry.hpp b/src/coreclr/binder/inc/assemblyentry.hpp new file mode 100644 index 00000000000..5a32e6f5cd9 --- /dev/null +++ b/src/coreclr/binder/inc/assemblyentry.hpp @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// AssemblyEntry.hpp +// + + +// +// Defines the AssemblyEntry class +// +// ============================================================ + +#ifndef __BINDER__ASSEMBLY_ENTRY_HPP__ +#define __BINDER__ASSEMBLY_ENTRY_HPP__ + +#include "bindertypes.hpp" +#include "assemblyname.hpp" + +namespace BINDER_SPACE +{ + class AssemblyEntry + { + public: + AssemblyEntry() + { + m_pAssemblyName = NULL; + } + virtual ~AssemblyEntry() + { + SAFE_RELEASE(m_pAssemblyName); + } + + AssemblyName *GetAssemblyName(BOOL fAddRef = FALSE) + { + AssemblyName *pAssemblyName = m_pAssemblyName; + + if (fAddRef && (pAssemblyName != NULL)) + { + pAssemblyName->AddRef(); + } + return pAssemblyName; + } + + void SetAssemblyName(AssemblyName *pAssemblyName, BOOL fAddRef = TRUE) + { + SAFE_RELEASE(m_pAssemblyName); + + if (fAddRef && (pAssemblyName != NULL)) + { + pAssemblyName->AddRef(); + } + + m_pAssemblyName = pAssemblyName; + } + protected: + AssemblyName *m_pAssemblyName; + }; +}; + +#endif diff --git a/src/coreclr/binder/inc/assemblyhashtraits.hpp b/src/coreclr/binder/inc/assemblyhashtraits.hpp new file mode 100644 index 00000000000..9c28151e1db --- /dev/null +++ b/src/coreclr/binder/inc/assemblyhashtraits.hpp @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// AssemblyHashTraits.hpp +// + + +// +// Defines the AssemblyHashTraits template class +// +// ============================================================ + +#ifndef __BINDER__ASSEMBLY_HASH_TRAITS_HPP__ +#define __BINDER__ASSEMBLY_HASH_TRAITS_HPP__ + +#include "bindertypes.hpp" +#include "assemblyentry.hpp" +#include "shash.h" + +namespace BINDER_SPACE +{ + template<typename HashEntry, DWORD dwAssemblyNameFlags> + class AssemblyHashTraits : public NoRemoveSHashTraits<DefaultSHashTraits<HashEntry> > + { + public: + typedef typename NoRemoveSHashTraits<DefaultSHashTraits<HashEntry> >::element_t element_t; + typedef typename NoRemoveSHashTraits<DefaultSHashTraits<HashEntry> >::count_t count_t; + + typedef AssemblyName* key_t; + + // GetKey, Equals and Hash can throw due to SString + static const bool s_NoThrow = false; + + static key_t GetKey(element_t pAssemblyEntry) + { + return pAssemblyEntry->GetAssemblyName(); + } + static BOOL Equals(key_t pAssemblyName1, key_t pAssemblyName2) + { + return pAssemblyName1->Equals(pAssemblyName2, dwAssemblyNameFlags); + } + static count_t Hash(key_t pAssemblyName) + { + return pAssemblyName->Hash(dwAssemblyNameFlags); + } + static element_t Null() + { + return NULL; + } + static bool IsNull(const element_t &assemblyEntry) + { + return (assemblyEntry == NULL); + } + }; +}; + +#endif diff --git a/src/coreclr/binder/inc/assemblyidentity.hpp b/src/coreclr/binder/inc/assemblyidentity.hpp new file mode 100644 index 00000000000..e1fdec87b26 --- /dev/null +++ b/src/coreclr/binder/inc/assemblyidentity.hpp @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// AssemblyIdentity.hpp +// + + +// +// Defines the AssemblyIdentity class +// +// ============================================================ + +#ifndef __BINDER__ASSEMBLY_IDENTITY_HPP__ +#define __BINDER__ASSEMBLY_IDENTITY_HPP__ + +#include "bindertypes.hpp" +#include "assemblyversion.hpp" + +namespace BINDER_SPACE +{ + class AssemblyIdentity + { + public: + enum + { + IDENTITY_FLAG_EMPTY = 0x000, + IDENTITY_FLAG_SIMPLE_NAME = 0x001, + IDENTITY_FLAG_VERSION = 0x002, + IDENTITY_FLAG_PUBLIC_KEY_TOKEN = 0x004, + IDENTITY_FLAG_PUBLIC_KEY = 0x008, + IDENTITY_FLAG_CULTURE = 0x010, + IDENTITY_FLAG_LANGUAGE = 0x020, + IDENTITY_FLAG_PROCESSOR_ARCHITECTURE = 0x040, + IDENTITY_FLAG_RETARGETABLE = 0x080, + IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL = 0x100, + IDENTITY_FLAG_CUSTOM = 0x200, + IDENTITY_FLAG_CUSTOM_NULL = 0x400, + IDENTITY_FLAG_CONTENT_TYPE = 0x800, + IDENTITY_FLAG_FULL_NAME = (IDENTITY_FLAG_SIMPLE_NAME | + IDENTITY_FLAG_VERSION) + }; + + AssemblyIdentity() + { + m_dwIdentityFlags = IDENTITY_FLAG_EMPTY; + m_kProcessorArchitecture = peNone; + m_kContentType = AssemblyContentType_Default; + + // Need to pre-populate SBuffers because of bogus asserts + static const BYTE byteArr[] = { 0 }; + m_publicKeyOrTokenBLOB.SetImmutable(byteArr, sizeof(byteArr)); + m_customBLOB.SetImmutable(byteArr, sizeof(byteArr)); + } + ~AssemblyIdentity() + { + // Nothing to do here + } + + static BOOL Have(DWORD dwUseIdentityFlags, DWORD dwIdentityFlags) + { + return ((dwUseIdentityFlags & dwIdentityFlags) != 0); + } + + BOOL Have(DWORD dwIdentityFlags) + { + return Have(m_dwIdentityFlags, dwIdentityFlags); + } + + void SetHave(DWORD dwIdentityFlags) + { + m_dwIdentityFlags |= dwIdentityFlags; + } + + void SetClear(DWORD dwIdentityFlags) + { + m_dwIdentityFlags &= ~dwIdentityFlags; + } + + SString m_simpleName; + AssemblyVersion m_version; + SString m_cultureOrLanguage; + SBuffer m_publicKeyOrTokenBLOB; + PEKIND m_kProcessorArchitecture; + AssemblyContentType m_kContentType; + SBuffer m_customBLOB; + DWORD m_dwIdentityFlags; + }; + + class AssemblyIdentityUTF8 : public AssemblyIdentity + { + public: + AssemblyIdentityUTF8() + { + m_szSimpleNameUTF8 = NULL; + m_szCultureOrLanguageUTF8 = NULL; + } + + void PopulateUTF8Fields() + { + m_szSimpleNameUTF8 = m_simpleName.GetUTF8(sSimpleNameBuffer); + + if (Have(IDENTITY_FLAG_CULTURE) && !m_cultureOrLanguage.IsEmpty()) + { + m_szCultureOrLanguageUTF8 = m_cultureOrLanguage.GetUTF8(sCultureBuffer); + } + } + + inline LPCSTR GetSimpleNameUTF8() + { + return m_szSimpleNameUTF8; + } + + inline LPCSTR GetCultureOrLanguageUTF8() + { + return m_szCultureOrLanguageUTF8; + } + + inline const BYTE *GetPublicKeyOrTokenArray() + { + const BYTE *pPublicKeyOrToken = m_publicKeyOrTokenBLOB; + + return pPublicKeyOrToken; + } + + protected: + StackScratchBuffer sSimpleNameBuffer; + StackScratchBuffer sCultureBuffer; + LPCSTR m_szSimpleNameUTF8; + LPCSTR m_szCultureOrLanguageUTF8; + }; +}; + +#endif diff --git a/src/coreclr/binder/inc/assemblyidentitycache.hpp b/src/coreclr/binder/inc/assemblyidentitycache.hpp new file mode 100644 index 00000000000..bb70af109fd --- /dev/null +++ b/src/coreclr/binder/inc/assemblyidentitycache.hpp @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// AssemblyIdentityCache.hpp +// + + +// +// Defines the AssemblyIdentityCache class and its helpers +// +// ============================================================ + +#ifndef __BINDER__ASSEMBLY_IDENTITY_CACHE_HPP__ +#define __BINDER__ASSEMBLY_IDENTITY_CACHE_HPP__ + +#include "bindertypes.hpp" +#include "assemblyidentity.hpp" +#include "utils.hpp" +#include "sstring.h" +#include "shash.h" + +namespace BINDER_SPACE +{ + class AssemblyIdentityCacheEntry + { + public: + inline AssemblyIdentityCacheEntry() + { + m_szTextualIdentity = NULL; + m_pAssemblyIdentity = NULL; + } + inline ~AssemblyIdentityCacheEntry() + { + SAFE_DELETE_ARRAY(m_szTextualIdentity); + SAFE_DELETE(m_pAssemblyIdentity); + } + + // Getters/Setters + inline LPCSTR GetTextualIdentity() + { + return m_szTextualIdentity; + } + inline void SetTextualIdentity(LPCSTR szTextualIdentity) + { + size_t len = strlen(szTextualIdentity) + 1; + + m_szTextualIdentity = new char[len]; + strcpy_s((LPSTR) m_szTextualIdentity, len, szTextualIdentity); + } + inline AssemblyIdentityUTF8 *GetAssemblyIdentity() + { + return m_pAssemblyIdentity; + } + inline void SetAssemblyIdentity(AssemblyIdentityUTF8 *pAssemblyIdentity) + { + m_pAssemblyIdentity = pAssemblyIdentity; + } + + protected: + LPCSTR m_szTextualIdentity; + AssemblyIdentityUTF8 *m_pAssemblyIdentity; + }; + + class AssemblyIdentityHashTraits : public DefaultSHashTraits<AssemblyIdentityCacheEntry *> + { + public: + typedef LPCSTR key_t; + + static key_t GetKey(element_t pAssemblyIdentityCacheEntry) + { + return pAssemblyIdentityCacheEntry->GetTextualIdentity(); + } + static BOOL Equals(key_t textualIdentity1, key_t textualIdentity2) + { + if ((textualIdentity1 == NULL) && (textualIdentity2 == NULL)) + return TRUE; + if ((textualIdentity1 == NULL) || (textualIdentity2 == NULL)) + return FALSE; + + return (strcmp(textualIdentity1, textualIdentity2) == 0); + } + static count_t Hash(key_t textualIdentity) + { + if (textualIdentity == NULL) + return 0; + else + return HashStringA(textualIdentity); + } + static element_t Null() + { + return NULL; + } + static bool IsNull(const element_t &assemblyIdentityCacheEntry) + { + return (assemblyIdentityCacheEntry == NULL); + } + + }; + + class AssemblyIdentityCache : protected SHash<AssemblyIdentityHashTraits> + { + private: + typedef SHash<AssemblyIdentityHashTraits> Hash; + public: + AssemblyIdentityCache(); + ~AssemblyIdentityCache(); + + HRESULT Add(/* in */ LPCSTR szTextualIdentity, + /* in */ AssemblyIdentityUTF8 *pAssemblyIdentity); + AssemblyIdentityUTF8 *Lookup(/* in */ LPCSTR szTextualIdentity); + }; +}; + +#endif diff --git a/src/coreclr/binder/inc/assemblyname.hpp b/src/coreclr/binder/inc/assemblyname.hpp new file mode 100644 index 00000000000..fb66d9830e0 --- /dev/null +++ b/src/coreclr/binder/inc/assemblyname.hpp @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// AssemblyName.hpp +// + + +// +// Defines the AssemblyName class +// +// ============================================================ + +#ifndef __BINDER__ASSEMBLY_NAME_HPP__ +#define __BINDER__ASSEMBLY_NAME_HPP__ + +#include "bindertypes.hpp" +#include "assemblyidentity.hpp" + +namespace BINDER_SPACE +{ + class AssemblyName : protected AssemblyIdentity + { + public: + typedef enum + { + INCLUDE_DEFAULT = 0x00, + INCLUDE_VERSION = 0x01, + INCLUDE_ARCHITECTURE = 0x02, + INCLUDE_RETARGETABLE = 0x04, + INCLUDE_CONTENT_TYPE = 0x08, + INCLUDE_PUBLIC_KEY_TOKEN = 0x10, + EXCLUDE_CULTURE = 0x20 + } INCLUDE_FLAGS; + + AssemblyName(); + ~AssemblyName(); + + HRESULT Init(/* in */ IMDInternalImport *pIMetaDataAssemblyImport, + /* in */ PEKIND PeKind, + /* in */ mdAssemblyRef mda = 0, + /* in */ BOOL fIsDefinition = TRUE); + HRESULT Init(/* in */ SString &assemblyDisplayName); + HRESULT Init(/* in */ IAssemblyName *pIAssemblyName); + + ULONG AddRef(); + ULONG Release(); + + // Getters/Setters + inline SString &GetSimpleName(); + inline void SetSimpleName(SString &simpleName); + inline AssemblyVersion *GetVersion(); + inline void SetVersion(/* in */ AssemblyVersion *pAssemblyVersion); + inline SString &GetCulture(); + inline void SetCulture(SString &culture); + inline SBuffer &GetPublicKeyTokenBLOB(); + inline PEKIND GetArchitecture(); + inline void SetArchitecture(PEKIND kArchitecture); + inline AssemblyContentType GetContentType(); + inline void SetContentType(AssemblyContentType kContentType); + inline BOOL GetIsRetargetable(); + inline void SetIsRetargetable(BOOL fIsRetargetable); + inline BOOL GetIsDefinition(); + inline void SetIsDefinition(BOOL fIsDefinition); + + inline void SetHave(DWORD dwIdentityFlags); + + BOOL IsCoreLib(); + + ULONG Hash(/* in */ DWORD dwIncludeFlags); + BOOL Equals(/* in */ AssemblyName *pAssemblyName, + /* in */ DWORD dwIncludeFlags); + + void GetDisplayName(/* out */ PathString &displayName, + /* in */ DWORD dwIncludeFlags); + + protected: + enum + { + NAME_FLAG_NONE = 0x00, + NAME_FLAG_RETARGETABLE = 0x01, + NAME_FLAG_DEFINITION = 0x02, + }; + + SString &GetNormalizedCulture(); + + LONG m_cRef; + DWORD m_dwNameFlags; + }; + +#include "assemblyname.inl" +}; + +#endif diff --git a/src/coreclr/binder/inc/assemblyname.inl b/src/coreclr/binder/inc/assemblyname.inl new file mode 100644 index 00000000000..11d5dbbe869 --- /dev/null +++ b/src/coreclr/binder/inc/assemblyname.inl @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// AssemblyName.inl +// + + +// +// Implements the inlined methods of AssemblyName class +// +// ============================================================ + +#ifndef __BINDER__ASSEMBLY_NAME_INL__ +#define __BINDER__ASSEMBLY_NAME_INL__ + +SString &AssemblyName::GetSimpleName() +{ + return m_simpleName; +} + +void AssemblyName::SetSimpleName(SString &simpleName) +{ + m_simpleName.Set(simpleName); + SetHave(AssemblyIdentity::IDENTITY_FLAG_SIMPLE_NAME); +} + +AssemblyVersion *AssemblyName::GetVersion() +{ + return &m_version; +} + +void AssemblyName::SetVersion(AssemblyVersion *pAssemblyVersion) +{ + m_version.SetVersion(pAssemblyVersion); +} + +SString &AssemblyName::GetCulture() +{ + return m_cultureOrLanguage; +} + +void AssemblyName::SetCulture(SString &culture) +{ + m_cultureOrLanguage.Set(culture); + SetHave(AssemblyIdentity::IDENTITY_FLAG_CULTURE); +} + +SBuffer &AssemblyName::GetPublicKeyTokenBLOB() +{ + return m_publicKeyOrTokenBLOB; +} + +PEKIND AssemblyName::GetArchitecture() +{ + return m_kProcessorArchitecture; +} + +void AssemblyName::SetArchitecture(PEKIND kArchitecture) +{ + m_kProcessorArchitecture = kArchitecture; + + if (kArchitecture != peNone) + { + SetHave(AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE); + } + else + { + SetClear(AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE); + } +} + +AssemblyContentType AssemblyName::GetContentType() +{ + return m_kContentType; +} + +void AssemblyName::SetContentType(AssemblyContentType kContentType) +{ + m_kContentType = kContentType; + + if (kContentType != AssemblyContentType_Default) + { + SetHave(AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE); + } + else + { + SetClear(AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE); + } +} + +BOOL AssemblyName::GetIsRetargetable() +{ + return m_dwIdentityFlags & AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE; +} + +void AssemblyName::SetIsRetargetable(BOOL fIsRetargetable) +{ + if (fIsRetargetable) + { + m_dwNameFlags |= NAME_FLAG_RETARGETABLE; + SetHave(AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE); + } + else + { + m_dwNameFlags &= ~NAME_FLAG_RETARGETABLE; + SetClear(AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE); + } +} + +BOOL AssemblyName::GetIsDefinition() +{ + return ((m_dwNameFlags & NAME_FLAG_DEFINITION) != 0); +} + +void AssemblyName::SetIsDefinition(BOOL fIsDefinition) +{ + if (fIsDefinition) + { + m_dwNameFlags |= NAME_FLAG_DEFINITION; + } + else + { + m_dwNameFlags &= ~NAME_FLAG_DEFINITION; + } +} + +void AssemblyName::SetHave(DWORD dwIdentityFlags) +{ + AssemblyIdentity::SetHave(dwIdentityFlags); +} + +#endif diff --git a/src/coreclr/binder/inc/assemblyversion.hpp b/src/coreclr/binder/inc/assemblyversion.hpp new file mode 100644 index 00000000000..ff8be72a11c --- /dev/null +++ b/src/coreclr/binder/inc/assemblyversion.hpp @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// AssemblyVersion.hpp +// + + +// +// Defines the AssemblyVersion class +// +// ============================================================ + +#ifndef __BINDER__ASSEMBLY_VERSION_HPP__ +#define __BINDER__ASSEMBLY_VERSION_HPP__ + +#include "bindertypes.hpp" +#include "textualidentityparser.hpp" + +namespace BINDER_SPACE +{ + class AssemblyVersion + { + private: + static const DWORD Unspecified = (DWORD)-1; + static const USHORT UnspecifiedShort = (USHORT)-1; + + public: + inline AssemblyVersion(); + inline ~AssemblyVersion(); + + inline BOOL HasMajor(); + inline BOOL HasMinor(); + inline BOOL HasBuild(); + inline BOOL HasRevision(); + + inline DWORD GetMajor(); + inline DWORD GetMinor(); + inline DWORD GetBuild(); + inline DWORD GetRevision(); + + inline void SetFeatureVersion(/* in */ DWORD dwMajor, + /* in */ DWORD dwMinor); + inline void SetServiceVersion(/* in */ DWORD dwBuild, + /* in */ DWORD dwRevision); + inline void SetVersion(AssemblyVersion *pAssemblyVersion); + + inline BOOL Equals(AssemblyVersion *pAssemblyVersion); + private: + DWORD m_dwMajor; + DWORD m_dwMinor; + DWORD m_dwBuild; + DWORD m_dwRevision; + }; + +#include "assemblyversion.inl" +}; + +#endif diff --git a/src/coreclr/binder/inc/assemblyversion.inl b/src/coreclr/binder/inc/assemblyversion.inl new file mode 100644 index 00000000000..b479c0e74fc --- /dev/null +++ b/src/coreclr/binder/inc/assemblyversion.inl @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// AssemblyVersion.inl +// + + +// +// Implements the inline methods of AssemblyVersion +// +// ============================================================ + +#ifndef __BINDER__ASSEMBLY_VERSION_INL__ +#define __BINDER__ASSEMBLY_VERSION_INL__ + +AssemblyVersion::AssemblyVersion() +{ + m_dwMajor = m_dwMinor = m_dwBuild = m_dwRevision = static_cast<DWORD>(-1); +} + +AssemblyVersion::~AssemblyVersion() +{ + // Noting to do here +} + +BOOL AssemblyVersion::HasMajor() +{ + return m_dwMajor != Unspecified; +} + +BOOL AssemblyVersion::HasMinor() +{ + return m_dwMinor != Unspecified; +} + +BOOL AssemblyVersion::HasBuild() +{ + return m_dwBuild != Unspecified; +} + +BOOL AssemblyVersion::HasRevision() +{ + return m_dwRevision != Unspecified; +} + +DWORD AssemblyVersion::GetMajor() +{ + return m_dwMajor; +} + +DWORD AssemblyVersion::GetMinor() +{ + return m_dwMinor; +} + +DWORD AssemblyVersion::GetBuild() +{ + return m_dwBuild; +} + +DWORD AssemblyVersion::GetRevision() +{ + return m_dwRevision; +} + +void AssemblyVersion::SetFeatureVersion(DWORD dwMajor, + DWORD dwMinor) +{ + // BaseAssemblySpec and AssemblyName properties store uint16 components for the version. Version and AssemblyVersion store + // int32 or uint32. When the former are initialized from the latter, the components are truncated to uint16 size. When the + // latter are initialized from the former, they are zero-extended to int32 size. For uint16 components, the max value is + // used to indicate an unspecified component. For int32 components, -1 is used. Since we're treating the version as an + // assembly version here, map the uint16 unspecified value to the int32 size. + m_dwMajor = dwMajor == UnspecifiedShort ? Unspecified : dwMajor; + m_dwMinor = dwMinor == UnspecifiedShort ? Unspecified : dwMinor; +} + +void AssemblyVersion::SetServiceVersion(DWORD dwBuild, + DWORD dwRevision) +{ + // See comment in SetFeatureVersion, the same applies here + m_dwBuild = dwBuild == UnspecifiedShort ? Unspecified : dwBuild; + m_dwRevision = dwRevision == UnspecifiedShort ? Unspecified : dwRevision; +} + +void AssemblyVersion::SetVersion(AssemblyVersion *pAssemblyVersion) +{ + m_dwMajor = pAssemblyVersion->GetMajor(); + m_dwMinor = pAssemblyVersion->GetMinor(); + m_dwBuild = pAssemblyVersion->GetBuild(); + m_dwRevision = pAssemblyVersion->GetRevision(); +} + +BOOL AssemblyVersion::Equals(AssemblyVersion *pAssemblyVersion) +{ + BOOL result = FALSE; + if ((GetMajor() == pAssemblyVersion->GetMajor()) && + (GetMinor() == pAssemblyVersion->GetMinor()) && + (GetBuild() == pAssemblyVersion->GetBuild()) && + (GetRevision() == pAssemblyVersion->GetRevision())) + { + result = TRUE; + } + return result; +} + +#endif diff --git a/src/coreclr/binder/inc/bindertracing.h b/src/coreclr/binder/inc/bindertracing.h new file mode 100644 index 00000000000..5d82888158a --- /dev/null +++ b/src/coreclr/binder/inc/bindertracing.h @@ -0,0 +1,189 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// bindertracing.h +// + +#ifndef __BINDER_TRACING_H__ +#define __BINDER_TRACING_H__ + +class Assembly; +class AssemblySpec; +class PEAssembly; + +namespace BINDER_SPACE +{ + class Assembly; + class AssemblyName; +} + +namespace BinderTracing +{ + bool IsEnabled(); + + // If tracing is enabled, this class fires an assembly bind start event on construction + // and the corresponding stop event on destruction + class AssemblyBindOperation + { + public: + // This class assumes the assembly spec will have a longer lifetime than itself + AssemblyBindOperation(AssemblySpec *assemblySpec, const WCHAR *assemblyPath = nullptr); + ~AssemblyBindOperation(); + + void SetResult(PEAssembly *assembly, bool cached = false); + + struct BindRequest + { + AssemblySpec *AssemblySpec; + SString AssemblyName; + SString AssemblyPath; + SString RequestingAssembly; + SString AssemblyLoadContext; + SString RequestingAssemblyLoadContext; + }; + + private: + bool ShouldIgnoreBind(); + + private: + BindRequest m_bindRequest; + bool m_populatedBindRequest; + + bool m_checkedIgnoreBind; + bool m_ignoreBind; + + PEAssembly *m_resultAssembly; + bool m_cached; + }; + + // An object of this class manages firing events for all the stages during a binder resolving + // attempt operation. It has minimal cost if tracing for this event is disabled. + // + // This class should be declared in the stack. As information is determined by each stage + // (e.g. an AssemblySpec is initialized), the appropriate Set*() method should be called. All + // pointers held by an object of this class must either be a nullptr, or point to a valid + // object during the time it is in scope. + // + // As the resolution progresses to different stages, the GoToStage() method should be called. + // Calling it will fire an event for the previous stage with whatever context the class had + // at that point; it is assumed that if GoToStage() is called, the previous stage failed + // (the HRESULT is read by the dtor to assess success). + // + // It holds a reference to a HRESULT (that must be live during the lifetime of this object), + // which is used to determine the success or failure of a stage either at the moment this + // class is destructed (e.g. last stage), or when moving from one stage to another. (This + // is especially useful if the HRESULT is captured from an exception handler.) + class ResolutionAttemptedOperation + { + public: + // This must match the ResolutionAttemptedStage value map in ClrEtwAll.man + enum class Stage : uint16_t + { + FindInLoadContext = 0, + AssemblyLoadContextLoad = 1, + ApplicationAssemblies = 2, + DefaultAssemblyLoadContextFallback = 3, + ResolveSatelliteAssembly = 4, + AssemblyLoadContextResolvingEvent = 5, + AppDomainAssemblyResolveEvent = 6, + NotYetStarted = 0xffff, // Used as flag to not fire event; not present in value map + }; + + public: // static + static void TraceAppDomainAssemblyResolve(AssemblySpec *spec, PEAssembly *resultAssembly, Exception *exception = nullptr); + + public: + // One of binder ID and managed ALC is expected to be non-zero. If the managed ALC is set, binder ID is ignored. + ResolutionAttemptedOperation(BINDER_SPACE::AssemblyName *assemblyName, UINT_PTR binderId, INT_PTR managedALC, const HRESULT& hr); + + void TraceBindResult(const BINDER_SPACE::BindResult &bindResult, bool mvidMismatch = false); + + void SetFoundAssembly(BINDER_SPACE::Assembly *assembly) + { + m_pFoundAssembly = assembly; + } + + void GoToStage(Stage stage) + { + assert(m_stage != stage); + assert(stage != Stage::NotYetStarted); + + if (!m_tracingEnabled) + return; + + // Going to a different stage should only happen if the current + // stage failed (or if the binding process wasn't yet started). + // Firing the event at this point not only helps timing each binding + // stage, but avoids keeping track of which stages were reached to + // resolve the assembly. + TraceStage(m_stage, m_hr, m_pFoundAssembly); + m_stage = stage; + m_exceptionMessage.Clear(); + } + + void SetException(Exception *ex) + { + if (!m_tracingEnabled) + return; + + ex->GetMessage(m_exceptionMessage); + } + +#ifdef FEATURE_EVENT_TRACE + ~ResolutionAttemptedOperation() + { + if (!m_tracingEnabled) + return; + + TraceStage(m_stage, m_hr, m_pFoundAssembly); + } +#endif // FEATURE_EVENT_TRACE + + private: + + // This must match the ResolutionAttemptedResult value map in ClrEtwAll.man + enum class Result : uint16_t + { + Success = 0, + AssemblyNotFound = 1, + IncompatibleVersion = 2, + MismatchedAssemblyName = 3, + Failure = 4, + Exception = 5, + }; + + // A reference to an HRESULT stored in the same scope as this object lets + // us determine if the last requested stage was successful or not, regardless + // if it was set through a function call (e.g. BindAssemblyByNameWorker()), or + // if an exception was thrown and captured by the EX_CATCH_HRESULT() macro. + const HRESULT &m_hr; + + Stage m_stage; + + bool m_tracingEnabled; + + BINDER_SPACE::AssemblyName *m_assemblyNameObject; + PathString m_assemblyName; + SString m_assemblyLoadContextName; + + SString m_exceptionMessage; + BINDER_SPACE::Assembly *m_pFoundAssembly; + + void TraceStage(Stage stage, HRESULT hr, BINDER_SPACE::Assembly *resultAssembly, const WCHAR *errorMessage = nullptr); + }; + + // This must match the BindingPathSource value map in ClrEtwAll.man + enum PathSource + { + ApplicationAssemblies, + AppNativeImagePaths, + AppPaths, + PlatformResourceRoots, + SatelliteSubdirectory, + Bundle + }; + + void PathProbed(const WCHAR *path, PathSource source, HRESULT hr); +}; + +#endif // __BINDER_TRACING_H__ diff --git a/src/coreclr/binder/inc/bindertypes.hpp b/src/coreclr/binder/inc/bindertypes.hpp new file mode 100644 index 00000000000..08159ebc840 --- /dev/null +++ b/src/coreclr/binder/inc/bindertypes.hpp @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// BinderTypes.hpp +// + + +// +// Declares a bunch of binder classes, types and macros +// +// ============================================================ + +#ifndef __BINDER_TYPES_HPP__ +#define __BINDER_TYPES_HPP__ + +#include "clrtypes.h" +#include "sstring.h" + +#include "fusionhelpers.hpp" + +extern void DECLSPEC_NORETURN ThrowOutOfMemory(); + +#ifndef S_TRUE +#define S_TRUE S_OK +#endif + +class PEImage; +class PEAssembly; + +namespace BINDER_SPACE +{ + class AssemblyVersion; + class AssemblyName; + class Assembly; + + class ContextEntry; + class ExecutionContext; + + class ApplicationContext; + + class BindResult; + class FailureCache; + class AssemblyBinder; +}; + +#define IF_FAIL_GO(expr) \ + hr = (expr); \ + if (FAILED(hr)) \ + { \ + goto Exit; \ + } + +#define IF_FALSE_GO(expr) \ + if (!(expr)) { \ + hr = E_FAIL; \ + goto Exit; \ + } + +#define GO_WITH_HRESULT(hrValue) \ + hr = hrValue; \ + goto Exit; + +#define IF_WIN32_ERROR_GO(expr) \ + if (!(expr)) \ + { \ + hr = HRESULT_FROM_GetLastError(); \ + goto Exit; \ + } + +#define NEW_CONSTR(Object, Constr) \ + (Object) = new (nothrow) Constr; + +#define SAFE_NEW_CONSTR(Object, Constr) \ + (Object) = new (nothrow) Constr; \ + if ((Object) == NULL) \ + { \ + hr = E_OUTOFMEMORY; \ + goto Exit; \ + } + +#define SAFE_NEW(Object, Class) \ + SAFE_NEW_CONSTR(Object, Class()); + +#define SAFE_RELEASE(objectPtr) \ + if ((objectPtr) != NULL) \ + { \ + (objectPtr)->Release(); \ + (objectPtr) = NULL; \ + } + +#define SAFE_DELETE(objectPtr) \ + if ((objectPtr) != NULL) \ + { \ + delete (objectPtr); \ + (objectPtr) = NULL; \ + } + +#define SAFE_DELETE_ARRAY(objectPtr) \ + if ((objectPtr) != NULL) \ + { \ + delete[] (objectPtr); \ + (objectPtr) = NULL; \ + } + +#define LENGTH_OF(x) \ + (sizeof(x) / sizeof(x[0])) + +#endif diff --git a/src/coreclr/binder/inc/bindresult.hpp b/src/coreclr/binder/inc/bindresult.hpp new file mode 100644 index 00000000000..b305c525c3b --- /dev/null +++ b/src/coreclr/binder/inc/bindresult.hpp @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// BindResult.hpp +// + + +// +// Defines the BindResult class +// +// ============================================================ + +#ifndef __BINDER__BIND_RESULT_HPP__ +#define __BINDER__BIND_RESULT_HPP__ + +#include "bindertypes.hpp" + +namespace BINDER_SPACE +{ + class BindResult + { + public: + inline BindResult(); + inline ~BindResult(); + + inline AssemblyName *GetAssemblyName(BOOL fAddRef = FALSE); + inline IUnknown *GetAssembly(BOOL fAddRef = FALSE); + inline Assembly *GetAsAssembly(BOOL fAddRef = FALSE); + + inline BOOL GetIsInGAC(); + inline void SetIsInGAC(BOOL fIsInGAC); + inline BOOL GetIsContextBound(); + inline void SetIsContextBound(BOOL fIsContextBound); + inline BOOL GetIsFirstRequest(); + inline void SetIsFirstRequest(BOOL fIsFirstRequest); + + inline void SetResult(ContextEntry *pContextEntry, BOOL fIsContextBound = TRUE); + inline void SetResult(Assembly *pAssembly); + inline void SetResult(BindResult *pBindResult); + + inline void SetNoResult(); + inline BOOL HaveResult(); + + inline void Reset(); + + struct AttemptResult + { + HRESULT HResult; + ReleaseHolder<Assembly> Assembly; + bool Attempted = false; + + void Set(const AttemptResult *result); + + void Reset() + { + Assembly = nullptr; + Attempted = false; + } + }; + + // Set attempt result for binding to existing context entry + void SetAttemptResult(HRESULT hr, ContextEntry *pContextEntry); + + // Set attempt result for binding to platform assemblies + void SetAttemptResult(HRESULT hr, Assembly *pAssembly); + + const AttemptResult* GetAttempt(bool foundInContext) const; + + protected: + DWORD m_dwResultFlags; + AssemblyName *m_pAssemblyName; + ReleaseHolder<IUnknown> m_pIUnknownAssembly; + + AttemptResult m_inContextAttempt; + AttemptResult m_applicationAssembliesAttempt; + }; +}; + +#endif diff --git a/src/coreclr/binder/inc/bindresult.inl b/src/coreclr/binder/inc/bindresult.inl new file mode 100644 index 00000000000..183c7807a48 --- /dev/null +++ b/src/coreclr/binder/inc/bindresult.inl @@ -0,0 +1,212 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// BindResult.inl +// + + +// +// Implements the inline methods of BindResult +// +// ============================================================ + +#ifndef __BINDER__BIND_RESULT_INL__ +#define __BINDER__BIND_RESULT_INL__ + +#include "contextentry.hpp" +#include "assembly.hpp" + +namespace BINDER_SPACE +{ +BindResult::BindResult() +{ + m_dwResultFlags = ContextEntry::RESULT_FLAG_NONE; + m_pAssemblyName = NULL; + m_pIUnknownAssembly = NULL; +} + +BindResult::~BindResult() +{ + SAFE_RELEASE(m_pAssemblyName); +} + +AssemblyName *BindResult::GetAssemblyName(BOOL fAddRef /* = FALSE */) +{ + AssemblyName *pAssemblyName = m_pAssemblyName; + + if (fAddRef && (pAssemblyName != NULL)) + { + pAssemblyName->AddRef(); + } + + return pAssemblyName; +} + +IUnknown *BindResult::GetAssembly(BOOL fAddRef /* = FALSE */) +{ + IUnknown *pIUnknownAssembly = m_pIUnknownAssembly; + + if (fAddRef && (pIUnknownAssembly != NULL)) + { + pIUnknownAssembly->AddRef(); + } + + return pIUnknownAssembly; +} + +Assembly *BindResult::GetAsAssembly(BOOL fAddRef /* = FALSE */) +{ + return static_cast<Assembly *>(GetAssembly(fAddRef)); +} + +BOOL BindResult::GetIsInGAC() +{ + return ((m_dwResultFlags & ContextEntry::RESULT_FLAG_IS_IN_GAC) != 0); +} + +void BindResult::SetIsInGAC(BOOL fIsInGAC) +{ + if (fIsInGAC) + { + m_dwResultFlags |= ContextEntry::RESULT_FLAG_IS_IN_GAC; + } + else + { + m_dwResultFlags &= ~ContextEntry::RESULT_FLAG_IS_IN_GAC; + } +} + +BOOL BindResult::GetIsContextBound() +{ + return ((m_dwResultFlags & ContextEntry::RESULT_FLAG_CONTEXT_BOUND) != 0); +} + +void BindResult::SetIsContextBound(BOOL fIsContextBound) +{ + if (fIsContextBound) + { + m_dwResultFlags |= ContextEntry::RESULT_FLAG_CONTEXT_BOUND; + } + else + { + m_dwResultFlags &= ~ContextEntry::RESULT_FLAG_CONTEXT_BOUND; + } +} + +BOOL BindResult::GetIsFirstRequest() +{ + return ((m_dwResultFlags & ContextEntry::RESULT_FLAG_FIRST_REQUEST) != 0); +} + +void BindResult::SetIsFirstRequest(BOOL fIsFirstRequest) +{ + if (fIsFirstRequest) + { + m_dwResultFlags |= ContextEntry::RESULT_FLAG_FIRST_REQUEST; + } + else + { + m_dwResultFlags &= ~ContextEntry::RESULT_FLAG_FIRST_REQUEST; + } +} + +void BindResult::SetResult(ContextEntry *pContextEntry, BOOL fIsContextBound /* = TRUE */) +{ + _ASSERTE(pContextEntry != NULL); + + SetIsInGAC(pContextEntry->GetIsInGAC()); + SetIsContextBound(fIsContextBound); + SAFE_RELEASE(m_pAssemblyName); + m_pAssemblyName = pContextEntry->GetAssemblyName(TRUE /* fAddRef */); + m_pIUnknownAssembly = pContextEntry->GetAssembly(TRUE /* fAddRef */); +} + +void BindResult::SetResult(Assembly *pAssembly) +{ + _ASSERTE(pAssembly != NULL); + + SetIsInGAC(pAssembly->GetIsInGAC()); + SAFE_RELEASE(m_pAssemblyName); + m_pAssemblyName = pAssembly->GetAssemblyName(TRUE /* fAddRef */); + pAssembly->AddRef(); + m_pIUnknownAssembly = static_cast<IUnknown *>(pAssembly); +} + +void BindResult::SetResult(BindResult *pBindResult) +{ + _ASSERTE(pBindResult != NULL); + + m_dwResultFlags = pBindResult->m_dwResultFlags; + SAFE_RELEASE(m_pAssemblyName); + m_pAssemblyName = pBindResult->GetAssemblyName(TRUE /* fAddRef */); + m_pIUnknownAssembly = pBindResult->GetAssembly(TRUE /* fAddRef */); + + const AttemptResult *attempt = pBindResult->GetAttempt(true /*foundInContext*/); + if (attempt != nullptr) + m_inContextAttempt.Set(attempt); + + attempt = pBindResult->GetAttempt(false /*foundInContext*/); + if (attempt != nullptr) + m_applicationAssembliesAttempt.Set(attempt); +} + +void BindResult::SetNoResult() +{ + m_pAssemblyName = NULL; +} + +BOOL BindResult::HaveResult() +{ + return (GetAssemblyName() != NULL); +} + +void BindResult::Reset() +{ + SAFE_RELEASE(m_pAssemblyName); + m_pIUnknownAssembly = NULL; + m_dwResultFlags = ContextEntry::RESULT_FLAG_NONE; + m_inContextAttempt.Reset(); + m_applicationAssembliesAttempt.Reset(); +} + +void BindResult::SetAttemptResult(HRESULT hr, ContextEntry *pContextEntry) +{ + Assembly *assembly = nullptr; + if (pContextEntry != nullptr) + assembly = static_cast<Assembly *>(pContextEntry->GetAssembly(TRUE /* fAddRef */)); + + m_inContextAttempt.Assembly = assembly; + m_inContextAttempt.HResult = hr; + m_inContextAttempt.Attempted = true; +} + +void BindResult::SetAttemptResult(HRESULT hr, Assembly *pAssembly) +{ + if (pAssembly != nullptr) + pAssembly->AddRef(); + + m_applicationAssembliesAttempt.Assembly = pAssembly; + m_applicationAssembliesAttempt.HResult = hr; + m_applicationAssembliesAttempt.Attempted = true; +} + +const BindResult::AttemptResult* BindResult::GetAttempt(bool foundInContext) const +{ + const BindResult::AttemptResult &result = foundInContext ? m_inContextAttempt : m_applicationAssembliesAttempt; + return result.Attempted ? &result : nullptr; +} + +void BindResult::AttemptResult::Set(const BindResult::AttemptResult *result) +{ + BINDER_SPACE::Assembly *assembly = result->Assembly; + if (assembly != nullptr) + assembly->AddRef(); + + Assembly = assembly; + HResult = result->HResult; + Attempted = result->Attempted; +} + +} +#endif diff --git a/src/coreclr/binder/inc/clrprivbinderassemblyloadcontext.h b/src/coreclr/binder/inc/clrprivbinderassemblyloadcontext.h new file mode 100644 index 00000000000..3c1c4062302 --- /dev/null +++ b/src/coreclr/binder/inc/clrprivbinderassemblyloadcontext.h @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +#ifndef __CLRPRIVBINDERASSEMBLYLOADCONTEXT_H__ +#define __CLRPRIVBINDERASSEMBLYLOADCONTEXT_H__ + +#include "coreclrbindercommon.h" +#include "applicationcontext.hpp" +#include "clrprivbindercoreclr.h" + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + +namespace BINDER_SPACE +{ + class AssemblyIdentityUTF8; +}; + +class AppDomain; + +class Object; +class Assembly; +class LoaderAllocator; + +class CLRPrivBinderAssemblyLoadContext : public AssemblyLoadContext +{ +public: + + //========================================================================= + // ICLRPrivBinder functions + //------------------------------------------------------------------------- + STDMETHOD(BindAssemblyByName)( + /* [in] */ IAssemblyName *pIAssemblyName, + /* [retval][out] */ ICLRPrivAssembly **ppAssembly); + + STDMETHOD(GetLoaderAllocator)( + /* [retval][out] */ LPVOID *pLoaderAllocator); + +public: + //========================================================================= + // Class functions + //------------------------------------------------------------------------- + + static HRESULT SetupContext(DWORD dwAppDomainId, + CLRPrivBinderCoreCLR *pTPABinder, + LoaderAllocator* pLoaderAllocator, + void* loaderAllocatorHandle, + UINT_PTR ptrAssemblyLoadContext, + CLRPrivBinderAssemblyLoadContext **ppBindContext); + + void PrepareForLoadContextRelease(INT_PTR ptrManagedStrongAssemblyLoadContext); + void ReleaseLoadContext(); + + CLRPrivBinderAssemblyLoadContext(); + + inline BINDER_SPACE::ApplicationContext *GetAppContext() + { + return &m_appContext; + } + + HRESULT BindUsingPEImage( /* in */ PEImage *pPEImage, + /* in */ BOOL fIsNativeImage, + /* [retval][out] */ ICLRPrivAssembly **ppAssembly); + + //========================================================================= + // Internal implementation details + //------------------------------------------------------------------------- +private: + HRESULT BindAssemblyByNameWorker(BINDER_SPACE::AssemblyName *pAssemblyName, BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly); + + BINDER_SPACE::ApplicationContext m_appContext; + + CLRPrivBinderCoreCLR *m_pTPABinder; + + // A strong GC handle to the managed AssemblyLoadContext. This handle is set when the unload of the AssemblyLoadContext is initiated + // to keep the managed AssemblyLoadContext alive until the unload is finished. + // We still keep the weak handle pointing to the same managed AssemblyLoadContext so that native code can use the handle above + // to refer to it during the whole lifetime of the AssemblyLoadContext. + INT_PTR m_ptrManagedStrongAssemblyLoadContext; + + LoaderAllocator* m_pAssemblyLoaderAllocator; + void* m_loaderAllocatorHandle; +}; + +#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) +#endif // __CLRPRIVBINDERASSEMBLYLOADCONTEXT_H__ diff --git a/src/coreclr/binder/inc/clrprivbindercoreclr.h b/src/coreclr/binder/inc/clrprivbindercoreclr.h new file mode 100644 index 00000000000..bf11bcf113f --- /dev/null +++ b/src/coreclr/binder/inc/clrprivbindercoreclr.h @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +#ifndef __CLR_PRIV_BINDER_CORECLR_H__ +#define __CLR_PRIV_BINDER_CORECLR_H__ + +#include "coreclrbindercommon.h" +#include "applicationcontext.hpp" +#include "assemblyloadcontext.h" + +namespace BINDER_SPACE +{ + class AssemblyIdentityUTF8; +}; + +class CLRPrivBinderCoreCLR : public AssemblyLoadContext +{ +public: + + //========================================================================= + // ICLRPrivBinder functions + //------------------------------------------------------------------------- + STDMETHOD(BindAssemblyByName)( + /* [in] */ IAssemblyName *pIAssemblyName, + /* [retval][out] */ ICLRPrivAssembly **ppAssembly); + + STDMETHOD(GetLoaderAllocator)( + /* [retval][out] */ LPVOID *pLoaderAllocator); + +public: + + HRESULT SetupBindingPaths(SString &sTrustedPlatformAssemblies, + SString &sPlatformResourceRoots, + SString &sAppPaths, + SString &sAppNiPaths); + + inline BINDER_SPACE::ApplicationContext *GetAppContext() + { + return &m_appContext; + } + + HRESULT Bind(SString &assemblyDisplayName, + LPCWSTR wszCodeBase, + PEAssembly *pParentAssembly, + BOOL fNgenExplicitBind, + BOOL fExplicitBindToNativeImage, + ICLRPrivAssembly **ppAssembly); + +#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + HRESULT BindUsingPEImage( /* in */ PEImage *pPEImage, + /* in */ BOOL fIsNativeImage, + /* [retval][out] */ ICLRPrivAssembly **ppAssembly); +#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) + + HRESULT BindAssemblyByNameWorker( + BINDER_SPACE::AssemblyName *pAssemblyName, + BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly, + bool excludeAppPaths); + + //========================================================================= + // Internal implementation details + //------------------------------------------------------------------------- +private: + BINDER_SPACE::ApplicationContext m_appContext; +}; + +#endif // __CLR_PRIV_BINDER_CORECLR_H__ diff --git a/src/coreclr/binder/inc/contextentry.hpp b/src/coreclr/binder/inc/contextentry.hpp new file mode 100644 index 00000000000..ff3c8dba83c --- /dev/null +++ b/src/coreclr/binder/inc/contextentry.hpp @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// ContextEntry.hpp +// + + +// +// Defines the ContextEntry class +// +// ============================================================ + +#ifndef __BINDER__CONTEXT_ENTRY_HPP__ +#define __BINDER__CONTEXT_ENTRY_HPP__ + +#include "assemblyentry.hpp" +#include "assembly.hpp" + +namespace BINDER_SPACE +{ + class ContextEntry : public AssemblyEntry + { + public: + typedef enum + { + RESULT_FLAG_NONE = 0x00, + //RESULT_FLAG_IS_DYNAMIC_BIND = 0x01, + RESULT_FLAG_IS_IN_GAC = 0x02, + //RESULT_FLAG_FROM_MANIFEST = 0x04, + RESULT_FLAG_CONTEXT_BOUND = 0x08, + RESULT_FLAG_FIRST_REQUEST = 0x10, + } ResultFlags; + + ContextEntry() : AssemblyEntry() + { + m_dwResultFlags = RESULT_FLAG_NONE; + m_pIUnknownAssembly = NULL; + } + + ~ContextEntry() + { + SAFE_RELEASE(m_pIUnknownAssembly); + } + + BOOL GetIsInGAC() + { + return ((m_dwResultFlags & RESULT_FLAG_IS_IN_GAC) != 0); + } + + void SetIsInGAC(BOOL fIsInGAC) + { + if (fIsInGAC) + { + m_dwResultFlags |= RESULT_FLAG_IS_IN_GAC; + } + else + { + m_dwResultFlags &= ~RESULT_FLAG_IS_IN_GAC; + } + } + + BOOL GetIsFirstRequest() + { + return ((m_dwResultFlags & RESULT_FLAG_FIRST_REQUEST) != 0); + } + + void SetIsFirstRequest(BOOL fIsFirstRequest) + { + if (fIsFirstRequest) + { + m_dwResultFlags |= RESULT_FLAG_FIRST_REQUEST; + } + else + { + m_dwResultFlags &= ~RESULT_FLAG_FIRST_REQUEST; + } + } + + IUnknown *GetAssembly(BOOL fAddRef = FALSE) + { + IUnknown *pIUnknownAssembly = m_pIUnknownAssembly; + + if (fAddRef && (pIUnknownAssembly != NULL)) + { + pIUnknownAssembly->AddRef(); + } + + return pIUnknownAssembly; + } + + void SetAssembly(IUnknown *pIUnknownAssembly) + { + SAFE_RELEASE(m_pIUnknownAssembly); + + if (pIUnknownAssembly != NULL) + { + pIUnknownAssembly->AddRef(); + } + + m_pIUnknownAssembly = pIUnknownAssembly; + } + + protected: + DWORD m_dwResultFlags; + IUnknown *m_pIUnknownAssembly; + }; +}; + +#endif diff --git a/src/coreclr/binder/inc/coreclrbindercommon.h b/src/coreclr/binder/inc/coreclrbindercommon.h new file mode 100644 index 00000000000..4fe3249363b --- /dev/null +++ b/src/coreclr/binder/inc/coreclrbindercommon.h @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +#ifndef __CORECLR_BINDER_COMMON_H__ +#define __CORECLR_BINDER_COMMON_H__ + +#include "clrprivbinding.h" +#include "internalunknownimpl.h" +#include "applicationcontext.hpp" + + +namespace BINDER_SPACE +{ + class AssemblyIdentityUTF8; +}; + +class CLRPrivBinderCoreCLR; + +// General purpose AssemblyBinder helper class +class CCoreCLRBinderHelper +{ +public: + static HRESULT Init(); + + static HRESULT DefaultBinderSetupContext(DWORD dwAppDomainId, + CLRPrivBinderCoreCLR **ppTPABinder); + + // ABHI-TODO: The call indicates that this can come from a case where + // pDomain->GetFusionContext() is null, hence this is static function + // which handles a null binder. See if this actually happens + static HRESULT GetAssemblyIdentity(LPCSTR szTextualIdentity, + BINDER_SPACE::ApplicationContext *pApplicationContext, + NewHolder<BINDER_SPACE::AssemblyIdentityUTF8> &assemblyIdentityHolder); + + //============================================================================= + // Class functions that provides binding services beyond the ICLRPrivInterface + //----------------------------------------------------------------------------- + static HRESULT BindToSystem(ICLRPrivAssembly **ppSystemAssembly, bool fBindToNativeImage); + + static HRESULT BindToSystemSatellite(SString &systemPath, + SString &sSimpleName, + SString &sCultureName, + ICLRPrivAssembly **ppSystemAssembly); +}; + +#endif // __CORECLR_BINDER_COMMON_H__ diff --git a/src/coreclr/binder/inc/failurecache.hpp b/src/coreclr/binder/inc/failurecache.hpp new file mode 100644 index 00000000000..bce67b33c50 --- /dev/null +++ b/src/coreclr/binder/inc/failurecache.hpp @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// FailureCache.hpp +// + + +// +// Defines the FailureCache class +// +// ============================================================ + + +#ifndef __BINDER__FAILURE_CACHE_HPP__ +#define __BINDER__FAILURE_CACHE_HPP__ + +#include "failurecachehashtraits.hpp" + +namespace BINDER_SPACE +{ + class FailureCache : protected SHash<FailureCacheHashTraits> + { + private: + typedef SHash<FailureCacheHashTraits> Hash; + public: + FailureCache(); + ~FailureCache(); + + HRESULT Add(/* in */ SString &assemblyNameorPath, + /* in */ HRESULT hrBindResult); + HRESULT Lookup(/* in */ SString &assemblyNameorPath); + void Remove(/* in */ SString &assemblyName); + }; +}; + +#endif diff --git a/src/coreclr/binder/inc/failurecachehashtraits.hpp b/src/coreclr/binder/inc/failurecachehashtraits.hpp new file mode 100644 index 00000000000..0d9ad26156d --- /dev/null +++ b/src/coreclr/binder/inc/failurecachehashtraits.hpp @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// FailureCache.hpp +// + + +// +// Defines the FailureCache class +// +// ============================================================ + +#ifndef __BINDER__FAILURE_CACHE_HASH_TRAITS_HPP__ +#define __BINDER__FAILURE_CACHE_HASH_TRAITS_HPP__ + +#include "bindertypes.hpp" +#include "utils.hpp" +#include "sstring.h" +#include "shash.h" + +namespace BINDER_SPACE +{ + class FailureCacheEntry + { + public: + inline FailureCacheEntry() + { + m_hrBindingResult = S_OK; + } + inline ~FailureCacheEntry() + { + // Nothing to do here + } + + // Getters/Setters + inline SString &GetAssemblyNameOrPath() + { + return m_assemblyNameOrPath; + } + inline HRESULT GetBindingResult() + { + return m_hrBindingResult; + } + inline void SetBindingResult(HRESULT hrBindingResult) + { + m_hrBindingResult = hrBindingResult; + } + + protected: + SString m_assemblyNameOrPath; + HRESULT m_hrBindingResult; + }; + + class FailureCacheHashTraits : public DefaultSHashTraits<FailureCacheEntry *> + { + public: + typedef SString& key_t; + + // GetKey, Equals, and Hash can throw due to SString + static const bool s_NoThrow = false; + + static key_t GetKey(element_t pFailureCacheEntry) + { + return pFailureCacheEntry->GetAssemblyNameOrPath(); + } + static BOOL Equals(key_t pAssemblyNameOrPath1, key_t pAssemblyNameOrPath2) + { + return EqualsCaseInsensitive(pAssemblyNameOrPath1, pAssemblyNameOrPath2); + } + static count_t Hash(key_t pAssemblyNameOrPath) + { + return HashCaseInsensitive(pAssemblyNameOrPath); + } + static element_t Null() + { + return NULL; + } + static bool IsNull(const element_t &propertyEntry) + { + return (propertyEntry == NULL); + } + + }; +}; + +#endif diff --git a/src/coreclr/binder/inc/fusionassemblyname.hpp b/src/coreclr/binder/inc/fusionassemblyname.hpp new file mode 100644 index 00000000000..d5676c7c1c6 --- /dev/null +++ b/src/coreclr/binder/inc/fusionassemblyname.hpp @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// FusionAssemblyName.hpp +// +// Defines the CAssemblyName class +// +// ============================================================ + +#ifndef __FUSION_ASSEMBLY_NAME_HPP__ +#define __FUSION_ASSEMBLY_NAME_HPP__ + +#include "fusionhelpers.hpp" + +struct FusionProperty +{ + union { + LPVOID pv; + WCHAR* asStr; // For debugging. + }; + DWORD cb; +}; + +class CPropertyArray +{ + friend class CAssemblyName; +private: + DWORD _dwSig; + FusionProperty _rProp[ASM_NAME_MAX_PARAMS]; + +public: + CPropertyArray(); + ~CPropertyArray(); + + inline HRESULT Set(DWORD PropertyId, LPCVOID pvProperty, DWORD cbProperty); + inline HRESULT Get(DWORD PropertyId, LPVOID pvProperty, LPDWORD pcbProperty); + inline FusionProperty operator [] (DWORD dwPropId); +}; + +class CAssemblyName final : public IAssemblyName +{ +private: + DWORD _dwSig; + Volatile<LONG> _cRef; + CPropertyArray _rProp; + BOOL _fPublicKeyToken; + BOOL _fCustom; + +public: + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID riid,void ** ppv); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + // IAssemblyName methods + STDMETHOD(SetProperty)( + /* in */ DWORD PropertyId, + /* in */ LPCVOID pvProperty, + /* in */ DWORD cbProperty); + + STDMETHOD(GetProperty)( + /* in */ DWORD PropertyId, + /* out */ LPVOID pvProperty, + /* in out */ LPDWORD pcbProperty); + + HRESULT SetPropertyInternal(/* in */ DWORD PropertyId, + /* in */ LPCVOID pvProperty, + /* in */ DWORD cbProperty); + + CAssemblyName(); + + HRESULT Parse(LPCWSTR szDisplayName); +}; + +STDAPI +CreateAssemblyNameObject( + LPASSEMBLYNAME *ppAssemblyName, + LPCOLESTR szAssemblyName); + +namespace fusion +{ + namespace util + { + // Fills the provided buffer with the contents of the property. pcbBuf is + // set to be either the required buffer space when insufficient buffer is + // provided, or the number of bytes written. + // + // Returns S_FALSE if the property has not been set, regardless of the values of pBuf and pcbBuf. + HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, PVOID pBuf, DWORD *pcbBuf); + + // Fills the provided buffer with the contents of the property. If no buffer is provided + // (*ppBuf == nullptr), then a buffer is allocated for the caller and ppBuf is set to point + // at the allocated buffer on return. pcbBuf is set to be either the required buffer space + // when insufficient buffer is provided, or the number of bytes written. + // + // Returns S_FALSE if the property has not been set, regardless of the values of pBuf and pcbBuf. + HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, PBYTE * ppBuf, DWORD *pcbBuf); + + // Fills the provided SString with the contents of the property. + // + // Returns S_FALSE if the property has not been set. + HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, SString & ssVal); + + inline HRESULT GetSimpleName(IAssemblyName * pName, SString & ssName) + { return GetProperty(pName, ASM_NAME_NAME, ssName); } + } // namespace fusion.util +} // namespace fusion + + +#endif diff --git a/src/coreclr/binder/inc/fusionhelpers.hpp b/src/coreclr/binder/inc/fusionhelpers.hpp new file mode 100644 index 00000000000..e3e31af8406 --- /dev/null +++ b/src/coreclr/binder/inc/fusionhelpers.hpp @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// FusionHelpers.hpp +// +// Defines various legacy fusion types +// +// ============================================================ + +#ifndef __FUSION_HELPERS_HPP__ +#define __FUSION_HELPERS_HPP__ + +#include "clrtypes.h" +#include "sstring.h" + +#include "clrhost.h" +#include "shlwapi.h" +#include "winwrap.h" +#include "ex.h" +#include "fusion.h" + + +#include "peinformation.h" + +#define FUSION_NEW_SINGLETON(_type) new (nothrow) _type +#define FUSION_NEW_ARRAY(_type, _n) new (nothrow) _type[_n] +#define FUSION_DELETE_ARRAY(_ptr) if((_ptr)) delete [] (_ptr) +#define FUSION_DELETE_SINGLETON(_ptr) if((_ptr)) delete (_ptr) + +#define SAFEDELETE(p) if ((p) != NULL) { FUSION_DELETE_SINGLETON((p)); (p) = NULL; }; +#define SAFEDELETEARRAY(p) if ((p) != NULL) { FUSION_DELETE_ARRAY((p)); (p) = NULL; }; +#define SAFERELEASE(p) if ((p) != NULL) { (p)->Release(); (p) = NULL; }; + +#ifndef NEW +#define NEW(_type) FUSION_NEW_SINGLETON(_type) +#endif // !NEW + +#ifndef ARRAYSIZE +#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) +#endif // !ARRAYSIZE + +#define MAX_VERSION_DISPLAY_SIZE sizeof("65535.65535.65535.65535") + +#define ASM_DISPLAYF_DEFAULT (ASM_DISPLAYF_VERSION \ + |ASM_DISPLAYF_CULTURE \ + |ASM_DISPLAYF_PUBLIC_KEY_TOKEN \ + |ASM_DISPLAYF_RETARGET) + +#define SIGNATURE_BLOB_LENGTH 0x80 +#define SIGNATURE_BLOB_LENGTH_HASH 0x14 +#define MVID_LENGTH sizeof(GUID) + +#define PUBLIC_KEY_TOKEN_LEN 8 + +#define MAX_URL_LENGTH 2084 // same as INTERNET_MAX_URL_LENGTH + +// bit mask macro helpers +#define MAX_ID_FROM_MASK(size) ((size) << 3) +#define MASK_SIZE_FROM_ID(id) ((id) >> 3) +#define IS_IN_RANGE(id, size) ((id) <= ((size) << 3)) +#define IS_BIT_SET(id, mask) (mask[((id)-1)>>3] & (0x1 << (((id)-1)&0x7))) +#define SET_BIT(id, mask) (mask[((id)-1)>>3] |= (0x1<< (((id)-1)&0x7))) +#define UNSET_BIT(id, mask) (mask[((id)-1)>>3] &= (0xFF - (0x1<<(((id)-1)&0x7)))) + +inline +int FusionCompareStringI(LPCWSTR pwz1, LPCWSTR pwz2) +{ + return SString::_wcsicmp(pwz1, pwz2); +} + +#endif diff --git a/src/coreclr/binder/inc/loadcontext.hpp b/src/coreclr/binder/inc/loadcontext.hpp new file mode 100644 index 00000000000..e840a405df0 --- /dev/null +++ b/src/coreclr/binder/inc/loadcontext.hpp @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// LoadContext.hpp +// + + +// +// Defines the LoadContext template class +// +// ============================================================ + +#ifndef __BINDER__LOAD_CONTEXT_HPP__ +#define __BINDER__LOAD_CONTEXT_HPP__ + +#include "assemblyhashtraits.hpp" +#include "contextentry.hpp" +#include "bindresult.hpp" + +namespace BINDER_SPACE +{ + template <DWORD dwIncludeFlags> + class LoadContext : protected SHash<AssemblyHashTraits<ContextEntry *, dwIncludeFlags> > + { + private: + typedef SHash<AssemblyHashTraits<ContextEntry *, dwIncludeFlags> > Hash; + public: + LoadContext(); + ~LoadContext(); + + ULONG AddRef(); + ULONG Release(); + ContextEntry *Lookup(/* in */ AssemblyName *pAssemblyName); + HRESULT Register(BindResult *pBindResult); + + protected: + LONG m_cRef; + }; + +#include "loadcontext.inl" + + class ExecutionContext : public LoadContext<AssemblyName::INCLUDE_DEFAULT> {}; +}; + +#endif diff --git a/src/coreclr/binder/inc/loadcontext.inl b/src/coreclr/binder/inc/loadcontext.inl new file mode 100644 index 00000000000..fb463523ebb --- /dev/null +++ b/src/coreclr/binder/inc/loadcontext.inl @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// LoadContext.inl +// + + +// +// Implements the inlined methods of LoadContext template class +// +// ============================================================ + +#ifndef __BINDER__LOAD_CONTEXT_INL__ +#define __BINDER__LOAD_CONTEXT_INL__ + +template <DWORD dwIncludeFlags> +LoadContext<dwIncludeFlags>::LoadContext() : + SHash<AssemblyHashTraits<ContextEntry *, dwIncludeFlags> >::SHash() +{ + m_cRef = 1; +} + +template <DWORD dwIncludeFlags> +LoadContext<dwIncludeFlags>::~LoadContext() +{ + // Delete context entries and contents array + for (typename Hash::Iterator i = Hash::Begin(), end = Hash::End(); i != end; i++) + { + const ContextEntry *pContextEntry = *i; + delete pContextEntry; + } + this->RemoveAll(); +} + +template <DWORD dwIncludeFlags> +ULONG LoadContext<dwIncludeFlags>::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +template <DWORD dwIncludeFlags> +ULONG LoadContext<dwIncludeFlags>::Release() +{ + ULONG ulRef = InterlockedDecrement(&m_cRef); + + if (ulRef == 0) + { + delete this; + } + + return ulRef; +} + +template <DWORD dwIncludeFlags> +ContextEntry *LoadContext<dwIncludeFlags>::Lookup(AssemblyName *pAssemblyName) +{ + ContextEntry *pContextEntry = + SHash<AssemblyHashTraits<ContextEntry *, dwIncludeFlags> >::Lookup(pAssemblyName); + + return pContextEntry; +} + +template <DWORD dwIncludeFlags> +HRESULT LoadContext<dwIncludeFlags>::Register(BindResult *pBindResult) +{ + HRESULT hr = S_OK; + ContextEntry *pContextEntry = NULL; + + SAFE_NEW(pContextEntry, ContextEntry); + + pContextEntry->SetIsInGAC(pBindResult->GetIsInGAC()); + pContextEntry->SetAssemblyName(pBindResult->GetAssemblyName(), TRUE /* fAddRef */); + pContextEntry->SetAssembly(pBindResult->GetAssembly()); + + if (pBindResult->GetIsFirstRequest()) + { + pContextEntry->SetIsFirstRequest(TRUE); + } + + SHash<AssemblyHashTraits<ContextEntry *, dwIncludeFlags> >::Add(pContextEntry); + + Exit: + return hr; +} + +#endif diff --git a/src/coreclr/binder/inc/stringlexer.hpp b/src/coreclr/binder/inc/stringlexer.hpp new file mode 100644 index 00000000000..41af7e7baa1 --- /dev/null +++ b/src/coreclr/binder/inc/stringlexer.hpp @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// StringLexer.hpp +// + + +// +// Defines the StringLexer class +// +// ============================================================ + +#ifndef __BINDER__STRING_LEXER_HPP__ +#define __BINDER__STRING_LEXER_HPP__ + +#include "bindertypes.hpp" + +#define GO_IF_NOT_EXPECTED(expr, kRequiredLexemeType) \ + if ((expr) != kRequiredLexemeType) \ + { \ + fIsValid = FALSE; \ + goto Exit; \ + } + +#define GO_IF_END_OR_NOT_EXPECTED(expr, kRequiredLexemeType) \ + { \ + LEXEME_TYPE kGotLexemeType = (expr); \ + if (kGotLexemeType == LEXEME_TYPE_END_OF_STREAM) \ + { \ + goto Exit; \ + } \ + else \ + { \ + GO_IF_NOT_EXPECTED(kGotLexemeType, kRequiredLexemeType); \ + } \ + } + +namespace BINDER_SPACE +{ + class StringLexer + { + public: + typedef enum + { + LEXEME_TYPE_INVALID, + LEXEME_TYPE_EQUALS, + LEXEME_TYPE_COMMA, + LEXEME_TYPE_COLON, + LEXEME_TYPE_SEMICOLON, + LEXEME_TYPE_STRING, + LEXEME_TYPE_END_OF_STREAM + } LEXEME_TYPE; + + inline StringLexer(); + inline ~StringLexer(); + + inline void Init(SString &inputString, BOOL fSupportEscaping); + + static inline BOOL IsWhitespace(WCHAR wcChar); + static inline BOOL IsEOS(WCHAR wcChar); + static inline BOOL IsQuoteCharacter(WCHAR wcChar); + + virtual BOOL IsSeparatorChar(WCHAR wcChar) = NULL; + virtual LEXEME_TYPE GetLexemeType(WCHAR wcChar) = NULL; + + protected: + static const WCHAR INVALID_CHARACTER = -1; + + LEXEME_TYPE GetNextLexeme(SString ¤tString, BOOL fPermitUnescapedQuotes = FALSE); + + inline WCHAR PopCharacter(BOOL *pfIsEscaped); + inline void PushCharacter(WCHAR wcCurrentChar, + BOOL fIsEscaped); + + inline WCHAR GetRawCharacter(); + inline void PushRawCharacter(); + inline WCHAR DecodeUTF16Character(); + inline WCHAR GetNextCharacter(BOOL *pfIsEscaped); + + inline WCHAR ParseUnicode(); + LEXEME_TYPE ParseString(SString ¤tString, + BOOL fPermitUnescapeQuotes); + + void TrimTrailingWhiteSpaces(SString ¤tString); + + SString::Iterator m_cursor; + SString::Iterator m_end; + + WCHAR m_wcCurrentChar; + BOOL m_fCurrentCharIsEscaped; + BOOL m_fSupportEscaping; + BOOL m_fReadRawCharacter; + }; + +#include "stringlexer.inl" +}; + +#endif diff --git a/src/coreclr/binder/inc/stringlexer.inl b/src/coreclr/binder/inc/stringlexer.inl new file mode 100644 index 00000000000..bfe4bddeaa4 --- /dev/null +++ b/src/coreclr/binder/inc/stringlexer.inl @@ -0,0 +1,242 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// StringLexer.inl +// + + +// +// Implements the inlined methods of StringLexer class +// +// ============================================================ + +#ifndef __BINDER__STRING_LEXER_INL__ +#define __BINDER__STRING_LEXER_INL__ + +StringLexer::StringLexer() +{ + m_wcCurrentChar = INVALID_CHARACTER; + m_fCurrentCharIsEscaped = FALSE; +} + +StringLexer::~StringLexer() +{ + // Nothing to do here +} + +void StringLexer::Init(SString &inputString, BOOL fSupportEscaping) +{ + m_cursor = inputString.Begin(); + m_end = inputString.End(); + m_fSupportEscaping = fSupportEscaping; + m_fReadRawCharacter = FALSE; +} + +BOOL StringLexer::IsWhitespace(WCHAR wcChar) +{ + return ((wcChar == L'\n') || (wcChar == L'\r') || (wcChar == L' ') || (wcChar == L'\t')); +} + +BOOL StringLexer::IsEOS(WCHAR wcChar) +{ + return (wcChar == 0); +} + +BOOL StringLexer::IsQuoteCharacter(WCHAR wcChar) +{ + return ((wcChar == L'\'') || (wcChar == L'"')); +} + +WCHAR StringLexer::PopCharacter(BOOL *pfIsEscaped) +{ + WCHAR wcCurrentChar = m_wcCurrentChar; + if (wcCurrentChar != INVALID_CHARACTER) + { + m_wcCurrentChar = INVALID_CHARACTER; + *pfIsEscaped = m_fCurrentCharIsEscaped; + } + else + { + wcCurrentChar = GetNextCharacter(pfIsEscaped); + } + + return wcCurrentChar; +} + +void StringLexer::PushCharacter(WCHAR wcCurrentChar, + BOOL fIsEscaped) +{ + _ASSERTE(m_wcCurrentChar == INVALID_CHARACTER); + + m_wcCurrentChar = wcCurrentChar; + m_fCurrentCharIsEscaped = fIsEscaped; +} + +WCHAR StringLexer::GetRawCharacter() +{ + WCHAR wcCurrentChar = 0; + + if (m_cursor <= m_end) + { + wcCurrentChar = m_cursor[0]; + m_fReadRawCharacter = TRUE; + m_cursor++; + } + else + { + m_fReadRawCharacter = FALSE; + } + + return wcCurrentChar; +} + +void StringLexer::PushRawCharacter() +{ + if (m_fReadRawCharacter) + { + m_cursor--; + m_fReadRawCharacter = FALSE; + } +} + +WCHAR StringLexer::DecodeUTF16Character() +{ + // See http://www.ietf.org/rfc/rfc2781.txt for details on UTF-16 encoding. + + WCHAR wcCurrentChar = 0; + SCOUNT_T nCharacters = m_end - m_cursor + 1; + WCHAR wcChar1 = GetRawCharacter(); + + if (wcChar1 < 0xd800) + { + wcCurrentChar = wcChar1; + } + else + { + // StringLexer is not designed to handle UTF-16 characters beyond the Basic Multilingual Plane, + // since it stores all characters in 16-bit WCHARs. + // However, since the vast majority of the time, we (Microsoft) produce the manifests, + // this is likely a non-scenario, as the other Unicode planes would never be used in practice. + + if (wcChar1 <= 0xdbff) // 0xd800 - 0xdbff indicates the first WCHAR of a surrogate pair + { + if (nCharacters >= 2) + { + GetRawCharacter(); // Skip the second WCHAR of the surrogate pair + } + } + // Otherwise, the character is either in the 0xdc00 - 0xdfff range, indicating the second WCHAR of a surrogate pair, + // or in the 0xE000 - 0xFFFF range, which has within it ranges of invalid characters, and which we conservatively treat + // as invalid. + + wcCurrentChar = INVALID_CHARACTER; + } + + return wcCurrentChar; +} + + +WCHAR StringLexer::GetNextCharacter(BOOL *pfIsEscaped) +{ + *pfIsEscaped = FALSE; + + WCHAR wcCurrentChar = GetRawCharacter(); // DecodeUTF16Character() + if (wcCurrentChar == L'\\') + { + WCHAR wcTempChar = GetRawCharacter(); // DecodeUTF16Character() + + if (m_fSupportEscaping) + { + // Handle standard escapes + switch (wcTempChar) + { + case L'"': + case L'\'': + case L',': + case L'\\': + case L'/': + case L'=': + break; + case L't': + wcTempChar = 9; + break; + case L'n': + wcTempChar = 10; + break; + case L'r': + wcTempChar = 13; + break; + case L'u': + wcTempChar = ParseUnicode(); + break; + default: + return INVALID_CHARACTER; + } + + *pfIsEscaped = TRUE; + wcCurrentChar = wcTempChar; + } + else + { + // Do not handle escapes except for quotes + switch (wcTempChar) + { + case L'"': + case L'\'': + *pfIsEscaped = TRUE; + wcCurrentChar = wcTempChar; + break; + default: + PushRawCharacter(); + break; + } + } + } + + return wcCurrentChar; +} + +WCHAR StringLexer::ParseUnicode() +{ + int nCharacters = 0; + WCHAR wcUnicodeChar = 0; + + for(;;) + { + WCHAR wcCurrentChar = DecodeUTF16Character(); + nCharacters++; + + if (wcCurrentChar == L';') + { + break; + } + else if ((wcCurrentChar == INVALID_CHARACTER) || (nCharacters >= 9)) + { + return INVALID_CHARACTER; + } + + wcUnicodeChar <<= 4; + + if ((wcCurrentChar >= L'0') && (wcCurrentChar <= L'9')) + { + wcUnicodeChar += (wcCurrentChar - L'0'); + } + else if ((wcCurrentChar >= L'a') && (wcCurrentChar <= L'f')) + { + wcUnicodeChar += (wcCurrentChar - L'a') + 10; + } + else if ((wcCurrentChar >= L'A') && (wcCurrentChar <= L'F')) + { + wcUnicodeChar += (wcCurrentChar - L'A') + 10; + } + else + { + return INVALID_CHARACTER; + } + } + + return wcUnicodeChar; +} + +#endif diff --git a/src/coreclr/binder/inc/textualidentityparser.hpp b/src/coreclr/binder/inc/textualidentityparser.hpp new file mode 100644 index 00000000000..a5187d254a6 --- /dev/null +++ b/src/coreclr/binder/inc/textualidentityparser.hpp @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// TextualIdentityParser.hpp +// + + +// +// Defines the TextualIdentityParser class +// +// ============================================================ + +#ifndef __BINDER__TEXTUAL_IDENTITY_PARSER_HPP__ +#define __BINDER__TEXTUAL_IDENTITY_PARSER_HPP__ + +#include "bindertypes.hpp" +#include "stringlexer.hpp" + +namespace BINDER_SPACE +{ + class AssemblyVersion; + class AssemblyIdentity; + + class TextualIdentityParser : public StringLexer + { + public: + TextualIdentityParser(AssemblyIdentity *pAssemblyIdentity); + ~TextualIdentityParser(); + + virtual BOOL IsSeparatorChar(WCHAR wcChar); + virtual StringLexer::LEXEME_TYPE GetLexemeType(WCHAR wcChar); + + static HRESULT Parse(/* in */ SString &textualIdentity, + /* out */ AssemblyIdentity *pAssemblyIdentity, + /* in */ BOOL fPermitUnescapedQuotes = FALSE); + static HRESULT ToString(/* in */ AssemblyIdentity *pAssemblyIdentity, + /* in */ DWORD dwIdentityFlags, + /* out */ SString &textualIdentity); + + static BOOL ParseVersion(/* in */ SString &versionString, + /* out */ AssemblyVersion *pAssemblyVersion); + + static BOOL HexToBlob(/* in */ SString &publicKeyOrToken, + /* in */ BOOL fValidateHex, + /* in */ BOOL fIsToken, + /* out */ SBuffer &publicKeyOrTokenBLOB); + static void BlobToHex(/* in */ SBuffer &publicKeyOrTokenBLOB, + /* out */ SString &publicKeyOrToken); + + BOOL ParseString(/* in */ SString &textualString, + /* out */ SString &contentString); + + protected: + BOOL Parse(/* in */ SString &textualIdentity, + /* in */ BOOL fPermitUnescapedQuotes = FALSE); + + BOOL PopulateAssemblyIdentity(/* in */ SString &attributeString, + /* in */ SString &valueString); + + static void EscapeString(/* in */ SString &input, + /* out*/ SString &result); + + AssemblyIdentity *m_pAssemblyIdentity; + DWORD m_dwAttributesSeen; + }; +}; + +#endif diff --git a/src/coreclr/binder/inc/utils.hpp b/src/coreclr/binder/inc/utils.hpp new file mode 100644 index 00000000000..1c8ea114f5e --- /dev/null +++ b/src/coreclr/binder/inc/utils.hpp @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// Utils.hpp +// + + +// +// Declares a bunch of binder auxilary functions +// +// ============================================================ + +#ifndef __BINDER_UTILS_HPP__ +#define __BINDER_UTILS_HPP__ + +#include "bindertypes.hpp" + +namespace BINDER_SPACE +{ + inline BOOL EqualsCaseInsensitive(SString &a, SString &b) + { + return a.EqualsCaseInsensitive(b); + } + + inline ULONG HashCaseInsensitive(SString &string) + { + return string.HashCaseInsensitive(); + } + + void MutateUrlToPath(SString &urlOrPath); + + // It is safe to use either A or B as CombinedPath. + void CombinePath(SString &pathA, + SString &pathB, + SString &combinedPath); + + HRESULT GetTokenFromPublicKey(SBuffer &publicKeyBLOB, + SBuffer &publicKeyTokenBLOB); + + BOOL IsFileNotFound(HRESULT hr); + + HRESULT GetNextPath(SString& paths, SString::Iterator& startPos, SString& outPath); + HRESULT GetNextTPAPath(SString& paths, SString::Iterator& startPos, bool dllOnly, SString& outPath, SString& simpleName, bool& isNativeImage); +}; + +#endif diff --git a/src/coreclr/binder/inc/variables.hpp b/src/coreclr/binder/inc/variables.hpp new file mode 100644 index 00000000000..d060a691bbe --- /dev/null +++ b/src/coreclr/binder/inc/variables.hpp @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// Variables.hpp +// + + +// +// Defines the Variables class +// +// ============================================================ + +#ifndef __BINDER__VARIABLES_HPP__ +#define __BINDER__VARIABLES_HPP__ + +#include "bindertypes.hpp" + +namespace BINDER_SPACE +{ + class Variables + { + public: + Variables(); + ~Variables(); + + HRESULT Init(); + + // AssemblyBinder string constants + SString httpURLPrefix; + + // AssemblyName string constants + SString cultureNeutral; + SString corelib; + }; + + extern Variables *g_BinderVariables; +}; + +#endif diff --git a/src/coreclr/binder/stringlexer.cpp b/src/coreclr/binder/stringlexer.cpp new file mode 100644 index 00000000000..b6b722fa77a --- /dev/null +++ b/src/coreclr/binder/stringlexer.cpp @@ -0,0 +1,150 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// StringLexer.cpp +// + + +// +// Implements the StringLexer class +// +// ============================================================ + +#include "stringlexer.hpp" +#include "utils.hpp" + +#include "ex.h" + +namespace BINDER_SPACE +{ + StringLexer::LEXEME_TYPE + StringLexer::GetNextLexeme(SString ¤tString, BOOL fPermitUnescapedQuotes) + { + BOOL fIsEscaped = FALSE; + WCHAR wcCurrentChar = INVALID_CHARACTER; + + // Remove any white spaces + do + { + wcCurrentChar = PopCharacter(&fIsEscaped); + } + while (IsWhitespace(wcCurrentChar)); + + // Determine lexeme type + if (!fIsEscaped) + { + LEXEME_TYPE kLexemeType = GetLexemeType(wcCurrentChar); + if (kLexemeType != LEXEME_TYPE_STRING) + { + return kLexemeType; + } + } + + // First character of string lexeme; push it back + PushCharacter(wcCurrentChar, fIsEscaped); + return ParseString(currentString, fPermitUnescapedQuotes); + } + + StringLexer::LEXEME_TYPE + StringLexer::ParseString(SString ¤tString, BOOL fPermitUnescapedQuotes) + { + BOOL fIsFirstCharacter = TRUE; + WCHAR wcCurrentChar = INVALID_CHARACTER; + WCHAR wcOpeningQuote = INVALID_CHARACTER; + + currentString.Clear(); + + // Read until we find another lexeme that's not a string character + for (;;) + { + BOOL fIsEscaped = FALSE; + wcCurrentChar = PopCharacter(&fIsEscaped); + + if (wcCurrentChar == INVALID_CHARACTER) + { + // Found invalid character encoding + return LEXEME_TYPE_INVALID; + } + + if (IsEOS(wcCurrentChar)) + { + if (IsQuoteCharacter(wcOpeningQuote)) + { + // EOS and unclosed quotes is an error + return LEXEME_TYPE_INVALID; + } + else + { + // Reached end of input and therefore of string + break; + } + } + + if (fIsFirstCharacter) + { + fIsFirstCharacter = FALSE; + + // If first character is quote, then record its quoteness + if (IsQuoteCharacter(wcCurrentChar)) + { + wcOpeningQuote = wcCurrentChar; + continue; + } + } + + if (wcCurrentChar == wcOpeningQuote) + { + // We've found the closing quote for a quoted string + break; + } + + if (!fPermitUnescapedQuotes && !fIsEscaped && IsQuoteCharacter(wcCurrentChar) && !IsQuoteCharacter(wcOpeningQuote)) + { + // Unescaped quotes in the middle of the string are an error + return LEXEME_TYPE_INVALID; + } + + if (IsSeparatorChar(wcCurrentChar) && !IsQuoteCharacter(wcOpeningQuote) && !fIsEscaped) + { + // Unescaped separator char terminates the string + PushCharacter(wcCurrentChar, fIsEscaped); + break; + } + + // Add character to current string + currentString.Append(wcCurrentChar); + } + + if (!IsQuoteCharacter(wcOpeningQuote)) + { + // Remove trailing white spaces from unquoted string + TrimTrailingWhiteSpaces(currentString); + } + + return LEXEME_TYPE_STRING; + } + + void StringLexer::TrimTrailingWhiteSpaces(SString ¤tString) + { + SString::Iterator begin = currentString.Begin(); + SString::Iterator cursor = currentString.End() - 1; + BOOL fFoundWhiteSpace = FALSE; + + for (;;) + { + if ((cursor >= begin) && IsWhitespace(cursor[0])) + { + fFoundWhiteSpace = TRUE; + cursor--; + continue; + } + break; + } + + if (fFoundWhiteSpace) + { + currentString.Truncate(cursor + 1); + } + } +}; diff --git a/src/coreclr/binder/textualidentityparser.cpp b/src/coreclr/binder/textualidentityparser.cpp new file mode 100644 index 00000000000..939ffe9f468 --- /dev/null +++ b/src/coreclr/binder/textualidentityparser.cpp @@ -0,0 +1,766 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// TextualIdentityParser.cpp +// + + +// +// Implements the TextualIdentityParser class +// +// ============================================================ + +#include "textualidentityparser.hpp" +#include "assemblyidentity.hpp" +#include "utils.hpp" + +#include "ex.h" + +#define GO_IF_SEEN(kAssemblyIdentityFlag) \ + if ((m_dwAttributesSeen & kAssemblyIdentityFlag) != 0) \ + { \ + fIsValid = FALSE; \ + goto Exit; \ + } \ + else \ + { \ + m_dwAttributesSeen |= kAssemblyIdentityFlag; \ + } + +#define GO_IF_WILDCARD(valueString) \ + { \ + SmallStackSString wildCard(W("*")); \ + if (valueString.Equals(wildCard)) \ + { \ + goto Exit; \ + } \ + } + +#define GO_IF_VALIDATE_FAILED(validateProc, kIdentityFlag) \ + if (!validateProc(valueString)) \ + { \ + fIsValid = FALSE; \ + goto Exit; \ + } \ + else \ + { \ + m_pAssemblyIdentity->SetHave(kIdentityFlag); \ + } + +#define FROMHEX(a) ((a)>=W('a') ? a - W('a') + 10 : a - W('0')) +#define TOHEX(a) ((a)>=10 ? W('a')+(a)-10 : W('0')+(a)) +#define TOLOWER(a) (((a) >= W('A') && (a) <= W('Z')) ? (W('a') + (a - W('A'))) : (a)) + +namespace BINDER_SPACE +{ + namespace + { + const int iPublicKeyTokenLength = 8; + + const int iVersionMax = 65535; + const int iVersionParts = 4; + + inline void UnicodeHexToBin(LPCWSTR pSrc, UINT cSrc, LPBYTE pDest) + { + BYTE v; + LPBYTE pd = pDest; + LPCWSTR ps = pSrc; + + if (cSrc == 0) + return; + + for (UINT i = 0; i < cSrc-1; i+=2) + { + v = (BYTE)FROMHEX(TOLOWER(ps[i])) << 4; + v |= FROMHEX(TOLOWER(ps[i+1])); + *(pd++) = v; + } + } + + inline void BinToUnicodeHex(const BYTE *pSrc, UINT cSrc, __out_ecount(2*cSrc) LPWSTR pDst) + { + UINT x; + UINT y; + + for (x = 0, y = 0 ; x < cSrc; ++x) + { + UINT v; + + v = pSrc[x]>>4; + pDst[y++] = (WCHAR)TOHEX(v); + v = pSrc[x] & 0x0f; + pDst[y++] = (WCHAR)TOHEX(v); + } + } + + inline BOOL EqualsCaseInsensitive(SString &a, LPCWSTR wzB) + { + SString b(SString::Literal, wzB); + + return ::BINDER_SPACE::EqualsCaseInsensitive(a, b); + } + + BOOL ValidateHex(SString &publicKeyOrToken) + { + if ((publicKeyOrToken.GetCount() == 0) || ((publicKeyOrToken.GetCount() % 2) != 0)) + { + return FALSE; + } + + SString::Iterator cursor = publicKeyOrToken.Begin(); + SString::Iterator end = publicKeyOrToken.End() - 1; + + while (cursor <= end) + { + WCHAR wcCurrentChar = cursor[0]; + + if (((wcCurrentChar >= W('0')) && (wcCurrentChar <= W('9'))) || + ((wcCurrentChar >= W('a')) && (wcCurrentChar <= W('f'))) || + ((wcCurrentChar >= W('A')) && (wcCurrentChar <= W('F')))) + { + cursor++; + continue; + } + + return FALSE; + } + + return TRUE; + } + + inline BOOL ValidatePublicKeyToken(SString &publicKeyToken) + { + return ((publicKeyToken.GetCount() == (iPublicKeyTokenLength * 2)) && + ValidateHex(publicKeyToken)); + } + + inline BOOL ValidatePublicKey(SString &publicKey) + { + return ValidateHex(publicKey); + } + + const struct { + LPCWSTR strValue; + PEKIND enumValue; + } wszKnownArchitectures[] = { { W("x86"), peI386 }, + { W("IA64"), peIA64 }, + { W("AMD64"), peAMD64 }, + { W("ARM"), peARM }, + { W("MSIL"), peMSIL } }; + + BOOL ValidateAndConvertProcessorArchitecture(SString &processorArchitecture, + PEKIND *pkProcessorArchitecture) + { + for (int i = LENGTH_OF(wszKnownArchitectures); i--;) + { + if (EqualsCaseInsensitive(processorArchitecture, wszKnownArchitectures[i].strValue)) + { + *pkProcessorArchitecture = wszKnownArchitectures[i].enumValue; + return TRUE; + } + } + + return FALSE; + } + + LPCWSTR PeKindToString(PEKIND kProcessorArchitecture) + { + _ASSERTE(kProcessorArchitecture != peNone); + + for (int i = LENGTH_OF(wszKnownArchitectures); i--;) + { + if (wszKnownArchitectures[i].enumValue == kProcessorArchitecture) + { + return wszKnownArchitectures[i].strValue; + } + } + + return NULL; + } + + LPCWSTR ContentTypeToString(AssemblyContentType kContentType) + { + _ASSERTE(kContentType != AssemblyContentType_Default); + + if (kContentType == AssemblyContentType_WindowsRuntime) + { + return W("WindowsRuntime"); + } + + return NULL; + } + }; // namespace (anonymous) + + TextualIdentityParser::TextualIdentityParser(AssemblyIdentity *pAssemblyIdentity) + { + m_pAssemblyIdentity = pAssemblyIdentity; + m_dwAttributesSeen = AssemblyIdentity::IDENTITY_FLAG_EMPTY; + } + + TextualIdentityParser::~TextualIdentityParser() + { + // Nothing to do here + } + + BOOL TextualIdentityParser::IsSeparatorChar(WCHAR wcChar) + { + return ((wcChar == W(',')) || (wcChar == W('='))); + } + + StringLexer::LEXEME_TYPE TextualIdentityParser::GetLexemeType(WCHAR wcChar) + { + switch (wcChar) + { + case W('='): + return LEXEME_TYPE_EQUALS; + case W(','): + return LEXEME_TYPE_COMMA; + case 0: + return LEXEME_TYPE_END_OF_STREAM; + default: + return LEXEME_TYPE_STRING; + } + } + + /* static */ + HRESULT TextualIdentityParser::Parse(SString &textualIdentity, + AssemblyIdentity *pAssemblyIdentity, + BOOL fPermitUnescapedQuotes) + { + HRESULT hr = S_OK; + + IF_FALSE_GO(pAssemblyIdentity != NULL); + + EX_TRY + { + TextualIdentityParser identityParser(pAssemblyIdentity); + + if (!identityParser.Parse(textualIdentity, fPermitUnescapedQuotes)) + { + IF_FAIL_GO(FUSION_E_INVALID_NAME); + } + } + EX_CATCH_HRESULT(hr); + + Exit: + return hr; + } + + /* static */ + HRESULT TextualIdentityParser::ToString(AssemblyIdentity *pAssemblyIdentity, + DWORD dwIdentityFlags, + SString &textualIdentity) + { + HRESULT hr = S_OK; + + IF_FALSE_GO(pAssemblyIdentity != NULL); + + EX_TRY + { + SmallStackSString tmpString; + + textualIdentity.Clear(); + + if (pAssemblyIdentity->m_simpleName.IsEmpty()) + { + goto Exit; + } + + EscapeString(pAssemblyIdentity->m_simpleName, tmpString); + textualIdentity.Append(tmpString); + + if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_VERSION)) + { + tmpString.Clear(); + tmpString.Printf(W("%d.%d.%d.%d"), + (DWORD)(USHORT)pAssemblyIdentity->m_version.GetMajor(), + (DWORD)(USHORT)pAssemblyIdentity->m_version.GetMinor(), + (DWORD)(USHORT)pAssemblyIdentity->m_version.GetBuild(), + (DWORD)(USHORT)pAssemblyIdentity->m_version.GetRevision()); + + textualIdentity.Append(W(", Version=")); + textualIdentity.Append(tmpString); + } + + if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_CULTURE)) + { + textualIdentity.Append(W(", Culture=")); + if (pAssemblyIdentity->m_cultureOrLanguage.IsEmpty()) + { + textualIdentity.Append(W("neutral")); + } + else + { + EscapeString(pAssemblyIdentity->m_cultureOrLanguage, tmpString); + textualIdentity.Append(tmpString); + } + } + + if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY)) + { + textualIdentity.Append(W(", PublicKey=")); + tmpString.Clear(); + BlobToHex(pAssemblyIdentity->m_publicKeyOrTokenBLOB, tmpString); + textualIdentity.Append(tmpString); + } + else if (AssemblyIdentity::Have(dwIdentityFlags, + AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN)) + { + textualIdentity.Append(W(", PublicKeyToken=")); + tmpString.Clear(); + BlobToHex(pAssemblyIdentity->m_publicKeyOrTokenBLOB, tmpString); + textualIdentity.Append(tmpString); + } + else if (AssemblyIdentity::Have(dwIdentityFlags, + AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL)) + { + textualIdentity.Append(W(", PublicKeyToken=null")); + } + + if (AssemblyIdentity::Have(dwIdentityFlags, + AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE)) + { + textualIdentity.Append(W(", processorArchitecture=")); + textualIdentity.Append(PeKindToString(pAssemblyIdentity->m_kProcessorArchitecture)); + } + + if (AssemblyIdentity::Have(dwIdentityFlags, + AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE)) + { + textualIdentity.Append(W(", Retargetable=Yes")); + } + + if (AssemblyIdentity::Have(dwIdentityFlags, + AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE)) + { + textualIdentity.Append(W(", ContentType=")); + textualIdentity.Append(ContentTypeToString(pAssemblyIdentity->m_kContentType)); + } + + if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_CUSTOM)) + { + textualIdentity.Append(W(", Custom=")); + tmpString.Clear(); + BlobToHex(pAssemblyIdentity->m_customBLOB, tmpString); + textualIdentity.Append(tmpString); + } + else if (AssemblyIdentity::Have(dwIdentityFlags, + AssemblyIdentity::IDENTITY_FLAG_CUSTOM_NULL)) + { + textualIdentity.Append(W(", Custom=null")); + } + } + EX_CATCH_HRESULT(hr); + + Exit: + return hr; + } + + /* static */ + BOOL TextualIdentityParser::ParseVersion(SString &versionString, + AssemblyVersion *pAssemblyVersion) + { + BOOL fIsValid = FALSE; + DWORD dwFoundNumbers = 0; + bool foundUnspecifiedComponent = false; + const DWORD UnspecifiedNumber = (DWORD)-1; + DWORD dwCurrentNumber = UnspecifiedNumber; + DWORD dwNumbers[iVersionParts] = {UnspecifiedNumber, UnspecifiedNumber, UnspecifiedNumber, UnspecifiedNumber}; + + if (versionString.GetCount() > 0) { + SString::Iterator cursor = versionString.Begin(); + SString::Iterator end = versionString.End(); + + while (cursor <= end) + { + WCHAR wcCurrentChar = cursor[0]; + + if (dwFoundNumbers >= static_cast<DWORD>(iVersionParts)) + { + goto Exit; + } + else if (wcCurrentChar == W('.') || wcCurrentChar == W('\0')) + { + if (dwCurrentNumber == UnspecifiedNumber) + { + // Difference from .NET Framework, compat with Version(string) constructor: A missing version component + // is considered invalid. + // + // Examples: + // "MyAssembly, Version=." + // "MyAssembly, Version=1." + // "MyAssembly, Version=.1" + // "MyAssembly, Version=1..1" + goto Exit; + } + + // Compat with .NET Framework: A value of iVersionMax is considered unspecified. Once an unspecified + // component is found, validate the remaining components but consider them as unspecified as well. + if (dwCurrentNumber == iVersionMax) + { + foundUnspecifiedComponent = true; + dwCurrentNumber = UnspecifiedNumber; + } + else if (!foundUnspecifiedComponent) + { + dwNumbers[dwFoundNumbers] = dwCurrentNumber; + dwCurrentNumber = UnspecifiedNumber; + } + + dwFoundNumbers++; + } + else if ((wcCurrentChar >= W('0')) && (wcCurrentChar <= W('9'))) + { + if (dwCurrentNumber == UnspecifiedNumber) + { + dwCurrentNumber = 0; + } + dwCurrentNumber = (dwCurrentNumber * 10) + (wcCurrentChar - W('0')); + + if (dwCurrentNumber > static_cast<DWORD>(iVersionMax)) + { + goto Exit; + } + } + else + { + goto Exit; + } + + cursor++; + } + + // Difference from .NET Framework: If the major or minor version are unspecified, the version is considered invalid. + // + // Examples: + // "MyAssembly, Version=" + // "MyAssembly, Version=1" + // "MyAssembly, Version=65535.1" + // "MyAssembly, Version=1.65535" + if (dwFoundNumbers < 2 || dwNumbers[0] == UnspecifiedNumber || dwNumbers[1] == UnspecifiedNumber) + { + goto Exit; + } + + pAssemblyVersion->SetFeatureVersion(dwNumbers[0], dwNumbers[1]); + pAssemblyVersion->SetServiceVersion(dwNumbers[2], dwNumbers[3]); + fIsValid = TRUE; + } + + Exit: + return fIsValid; + } + + /* static */ + BOOL TextualIdentityParser::HexToBlob(SString &publicKeyOrToken, + BOOL fValidateHex, + BOOL fIsToken, + SBuffer &publicKeyOrTokenBLOB) + { + // Optional input verification + if (fValidateHex) + { + if ((fIsToken && !ValidatePublicKeyToken(publicKeyOrToken)) || + (!fIsToken && !ValidatePublicKey(publicKeyOrToken))) + { + return FALSE; + } + } + + UINT ccPublicKeyOrToken = publicKeyOrToken.GetCount(); + BYTE *pByteBLOB = publicKeyOrTokenBLOB.OpenRawBuffer(ccPublicKeyOrToken / 2); + + UnicodeHexToBin(publicKeyOrToken.GetUnicode(), ccPublicKeyOrToken, pByteBLOB); + publicKeyOrTokenBLOB.CloseRawBuffer(); + + return TRUE; + } + + /* static */ + void TextualIdentityParser::BlobToHex(SBuffer &publicKeyOrTokenBLOB, + SString &publicKeyOrToken) + { + UINT cbPublicKeyOrTokenBLOB = publicKeyOrTokenBLOB.GetSize(); + WCHAR *pwzpublicKeyOrToken = + publicKeyOrToken.OpenUnicodeBuffer(cbPublicKeyOrTokenBLOB * 2); + + BinToUnicodeHex(publicKeyOrTokenBLOB, cbPublicKeyOrTokenBLOB, pwzpublicKeyOrToken); + publicKeyOrToken.CloseBuffer(cbPublicKeyOrTokenBLOB * 2); + } + + BOOL TextualIdentityParser::Parse(SString &textualIdentity, BOOL fPermitUnescapedQuotes) + { + BOOL fIsValid = TRUE; + SString unicodeTextualIdentity; + + // Lexer modifies input string + textualIdentity.ConvertToUnicode(unicodeTextualIdentity); + Init(unicodeTextualIdentity, TRUE /* fSupportEscaping */); + + SmallStackSString currentString; + + // Identity format is simple name (, attr = value)* + GO_IF_NOT_EXPECTED(GetNextLexeme(currentString, fPermitUnescapedQuotes), LEXEME_TYPE_STRING); + m_pAssemblyIdentity->m_simpleName.Set(currentString); + m_pAssemblyIdentity->m_simpleName.Normalize(); + m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_SIMPLE_NAME); + + for (;;) + { + SmallStackSString attributeString; + SmallStackSString valueString; + + GO_IF_END_OR_NOT_EXPECTED(GetNextLexeme(currentString), LEXEME_TYPE_COMMA); + GO_IF_NOT_EXPECTED(GetNextLexeme(attributeString), LEXEME_TYPE_STRING); + GO_IF_NOT_EXPECTED(GetNextLexeme(currentString), LEXEME_TYPE_EQUALS); + GO_IF_NOT_EXPECTED(GetNextLexeme(valueString), LEXEME_TYPE_STRING); + + if (!PopulateAssemblyIdentity(attributeString, valueString)) + { + fIsValid = FALSE; + break; + } + } + + Exit: + return fIsValid; + } + + BOOL TextualIdentityParser::ParseString(SString &textualString, + SString &contentString) + { + BOOL fIsValid = TRUE; + SString unicodeTextualString; + + // Lexer modifies input string + textualString.ConvertToUnicode(unicodeTextualString); + Init(unicodeTextualString, TRUE /* fSupportEscaping */); + + SmallStackSString currentString; + GO_IF_NOT_EXPECTED(GetNextLexeme(currentString), LEXEME_TYPE_STRING); + + contentString.Set(currentString); + currentString.Normalize(); + + Exit: + return fIsValid; + } + + BOOL TextualIdentityParser::PopulateAssemblyIdentity(SString &attributeString, + SString &valueString) + { + BOOL fIsValid = TRUE; + + if (EqualsCaseInsensitive(attributeString, W("culture")) || + EqualsCaseInsensitive(attributeString, W("language"))) + { + GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_CULTURE); + GO_IF_WILDCARD(valueString); + + if (!EqualsCaseInsensitive(valueString, W("neutral"))) + { + // culture/language is preserved as is + m_pAssemblyIdentity->m_cultureOrLanguage.Set(valueString); + m_pAssemblyIdentity->m_cultureOrLanguage.Normalize(); + } + + m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_CULTURE); + } + else if (EqualsCaseInsensitive(attributeString, W("version"))) + { + AssemblyVersion *pAssemblyVersion = &(m_pAssemblyIdentity->m_version); + + GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_VERSION); + GO_IF_WILDCARD(valueString); + + if (ParseVersion(valueString, pAssemblyVersion)) + { + m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_VERSION); + } + else + { + fIsValid = FALSE; + } + } + else if (EqualsCaseInsensitive(attributeString, W("publickeytoken"))) + { + GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY); + GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN); + GO_IF_WILDCARD(valueString); + + if (!EqualsCaseInsensitive(valueString, W("null")) && + !EqualsCaseInsensitive(valueString, W("neutral"))) + { + GO_IF_VALIDATE_FAILED(ValidatePublicKeyToken, + AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN); + HexToBlob(valueString, + FALSE /* fValidateHex */, + TRUE /* fIsToken */, + m_pAssemblyIdentity->m_publicKeyOrTokenBLOB); + } + else + { + m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL); + } + } + else if (EqualsCaseInsensitive(attributeString, W("publickey"))) + { + GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN); + GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY); + + if (!EqualsCaseInsensitive(valueString, W("null")) && + !EqualsCaseInsensitive(valueString, W("neutral"))) + { + GO_IF_VALIDATE_FAILED(ValidatePublicKey, AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY); + HexToBlob(valueString, + FALSE /* fValidateHex */, + FALSE /* fIsToken */, + m_pAssemblyIdentity->m_publicKeyOrTokenBLOB); + } + } + else if (EqualsCaseInsensitive(attributeString, W("processorarchitecture"))) + { + PEKIND kProcessorArchitecture = peNone; + + GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE); + GO_IF_WILDCARD(valueString); + + if (ValidateAndConvertProcessorArchitecture(valueString, &kProcessorArchitecture)) + { + m_pAssemblyIdentity->m_kProcessorArchitecture = kProcessorArchitecture; + m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE); + } + else + { + fIsValid = FALSE; + } + } + else if (EqualsCaseInsensitive(attributeString, W("retargetable"))) + { + GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE); + + if (EqualsCaseInsensitive(valueString, W("yes"))) + { + m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE); + } + else if (!EqualsCaseInsensitive(valueString, W("no"))) + { + fIsValid = FALSE; + } + } + else if (EqualsCaseInsensitive(attributeString, W("contenttype"))) + { + GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE); + GO_IF_WILDCARD(valueString); + + if (EqualsCaseInsensitive(valueString, W("windowsruntime"))) + { + m_pAssemblyIdentity->m_kContentType = AssemblyContentType_WindowsRuntime; + m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE); + } + else + { + fIsValid = FALSE; + } + } + else if (EqualsCaseInsensitive(attributeString, W("custom"))) + { + GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_CUSTOM); + + if (EqualsCaseInsensitive(valueString, W("null"))) + { + m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_CUSTOM_NULL); + } + else + { + GO_IF_VALIDATE_FAILED(ValidateHex, AssemblyIdentity::IDENTITY_FLAG_CUSTOM); + HexToBlob(valueString, + FALSE /* fValidateHex */, + FALSE /* fIsToken */, + m_pAssemblyIdentity->m_customBLOB); + } + } + + Exit: + return fIsValid; + } + + /* static */ + void TextualIdentityParser::EscapeString(SString &input, + SString &result) + { + BOOL fNeedQuotes = FALSE; + WCHAR wcQuoteCharacter = W('"'); + + SmallStackSString tmpString; + SString::Iterator cursor = input.Begin(); + SString::Iterator end = input.End() - 1; + + // Leading/Trailing white space require quotes + if (IsWhitespace(cursor[0]) || IsWhitespace(end[0])) + { + fNeedQuotes = TRUE; + } + + // Fusion textual identity compat: escape all non-quote characters even if quoted + while (cursor <= end) + { + WCHAR wcCurrentChar = cursor[0]; + + switch (wcCurrentChar) + { + case W('"'): + case W('\''): + if (fNeedQuotes && (wcQuoteCharacter != wcCurrentChar)) + { + tmpString.Append(wcCurrentChar); + } + else if (!fNeedQuotes) + { + fNeedQuotes = TRUE; + wcQuoteCharacter = (wcCurrentChar == W('"') ? W('\'') : W('"')); + tmpString.Append(wcCurrentChar); + } + else + { + tmpString.Append(W('\\')); + tmpString.Append(wcCurrentChar); + } + break; + case W('='): + case W(','): + case W('\\'): + tmpString.Append(W('\\')); + tmpString.Append(wcCurrentChar); + break; + case 9: + tmpString.Append(W("\\t")); + break; + case 10: + tmpString.Append(W("\\n")); + break; + case 13: + tmpString.Append(W("\\r")); + break; + default: + tmpString.Append(wcCurrentChar); + break; + } + + cursor++; + } + + if (fNeedQuotes) + { + result.Clear(); + result.Append(wcQuoteCharacter); + result.Append(tmpString); + result.Append(wcQuoteCharacter); + } + else + { + result.Set(tmpString); + } + } +}; diff --git a/src/coreclr/binder/utils.cpp b/src/coreclr/binder/utils.cpp new file mode 100644 index 00000000000..21fc115a776 --- /dev/null +++ b/src/coreclr/binder/utils.cpp @@ -0,0 +1,255 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// Utils.cpp +// + + +// +// Implements a bunch of binder auxilary functions +// +// ============================================================ + +#include "utils.hpp" + +#include "strongnameinternal.h" +#include "corpriv.h" +#include "clr/fs/path.h" +using namespace clr::fs; + +namespace BINDER_SPACE +{ + namespace + { + inline const WCHAR *GetPlatformPathSeparator() + { +#ifdef TARGET_UNIX + return W("/"); +#else + return W("\\"); +#endif // TARGET_UNIX + } + } + + void MutateUrlToPath(SString &urlOrPath) + { + const SString fileUrlPrefix(SString::Literal, W("file://")); + SString::Iterator i = urlOrPath.Begin(); + + if (urlOrPath.MatchCaseInsensitive(i, fileUrlPrefix)) + { + urlOrPath.Delete(i, fileUrlPrefix.GetCount()); + + i = urlOrPath.Begin() + 1; + if (i[0] == W(':')) + { + // CLR erroneously passes in file:// prepended to file paths, + // so we can't tell the difference between UNC and local file. + goto Exit; + } + + i = urlOrPath.Begin(); +#if !defined(TARGET_UNIX) + if (i[0] == W('/')) + { + // Disk path file:/// + urlOrPath.Delete(i, 1); + } + else if (i[0] != W('\\')) + { + // UNC Path, re-insert "//" if not the wrong file://\\... + urlOrPath.Insert(i, W("//")); + } +#else + // Unix doesn't have a distinction between local and network path + _ASSERTE(i[0] == W('\\') || i[0] == W('/')); +#endif + } + + Exit: + while (urlOrPath.Find(i, W('/'))) + { + urlOrPath.Replace(i, W('\\')); + } + } + + void CombinePath(SString &pathA, + SString &pathB, + SString &combinedPath) + { + SString platformPathSeparator(SString::Literal, GetPlatformPathSeparator()); + combinedPath.Set(pathA); + + if (!combinedPath.IsEmpty() && !combinedPath.EndsWith(platformPathSeparator)) + { + combinedPath.Append(platformPathSeparator); + } + + combinedPath.Append(pathB); + } + + HRESULT GetTokenFromPublicKey(SBuffer &publicKeyBLOB, + SBuffer &publicKeyTokenBLOB) + { + HRESULT hr = S_OK; + + const BYTE *pByteKey = publicKeyBLOB; + DWORD dwKeyLen = publicKeyBLOB.GetSize(); + BYTE *pByteToken = NULL; + DWORD dwTokenLen = 0; + + IF_FAIL_GO(StrongNameTokenFromPublicKey( + const_cast<BYTE*>(pByteKey), + dwKeyLen, + &pByteToken, + &dwTokenLen)); + + _ASSERTE(pByteToken != NULL); + publicKeyTokenBLOB.Set(pByteToken, dwTokenLen); + StrongNameFreeBuffer(pByteToken); + + Exit: + return hr; + } + + BOOL IsFileNotFound(HRESULT hr) + { + return RuntimeFileNotFound(hr); + } + + HRESULT GetNextPath(SString& paths, SString::Iterator& startPos, SString& outPath) + { + HRESULT hr = S_OK; + + bool wrappedWithQuotes = false; + + // Skip any leading spaces or path separators + while (paths.Skip(startPos, W(' ')) || paths.Skip(startPos, PATH_SEPARATOR_CHAR_W)) {} + + if (startPos == paths.End()) + { + // No more paths in the string and we just skipped over some white space + outPath.Set(W("")); + return S_FALSE; + } + + // Support paths being wrapped with quotations + if (paths.Skip(startPos, W('\"'))) + { + wrappedWithQuotes = true; + } + + SString::Iterator iEnd = startPos; // Where current path ends + SString::Iterator iNext; // Where next path starts + if (wrappedWithQuotes) + { + if (paths.Find(iEnd, W('\"'))) + { + iNext = iEnd; + // Find where the next path starts - there should be a path separator right after the closing quotation mark + if (paths.Find(iNext, PATH_SEPARATOR_CHAR_W)) + { + iNext++; + } + else + { + iNext = paths.End(); + } + } + else + { + // There was no terminating quotation mark - that's bad + GO_WITH_HRESULT(E_INVALIDARG); + } + } + else if (paths.Find(iEnd, PATH_SEPARATOR_CHAR_W)) + { + iNext = iEnd + 1; + } + else + { + iNext = iEnd = paths.End(); + } + + // Skip any trailing spaces + while (iEnd[-1] == W(' ')) + { + iEnd--; + } + + _ASSERTE(startPos < iEnd); + + outPath.Set(paths, startPos, iEnd); + startPos = iNext; + Exit: + return hr; + } + + HRESULT GetNextTPAPath(SString& paths, SString::Iterator& startPos, bool dllOnly, SString& outPath, SString& simpleName, bool& isNativeImage) + { + HRESULT hr = S_OK; + isNativeImage = false; + + HRESULT pathResult = S_OK; + IF_FAIL_GO(pathResult = GetNextPath(paths, startPos, outPath)); + if (pathResult == S_FALSE) + { + return S_FALSE; + } + +#ifndef CROSSGEN_COMPILE + if (Path::IsRelative(outPath)) + { + GO_WITH_HRESULT(E_INVALIDARG); + } +#endif + + { + // Find the beginning of the simple name + SString::Iterator iSimpleNameStart = outPath.End(); + + if (!outPath.FindBack(iSimpleNameStart, DIRECTORY_SEPARATOR_CHAR_W)) + { + iSimpleNameStart = outPath.Begin(); + } + else + { + // Advance past the directory separator to the first character of the file name + iSimpleNameStart++; + } + + if (iSimpleNameStart == outPath.End()) + { + GO_WITH_HRESULT(E_INVALIDARG); + } + + // GCC complains if we create SStrings inline as part of a function call + SString sNiDll(W(".ni.dll")); + SString sNiExe(W(".ni.exe")); + SString sDll(W(".dll")); + SString sExe(W(".exe")); + + if (!dllOnly && (outPath.EndsWithCaseInsensitive(sNiDll) || + outPath.EndsWithCaseInsensitive(sNiExe))) + { + simpleName.Set(outPath, iSimpleNameStart, outPath.End() - 7); + isNativeImage = true; + } + else if (outPath.EndsWithCaseInsensitive(sDll) || + (!dllOnly && outPath.EndsWithCaseInsensitive(sExe))) + { + simpleName.Set(outPath, iSimpleNameStart, outPath.End() - 4); + } + else + { + // Invalid filename + GO_WITH_HRESULT(E_INVALIDARG); + } + } + + Exit: + return hr; + } + +}; diff --git a/src/coreclr/binder/variables.cpp b/src/coreclr/binder/variables.cpp new file mode 100644 index 00000000000..fbdd106b4dd --- /dev/null +++ b/src/coreclr/binder/variables.cpp @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ============================================================ +// +// Variables.cpp +// + + +// +// Implements the Variables class +// +// ============================================================ + +#include "variables.hpp" + +#include "ex.h" + +namespace BINDER_SPACE +{ + Variables *g_BinderVariables = NULL; + + Variables::Variables() + { + // Nothing to do here + } + + Variables::~Variables() + { + // Nothing to do here + } + + HRESULT Variables::Init() + { + HRESULT hr = S_OK; + + EX_TRY + { + // AssemblyBinder string constants + httpURLPrefix.SetLiteral(W("http://")); + + // AssemblyName string constants + cultureNeutral.SetLiteral(W("neutral")); + corelib.SetLiteral(CoreLibName_W); + } + EX_CATCH_HRESULT(hr); + + return hr; + } +}; |