diff options
author | dotnet-bot <dotnet-bot@microsoft.com> | 2015-01-30 14:14:42 -0800 |
---|---|---|
committer | dotnet-bot <dotnet-bot@microsoft.com> | 2015-01-30 14:14:42 -0800 |
commit | ef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch) | |
tree | dee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/binder | |
download | coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2 coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip |
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/binder')
70 files changed, 14593 insertions, 0 deletions
diff --git a/src/binder/.gitmirror b/src/binder/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/binder/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/binder/CMakeLists.txt b/src/binder/CMakeLists.txt new file mode 100644 index 0000000000..12a61ac0d0 --- /dev/null +++ b/src/binder/CMakeLists.txt @@ -0,0 +1,38 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +include_directories(BEFORE "../vm/amd64") +include_directories(BEFORE "../vm") +include_directories(BEFORE "inc") + +set(BINDER_SOURCES + variables.cpp + utils.cpp + assemblyname.cpp + propertymap.cpp + applicationcontext.cpp + assembly.cpp + failurecache.cpp + assemblybinder.cpp + stringlexer.cpp + clrprivbindercoreclr.cpp + binderinterface.cpp + debuglog.cpp + bindinglog.cpp + cdebuglog.cpp + compatibility.cpp + textualidentityparser.cpp + assemblyidentitycache.cpp + clrprivbinderassemblyloadcontext.cpp + coreclrbindercommon.cpp + fusionassemblyname.cpp + fusionhelpers.cpp +) + +if(CLR_CMAKE_PLATFORM_UNIX) + add_compile_options(-fPIC) +endif(CLR_CMAKE_PLATFORM_UNIX) + +add_library(v3binder + STATIC + ${BINDER_SOURCES} +) diff --git a/src/binder/applicationcontext.cpp b/src/binder/applicationcontext.cpp new file mode 100644 index 0000000000..8aadd409c0 --- /dev/null +++ b/src/binder/applicationcontext.cpp @@ -0,0 +1,570 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// ApplicationContext.cpp +// + + +// +// Implements the ApplicationContext class +// +// ============================================================ + +#ifndef FEATURE_CORESYSTEM +#define DISABLE_BINDER_DEBUG_LOGGING +#endif + +#include "applicationcontext.hpp" +#include "stringarraylist.h" +#include "loadcontext.hpp" +#include "propertymap.hpp" +#include "failurecache.hpp" +#include "assemblyidentitycache.hpp" +#ifdef FEATURE_VERSIONING_LOG +#include "debuglog.hpp" +#endif // FEATURE_VERSIONING_LOG +#include "utils.hpp" +#include "variables.hpp" +#include "ex.h" + +namespace BINDER_SPACE +{ + namespace + { + void CopyIntoBuffer(/* in */ SBuffer *pPropertyValue, + /* in */ LPVOID pvValue, + /* in */ DWORD cbValue) + { + _ASSERTE(pPropertyValue != NULL); + + BYTE *pRawBuffer = pPropertyValue->OpenRawBuffer(cbValue); + + memcpy(pRawBuffer, pvValue, cbValue); + pPropertyValue->CloseRawBuffer(); + } + + const void *GetRawBuffer(SBuffer *pPropertyValue) + { + _ASSERTE(pPropertyValue != NULL); + + // SBuffer provides const void *() operator + const void *pPropertyRawBuffer = *pPropertyValue; + + _ASSERTE(pPropertyRawBuffer != NULL); + _ASSERTE(pPropertyRawBuffer != pPropertyValue); + + return pPropertyRawBuffer; + } + + HRESULT CheckRequiredBufferSize(/* in */ SBuffer *pPropertyValue, + /* in */ LPVOID pvValue, + /* in, out */ LPDWORD pcbValue) + { + _ASSERTE(pPropertyValue != NULL); + + HRESULT hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + DWORD cbPropertySize = static_cast<DWORD>(pPropertyValue->GetSize()); + + if (pcbValue == NULL) + { + hr = E_INVALIDARG; + } + else if ((cbPropertySize <= *pcbValue) && (pvValue != NULL)) + { + *pcbValue = cbPropertySize; + hr = S_OK; + } + else + { + *pcbValue = cbPropertySize; + } + + return hr; + } + + HRESULT CopyTextPropertyIntoBuffer(/* in */ SBuffer *pPropertyValue, + /* out */ LPWSTR wzPropertyBuffer, + /* in, out */ DWORD *pdwPropertyBufferSize) + { + HRESULT hr = S_OK; + void *pvValue = static_cast<void *>(wzPropertyBuffer); + DWORD cbValue = *pdwPropertyBufferSize * sizeof(WCHAR); + + if ((hr = CheckRequiredBufferSize(pPropertyValue, pvValue, &cbValue)) == S_OK) + { + memcpy(pvValue, GetRawBuffer(pPropertyValue), cbValue); + } + + // Adjust byte size to character count + _ASSERTE(cbValue % sizeof(WCHAR) == 0); + *pdwPropertyBufferSize = cbValue / sizeof(WCHAR); + + return hr; + } + + BOOL EndsWithPathSeparator(/* in */ PathString &path) + { + SString winDirSeparor(SString::Literal, W("\\")); + SString unixDirSeparor(SString::Literal, W("/")); + + return (path.EndsWith(winDirSeparor) || path.EndsWith(unixDirSeparor)); + } + }; + + 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_pInspectionContext = NULL; + m_pFailureCache = NULL; + m_contextCS = NULL; + m_pTrustedPlatformAssemblyMap = nullptr; + m_pFileNameHash = nullptr; + } + + ApplicationContext::~ApplicationContext() + { + SAFE_RELEASE(m_pExecutionContext); + SAFE_RELEASE(m_pInspectionContext); + SAFE_DELETE(m_pFailureCache); + + if (m_contextCS != NULL) + { + ClrDeleteCriticalSection(m_contextCS); + } + + if (m_pTrustedPlatformAssemblyMap != nullptr) + { + delete m_pTrustedPlatformAssemblyMap; + } + + if (m_pFileNameHash != nullptr) + { + delete m_pFileNameHash; + } + } + + HRESULT ApplicationContext::Init() + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("ApplicationContext::Init")); + BINDER_LOG_POINTER(W("this"), this); + + ReleaseHolder<ExecutionContext> pExecutionContext; + ReleaseHolder<InspectionContext> pInspectionContext; + + PropertyMap *pPropertyMap = NULL; + FailureCache *pFailureCache = NULL; + + // Allocate context objects + SAFE_NEW(pExecutionContext, ExecutionContext); + SAFE_NEW(pInspectionContext, InspectionContext); + + SAFE_NEW(pFailureCache, FailureCache); + + m_contextCS = ClrCreateCriticalSection( + CrstFusionAppCtx, + CRST_REENTRANCY); + if (!m_contextCS) + { + SAFE_DELETE(pPropertyMap); + SAFE_DELETE(pFailureCache); + hr = E_OUTOFMEMORY; + } + else + { + m_pExecutionContext = pExecutionContext.Extract(); + m_pInspectionContext = pInspectionContext.Extract(); + + m_pFailureCache = pFailureCache; + } + +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) + m_fCanExplicitlyBindToNativeImages = false; +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) + + Exit: + BINDER_LOG_LEAVE_HR(W("ApplicationContext::Init"), hr); + return 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 ApplicationContext::SetupBindingPaths(SString &sTrustedPlatformAssemblies, + SString &sPlatformResourceRoots, + SString &sAppPaths, + SString &sAppNiPaths, + BOOL fAcquireLock) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("ApplicationContext::SetupBindingPaths")); + BINDER_LOG_POINTER(W("this"), this); + +#ifndef CROSSGEN_COMPILE + CRITSEC_Holder contextLock(fAcquireLock ? GetCriticalSectionCookie() : NULL); +#endif + if (m_pTrustedPlatformAssemblyMap != nullptr) + { +#if defined(BINDER_DEBUG_LOG) + BINDER_LOG(W("ApplicationContext::SetupBindingPaths: Binding paths already setup")); +#endif // BINDER_LOG_STRING + GO_WITH_HRESULT(S_OK); + } + + + // + // Parse TrustedPlatformAssemblies + // + m_pTrustedPlatformAssemblyMap = new SimpleNameToFileNameMap(); + m_pFileNameHash = new TpaFileNameHash(); + + sTrustedPlatformAssemblies.Normalize(); + + for (SString::Iterator i = sTrustedPlatformAssemblies.Begin(); i != sTrustedPlatformAssemblies.End(); ) + { + SString fileName; + HRESULT pathResult = S_OK; + IF_FAIL_GO(pathResult = GetNextPath(sTrustedPlatformAssemblies, i, fileName)); + if (pathResult == S_FALSE) + { + break; + } + + // Find the beginning of the simple name + SString::Iterator iSimpleNameStart = fileName.End(); + + if (!fileName.FindBack(iSimpleNameStart, DIRECTORY_SEPARATOR_CHAR_W)) + { + // Couldn't find a directory separator. File must have been specified as a relative path. Not allowed. + GO_WITH_HRESULT(E_INVALIDARG); + } + + if (iSimpleNameStart == fileName.End()) + { + GO_WITH_HRESULT(E_INVALIDARG); + } + + // Advance past the directory separator to the first character of the file name + iSimpleNameStart++; + + SString simpleName; + bool isNativeImage = false; + + // GCC complains if we create SStrings inline as part of a function call + SString sNiDll(W(".ni.dll")); + SString sNiExe(W(".ni.exe")); + SString sNiWinmd(W(".ni.winmd")); + SString sDll(W(".dll")); + SString sExe(W(".exe")); + SString sWinmd(W(".winmd")); + + if (fileName.EndsWithCaseInsensitive(sNiDll) || + fileName.EndsWithCaseInsensitive(sNiExe)) + { + simpleName.Set(fileName, iSimpleNameStart, fileName.End() - 7); + isNativeImage = true; + } + else if (fileName.EndsWithCaseInsensitive(sNiWinmd)) + { + simpleName.Set(fileName, iSimpleNameStart, fileName.End() - 9); + isNativeImage = true; + } + else if (fileName.EndsWithCaseInsensitive(sDll) || + fileName.EndsWithCaseInsensitive(sExe)) + { + simpleName.Set(fileName, iSimpleNameStart, fileName.End() - 4); + } + else if (fileName.EndsWithCaseInsensitive(sWinmd)) + { + simpleName.Set(fileName, iSimpleNameStart, fileName.End() - 6); + } + else + { + // Invalid filename + GO_WITH_HRESULT(E_INVALIDARG); + } + + 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) + { + BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Skipping TPA entry because of already existing IL/NI entry for short name "), fileName.GetUnicode()); + 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); + + FileNameMapEntry fileNameExistenceEntry; + fileNameExistenceEntry.m_wszFileName = wszFileName; + m_pFileNameHash->AddOrReplace(fileNameExistenceEntry); + + BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Added TPA entry"), wszFileName); + } + + // + // 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; + } + + m_platformResourceRoots.Append(pathName); + BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Added resource root"), 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; + } + + m_appPaths.Append(pathName); + BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Added App Path"), 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; + } + + m_appNiPaths.Append(pathName); + BINDER_LOG_STRING(W("ApplicationContext::SetupBindingPaths: Added App NI Path"), pathName); + } + + Exit: + BINDER_LOG_LEAVE_HR(W("ApplicationContext::SetupBindingPaths"), hr); + return hr; + } + + HRESULT ApplicationContext::GetAssemblyIdentity(LPCSTR szTextualIdentity, + AssemblyIdentityUTF8 **ppAssemblyIdentity) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("ApplicationContext::GetAssemblyIdentity")); + BINDER_LOG_POINTER(W("this"), this); + + _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); + + + BOOL fWindowsPhone7 = false; +#ifdef FEATURE_LEGACYNETCF + fWindowsPhone7 = RuntimeIsLegacyNetCF(GetAppDomainId()); +#endif // FEATURE_LEGACYNETCF + + IF_FAIL_GO(TextualIdentityParser::Parse(sTextualIdentity, pNewAssemblyIdentity, fWindowsPhone7)); + IF_FAIL_GO(m_assemblyIdentityCache.Add(szTextualIdentity, pNewAssemblyIdentity)); + + pNewAssemblyIdentity->PopulateUTF8Fields(); + + pAssemblyIdentity = pNewAssemblyIdentity.Extract(); + } + + *ppAssemblyIdentity = pAssemblyIdentity; + + Exit: + BINDER_LOG_LEAVE_HR(W("ApplicationContext::GetAssemblyIdentity"), hr); + return hr; + } + + bool ApplicationContext::IsTpaListProvided() + { + return m_pTrustedPlatformAssemblyMap != nullptr; + } +}; diff --git a/src/binder/assembly.cpp b/src/binder/assembly.cpp new file mode 100644 index 0000000000..ed04368fc2 --- /dev/null +++ b/src/binder/assembly.cpp @@ -0,0 +1,438 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// Assembly.cpp +// + + +// +// Implements the Assembly class +// +// ============================================================ +#include "common.h" +#include "clrprivbinderutil.h" +#include "assembly.hpp" +#include "utils.hpp" + +#ifdef FEATURE_LEGACYNETCF +extern BOOL RuntimeIsLegacyNetCF(DWORD adid); +#endif + +namespace BINDER_SPACE +{ + namespace + { + BOOL IsPlatformArchicture(PEKIND kArchitecture) + { + return ((kArchitecture != peMSIL) && (kArchitecture != peNone)); + } + + HRESULT GetAssemblyRefTokens(IMDInternalImport *pMDImport, + mdAssembly **ppAssemblyRefTokens, + DWORD *pdwCAssemblyRefTokens) + { + HRESULT hr = S_OK; + + _ASSERTE(pMDImport != NULL); + _ASSERTE(ppAssemblyRefTokens != NULL); + _ASSERTE(pdwCAssemblyRefTokens != NULL); + + mdAssembly *pAssemblyRefTokens = NULL; + COUNT_T assemblyRefCount; + + HENUMInternalHolder hEnumAssemblyRef(pMDImport); + IF_FAIL_GO(hEnumAssemblyRef.EnumInitNoThrow(mdtAssemblyRef, mdTokenNil)); + + assemblyRefCount = hEnumAssemblyRef.EnumGetCount(); + + pAssemblyRefTokens = new (nothrow) mdAssemblyRef[assemblyRefCount]; + if (pAssemblyRefTokens == NULL) + { + IF_FAIL_GO(E_OUTOFMEMORY); + } + ZeroMemory(pAssemblyRefTokens, assemblyRefCount * sizeof(mdAssemblyRef)); + + for (COUNT_T i = 0; i < assemblyRefCount; i++) + { + bool ret = hEnumAssemblyRef.EnumNext(&(pAssemblyRefTokens[i])); + _ASSERTE(ret); + } + + *ppAssemblyRefTokens = pAssemblyRefTokens; + pAssemblyRefTokens = NULL; + + *pdwCAssemblyRefTokens= assemblyRefCount; + hr = S_OK; + +Exit: + SAFE_DELETE_ARRAY(pAssemblyRefTokens); + + return hr; + } + }; + + 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_pAssemblyRefTokens = NULL; + m_dwCAssemblyRefTokens = static_cast<DWORD>(-1); + m_dwAssemblyFlags = FLAG_NONE; + m_pBinder = NULL; + } + + Assembly::~Assembly() + { + BINDER_LOG_ASSEMBLY_NAME(L"destructing assembly", m_pAssemblyName); + + 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); + SAFE_DELETE_ARRAY(m_pAssemblyRefTokens); + } + + HRESULT Assembly::Init( + IMDInternalImport *pIMetaDataAssemblyImport, + PEKIND PeKind, + PEImage *pPEImage, + PEImage *pNativePEImage, + SString &assemblyPath, + BOOL fInspectionOnly, + BOOL fIsInGAC) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"Assembly::Init"); + + 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); + } + + BINDER_LOG_ASSEMBLY_NAME(L"AssemblyNameDef", pAssemblyName); + BINDER_LOG_STRING(L"System Architecture", + AssemblyName::ArchitectureToString(GetSystemArchitecture())); + + // Safe architecture for validation + PEKIND kAssemblyArchitecture; + kAssemblyArchitecture = pAssemblyName->GetArchitecture(); + SetInspectionOnly(fInspectionOnly); + SetIsInGAC(fIsInGAC); + SetPEImage(pPEImage); + SetNativePEImage(pNativePEImage); + pAssemblyName->SetIsDefinition(TRUE); + + // Now take ownership of assembly names + SetAssemblyName(pAssemblyName.Extract(), FALSE /* fAddRef */); + + // Finally validate architecture + if (!fInspectionOnly && !IsValidArchitecture(kAssemblyArchitecture)) + { + // Assembly image can't be executed on this platform + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT)); + } + + Exit: + BINDER_LOG_LEAVE_HR(L"Assembly::Init", hr); + return hr; + } + + HRESULT Assembly::GetMVID(GUID *pMVID) + { + // Zero init the GUID incase we fail + ZeroMemory(pMVID, sizeof(GUID)); + + return m_pMDImport->GetScopeProps(NULL, pMVID); + } + + HRESULT Assembly::GetNextAssemblyNameRef(DWORD nIndex, + AssemblyName **ppAssemblyName) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"Assembly::GetNextAssemblyNameRef"); + + if (ppAssemblyName == NULL) + { + IF_FAIL_GO(E_INVALIDARG); + } + else if (GetNbAssemblyRefTokens() == static_cast<DWORD>(-1)) + { + mdAssembly *pAssemblyRefTokens = NULL; + DWORD dwCAssemblyRefTokens = 0; + + IF_FAIL_GO(BINDER_SPACE::GetAssemblyRefTokens(GetMDImport(), + &pAssemblyRefTokens, + &dwCAssemblyRefTokens)); + + if (InterlockedCompareExchangeT(&m_pAssemblyRefTokens, + pAssemblyRefTokens, + NULL)) + { + SAFE_DELETE_ARRAY(pAssemblyRefTokens); + } + SetNbAsssemblyRefTokens(dwCAssemblyRefTokens); + } + + _ASSERTE(GetNbAssemblyRefTokens() != static_cast<DWORD>(-1)); + + // Verify input index + if (nIndex >= GetNbAssemblyRefTokens()) + { + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)); + } + else + { + ReleaseHolder<AssemblyName> pAssemblyName; + + SAFE_NEW(pAssemblyName, AssemblyName); + IF_FAIL_GO(pAssemblyName->Init(GetMDImport(), + peNone, + GetAssemblyRefTokens()[nIndex], + FALSE /* fIsDefinition */)); + + *ppAssemblyName = pAssemblyName.Extract(); + } + + Exit: + BINDER_LOG_LEAVE_HR(L"Assembly::GetNextAssemblyNameRef", hr); + return hr; + } + + /* 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 (!IsPlatformArchicture(kArchitecture)) + return TRUE; + +#ifdef FEATURE_LEGACYNETCF + if (kArchitecture == peI386 && RuntimeIsLegacyNetCF(0)) + return TRUE; +#endif + + return (kArchitecture == GetSystemArchitecture()); + } + + // -------------------------------------------------------------------- + // ICLRPrivAssembly methods + // -------------------------------------------------------------------- + LPCWSTR Assembly::GetSimpleName() + { + AssemblyName *pAsmName = GetAssemblyName(); + return (pAsmName == nullptr ? nullptr : pAsmName->GetSimpleName()); + } + + HRESULT Assembly::BindAssemblyByName(IAssemblyName * pIAssemblyName, ICLRPrivAssembly ** ppAssembly) + { + return (m_pBinder == NULL) ? E_FAIL : m_pBinder->BindAssemblyByName(pIAssemblyName, ppAssembly); + } + + HRESULT Assembly::FindAssemblyBySpec( + LPVOID pvAppDomain, + LPVOID pvAssemblySpec, + HRESULT * pResult, + ICLRPrivAssembly ** ppAssembly) + { + return (m_pBinder == NULL) ? E_FAIL : m_pBinder->FindAssemblyBySpec(pvAppDomain, pvAssemblySpec, pResult, ppAssembly); + } + + HRESULT Assembly::VerifyBind ( + IAssemblyName * pIAssemblyName, + ICLRPrivAssembly *pAssembly, + ICLRPrivAssemblyInfo *pAssemblyInfo) + { + return (m_pBinder == NULL) ? E_FAIL : m_pBinder->VerifyBind(pIAssemblyName, pAssembly, pAssemblyInfo); + } + + HRESULT Assembly::GetBinderID(UINT_PTR *pBinderId) + { + return (m_pBinder == NULL) ? E_FAIL : m_pBinder->GetBinderID(pBinderId); + } + + HRESULT Assembly::GetBinderFlags(DWORD *pBinderFlags) + { + return (m_pBinder == NULL) ? E_FAIL : m_pBinder->GetBinderFlags(pBinderFlags); + } + + HRESULT Assembly::IsShareable( + BOOL * pbIsShareable) + { + if(pbIsShareable == nullptr) + return E_INVALIDARG; + + *pbIsShareable = GetIsSharable(); + return S_OK; + } + + HRESULT Assembly::GetAvailableImageTypes( + LPDWORD pdwImageTypes) + { + HRESULT hr = E_FAIL; + + if(pdwImageTypes == nullptr) + return E_INVALIDARG; + + *pdwImageTypes = ASSEMBLY_IMAGE_TYPE_ASSEMBLY; + + return S_OK; + } + + HRESULT Assembly::GetImageResource( + DWORD dwImageType, + DWORD * pdwImageType, + ICLRPrivResource ** ppIResource) + { + HRESULT hr = S_OK; + if(ppIResource == nullptr) + return E_INVALIDARG; + + if ((dwImageType & ASSEMBLY_IMAGE_TYPE_ASSEMBLY) == ASSEMBLY_IMAGE_TYPE_ASSEMBLY) + { + *ppIResource = clr::SafeAddRef(&m_clrPrivRes); + if (pdwImageType != nullptr) + *pdwImageType = ASSEMBLY_IMAGE_TYPE_ASSEMBLY; + } + else + { + hr = CLR_E_BIND_IMAGE_UNAVAILABLE; + } + + return hr; + } + + // get parent pointer from nested type + #define GetPThis() ((BINDER_SPACE::Assembly*)(((PBYTE)this) - offsetof(BINDER_SPACE::Assembly, m_clrPrivRes))) + + HRESULT Assembly::CLRPrivResourceAssembly::QueryInterface(REFIID riid, void ** ppv) + { + HRESULT hr = S_OK; + VALIDATE_ARG_RET(ppv != NULL); + + if (IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppv = this; + } + else if (IsEqualIID(riid, __uuidof(ICLRPrivResourceAssembly))) + { + AddRef(); + *ppv = static_cast<ICLRPrivResourceAssembly *>(this); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + + return hr; + } + + ULONG Assembly::CLRPrivResourceAssembly::AddRef() + { + return GetPThis()->AddRef(); + } + + ULONG Assembly::CLRPrivResourceAssembly::Release() + { + return GetPThis()->Release(); + } + + HRESULT Assembly::CLRPrivResourceAssembly::GetResourceType(IID *pIID) + { + VALIDATE_ARG_RET(pIID != nullptr); + *pIID = __uuidof(ICLRPrivResourceAssembly); + return S_OK; + } + + HRESULT Assembly::CLRPrivResourceAssembly::GetAssembly(LPVOID *ppAssembly) + { + VALIDATE_ARG_RET(ppAssembly != nullptr); + AddRef(); + *ppAssembly = GetPThis(); + return S_OK; + } + +} + diff --git a/src/binder/assemblybinder.cpp b/src/binder/assemblybinder.cpp new file mode 100644 index 0000000000..9863ae27a7 --- /dev/null +++ b/src/binder/assemblybinder.cpp @@ -0,0 +1,2082 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// AssemblyBinder.cpp +// + + +// +// Implements the AssemblyBinder class +// +// ============================================================ + +#include "assemblybinder.hpp" +#include "assemblyname.hpp" + +#include "assembly.hpp" +#include "applicationcontext.hpp" +#include "loadcontext.hpp" +#include "bindresult.inl" +#include "failurecache.hpp" +#ifdef FEATURE_VERSIONING_LOG +#include "bindinglog.hpp" +#endif // FEATURE_VERSIONING_LOG +#include "compatibility.hpp" +#include "utils.hpp" +#include "variables.hpp" +#include "stringarraylist.h" + +#include "strongname.h" +#ifdef FEATURE_VERSIONING_LOG +#include "../dlls/mscorrc/fusres.h" +#endif // FEATURE_VERSIONING_LOG + +#define APP_DOMAIN_LOCKED_INSPECTION_ONLY 0x01 +#define APP_DOMAIN_LOCKED_UNLOCKED 0x02 +#define APP_DOMAIN_LOCKED_CONTEXT 0x04 + +#define BIND_BEHAVIOR_STATIC 0 +#define BIND_BEHAVIOR_ORDER_INDEPENDENT 1 +#define BIND_BEHAVIOR_BEST_MATCH 2 + +#ifndef IMAGE_FILE_MACHINE_ARM64 +#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian +#endif + +BOOL IsCompilationProcess(); + +#ifdef FEATURE_LEGACYNETCF +extern BOOL RuntimeIsLegacyNetCF(DWORD adid); +#endif + +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) +#include "clrprivbindercoreclr.h" +#include "clrprivbinderassemblyloadcontext.h" +// Helper function in the VM, invoked by the Binder, to invoke the host assembly resolver +extern HRESULT RuntimeInvokeHostAssemblyResolver(CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly); + +// Helper to check if we have a host assembly resolver set +extern BOOL RuntimeCanUseAppPathAssemblyResolver(DWORD adid); + +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + +namespace BINDER_SPACE +{ + typedef enum + { + kVersionIgnore, + kVersionExact, + kVersionServiceRollForward, + kVersionFeatureRollForward, + kVersionFeatureExact, + kVersionFeatureHighest, + kVersionFeatureLowestHigher, + kVersionFeatureHighestLower + } VersionMatchMode; + + namespace + { + BOOL fAssemblyBinderInitialized = FALSE; + + // + // This defines the assembly equivalence relation + // + HRESULT IsValidAssemblyVersion(/* in */ AssemblyName *pRequestedName, + /* in */ AssemblyName *pFoundName, + /* in */ ApplicationContext *pApplicationContext, + /* in */ BOOL fBeingBoundToPlatformAssembly) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("IsValidAssemblyVersion")); + AssemblyVersion *pRequestedVersion = pRequestedName->GetVersion(); + AssemblyVersion *pFoundVersion = pFoundName->GetVersion(); + + bool fWindowsPhone7 = false; +#ifdef FEATURE_LEGACYNETCF + fWindowsPhone7 = RuntimeIsLegacyNetCF(pApplicationContext->GetAppDomainId()) == TRUE; +#endif + + // + // If the AssemblyRef has no version and we're not binding to a platform assembly, + // we can skip version checking and allow the bind + // + // + // Windows Phone 7 Quirk: + // + // NetCF allows partial binds to platform assemblies. If we are running a + // Mango application, skip the version check if no Ref version is provided, + // since there are apps in the Marketplace that do Assembly.Load("System") + // + if (((fWindowsPhone7 && fBeingBoundToPlatformAssembly) || !fBeingBoundToPlatformAssembly) && !pRequestedName->HaveAssemblyVersion()) + + { + return hr; + } + + + // + // Windows Phone 7 Quirk: + // + // NetCF does not respect version numbers when binding unsigned assemblies. If you ask + // for a version you can get any version back, including an unversioned assembly. + // + if (!fWindowsPhone7 || pRequestedName->IsStronglyNamed()) + { + // + // This if condition is paired with the one above that checks for pRequestedName + // not having an assembly version. If we didn't exit in the above if condition, + // and satisfy this one's requirements, we're in a situation where the assembly + // Ref has a version, but the Def doesn't, which cannot succeed a bind + // + if (!fBeingBoundToPlatformAssembly + && pRequestedName->HaveAssemblyVersion() + && !pFoundName->HaveAssemblyVersion()) + { + hr = FUSION_E_APP_DOMAIN_LOCKED; + } + else if (pRequestedVersion->IsEqualFeatureVersion(pFoundVersion)) + { + // Now service version matters + if (pRequestedVersion->IsLargerServiceVersion(pFoundVersion)) + { + hr = FUSION_E_APP_DOMAIN_LOCKED; + } + } + else if (pRequestedVersion->IsLargerFeatureVersion(pFoundVersion)) + { + hr = FUSION_E_APP_DOMAIN_LOCKED; + } + } + + if (pApplicationContext->IsTpaListProvided() && hr == FUSION_E_APP_DOMAIN_LOCKED) + { + // For our new binding models, use a more descriptive error code than APP_DOMAIN_LOCKED for bind + // failures. + hr = FUSION_E_REF_DEF_MISMATCH; + } + + BINDER_LOG_LEAVE_HR(W("IsValidAssemblyVersion"), hr) + return hr; + } + + 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 + { + PathString fullAssemblyPath; + WCHAR *pwzFullAssemblyPath = fullAssemblyPath.OpenUnicodeBuffer(MAX_PATH); + DWORD dwCCFullAssemblyPath = MAX_PATH + 1; // SString allocates extra byte for null. + + MutateUrlToPath(assemblyPath); + + dwCCFullAssemblyPath = WszGetFullPathName(assemblyPath.GetUnicode(), + dwCCFullAssemblyPath, + pwzFullAssemblyPath, + NULL); + fullAssemblyPath.CloseBuffer(dwCCFullAssemblyPath); + + if ((dwCCFullAssemblyPath == 0) || (dwCCFullAssemblyPath > (MAX_PATH + 1))) + { + hr = HRESULT_FROM_GetLastError(); + } + else + { + assemblyPath.Set(fullAssemblyPath); + } + + // Now turn this path into our canonical representation + CanonicalizePath(assemblyPath); + } + + return hr; + } + + +#ifdef FEATURE_VERSIONING_LOG + // + // This function outputs the current binding result + // and flushes the bind log. + // + HRESULT LogBindResult(ApplicationContext *pApplicationContext, + HRESULT hrLog, + BindResult *pBindResult) + { + HRESULT hr = S_OK; + BindingLog *pBindingLog = pApplicationContext->GetBindingLog(); + + if (!pBindingLog->CanLog()) + { + // For non-logging, return the bind result + hr = hrLog; + goto Exit; + } + + IF_FAIL_GO(pBindingLog->LogHR(hrLog)); + + if ((hrLog == S_OK) && pBindResult->HaveResult()) + { + IF_FAIL_GO(pBindingLog->LogResult(pBindResult)); + } + + IF_FAIL_GO(pBindingLog->Flush()); + + // For failure-free logging, return the bind result + hr = hrLog; + + Exit: + // SilverLight does not propagate binding log; therefore kill the information here. + pApplicationContext->ClearBindingLog(); + return hr; + } + + inline UINT GetLockedContextEntry(BOOL fInspectionOnly) + { + if (fInspectionOnly) + return ID_FUSLOG_BINDING_LOCKED_ASSEMBLY_INS_CONTEXT; + else + return ID_FUSLOG_BINDING_LOCKED_ASSEMBLY_EXE_CONTEXT; + } + + inline UINT GetLockedEntry(BOOL fInspectionOnly) + { + if (fInspectionOnly) + return ID_FUSLOG_BINDING_LOCKED_MT_INS_LOCKED_ENTRY; + else + return ID_FUSLOG_BINDING_LOCKED_MT_EXE_LOCKED_ENTRY; + } + + inline UINT GetLocalizedEntry(BOOL fInspectionOnly) + { + if (fInspectionOnly) + return ID_FUSLOG_BINDING_LOCKED_MT_INS_LOCALI_ENTRY; + else + return ID_FUSLOG_BINDING_LOCKED_MT_EXE_LOCALI_ENTRY; + } + + inline UINT GetCBaseEntry(BOOL fInspectionOnly) + { + if (fInspectionOnly) + return ID_FUSLOG_BINDING_LOCKED_MT_INS_CBASE_ENTRY; + else + return ID_FUSLOG_BINDING_LOCKED_MT_EXE_CBASE_ENTRY; + } + + HRESULT LogAppDomainLocked(ApplicationContext *pApplicationContext, + DWORD dwLockedReason, + AssemblyName *pAssemblyName = NULL) + { + HRESULT hr = S_OK; + BindingLog *pBindingLog = pApplicationContext->GetBindingLog(); + + if (pBindingLog->CanLog()) + { + PathString info; + PathString format; + BOOL fInspectionOnly = FALSE; + + if ((dwLockedReason & APP_DOMAIN_LOCKED_INSPECTION_ONLY) != 0) + { + dwLockedReason &= ~APP_DOMAIN_LOCKED_INSPECTION_ONLY; + fInspectionOnly = TRUE; + } + + switch (dwLockedReason) + { + case APP_DOMAIN_LOCKED_UNLOCKED: + { + IF_FAIL_GO(info. + LoadResourceAndReturnHR(CCompRC::Debugging, + ID_FUSLOG_BINDING_LOCKED_UNLOCKED)); + } + break; + case APP_DOMAIN_LOCKED_CONTEXT: + { + PathString displayName; + + _ASSERTE(pAssemblyName != NULL); + + IF_FAIL_GO(format. + LoadResourceAndReturnHR(CCompRC::Debugging, + GetLockedContextEntry(fInspectionOnly))); + + pAssemblyName->GetDisplayName(displayName, + AssemblyName::INCLUDE_VERSION | + AssemblyName::INCLUDE_ARCHITECTURE); + + info.Printf(format.GetUnicode(), displayName.GetUnicode()); + } + break; + default: + _ASSERTE(0); + IF_FAIL_GO(E_INVALIDARG); + break; + } + + IF_FAIL_GO(pBindingLog->Log(info)); + } + + Exit: + return hr; + } + + HRESULT LogBindBehavior(ApplicationContext *pApplicationContext, + DWORD dwBindBehavior) + { + HRESULT hr = S_OK; + BindingLog *pBindingLog = pApplicationContext->GetBindingLog(); + + if (pBindingLog->CanLog()) + { + PathString bindBehavior; + UINT uiBindBehavior = 0; + + switch (dwBindBehavior) + { + case BIND_BEHAVIOR_STATIC: + uiBindBehavior = ID_FUSLOG_BINDING_BEHAVIOR_STATIC; + break; + case BIND_BEHAVIOR_ORDER_INDEPENDENT: + uiBindBehavior = ID_FUSLOG_BINDING_BEHAVIOR_ORDER_INDEPENDENT; + break; + case BIND_BEHAVIOR_BEST_MATCH: + uiBindBehavior = ID_FUSLOG_BINDING_BEHAVIOR_BEST_MATCH; + break; + default: + _ASSERTE(0); + IF_FAIL_GO(E_INVALIDARG); + break; + } + + IF_FAIL_GO(bindBehavior.LoadResourceAndReturnHR(CCompRC::Debugging, uiBindBehavior)); + IF_FAIL_GO(pBindingLog->Log(bindBehavior.GetUnicode())); + } + + Exit: + return hr; + } + + HRESULT LogAssemblyNameWhereRef(ApplicationContext *pApplicationContext, + Assembly *pAssembly) + { + HRESULT hr = S_OK; + BindingLog *pBindingLog = pApplicationContext->GetBindingLog(); + + if (pBindingLog->CanLog()) + { + PathString info; + + IF_FAIL_GO(info. + LoadResourceAndReturnHR(CCompRC::Debugging, ID_FUSLOG_BINDING_LOG_WHERE_REF_NAME)); + IF_FAIL_GO(pBindingLog->LogAssemblyName(info.GetUnicode(), + pAssembly->GetAssemblyName())); + } + + Exit: + return hr; + } + + HRESULT LogConfigurationError(ApplicationContext *pApplicationContext, + AssemblyName *pCulturedManifestName, + AssemblyName *pLocalPathAssemblyName) + { + HRESULT hr = S_OK; + BindingLog *pBindingLog = pApplicationContext->GetBindingLog(); + + if (pBindingLog->CanLog()) + { + PathString tmp; + PathString culturedManifestDisplayName; + PathString localPathDisplayName; + PathString info; + + IF_FAIL_GO(tmp. + LoadResourceAndReturnHR(CCompRC::Debugging, ID_FUSLOG_BINDING_LOG_ERRONOUS_MANIFEST_ENTRY)); + + pCulturedManifestName->GetDisplayName(culturedManifestDisplayName, + AssemblyName::INCLUDE_VERSION | + AssemblyName::INCLUDE_ARCHITECTURE); + pLocalPathAssemblyName->GetDisplayName(localPathDisplayName, + AssemblyName::INCLUDE_VERSION | + AssemblyName::INCLUDE_ARCHITECTURE); + + info.Printf(tmp.GetUnicode(), + culturedManifestDisplayName.GetUnicode(), + localPathDisplayName.GetUnicode()); + IF_FAIL_GO(pBindingLog->Log(info.GetUnicode())); + } + + Exit: + return hr; + } + + HRESULT LogPathAttempt(ApplicationContext *pApplicationContext, + PathString &assemblyPath) + { + HRESULT hr = S_OK; + BindingLog *pBindingLog = pApplicationContext->GetBindingLog(); + + if (pBindingLog->CanLog()) + { + PathString tmp; + PathString info; + + IF_FAIL_GO(tmp.LoadResourceAndReturnHR(CCompRC::Debugging, + ID_FUSLOG_BINDING_LOG_PATH_ATTEMPT)); + info.Printf(tmp.GetUnicode(), assemblyPath.GetUnicode()); + + IF_FAIL_GO(pBindingLog->Log(info)); + } + + Exit: + return hr; + } +#endif // FEATURE_VERSIONING_LOG + + HRESULT CreateImageAssembly(IMDInternalImport *pIMetaDataAssemblyImport, + PEKIND PeKind, + PEImage *pPEImage, + PEImage *pNativePEImage, + BOOL fInspectionOnly, + 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, + fInspectionOnly, + FALSE /* fIsInGAC */)); + + pBindResult->SetResult(pAssembly); + pBindResult->SetIsFirstRequest(TRUE); + + Exit: + return hr; + } + + // Host assembly "equivalence" relation + HRESULT IsValidHostAssembly(AssemblyName *pProposedAssemblyName, + AssemblyName *pRequestedAssemblyName, + VersionMatchMode versionMatchMode, + BOOL fRequireHigherSV, + BOOL *pFIsValidHostAssembly) + { + HRESULT hr = S_OK; + + AssemblyVersion *pProposedVersion = pProposedAssemblyName->GetVersion(); + AssemblyVersion *pRequestedVersion = pRequestedAssemblyName->GetVersion(); + + if (pProposedVersion->IsEqualFeatureVersion(pRequestedVersion)) + { + if (fRequireHigherSV) + { + *pFIsValidHostAssembly = + pProposedVersion->IsLargerServiceVersion(pRequestedVersion); + } + else if (versionMatchMode == kVersionExact) + { + *pFIsValidHostAssembly = + pProposedVersion->IsEqualServiceVersion(pRequestedVersion); + } + else + { + *pFIsValidHostAssembly = + (pProposedVersion->IsLargerServiceVersion(pRequestedVersion) || + pProposedVersion->IsEqualServiceVersion(pRequestedVersion)); + } + } + else if ((versionMatchMode == kVersionFeatureRollForward) && + (pProposedVersion->IsLargerFeatureVersion(pRequestedVersion))) + { + *pFIsValidHostAssembly = TRUE; + } + else + { + *pFIsValidHostAssembly = FALSE; + } + + return hr; + } + + inline DWORD BindingStoreEnumToDWORD(INT32 kBindingStore) + { + switch (kBindingStore) + { + case 0: + return kBindingStoreGAC; + case 1: + return kBindingStoreManifest; + case 2: + return kBindingStoreHost; + default: + _ASSERTE(0); + return 0; + } + } + }; + + /* static */ + HRESULT AssemblyBinder::Startup() + { + HRESULT hr = S_OK; + + if (!BINDER_SPACE::fAssemblyBinderInitialized) + { + g_BinderVariables = new Variables(); + IF_FAIL_GO(g_BinderVariables->Init()); + + // Setup Debug log + BINDER_LOG_STARTUP(); + + // We're done + BINDER_SPACE::fAssemblyBinderInitialized = TRUE; + } + + Exit: + return hr; + } + + + HRESULT AssemblyBinder::TranslatePEToArchitectureType(DWORD *pdwPAFlags, PEKIND *PeKind) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("TranslatePEToArchitectureType")) + + _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. + BINDER_LOG(W("Not a PE!")); + 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) + BINDER_LOG(W("Processor-agnostic (MSIL)")); + *PeKind = peMSIL; + } + else if (CLRPeKind & pe32Plus) + { + // 64-bit + if (CLRPeKind & pe32BitRequired) + { + // Invalid + BINDER_LOG(W("CLRPeKind & pe32BitRequired is true")); + 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 + BINDER_LOG(W("Unknown architecture")); + 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 + BINDER_LOG(W("32-bit, non-agnostic")); + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT)); + } + } + } + + Exit: + BINDER_LOG_LEAVE_HR(W("TranslatePEToArchitectureType"), 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 AssemblyBinder::BindAssembly(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* in */ LPCWSTR szCodeBase, + /* in */ PEAssembly *pParentAssembly, + /* in */ BOOL fNgenExplicitBind, + /* in */ BOOL fExplicitBindToNativeImage, + /* out */ Assembly **ppAssembly) + { + HRESULT hr = S_OK; + LONG kContextVersion = 0; + BindResult bindResult; + + BINDER_LOG_ENTER(W("AssemblyBinder::BindAssembly")); + +#ifndef CROSSGEN_COMPILE + Retry: + { + // Lock the binding application context + CRITSEC_Holder contextLock(pApplicationContext->GetCriticalSectionCookie()); +#endif + + if (szCodeBase == NULL) + { +#ifdef FEATURE_VERSIONING_LOG + // Log bind + IF_FAIL_GO(BindingLog::CreateInContext(pApplicationContext, + pAssemblyName, + pParentAssembly)); +#endif // FEATURE_VERSIONING_LOG + + + hr = BindByName(pApplicationContext, + pAssemblyName, + BIND_CACHE_FAILURES, + &bindResult); + IF_FAIL_GO(hr); + } + else + { + PathString assemblyPath(szCodeBase); + +#ifdef FEATURE_VERSIONING_LOG + // Log bind + IF_FAIL_GO(BindingLog::CreateInContext(pApplicationContext, + assemblyPath, + pParentAssembly)); +#endif // FEATURE_VERSIONING_LOG + + // 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, + &bindResult)); + } + + // Remember the post-bind version + kContextVersion = pApplicationContext->GetVersion(); + + Exit: +#ifdef FEATURE_VERSIONING_LOG + hr = LogBindResult(pApplicationContext, hr, &bindResult); +#else // FEATURE_VERSIONING_LOG + // Shut up GCC + hr = hr; +#endif // FEATURE_VERSIONING_LOG + +#ifndef CROSSGEN_COMPILE + } // lock(pApplicationContext) +#endif + + 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 constuction 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 + } + + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::BindAssembly"), hr); + return hr; + } + + /* static */ + HRESULT AssemblyBinder::BindToSystem(SString &systemDirectory, + Assembly **ppSystemAssembly, + bool fBindToNativeImage) + { + _ASSERTE(BINDER_SPACE::fAssemblyBinderInitialized == TRUE); + + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("AssemblyBinder:BindToSystem")); + + _ASSERTE(ppSystemAssembly != NULL); + + StackSString sMscorlibDir(systemDirectory); + ReleaseHolder<Assembly> pSystemAssembly; + + if(!sMscorlibDir.EndsWith(DIRECTORY_SEPARATOR_CHAR_W)) + { + sMscorlibDir.Append(DIRECTORY_SEPARATOR_CHAR_W); + } + + StackSString sMscorlib; + + // At run-time, mscorlib.ni.dll is typically always available, and + // mscorlib.dll is typically not. So check for the NI first. + sMscorlib = sMscorlibDir; + sMscorlib.Append(W("mscorlib.ni.dll")); + if (!fBindToNativeImage || FAILED(AssemblyBinder::GetAssembly(sMscorlib, + FALSE /* fInspectionOnly */, + TRUE /* fIsInGAC */, + TRUE /* fExplicitBindToNativeImage */, + &pSystemAssembly))) + { + // If mscorlib.ni.dll is unavailable, look for mscorlib.dll instead + sMscorlib = sMscorlibDir; + sMscorlib.Append(W("mscorlib.dll")); + IF_FAIL_GO(AssemblyBinder::GetAssembly(sMscorlib, + FALSE /* fInspectionOnly */, + TRUE /* fIsInGAC */, + FALSE /* fExplicitBindToNativeImage */, + &pSystemAssembly)); + } + + *ppSystemAssembly = pSystemAssembly.Extract(); + + Exit: + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::BindToSystem"), hr); + return hr; + } + + + /* static */ + HRESULT AssemblyBinder::BindToSystemSatellite(SString &systemDirectory, + SString &simpleName, + SString &cultureName, + Assembly **ppSystemAssembly) + { + _ASSERTE(BINDER_SPACE::fAssemblyBinderInitialized == TRUE); + + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("AssemblyBinder:BindToSystemSatellite")); + + _ASSERTE(ppSystemAssembly != NULL); + + StackSString sMscorlibSatellite(systemDirectory); + ReleaseHolder<Assembly> pSystemAssembly; + + // append culture name + if (!cultureName.IsEmpty()) + { + CombinePath(sMscorlibSatellite, cultureName, sMscorlibSatellite); + } + + // append satellite assembly's simple name + CombinePath(sMscorlibSatellite, simpleName, sMscorlibSatellite); + + // append extension + sMscorlibSatellite.Append(W(".dll")); + + IF_FAIL_GO(AssemblyBinder::GetAssembly(sMscorlibSatellite, + FALSE /* fInspectionOnly */, + TRUE /* fIsInGAC */, + FALSE /* fExplicitBindToNativeImage */, + &pSystemAssembly)); + + *ppSystemAssembly = pSystemAssembly.Extract(); + + Exit: + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::BindToSystemSatellite"), hr); + return hr; + } + + /* static */ + HRESULT AssemblyBinder::GetAssemblyFromImage(PEImage *pPEImage, + PEImage *pNativePEImage, + Assembly **ppAssembly) + { + _ASSERTE(BINDER_SPACE::fAssemblyBinderInitialized == TRUE); + + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("AssemblyBinder::GetAssemblyFromImage")); + + _ASSERTE(pPEImage != NULL); + _ASSERTE(ppAssembly != NULL); + + ReleaseHolder<Assembly> pAssembly; + ReleaseHolder<IMDInternalImport> pIMetaDataAssemblyImport; + DWORD dwPAFlags[2]; + PEKIND PeKind = peNone; + + SAFE_NEW(pAssembly, Assembly); + if(pNativePEImage) + { + IF_FAIL_GO(BinderAcquireImport(pNativePEImage, &pIMetaDataAssemblyImport, dwPAFlags, TRUE)); + } + else + { + IF_FAIL_GO(BinderAcquireImport(pPEImage, &pIMetaDataAssemblyImport, dwPAFlags, FALSE)); + } + IF_FAIL_GO(TranslatePEToArchitectureType(dwPAFlags, &PeKind)); + IF_FAIL_GO(pAssembly->Init(pIMetaDataAssemblyImport, + PeKind, + pPEImage, + pNativePEImage, + g_BinderVariables->emptyString, + FALSE /* fInspectionOnly */, + FALSE /* fIsInGAC */)); + + // TODO: Is this correct? + pAssembly->SetIsByteArray(TRUE); + + *ppAssembly = pAssembly.Extract(); + + Exit: + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::GetAssemblyFromImage"), hr); + return hr; + } + +#ifndef CROSSGEN_COMPILE + /* static */ + HRESULT AssemblyBinder::PreBindByteArray(ApplicationContext *pApplicationContext, + PEImage *pPEImage, + BOOL fInspectionOnly) + { + _ASSERTE(BINDER_SPACE::fAssemblyBinderInitialized == TRUE); + + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("AssemblyBinder::PreBindByteArray")); + + ReleaseHolder<AssemblyName> pAssemblyName; + BOOL fNeedHostRegister = FALSE; + LONG kContextVersion = 0; + ReleaseHolder<IMDInternalImport> pIMetaDataAssemblyImport; + DWORD dwPAFlags[2]; + PEKIND PeKind = peNone; + BindResult bindResult; + + // Prepare binding data + SAFE_NEW(pAssemblyName, AssemblyName); + IF_FAIL_GO(BinderAcquireImport(pPEImage, &pIMetaDataAssemblyImport, dwPAFlags, FALSE)); + IF_FAIL_GO(TranslatePEToArchitectureType(dwPAFlags, &PeKind)); + IF_FAIL_GO(pAssemblyName->Init(pIMetaDataAssemblyImport, PeKind)); + pAssemblyName->SetIsDefinition(TRUE); + + // Validate architecture + if (!fInspectionOnly && !Assembly::IsValidArchitecture(pAssemblyName->GetArchitecture())) + { + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT)); + } + + // Attempt the actual bind (eventually more than once) + Retry: + { + // Lock the application context + CRITSEC_Holder contextLock(pApplicationContext->GetCriticalSectionCookie()); + + // Attempt uncached bind and register stream if possible + if (!fInspectionOnly && + ((hr = BindByName(pApplicationContext, + pAssemblyName, + BIND_CACHE_FAILURES | BIND_CACHE_RERUN_BIND, + &bindResult)) == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + ) + { + if ((fInspectionOnly && !bindResult.HaveResult()) || + (bindResult.GetRetargetedAssemblyName() == NULL)) + { + IF_FAIL_GO(CreateImageAssembly(pIMetaDataAssemblyImport, + PeKind, + pPEImage, + NULL, + fInspectionOnly, + &bindResult)); + if (fInspectionOnly) + { + // For inspection-only, we do not share the map. + IF_FAIL_GO(Register(pApplicationContext, fInspectionOnly, &bindResult)); + } + else + { + // Remember the post-bind version of the context + kContextVersion = pApplicationContext->GetVersion(); + fNeedHostRegister = TRUE; + } + } + } + } // lock(pApplicationContext) + + if (fNeedHostRegister) + { + 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) + { + // Another bind interfered. We need to retry entire bind. + // This by design loops as long as needed because by constuction we eventually + // will succeed or fail the bind. + bindResult.Reset(); + goto Retry; + } + } + + // Ignore bind errors here because we need to attempt by-name load to get log entry. + GO_WITH_HRESULT(S_OK); + + Exit: + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::PreBindByteArray"), hr); + return hr; + } + +#endif //CROSSGEN_COMPILE + + /* static */ + HRESULT AssemblyBinder::BindByName(ApplicationContext *pApplicationContext, + AssemblyName *pAssemblyName, + DWORD dwBindFlags, + BindResult *pBindResult) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("AssemblyBinder::BindByName")); + PathString assemblyDisplayName; + ReleaseHolder<AssemblyName> pRetargetedAssemblyName; + BOOL fIsRetargeted = FALSE; + + // Apply retargeting + IF_FAIL_GO(Compatibility::Retarget(pAssemblyName, &pRetargetedAssemblyName, &fIsRetargeted)); + + // Look for already cached binding failure (ignore PA, every PA will lock the context) + pRetargetedAssemblyName->GetDisplayName(assemblyDisplayName, + AssemblyName::INCLUDE_VERSION); + + hr = pApplicationContext->GetFailureCache()->Lookup(assemblyDisplayName); + if (FAILED(hr)) + { + if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) && RerunBind(dwBindFlags)) + { + // 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. + pRetargetedAssemblyName->SetIsDefinition(TRUE); + hr = S_OK; + } + + if (!Assembly::IsValidArchitecture(pRetargetedAssemblyName->GetArchitecture())) + { + // Assembly reference contains wrong architecture + IF_FAIL_GO(FUSION_E_INVALID_NAME); + } + + IF_FAIL_GO(BindLocked(pApplicationContext, + pRetargetedAssemblyName, + dwBindFlags, + pBindResult)); + + if (!pBindResult->HaveResult()) + { + // Behavior rules are clueless now + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + } + + Exit: + if (FAILED(hr) && CacheBindFailures(dwBindFlags)) + { + if (RerunBind(dwBindFlags)) + { + 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: + + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::BindByName"), hr); + 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, + BindResult *pBindResult) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("AssemblyBinder::BindWhereRef")); + + ReleaseHolder<Assembly> pAssembly; + ReleaseHolder<AssemblyName> pRetargetedAssemblyName; + BOOL fIsRetargeted = FALSE; + 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 /* fInspectionOnly */, + 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)); +#ifdef FEATURE_VERSIONING_LOG + IF_FAIL_GO(LogAssemblyNameWhereRef(pApplicationContext, pAssembly)); +#endif // FEATURE_VERSIONING_LOG + + AssemblyName *pAssemblyName; + pAssemblyName = pAssembly->GetAssemblyName(); + + IF_FAIL_GO(Compatibility::Retarget(pAssemblyName, &pRetargetedAssemblyName, &fIsRetargeted)); + + if (!fNgenExplicitBind) + { + IF_FAIL_GO(BindLockedOrService(pApplicationContext, + pRetargetedAssemblyName, + &lockedBindResult)); + if (lockedBindResult.HaveResult()) + { + pBindResult->SetResult(&lockedBindResult); + GO_WITH_HRESULT(S_OK); + } + + else if (fIsRetargeted) + { + // We did not find retargeted assembly. + // Check that assembly we have does match (must have retargetable property set). + ReleaseHolder<AssemblyName> pClonedAssemblyName; + + IF_FAIL_GO(pRetargetedAssemblyName->Clone(&pClonedAssemblyName)); + pClonedAssemblyName->SetIsRetargetable(TRUE); + + if (!pClonedAssemblyName->Equals(pAssemblyName, AssemblyName::INCLUDE_VERSION)) + { + IF_FAIL_GO(FUSION_E_REF_DEF_MISMATCH); + } + } + } + + hr = S_OK; + pAssembly->SetIsDynamicBind(TRUE); + pBindResult->SetResult(pAssembly); + + Exit: + + if (FAILED(hr)) + { + // Always cache binding failures + hr = pApplicationContext->AddToFailureCache(assemblyPath, hr); + } + + LogExit: + + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::BindWhereRef"), hr); + return hr; + } + + /* static */ + HRESULT AssemblyBinder::BindLocked(ApplicationContext *pApplicationContext, + AssemblyName *pAssemblyName, + DWORD dwBindFlags, + BindResult *pBindResult) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("AssemblyBinder::BindLocked")); + + BOOL fIgnoreDynamicBinds = IgnoreDynamicBinds(dwBindFlags); + +#ifndef CROSSGEN_COMPILE + ContextEntry *pContextEntry = NULL; + IF_FAIL_GO(FindInExecutionContext(pApplicationContext, pAssemblyName, &pContextEntry)); + if (pContextEntry != NULL) + { + if (fIgnoreDynamicBinds && pContextEntry->GetIsDynamicBind()) + { + // Dynamic binds need to be always considered a failure for binding closures + IF_FAIL_GO(FUSION_E_APP_DOMAIN_LOCKED); + } +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + else if (IgnoreRefDefMatch(dwBindFlags)) + { + // Skip RefDef matching if we have been asked to. + } +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + else + { + // Can't give higher serciving than already bound + IF_FAIL_GO(IsValidAssemblyVersion(pAssemblyName, + pContextEntry->GetAssemblyName(), + pApplicationContext, + !!pContextEntry->GetIsInGAC())); + } + + pBindResult->SetResult(pContextEntry); + } + else +#endif // !CROSSGEN_COMPILE + if (pApplicationContext->IsTpaListProvided()) + { + bool fUnifiedAppAssemblyToPlatform = false; + IF_FAIL_GO(BindByTpaList(pApplicationContext, + pAssemblyName, + FALSE /*fInspectionOnly*/, + pBindResult, + &fUnifiedAppAssemblyToPlatform)); + if (pBindResult->HaveResult()) + { + bool fBeingBoundToPlatformAssembly = !!pBindResult->GetIsInGAC(); + if (fUnifiedAppAssemblyToPlatform) + { + // Pretend as if we have bound to an app assembly since the RequestedAssemblyName came in for an app + // assembly and thus, may not contain a version number. + fBeingBoundToPlatformAssembly = false; + } + + hr = IsValidAssemblyVersion(pAssemblyName, pBindResult->GetAssemblyName(), pApplicationContext, fBeingBoundToPlatformAssembly); + if (FAILED(hr)) + { + pBindResult->SetNoResult(); + } + } + } + Exit: + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::BindLocked"), hr); + return hr; + } + + /* static */ + HRESULT AssemblyBinder::BindLockedOrService(ApplicationContext *pApplicationContext, + AssemblyName *pAssemblyName, + BindResult *pBindResult) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("AssemblyBinder::BindLockedOrService")); + + BindResult lockedBindResult; + + IF_FAIL_GO(BindLocked(pApplicationContext, + pAssemblyName, + 0 /* Do not IgnoreDynamicBinds */, + &lockedBindResult)); + + if (lockedBindResult.HaveResult()) + { + // Locked Bind succeeded + pBindResult->SetResult(&lockedBindResult); + } + else + { + pBindResult->SetNoResult(); + } + + Exit: + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::BindLockedOrService"), hr); + return hr; + } + +#ifndef CROSSGEN_COMPILE + /* static */ + HRESULT AssemblyBinder::FindInExecutionContext(ApplicationContext *pApplicationContext, + AssemblyName *pAssemblyName, + ContextEntry **ppContextEntry) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("AssemblyBinder::FindInExecutionContext")); + + _ASSERTE(pApplicationContext != NULL); + _ASSERTE(pAssemblyName != NULL); + _ASSERTE(ppContextEntry != NULL); + + ExecutionContext *pExecutionContext = pApplicationContext->GetExecutionContext(); + ContextEntry *pContextEntry = pExecutionContext->Lookup(pAssemblyName); + + if (pContextEntry != NULL) + { + AssemblyName *pContextName = pContextEntry->GetAssemblyName(); + +#ifdef FEATURE_VERSIONING_LOG + // First-time requests are considered unlocked, everything else is locked + DWORD dwLockedReason = (pContextEntry->GetIsFirstRequest() ? + APP_DOMAIN_LOCKED_UNLOCKED : APP_DOMAIN_LOCKED_CONTEXT); + + IF_FAIL_GO(LogAppDomainLocked(pApplicationContext, dwLockedReason, pContextName)); + pContextEntry->SetIsFirstRequest(FALSE); +#endif // FEATURE_VERSIONING_LOG + + if (pAssemblyName->GetIsDefinition() && + (pContextName->GetArchitecture() != pAssemblyName->GetArchitecture())) + { + IF_FAIL_GO(FUSION_E_APP_DOMAIN_LOCKED); + } + } + + *ppContextEntry = pContextEntry; + + Exit: + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::FindInExecutionContext"), hr); + return hr; + } + +#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(ApplicationContext *pApplicationContext, + AssemblyName *pRequestedAssemblyName, + AssemblyName *pBoundAssemblyName, + BOOL tpaListAssembly) + { + DWORD dwIncludeFlags = AssemblyName::INCLUDE_DEFAULT; + + bool fWindowsPhone7 = false; +#ifdef FEATURE_LEGACYNETCF + fWindowsPhone7 = RuntimeIsLegacyNetCF(pApplicationContext->GetAppDomainId()) == TRUE; +#endif + // + // Windows Phone 7 Quirk: + // + // NetCF allows partial binds to platform assemblies. If we are running a + // Mango application, skip the PKT check if no Ref version is provided, + // since there are apps in the Marketplace that do Assembly.Load("System") + // + if (!tpaListAssembly || (fWindowsPhone7 && tpaListAssembly)) + { + dwIncludeFlags |= AssemblyName::EXCLUDE_PUBLIC_KEY_TOKEN_IF_MISSING; + + // + // On Windows Phone 7, exclude culture comparisons when requesting an uncultured + // assembly for app compat reasons (there are main app assemblies with spurious cultures) + // + SString &culture = pRequestedAssemblyName->GetCulture(); + if (culture.IsEmpty() || culture.EqualsCaseInsensitive(g_BinderVariables->cultureNeutral)) + { + dwIncludeFlags |= AssemblyName::EXCLUDE_CULTURE; + } + } + + if (pRequestedAssemblyName->GetArchitecture() != peNone) + { + dwIncludeFlags |= AssemblyName::INCLUDE_ARCHITECTURE; + } + + BINDER_LOG_ASSEMBLY_NAME(W("pBoundAssemblyName"), pBoundAssemblyName); + + return pBoundAssemblyName->Equals(pRequestedAssemblyName, dwIncludeFlags); + } + + HRESULT BindSatelliteResourceByResourceRoots(ApplicationContext *pApplicationContext, + StringArrayList *pResourceRoots, + AssemblyName *pRequestedAssemblyName, + BOOL fInspectionOnly, + BindResult *pBindResult) + { + HRESULT hr = S_OK; + + SString &simpleNameRef = pRequestedAssemblyName->GetSimpleName(); + SString &cultureRef = pRequestedAssemblyName->GetCulture(); + + _ASSERTE(!cultureRef.IsEmpty() && !cultureRef.EqualsCaseInsensitive(g_BinderVariables->cultureNeutral)); + + for (UINT i = 0; i < pResourceRoots->GetCount(); i++) + { + ReleaseHolder<Assembly> pAssembly; + SString &wszBindingPath = (*pResourceRoots)[i]; + SString fileName(wszBindingPath); + + CombinePath(fileName, cultureRef, fileName); + CombinePath(fileName, simpleNameRef, fileName); + fileName.Append(W(".dll")); + + hr = AssemblyBinder::GetAssembly(fileName, + fInspectionOnly, + FALSE /* fIsInGAC */, + FALSE /* fExplicitBindToNativeImage */, + &pAssembly); + + // Missing files are okay and expected when probing + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + continue; + } + + IF_FAIL_GO(hr); + + AssemblyName *pBoundAssemblyName = pAssembly->GetAssemblyName(); + if (TestCandidateRefMatchesDef(pApplicationContext, pRequestedAssemblyName, pBoundAssemblyName, false /*tpaListAssembly*/)) + { + pBindResult->SetResult(pAssembly); + GO_WITH_HRESULT(S_OK); + } + +#ifdef FEATURE_VERSIONING_LOG + // Log the candidates we throw out for diagnostics + IF_FAIL_GO(LogConfigurationError(pApplicationContext, + pRequestedAssemblyName, + pBoundAssemblyName)); +#endif // FEATURE_VERSIONING_LOG + + IF_FAIL_GO(FUSION_E_REF_DEF_MISMATCH); + } + + // Up-stack expects S_OK when we don't find any candidate assemblies and no fatal error occured (ie, no S_FALSE) + hr = S_OK; + Exit: + return hr; + } + + /* + * BindByTpaList is the entry-point for the custom binding algorithm on the Phone. + * 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 fInspectionOnly, + BindResult *pBindResult, + bool *pfUnifiedAppAssemblyToPlatform) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("AssemblyBinder::BindByTpaList")); + + SString &culture = pRequestedAssemblyName->GetCulture(); + bool fPartialMatchOnTpa = false; + + // Did we unify an app assembly (with same SimpleName+Culture+PKT) to a platform (TPA) assembly? + *pfUnifiedAppAssemblyToPlatform = false; + + if (!culture.IsEmpty() && !culture.EqualsCaseInsensitive(g_BinderVariables->cultureNeutral)) + { + // + // Satellite resource probing strategy is to look under each of the Platform Resource Roots + // followed by App Paths. + // + + hr = BindSatelliteResourceByResourceRoots(pApplicationContext, + pApplicationContext->GetPlatformResourceRoots(), + pRequestedAssemblyName, + fInspectionOnly, + pBindResult); + + // We found 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. + if (hr != FUSION_E_CONFIGURATION_ERROR) + { + IF_FAIL_GO(hr); + } + + if (!pBindResult->HaveResult()) + { + IF_FAIL_GO(BindSatelliteResourceByResourceRoots(pApplicationContext, + pApplicationContext->GetAppPaths(), + pRequestedAssemblyName, + fInspectionOnly, + pBindResult)); + } + } + else + { + // Is assembly on TPA list? + SString &simpleName = pRequestedAssemblyName->GetSimpleName(); + SimpleNameToFileNameMap * tpaMap = pApplicationContext->GetTpaList(); + const SimpleNameToFileNameMapEntry *pTpaEntry = tpaMap->LookupPtr(simpleName.GetUnicode()); + ReleaseHolder<Assembly> pTPAAssembly; + if (pTpaEntry != nullptr) + { + if (pTpaEntry->m_wszNIFileName != nullptr) + { + SString fileName(pTpaEntry->m_wszNIFileName); + + // A GetAssembly overload perhaps, or just another parameter to the existing method + hr = GetAssembly(fileName, + fInspectionOnly, + TRUE, /* fIsInGAC */ + TRUE /* fExplicitBindToNativeImage */, + &pTPAAssembly + ); + } + else + { + _ASSERTE(pTpaEntry->m_wszILFileName != nullptr); + SString fileName(pTpaEntry->m_wszILFileName); + + hr = GetAssembly(fileName, + fInspectionOnly, + TRUE, /* fIsInGAC */ + FALSE /* fExplicitBindToNativeImage */, + &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(pApplicationContext, 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); + GO_WITH_HRESULT(S_OK); + } + else + { + // We found the assembly on TPA but it didnt match the RequestedAssembly assembly-name. In this case, lets proceed to see if we find the requested + // assembly in the App paths. + fPartialMatchOnTpa = true; + } + } + + // We either didn't find a candidate, or the ref-def failed. Either way; fall back to app path probing. + } + + bool fUseAppPathsBasedResolver = true; + +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + // If Host Assembly Resolver is specified, then we will use that as the override for the default resolution mechanism (that uses AppPath probing). + if (!RuntimeCanUseAppPathAssemblyResolver(pApplicationContext->GetAppDomainId())) + { + fUseAppPathsBasedResolver = false; + } +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + + // This loop executes twice max. First time through we probe AppNiPaths, the second time we probe AppPaths + bool parseNiPaths = true; + while (fUseAppPathsBasedResolver) + { + StringArrayList *pBindingPaths = parseNiPaths ? pApplicationContext->GetAppNiPaths() : pApplicationContext->GetAppPaths(); + + // Loop through the binding paths looking for a matching assembly + for (DWORD i = 0; i < pBindingPaths->GetCount(); i++) + { + ReleaseHolder<Assembly> pAssembly; + LPCWSTR wszBindingPath = (*pBindingPaths)[i]; + + SString &simpleName = pRequestedAssemblyName->GetSimpleName(); + + // Look for a matching dll first + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + { + SString fileName(wszBindingPath); + CombinePath(fileName, simpleName, fileName); + if (parseNiPaths) + { + fileName.Append(W(".ni.dll")); + hr = GetAssembly(fileName, + fInspectionOnly, + FALSE, /* fIsInGAC */ + TRUE /* fExplicitBindToNativeImage */, + &pAssembly); + } + else + { + if (FAILED(hr)) + { + fileName.Append(W(".dll")); + + hr = GetAssembly(fileName, + fInspectionOnly, + FALSE, /* fIsInGAC */ + FALSE /* fExplicitBindToNativeImage */, + &pAssembly); + } + } + } + + if (FAILED(hr)) + { + SString fileName(wszBindingPath); + CombinePath(fileName, simpleName, fileName); + + if (parseNiPaths) + { + fileName.Append(W(".ni.exe")); + hr = GetAssembly(fileName, + fInspectionOnly, + FALSE, /* fIsInGAC */ + TRUE /* fExplicitBindToNativeImage */, + &pAssembly); + } + else + { + if (FAILED(hr)) + { + fileName.Append(W(".exe")); + + hr = GetAssembly(fileName, + fInspectionOnly, + FALSE, /* fIsInGAC */ + FALSE /* fExplicitBindToNativeImage */, + &pAssembly); + } + } + } + + // 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; + } + IF_FAIL_GO(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(pApplicationContext, pRequestedAssemblyName, pAssembly->GetAssemblyName(), false /*tpaListAssembly*/)) + { + // 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(pApplicationContext, pAssembly->GetAssemblyName(), pTPAAssembly->GetAssemblyName(), true /*tpaListAssembly*/)) + { + // Fullname (SimpleName+Culture+PKT) matched for TPA and app assembly - so bind to TPA instance. + *pfUnifiedAppAssemblyToPlatform = true; + pBindResult->SetResult(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 didnt see this assembly on TPA - so simply bind to the app instance. + pBindResult->SetResult(pAssembly); + GO_WITH_HRESULT(S_OK); + } + } + + #ifdef FEATURE_VERSIONING_LOG + // Log the candidates we throw out for diagnostics + IF_FAIL_GO(LogConfigurationError(pApplicationContext, + pRequestedAssemblyName, + pAssembly->GetAssemblyName())); + #endif // FEATURE_VERSIONING_LOG + + IF_FAIL_GO(FUSION_E_REF_DEF_MISMATCH); + + } + + if (!parseNiPaths) + { + break; + } + + parseNiPaths = false; + } + } + + // Couldn't find a matching assembly in any of the probing paths + // Return S_OK here. BindByName will interpret a lack of BindResult + // as a failure to find a matching assembly. Other callers of this + // function, such as BindLockedOrService will interpret as deciding + // not to override an explicit bind with a probed assembly + hr = S_OK; + + Exit: + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::BindByTpaList"), hr); + return hr; + } + + /* static */ + HRESULT AssemblyBinder::GetAssembly(SString &assemblyPath, + BOOL fInspectionOnly, + 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) + { + HRESULT hr = S_OK; + + BINDER_LOG_ENTER(W("Assembly::GetAssembly")); + BINDER_LOG_STRING(W("assemblyPath"), assemblyPath); + + _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()); + + BINDER_LOG_ENTER(W("BinderAcquirePEImage")); + hr = BinderAcquirePEImage(szAssemblyPath, &pPEImage, &pNativePEImage, fExplicitBindToNativeImage); + BINDER_LOG_LEAVE_HR(W("BinderAcquirePEImage"), hr); + 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) + { + pPEImage = pNativePEImage; + pNativePEImage = NULL; + } + } + + BINDER_LOG_ENTER(W("BinderAcquireImport")); + if(pNativePEImage) + hr = BinderAcquireImport(pNativePEImage, &pIMetaDataAssemblyImport, dwPAFlags, TRUE); + else + hr = BinderAcquireImport(pPEImage, &pIMetaDataAssemblyImport, dwPAFlags, FALSE); + + BINDER_LOG_LEAVE_HR(W("BinderAcquireImport"), hr); + IF_FAIL_GO(hr); + + if (pIMetaDataAssemblyImport == NULL && pNativePEImage != NULL) + { + // The native image doesn't contain metadata. Currently this is only supported for Windows.ni.winmd. + // While loading Windows.winmd, CLRPrivBinderWinRT::GetAssemblyAndTryFindNativeImage should have passed + // in a non-NULL szMDAssemblyPath, where we can load metadata from. If szMDAssemblyPath is NULL, + // it indicates that the app is trying to load a non-WinMD assembly named Windows (it's possible + // that the app happens to contain an assembly Windows.dll). To handle this case properly, we + // return a file-not-found error, so the caller can continues it's search. + if (szMDAssemblyPath == NULL) + { + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + } + else + { + BINDER_LOG_ENTER(W("BinderAcquirePEImage")); + hr = BinderAcquirePEImage(szMDAssemblyPath, &pPEImage, NULL, FALSE); + BINDER_LOG_LEAVE_HR(W("BinderAcquirePEImage"), hr); + IF_FAIL_GO(hr); + + BINDER_LOG_ENTER(W("BinderAcquireImport")); + hr = BinderAcquireImport(pPEImage, &pIMetaDataAssemblyImport, dwPAFlags, FALSE); + BINDER_LOG_LEAVE_HR(W("BinderAcquireImport"), hr); + IF_FAIL_GO(hr); + } + } + + IF_FAIL_GO(TranslatePEToArchitectureType(dwPAFlags, &PeKind)); + } + + // Initialize assembly object + IF_FAIL_GO(pAssembly->Init(pIMetaDataAssemblyImport, + PeKind, + pPEImage, + pNativePEImage, + assemblyPath, + fInspectionOnly, + fIsInGAC)); + + // We're done + *ppAssembly = pAssembly.Extract(); + + Exit: + + BinderReleasePEImage(pPEImage); + BinderReleasePEImage(pNativePEImage); + + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::GetAssembly"), hr); + + // 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, + BOOL fInspectionOnly, + BindResult *pBindResult) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("AssemblyBinder::Register")); + + if (!pBindResult->GetIsContextBound()) + { + pApplicationContext->IncrementVersion(); + + if (fInspectionOnly) + { + InspectionContext *pInspectionContext = pApplicationContext->GetInspectionContext(); + IF_FAIL_GO(pInspectionContext->Register(pBindResult)); + } + else + { + // 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 (hr == S_OK) + { + if (pContextEntry == NULL) + { + ExecutionContext *pExecutionContext = pApplicationContext->GetExecutionContext(); + IF_FAIL_GO(pExecutionContext->Register(pBindResult)); + } + else + { + // The dynamic binds are compiled in CoreCLR, but they are not supported. They are only reachable by internal API Assembly.Load(byte[]) that nobody should be calling. + // This code path does not handle dynamic binds correctly (and is not expected to). We do not expect to come here for dynamic binds. + + _ASSERTE(!pContextEntry->GetIsDynamicBind()); + + // Update the BindResult with the contents of the ContextEntry we found + pBindResult->SetResult(pContextEntry); + } + } + } + } + + Exit: + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::Register"), hr); + return hr; + } + + /* static */ + HRESULT AssemblyBinder::RegisterAndGetHostChosen(ApplicationContext *pApplicationContext, + LONG kContextVersion, + BindResult *pBindResult, + BindResult *pHostBindResult) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("AssemblyBinder::RegisterHostChosen")); + + _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, + FALSE /* fInspectionOnly */, + pHostBindResult)); + } + } + else + { + // No work required. Return the input + pHostBindResult->SetResult(pBindResult); + } + + Exit: + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::RegisterHostChosen"), hr); + return hr; + } + + /* static */ + HRESULT AssemblyBinder::OtherBindInterfered(ApplicationContext *pApplicationContext, + BindResult *pBindResult) + { + HRESULT hr = S_FALSE; + BINDER_LOG_ENTER(W("AssemblyBinder::OtherBindInterfered")); + 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 ((hr == S_OK) && (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: + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::OtherBindInterfered"), hr); + return hr; + } + +#endif //CROSSGEN_COMPILE + +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) +HRESULT AssemblyBinder::BindUsingHostAssemblyResolver (/* in */ CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin, + /* in */ AssemblyName *pAssemblyName, + /* in */ IAssemblyName *pIAssemblyName, + /* out */ Assembly **ppAssembly) +{ + HRESULT hr = E_FAIL; + BINDER_LOG_ENTER(W("AssemblyBinder::BindUsingHostAssemblyResolver")); + + _ASSERTE(pLoadContextToBindWithin != NULL); + + // Get the application context within which the assembly will be bound and loaded + ApplicationContext *pApplicationContext = pLoadContextToBindWithin->GetAppContext(); + _ASSERTE(pApplicationContext != NULL); + + // Call into the VM to use the HostAssemblyResolver and load the assembly + ICLRPrivAssembly *pLoadedAssembly = NULL; + hr = RuntimeInvokeHostAssemblyResolver(pLoadContextToBindWithin, pIAssemblyName, &pLoadedAssembly); + if (SUCCEEDED(hr)) + { + _ASSERTE(pLoadedAssembly != NULL); + *ppAssembly = static_cast<Assembly *>(pLoadedAssembly); + } + + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::BindUsingHostAssemblyResolver"), hr); + 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; + BINDER_LOG_ENTER(W("AssemblyBinder::BindUsingPEImage")); + + _ASSERTE(BINDER_SPACE::fAssemblyBinderInitialized == TRUE); + + LONG kContextVersion = 0; + BindResult bindResult; + + // Prepare binding data + *ppAssembly = NULL; + + // Attempt the actual bind (eventually more than once) +Retry: + { + // Lock the application context + CRITSEC_Holder contextLock(pApplicationContext->GetCriticalSectionCookie()); + + // Attempt uncached bind and register stream if possible + hr = BindByName(pApplicationContext, + pAssemblyName, + BIND_CACHE_FAILURES|BIND_CACHE_RERUN_BIND|BIND_IGNORE_REFDEF_MATCH, + &bindResult); + + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + IF_FAIL_GO(CreateImageAssembly(pIMetaDataAssemblyImport, + peKind, + pPEImage, + NULL, + FALSE, + &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)); + + if (incomingMVID != boundMVID) + { + // 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 is 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) + { + // Another bind interfered. We need to retry entire bind. + // This by design loops as long as needed because by constuction we eventually + // will succeed or fail the bind. + bindResult.Reset(); + goto Retry; + } + else if (hr == S_OK) + { + *ppAssembly = hostBindResult.GetAsAssembly(TRUE /* fAddRef */); + } + } + +Exit: + + BINDER_LOG_LEAVE_HR(W("AssemblyBinder::BindUsingPEImage"), hr); + return hr; +} +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) +}; + + diff --git a/src/binder/assemblyidentitycache.cpp b/src/binder/assemblyidentitycache.cpp new file mode 100644 index 0000000000..798b60b0f0 --- /dev/null +++ b/src/binder/assemblyidentitycache.cpp @@ -0,0 +1,73 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// AssemblyIdentityCache.cpp +// + + +// +// Implements the AssemblyIdentityCache class +// +// ============================================================ + +#define DISABLE_BINDER_DEBUG_LOGGING + +#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; + BINDER_LOG_ENTER(L"AssemblyIdentityCache::Add"); + + NewHolder<AssemblyIdentityCacheEntry> pAssemblyIdentityCacheEntry; + SAFE_NEW(pAssemblyIdentityCacheEntry, AssemblyIdentityCacheEntry); + + pAssemblyIdentityCacheEntry->SetTextualIdentity(szTextualIdentity); + pAssemblyIdentityCacheEntry->SetAssemblyIdentity(pAssemblyIdentity); + + Hash::Add(pAssemblyIdentityCacheEntry); + pAssemblyIdentityCacheEntry.SuppressRelease(); + + Exit: + BINDER_LOG_LEAVE_HR(L"AssemblyIdentityCache::Add", hr); + return hr; + } + + AssemblyIdentityUTF8 *AssemblyIdentityCache::Lookup(LPCSTR szTextualIdentity) + { + BINDER_LOG_ENTER(L"AssemblyIdentityCache::Lookup"); + AssemblyIdentityUTF8 *pAssemblyIdentity = NULL; + AssemblyIdentityCacheEntry *pAssemblyIdentityCacheEntry = Hash::Lookup(szTextualIdentity); + + if (pAssemblyIdentityCacheEntry != NULL) + { + BINDER_LOG(L"Found cached identity"); + pAssemblyIdentity = pAssemblyIdentityCacheEntry->GetAssemblyIdentity(); + } + + BINDER_LOG_LEAVE(L"AssemblyIdentityCache::Lookup"); + return pAssemblyIdentity; + } +}; diff --git a/src/binder/assemblyname.cpp b/src/binder/assemblyname.cpp new file mode 100644 index 0000000000..3821b5c5a3 --- /dev/null +++ b/src/binder/assemblyname.cpp @@ -0,0 +1,705 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// AssemblyName.cpp +// + + +// +// Implements the AssemblyName class +// +// ============================================================ + +#define DISABLE_BINDER_DEBUG_LOGGING + +#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; + + BINDER_LOG_ENTER(L"AssemblyName::Init(IMetaDataAssemblyImport)"); + + if (fIsDefinition) + { + // Get the assembly token + IF_FAIL_GO(pIMetaDataAssemblyImport->GetAssemblyFromScope(&mda)); + } + + BINDER_LOG(L"Have mda scope!"); + + // 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. + )); + } + + BINDER_LOG(L"Have props!"); + + { + 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) + { + 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 (IsAfContentType_WindowsRuntime(dwRefOrDefFlags)) + { + SetContentType(AssemblyContentType_WindowsRuntime); + } + 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: + BINDER_LOG_LEAVE_HR(L"AssemblyName::Init(IMetaDataAssemblyImport)", hr); + return hr; + } + + HRESULT AssemblyName::Init(SString &assemblyDisplayName) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"AssemblyName::Init(assemblyDisplayName)"); + + BINDER_LOG_STRING(L"assemblyDisplayName", assemblyDisplayName); + + IF_FAIL_GO(TextualIdentityParser::Parse(assemblyDisplayName, this)); + + Exit: + BINDER_LOG_LEAVE_HR(L"AssemblyName::Init(assemblyDisplayName)", hr); + return hr; + } + + 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; + } + + HRESULT AssemblyName::CreateFusionName(IAssemblyName **ppIAssemblyName) + { + HRESULT hr = S_OK; + ReleaseHolder<IAssemblyName> pIAssemblyName; + + IF_FAIL_GO(CreateAssemblyNameObject(&pIAssemblyName, NULL, 0, NULL)); + + IF_FAIL_GO(LegacyFusion::SetStringProperty(pIAssemblyName, ASM_NAME_NAME, GetSimpleName())); + + if (Have(AssemblyIdentity::IDENTITY_FLAG_VERSION)) + { + AssemblyVersion *pAssemblyVersion = GetVersion(); + + IF_FAIL_GO(LegacyFusion::SetWordProperty(pIAssemblyName, + ASM_NAME_MAJOR_VERSION, + pAssemblyVersion->GetMajor())); + IF_FAIL_GO(LegacyFusion::SetWordProperty(pIAssemblyName, + ASM_NAME_MINOR_VERSION, + pAssemblyVersion->GetMinor())); + IF_FAIL_GO(LegacyFusion::SetWordProperty(pIAssemblyName, + ASM_NAME_BUILD_NUMBER, + pAssemblyVersion->GetBuild())); + IF_FAIL_GO(LegacyFusion::SetWordProperty(pIAssemblyName, + ASM_NAME_REVISION_NUMBER, + pAssemblyVersion->GetRevision())); + } + + if (Have(AssemblyIdentity::IDENTITY_FLAG_CULTURE)) + { + IF_FAIL_GO(LegacyFusion::SetStringProperty(pIAssemblyName, ASM_NAME_CULTURE, GetCulture())); + } + + if (Have(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY)) + { + // GetPublicKeyTokenBLOB contains either PK or PKT. + IF_FAIL_GO(LegacyFusion::SetBufferProperty(pIAssemblyName, + ASM_NAME_PUBLIC_KEY, + GetPublicKeyTokenBLOB())); + } + else if (Have(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN)) + { + // GetPublicKeyTokenBLOB contains either PK or PKT. + IF_FAIL_GO(LegacyFusion::SetBufferProperty(pIAssemblyName, + ASM_NAME_PUBLIC_KEY_TOKEN, + GetPublicKeyTokenBLOB())); + } + + if (Have(AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE)) + { + IF_FAIL_GO(LegacyFusion::SetDwordProperty(pIAssemblyName, + ASM_NAME_ARCHITECTURE, + static_cast<DWORD>(GetArchitecture()))); + } + + if (Have(AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE)) + { + IF_FAIL_GO(LegacyFusion::SetDwordProperty(pIAssemblyName, + ASM_NAME_CONTENT_TYPE, + GetContentType())); + } + + *ppIAssemblyName = pIAssemblyName.Extract(); + + 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; + } + + SString &AssemblyName::GetDeNormalizedCulture() + { + SString &culture = GetCulture(); + + if (EqualsCaseInsensitive(culture, g_BinderVariables->cultureNeutral)) + { + culture = g_BinderVariables->emptyString; + } + + return culture; + } + + BOOL AssemblyName::IsStronglyNamed() + { + return Have(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN); + } + + BOOL AssemblyName::IsMscorlib() + { + // TODO: Is this simple comparison enough? + return EqualsCaseInsensitive(GetSimpleName(), g_BinderVariables->mscorlib); + } + + HRESULT AssemblyName::SetArchitecture(SString &architecture) + { + HRESULT hr = S_OK; + + if (architecture.IsEmpty()) + { + SetArchitecture(peNone); + } + else if (EqualsCaseInsensitive(architecture, g_BinderVariables->architectureMSIL)) + { + SetArchitecture(peMSIL); + } + else if (EqualsCaseInsensitive(architecture, g_BinderVariables->architectureX86)) + { + SetArchitecture(peI386); + } + else if (EqualsCaseInsensitive(architecture, g_BinderVariables->architectureAMD64)) + { + SetArchitecture(peAMD64); + } + else if (EqualsCaseInsensitive(architecture, g_BinderVariables->architectureARM)) + { + SetArchitecture(peARM); + } + else if (EqualsCaseInsensitive(architecture, g_BinderVariables->architectureARM64)) + { + SetArchitecture(peARM64); + } + else + { + hr = FUSION_E_MANIFEST_PARSE_ERROR; + } + + return hr; + } + + 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; + } + + 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 & EXCLUDE_PUBLIC_KEY_TOKEN_IF_MISSING) == 0 || + (pAssemblyName->Have(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN)))) + { + 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; + } + + BOOL AssemblyName::RefEqualsDef(AssemblyName *pAssemblyNameDef, + BOOL fInspectionOnly) + { + BOOL fEquals = FALSE; + + if (GetContentType() == AssemblyContentType_WindowsRuntime) + { // Assembly is meaningless for WinRT, all assemblies form one joint type namespace + return (GetContentType() == pAssemblyNameDef->GetContentType()); + } + + if (EqualsCaseInsensitive(GetSimpleName(), pAssemblyNameDef->GetSimpleName()) && + EqualsCaseInsensitive(GetNormalizedCulture(), + pAssemblyNameDef->GetNormalizedCulture()) && + GetPublicKeyTokenBLOB().Equals(pAssemblyNameDef->GetPublicKeyTokenBLOB()) && + (GetContentType() == pAssemblyNameDef->GetContentType())) + { + PEKIND kRefArchitecture = GetArchitecture(); + PEKIND kDefArchitecture = pAssemblyNameDef->GetArchitecture(); + + if (kRefArchitecture == peNone) + { + fEquals = (fInspectionOnly || + (kDefArchitecture == peNone) || + (kDefArchitecture == peMSIL) || + (kDefArchitecture == Assembly::GetSystemArchitecture())); + } + else + { + fEquals = (kRefArchitecture == kDefArchitecture); + } + } + + return fEquals; + } + + HRESULT AssemblyName::Clone(AssemblyName **ppAssemblyName) + { + HRESULT hr = S_OK; + AssemblyName *pClonedAssemblyName = NULL; + + SAFE_NEW(pClonedAssemblyName, AssemblyName); + CloneInto(pClonedAssemblyName); + pClonedAssemblyName->m_dwNameFlags = m_dwNameFlags; + + *ppAssemblyName = pClonedAssemblyName; + + Exit: + return hr; + } + + 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::ArchitectureToString(PEKIND kArchitecture) + { + switch (kArchitecture) + { + case peNone: + return g_BinderVariables->emptyString; + case peMSIL: + return g_BinderVariables->architectureMSIL; + case peI386: + return g_BinderVariables->architectureX86; + case peAMD64: + return g_BinderVariables->architectureAMD64; + case peARM: + return g_BinderVariables->architectureARM; + case peARM64: + return g_BinderVariables->architectureARM64; + default: + _ASSERTE(0); + return g_BinderVariables->emptyString; + } + } + + SString &AssemblyName::GetNormalizedCulture() + { + SString &culture = GetCulture(); + + if (culture.IsEmpty()) + { + culture = g_BinderVariables->cultureNeutral; + } + + return culture; + } +}; // namespace BINDER_SPACE diff --git a/src/binder/binder.targets b/src/binder/binder.targets new file mode 100644 index 0000000000..fc214c9ecb --- /dev/null +++ b/src/binder/binder.targets @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <PropertyGroup Label="Globals"> + <SccProjectName>SAK</SccProjectName> + <SccAuxPath>SAK</SccAuxPath> + <SccLocalPath>SAK</SccLocalPath> + <SccProvider>SAK</SccProvider> + </PropertyGroup> + <!--Leaf project Properties--> + <PropertyGroup> + <TargetType>LIBRARY</TargetType> + <OutputPath>$(ClrLibDest)</OutputPath> + <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE </ClAdditionalOptions> + <LinkAdditionalOptions>$(LinkAdditionalOptions) /VERBOSE</LinkAdditionalOptions> + <BinderPath>$(Clrbase)\src\binder</BinderPath> + <UserIncludes> + $(UserIncludes); + $(BinderPath); + $(BinderPath)\inc; + $(Clrbase)\src\inc; + $(Clrbase)\src\vm; + $(Clrbase)\src\vm\$(TargetCpu); + $(Clrbase)\src\strongname\inc; + $(SdkIncPath); + </UserIncludes> + </PropertyGroup> + <!--Leaf Project Items--> + <ItemGroup> + <CppCompile Include="..\Variables.cpp" /> + <CppCompile Include="..\Utils.cpp" /> + <CppCompile Include="..\AssemblyName.cpp" /> + <CppCompile Include="..\PropertyMap.cpp" /> + <CppCompile Include="..\ApplicationContext.cpp" /> + <CppCompile Include="..\Assembly.cpp" /> + <CppCompile Include="..\FailureCache.cpp" /> + <CppCompile Include="..\AssemblyBinder.cpp" /> + <CppCompile Include="..\StringLexer.cpp" /> + <CppCompile Include="..\CLRPrivBinderCoreCLR.cpp" /> + <CppCompile Include="..\BinderInterface.cpp" /> + <CppCompile Include="..\DebugLog.cpp" /> + <CppCompile Include="..\BindingLog.cpp" /> + <CppCompile Include="..\CDebugLog.cpp" /> + <CppCompile Include="..\Compatibility.cpp" /> + <CppCompile Include="..\TextualIdentityParser.cpp" /> + <CppCompile Include="..\AssemblyIdentityCache.cpp" /> + <CppCompile Include="..\CLRPrivBinderAssemblyLoadContext.cpp" Condition="'$(FeatureHostAssemblyResolver)' == 'true'"/> + <CppCompile Include="..\CoreCLRBinderCommon.cpp"/> + <CppCompile Include="..\FusionAssemblyName.cpp"/> + <CppCompile Include="..\FusionHelpers.cpp"/> + </ItemGroup> +</Project> diff --git a/src/binder/binderinterface.cpp b/src/binder/binderinterface.cpp new file mode 100644 index 0000000000..a5799e9009 --- /dev/null +++ b/src/binder/binderinterface.cpp @@ -0,0 +1,191 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// BinderInterface.cpp +// + + +// +// Implements the public AssemblyBinder interface +// +// ============================================================ + +#include "assemblybinder.hpp" +#include "assemblyname.hpp" +#include "applicationcontext.hpp" +#include "binderinterface.hpp" +#include "bindresult.inl" +#include "utils.hpp" + +#include "ex.h" + +using namespace BINDER_SPACE; + +namespace BinderInterface +{ + + HRESULT Init() + { + HRESULT hr = S_OK; + + EX_TRY + { + hr = AssemblyBinder::Startup(); + } + EX_CATCH_HRESULT(hr); + + return hr; + } + + HRESULT SetupContext(LPCWSTR wszApplicationBase, + DWORD dwAppDomainId, + IUnknown **ppIApplicationContext) + { + HRESULT hr = S_OK; + + EX_TRY + { + BINDER_LOG_LOCK(); + BINDER_LOG_ENTER(L"BinderInterface::SetupContext"); + + // Verify input arguments + IF_FALSE_GO(ppIApplicationContext != NULL); + + { + ReleaseHolder<ApplicationContext> pApplicationContext; + + SAFE_NEW(pApplicationContext, ApplicationContext); + IF_FAIL_GO(pApplicationContext->Init()); + pApplicationContext->SetAppDomainId(dwAppDomainId); + *ppIApplicationContext = static_cast<IUnknown *>(pApplicationContext.Extract()); + } + + Exit: + BINDER_LOG_LEAVE_HR(L"BinderInterface::SetupContext", hr); + } + 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 Bind(IUnknown *pIApplicationContext, + SString &assemblyDisplayName, + LPCWSTR wszCodeBase, + PEAssembly *pParentAssembly, + BOOL fNgenExplicitBind, + BOOL fExplicitBindToNativeImage, + BINDER_SPACE::Assembly **ppAssembly) + { + HRESULT hr = S_OK; + + EX_TRY + { + BINDER_LOG_LOCK(); + BINDER_LOG_ENTER(L"BinderInterface::Bind"); + + // Verify input arguments + IF_FALSE_GO(pIApplicationContext != NULL); + IF_FALSE_GO(ppAssembly != NULL); + + { + ApplicationContext *pApplicationContext = + static_cast<ApplicationContext *>(pIApplicationContext); + + ReleaseHolder<AssemblyName> pAssemblyName; + if (!assemblyDisplayName.IsEmpty()) + { + SAFE_NEW(pAssemblyName, AssemblyName); + IF_FAIL_GO(pAssemblyName->Init(assemblyDisplayName)); + } + + IF_FAIL_GO(AssemblyBinder::BindAssembly(pApplicationContext, + pAssemblyName, + wszCodeBase, + pParentAssembly, + fNgenExplicitBind, + fExplicitBindToNativeImage, + ppAssembly)); + } + + Exit: + BINDER_LOG_LEAVE_HR(L"BinderInterface::Bind", hr); + } + EX_CATCH_HRESULT(hr); + + return hr; + } + + HRESULT BindToSystem(SString &sSystemDirectory, + BINDER_SPACE::Assembly **ppSystemAssembly, + bool fBindToNativeImage) + { + HRESULT hr = S_OK; + + IF_FALSE_GO(ppSystemAssembly != NULL); + + EX_TRY + { + BINDER_LOG_LOCK(); + + IF_FAIL_GO(AssemblyBinder::BindToSystem(sSystemDirectory, ppSystemAssembly, fBindToNativeImage)); + } + EX_CATCH_HRESULT(hr); + + Exit: + return hr; + } + + HRESULT SetupBindingPaths(IUnknown *pIApplicationContext, + SString &sTrustedPlatformAssemblies, + SString &sPlatformResourceRoots, + SString &sAppPaths, + SString &sAppNiPaths) + { + HRESULT hr = S_OK; + + EX_TRY + { + BINDER_LOG_LOCK(); + BINDER_LOG_ENTER(L"BinderInterface::SetupBindingPaths"); + + // Verify input arguments + IF_FALSE_GO(pIApplicationContext != NULL); + + { + ApplicationContext *pApplicationContext = + static_cast<ApplicationContext *>(pIApplicationContext); + _ASSERTE(pApplicationContext != NULL); + + IF_FAIL_GO(pApplicationContext->SetupBindingPaths(sTrustedPlatformAssemblies, sPlatformResourceRoots, sAppPaths, sAppNiPaths, TRUE /* fAcquireLock */)); + } + + Exit: + BINDER_LOG_LEAVE_HR(L"BinderInterface::SetupBindingPaths", hr); + } + EX_CATCH_HRESULT(hr); + + return hr; + } + +#ifdef BINDER_DEBUG_LOG + HRESULT Log(LPCWSTR wszMessage) + { + HRESULT hr = S_OK; + + EX_TRY + { + BINDER_LOG_LOCK(); + BINDER_LOG((WCHAR *) wszMessage); + } + EX_CATCH_HRESULT(hr); + + return hr; + } +#endif // BINDER_DEBUG_LOG +}; diff --git a/src/binder/bindinglog.cpp b/src/binder/bindinglog.cpp new file mode 100644 index 0000000000..a5a9cd352b --- /dev/null +++ b/src/binder/bindinglog.cpp @@ -0,0 +1,333 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// BindingLog.cpp +// + + +// +// Implements the fusion-like BindingLog class +// +// ============================================================ + +#ifdef FEATURE_VERSIONING_LOG + +#define DISABLE_BINDER_DEBUG_LOGGING + +#include "bindinglog.hpp" +#include "assemblyname.hpp" +#include "assembly.hpp" +#include "applicationcontext.hpp" +#include "bindresult.hpp" +#include "cdebuglog.hpp" +#include "variables.hpp" +#include "bindresult.inl" + +#include "strsafe.h" + +#define SIZE_OF_TOKEN_INFORMATION \ + sizeof( TOKEN_USER ) \ + + sizeof( SID ) \ + + sizeof( ULONG ) * SID_MAX_SUB_AUTHORITIES + +#include "../dlls/mscorrc/fusres.h" + +STDAPI BinderGetDisplayName(PEAssembly *pAssembly, + SString &displayName); + +namespace BINDER_SPACE +{ + namespace + { + // Ripped from Fusion + + inline UINT GetPreBindStateName(AssemblyName *pAssemblyName) + { + if (pAssemblyName->HaveAssemblyVersion()) + { + return ID_FUSLOG_BINDING_PRE_BIND_STATE_BY_NAME; + } + else + { + return ID_FUSLOG_BINDING_PRE_BIND_STATE_BY_NAME_PARTIAL; + } + } + }; + + BindingLog::BindingLog() + { + m_pCDebugLog = NULL; + } + + BindingLog::~BindingLog() + { + SAFE_RELEASE(m_pCDebugLog); + } + + /* static */ + HRESULT BindingLog::CreateInContext(ApplicationContext *pApplicationContext, + SString &assemblyPath, + PEAssembly *pParentAssembly) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"BindingLog::CreateInContext (assemblyPath)"); + + if (IsLoggingNeeded()) + { + IF_FAIL_GO(CreateInContext(pApplicationContext, NULL, assemblyPath, pParentAssembly)); + } + + Exit: + BINDER_LOG_LEAVE_HR(L"BindingLog::CreateInContext (assemblyPath)", hr); + return hr; + } + + /* static */ + BOOL BindingLog::IsLoggingNeeded() + { +#ifdef FEATURE_VERSIONING_LOG + return g_BinderVariables->fLoggingNeeded; +#else // FEATURE_VERSIONING_LOG + return FALSE; +#endif // FEATURE_VERSIONING_LOG + } + + /* static */ + HRESULT BindingLog::CreateInContext(ApplicationContext *pApplicationContext, + AssemblyName *pAssemblyName, + PEAssembly *pParentAssembly) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"BindingLog::CreateInContext (pAssemblyName)"); + + if (IsLoggingNeeded()) + { + SmallStackSString emptyString; + + IF_FALSE_GO(pAssemblyName != NULL); + IF_FAIL_GO(CreateInContext(pApplicationContext, + pAssemblyName, + emptyString, + pParentAssembly)); + } + + Exit: + BINDER_LOG_LEAVE_HR(L"BindingLog::CreateInContext (pAssemblyName)", hr); + return hr; + } + + HRESULT BindingLog::Log(SString &info) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"BindingLog::Log"); + + IF_FAIL_GO(GetDebugLog()->LogMessage(0, FUSION_BIND_LOG_CATEGORY_DEFAULT, info)); + + Exit: + BINDER_LOG_LEAVE_HR(L"BindingLog::Log", hr); + return hr; + } + + HRESULT BindingLog::LogAssemblyName(LPCWSTR pwzPrefix, + AssemblyName *pAssemblyName) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"BindingLog::LogAssemblyName"); + PathString assemblyDisplayName; + + // Verify input arguments + IF_FALSE_GO(pwzPrefix != NULL); + IF_FALSE_GO(pAssemblyName != NULL); + + pAssemblyName->GetDisplayName(assemblyDisplayName, + AssemblyName::INCLUDE_VERSION | + AssemblyName::INCLUDE_ARCHITECTURE); + IF_FAIL_GO(Log(pwzPrefix, assemblyDisplayName)); + + Exit: + BINDER_LOG_LEAVE_HR(L"BindingLog::LogAssemblyName", hr); + return hr; + } + + HRESULT BindingLog::LogHR(HRESULT logHR) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"BindingLog::LogHR"); + + IF_FAIL_GO(GetDebugLog()->SetResultCode(0, logHR)); + + Exit: + BINDER_LOG_LEAVE_HR(L"BindingLog::LogHR", hr); + return hr; + } + + HRESULT BindingLog::LogResult(BindResult *pBindResult) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"BindingLog::LogResult"); + PathString assemblyDisplayName; + PathString format; + PathString info; + + pBindResult->GetAssemblyName()->GetDisplayName(assemblyDisplayName, + AssemblyName::INCLUDE_VERSION | + AssemblyName::INCLUDE_ARCHITECTURE); + + IF_FAIL_GO(format.LoadResourceAndReturnHR(CCompRC::Debugging, + ID_FUSLOG_ASSEMBLY_STATUS_BOUND_TO_ID)); + info.Printf(format.GetUnicode(), assemblyDisplayName.GetUnicode()); + IF_FAIL_GO(Log(info)); + + IUnknown *pIUnknownAssembly; + pIUnknownAssembly = pBindResult->GetAssembly(FALSE /* fAddRef */); + Assembly *pAssembly; + pAssembly = static_cast<Assembly *>(static_cast<void *>(pIUnknownAssembly)); + _ASSERTE(pAssembly != NULL); + + if (pAssembly->GetIsInGAC()) + { + IF_FAIL_GO(info. + LoadResourceAndReturnHR(CCompRC::Debugging, + ID_FUSLOG_ASSEMBLY_STATUS_BOUND_GAC)); + } + else if (pAssembly->GetIsByteArray()) + { + IF_FAIL_GO(info. + LoadResourceAndReturnHR(CCompRC::Debugging, + ID_FUSLOG_ASSEMBLY_STATUS_BOUND_BYTE_ARRAY)); + } + else + { + PathString assemblyPath; + + BinderGetImagePath(pAssembly->GetPEImage(), assemblyPath); + IF_FAIL_GO(format. + LoadResourceAndReturnHR(CCompRC::Debugging, + ID_FUSLOG_ASSEMBLY_STATUS_BOUND_TO_LOCATION)); + info.Printf(format.GetUnicode(), assemblyPath.GetUnicode()); + } + IF_FAIL_GO(Log(info)); + + Exit: + BINDER_LOG_LEAVE_HR(L"BindingLog::LogResult", hr); + return hr; + } + + HRESULT BindingLog::Flush() + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"BindingLog::Flush"); + + hr = GetDebugLog()->Flush(0, FUSION_BIND_LOG_CATEGORY_DEFAULT); + if (hr == E_ACCESSDENIED) + { + // We've been impersonated differently and have a old log entry + BINDER_LOG(L"Impersonated: E_ACCESSDENIED"); + hr = S_OK; + } + + BINDER_LOG_LEAVE_HR(L"BindingLog::Flush", hr); + return hr; + } + + /* static */ + HRESULT BindingLog::CreateInContext(ApplicationContext *pApplicationContext, + AssemblyName *pAssemblyName, + SString &assemblyPath, + PEAssembly *pParentAssembly) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"BindingLog::CreateInContext"); + + BindingLog *pBindingLog = pApplicationContext->GetBindingLog(); + + // Invalidate existing debug log + pBindingLog->SetDebugLog(NULL); + + IF_FAIL_GO(CDebugLog::Create(pApplicationContext, + pAssemblyName, + assemblyPath, + &pBindingLog->m_pCDebugLog)); + + IF_FAIL_GO(pBindingLog->LogPreBindState(pApplicationContext, + pAssemblyName, + assemblyPath, + pParentAssembly)); + Exit: + BINDER_LOG_LEAVE_HR(L"BindingLog::CreateInContext", hr); + return hr; + } + + HRESULT BindingLog::LogPreBindState(ApplicationContext *pApplicationContext, + AssemblyName *pAssemblyName, + SString &assemblyPath, + PEAssembly *pParentAssembly) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"BindingLog::LogPreBindState"); + PathString format; + PathString info; + + IF_FAIL_GO(info.LoadResourceAndReturnHR(CCompRC::Debugging, + ID_FUSLOG_BINDING_PRE_BIND_STATE_BEGIN)); + IF_FAIL_GO(Log(info)); + + if (pAssemblyName != NULL) + { + PathString assemblyDisplayName; + + pAssemblyName->GetDisplayName(assemblyDisplayName, + AssemblyName::INCLUDE_VERSION | + AssemblyName::INCLUDE_ARCHITECTURE | + AssemblyName::INCLUDE_RETARGETABLE); + + IF_FAIL_GO(format.LoadResourceAndReturnHR(CCompRC::Debugging, + GetPreBindStateName(pAssemblyName))); + info.Printf(format.GetUnicode(), assemblyDisplayName.GetUnicode()); + } + else + { + IF_FAIL_GO(format. + LoadResourceAndReturnHR(CCompRC::Debugging, + ID_FUSLOG_BINDING_PRE_BIND_STATE_WHERE_REF)); + info.Printf(format.GetUnicode(), assemblyPath.GetUnicode()); + } + IF_FAIL_GO(Log(info)); + + if (pParentAssembly != NULL) + { + PathString parentAssemblyDisplayName; + + IF_FAIL_GO(BinderGetDisplayName(pParentAssembly, parentAssemblyDisplayName)); + IF_FAIL_GO(format.LoadResourceAndReturnHR(CCompRC::Debugging, + ID_FUSLOG_BINDING_PRE_BIND_STATE_CALLER)); + info.Printf(format.GetUnicode(), parentAssemblyDisplayName.GetUnicode()); + } + else + { + IF_FAIL_GO(info. + LoadResourceAndReturnHR(CCompRC::Debugging, + ID_FUSLOG_BINDING_PRE_BIND_STATE_CALLER_UNKNOWN)); + } + IF_FAIL_GO(Log(info)); + + IF_FAIL_GO(info.LoadResourceAndReturnHR(CCompRC::Debugging, + ID_FUSLOG_BINDING_PRE_BIND_STATE_END)); + IF_FAIL_GO(Log(info)); + + Exit: + BINDER_LOG_LEAVE_HR(L"BindingLog::LogPreBindState", hr); + return hr; + } + + void BindingLog::SetDebugLog(CDebugLog *pCDebugLog) + { + SAFE_RELEASE(m_pCDebugLog); + m_pCDebugLog = pCDebugLog; + } +}; + +#endif // FEATURE_VERSIONING_LOG diff --git a/src/binder/cdebuglog.cpp b/src/binder/cdebuglog.cpp new file mode 100644 index 0000000000..e6dcefd77f --- /dev/null +++ b/src/binder/cdebuglog.cpp @@ -0,0 +1,486 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// CDebugLog.cpp +// + + +// +// Implements the fusion-derived CDebugLog class +// +// ============================================================ + +#ifdef FEATURE_VERSIONING_LOG + +#include "cdebuglog.hpp" +#include "applicationcontext.hpp" +#include "assemblyname.hpp" +#include "variables.hpp" +#include "utils.hpp" + +#include "shlwapi.h" +#include "strsafe.h" + +#include "../dlls/mscorrc/fusres.h" + +#define MAX_DBG_STR_LEN 1024 +#define MAX_DATE_LEN 128 + +#define DEBUG_LOG_HTML_START L"<html><pre>\r\n" +#define DEBUG_LOG_HTML_META_LANGUAGE L"<meta http-equiv=\"Content-Type\" content=\"charset=unicode-1-1-utf-8\">" +#define DEBUG_LOG_MARK_OF_THE_WEB L"<!-- saved from url=(0015)assemblybinder: -->" +#define DEBUG_LOG_HTML_END L"\r\n</pre></html>" +#define DEBUG_LOG_NEW_LINE L"\r\n" + +namespace BINDER_SPACE +{ + namespace + { + inline LPCWSTR LogCategoryToString(DWORD dwLogCategory) + { + switch (dwLogCategory) + { + case FUSION_BIND_LOG_CATEGORY_DEFAULT: + return L"default"; + case FUSION_BIND_LOG_CATEGORY_NGEN: + return L"Native"; + default: + return L"Unknown"; + } + } + + HRESULT CreateFilePathHierarchy(LPCOLESTR pszName) + { + HRESULT hr=S_OK; + LPTSTR pszFileName; + TCHAR szPath[MAX_PATH]; + DWORD dw = 0; + + // _ASSERTE (pszPath ) ; + if (wcslen(pszName) >= MAX_PATH) + { + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW)); + } + + IF_FAIL_GO(StringCbCopy(szPath, sizeof(szPath), pszName)); + + pszFileName = PathFindFileName(szPath); + + if (pszFileName <= szPath) + { + IF_FAIL_GO(E_INVALIDARG); + } + + *(pszFileName-1) = 0; + + dw = WszGetFileAttributes(szPath); + if (dw != INVALID_FILE_ATTRIBUTES) + { + return S_OK; + } + + hr = HRESULT_FROM_GetLastError(); + + switch (hr) + { + case __HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND): + { + hr = CreateFilePathHierarchy(szPath); + if (hr != S_OK) + return hr; + } + + case __HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): + { + if (WszCreateDirectory(szPath, NULL)) + return S_OK; + else + { + hr = HRESULT_FROM_WIN32(GetLastError()); + if(hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) + hr = S_OK; + else + return hr; + } + } + + default: + break; + } + + Exit: + return hr; + } + + HRESULT WriteLog(HANDLE hLogFile, + LPCWSTR pwzInfo) + { + HRESULT hr = S_OK; + DWORD dwLen = 0; + DWORD dwWritten = 0; + CHAR szBuf[MAX_DBG_STR_LEN]; + + dwLen = WszWideCharToMultiByte(CP_UTF8, + 0, + pwzInfo, + -1, + szBuf, + MAX_DBG_STR_LEN, + NULL, + NULL); + + if (!dwLen) + { + IF_FAIL_GO(HRESULT_FROM_GetLastError()); + } + + // dwLen includes NULL terminator. We don't want to write this out. + if (dwLen > 1) { + dwLen--; + + if (!WriteFile(hLogFile, szBuf, dwLen, &dwWritten, NULL)) { + IF_FAIL_GO(HRESULT_FROM_GetLastError()); + } + } + + Exit: + return hr; + } + + HRESULT GetBindTimeInfo(PathString &info) + { + HRESULT hr = S_OK; + SYSTEMTIME systime; + + { + WCHAR wzDateBuffer[MAX_DATE_LEN]; + WCHAR wzTimeBuffer[MAX_DATE_LEN]; + + GetLocalTime(&systime); + + if (!WszGetDateFormat(LOCALE_USER_DEFAULT, + 0, + &systime, + NULL, + wzDateBuffer, + MAX_DATE_LEN)) + { + return HRESULT_FROM_GetLastError(); + } + + if (!WszGetTimeFormat(LOCALE_USER_DEFAULT, + 0, + &systime, + NULL, + wzTimeBuffer, + MAX_DATE_LEN)) + { + return HRESULT_FROM_GetLastError(); + } + + info.Printf(L"(%s @ %s)", wzDateBuffer, wzTimeBuffer); + } + return hr; + } + + HRESULT GetHrResultInfo(PathString &info, HRESULT hrResult) + { + HRESULT hr = S_OK; + + // TODO: Get the result information in here. + info.Printf(L"%p.", hrResult); + + return hr; + } + + inline BOOL IsInvalidCharacter(WCHAR wcChar) + { + switch (wcChar) + { + case L':': + case L'/': + case L'\\': + case L'*': + case L'<': + case L'>': + case L'?': + case L'|': + case L'"': + return TRUE; + default: + return FALSE; + } + } + + inline void ReplaceInvalidFileCharacters(SString &assemblyName) + { + SString::Iterator pos = assemblyName.Begin(); + SString::Iterator end = assemblyName.End(); + + while (pos < end) + { + if (IsInvalidCharacter(pos[0])) + { + assemblyName.Replace(pos, L'_'); + } + + pos++; + } + } + }; + + CDebugLog::CDebugLog() + { + m_cRef = 1; + } + + CDebugLog::~CDebugLog() + { + // Nothing to do here + } + + /* static */ + HRESULT CDebugLog::Create(ApplicationContext *pApplicationContext, + AssemblyName *pAssemblyName, + SString &sCodeBase, + CDebugLog **ppCDebugLog) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"CDebugLog::Create"); + ReleaseHolder<CDebugLog> pDebugLog; + + // Validate input arguments + IF_FALSE_GO(pApplicationContext != NULL); + IF_FALSE_GO(ppCDebugLog != NULL); + + SAFE_NEW(pDebugLog, CDebugLog); + IF_FAIL_GO(pDebugLog->Init(pApplicationContext, pAssemblyName, sCodeBase)); + + *ppCDebugLog = pDebugLog.Extract(); + + Exit: + BINDER_LOG_LEAVE_HR(L"CDebugLog::Create", hr); + return hr; + } + + ULONG CDebugLog::AddRef() + { + return InterlockedIncrement(&m_cRef); + } + + ULONG CDebugLog::Release() + { + ULONG ulRef; + + ulRef = InterlockedDecrement(&m_cRef); + + if (ulRef == 0) + { + delete this; + } + + return ulRef; + } + + HRESULT CDebugLog::SetResultCode(DWORD dwLogCategory, + HRESULT hrResult) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"CDebugLog::SetResultCode"); + + IF_FALSE_GO(dwLogCategory < FUSION_BIND_LOG_CATEGORY_MAX); + + m_HrResult[dwLogCategory] = hrResult; + + Exit: + BINDER_LOG_LEAVE_HR(L"CDebugLog::SetResultCode", hr); + return hr; + } + + HRESULT CDebugLog::LogMessage(DWORD, + DWORD dwLogCategory, + SString &sDebugString) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"CDebugLog::LogMessage"); + + IF_FALSE_GO(dwLogCategory < FUSION_BIND_LOG_CATEGORY_MAX); + + m_content[dwLogCategory].AddTail(const_cast<const SString &>(sDebugString)); + + Exit: + BINDER_LOG_LEAVE_HR(L"CDebugLog::LogMessage", hr); + return hr; + } + + HRESULT CDebugLog::Flush(DWORD, + DWORD dwLogCategory) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"CDebugLog::Flush"); + SmallStackSString sCategory(LogCategoryToString(dwLogCategory)); + PathString logFilePath; + ListNode<SString> *pListNode = NULL; + + IF_FALSE_GO(dwLogCategory < FUSION_BIND_LOG_CATEGORY_MAX); + + CombinePath(g_BinderVariables->logPath, sCategory, logFilePath); + CombinePath(logFilePath, m_applicationName, logFilePath); + CombinePath(logFilePath, m_logFileName, logFilePath); + CanonicalizePath(logFilePath); + + BINDER_LOG_STRING(L"logFilePath", logFilePath); + + IF_FAIL_GO(CreateFilePathHierarchy(logFilePath.GetUnicode())); + + m_hLogFile = WszCreateFile(logFilePath.GetUnicode(), + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (m_hLogFile == INVALID_HANDLE_VALUE) + { + // Silently ignore unability to log. + BINDER_LOG(L"Unable to open binding log"); + GO_WITH_HRESULT(S_OK); + } + + LogHeader(dwLogCategory); + + pListNode = static_cast<ListNode<SString> *>(m_content[dwLogCategory].GetHeadPosition()); + while (pListNode != NULL) + { + SString item = pListNode->GetItem(); + + IF_FAIL_GO(WriteLog(m_hLogFile, item.GetUnicode())); + IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_NEW_LINE)); + + pListNode = pListNode->GetNext(); + } + + LogFooter(dwLogCategory); + + // Ignore failure + CloseHandle(m_hLogFile.Extract()); + + Exit: + BINDER_LOG_LEAVE_HR(L"CDebugLog::Flush", hr); + return hr; + } + + HRESULT CDebugLog::Init(ApplicationContext *pApplicationContext, + AssemblyName *pAssemblyName, + SString &sCodeBase) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"CDebugLog::Init"); + + m_applicationName.Set(pApplicationContext->GetApplicationName()); + ReplaceInvalidFileCharacters(m_applicationName); + + if (m_applicationName.IsEmpty()) + { + BINDER_LOG(L"empty application name"); + m_applicationName.Set(L"unknown"); + } + + if (pAssemblyName == NULL) + { + m_logFileName.Set(L"WhereRefBind!Host=(LocalMachine)!FileName=("); + + LPCWSTR pwzFileName = PathFindFileNameW(sCodeBase.GetUnicode()); + if (pwzFileName != NULL) + { + m_logFileName.Append(pwzFileName); + } + m_logFileName.Append(L").HTM"); + } + else + { + PathString assemblyDisplayName; + + pAssemblyName->GetDisplayName(assemblyDisplayName, + AssemblyName::INCLUDE_VERSION | + AssemblyName::INCLUDE_ARCHITECTURE | + AssemblyName::INCLUDE_RETARGETABLE); + + ReplaceInvalidFileCharacters(assemblyDisplayName); + + m_logFileName.Set(assemblyDisplayName); + m_logFileName.Append(L".HTM"); + } + + BINDER_LOG_LEAVE_HR(L"CDebugLog::Init", hr); + return hr; + } + + HRESULT CDebugLog::LogHeader(DWORD dwLogCategory) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"CDebugLog::LogHeader"); + PathString info; + PathString temp; + PathString format; + + IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_HTML_META_LANGUAGE)); + IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_MARK_OF_THE_WEB)); + IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_HTML_START)); + + IF_FAIL_GO(GetBindTimeInfo(temp)); + IF_FAIL_GO(format.LoadResourceAndReturnHR(CCompRC::Debugging, ID_FUSLOG_BINDING_HEADER_BEGIN)); + info.Printf(format.GetUnicode(), temp.GetUnicode()); + IF_FAIL_GO(WriteLog(m_hLogFile, info.GetUnicode())); + IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_NEW_LINE DEBUG_LOG_NEW_LINE)); + + if (SUCCEEDED(m_HrResult[dwLogCategory])) + { + IF_FAIL_GO(temp. + LoadResourceAndReturnHR(CCompRC::Debugging, ID_FUSLOG_BINDING_HEADER_BIND_RESULT_SUCCESS)); + IF_FAIL_GO(WriteLog(m_hLogFile, temp.GetUnicode())); + } + else + { + IF_FAIL_GO(temp. + LoadResourceAndReturnHR(CCompRC::Debugging, ID_FUSLOG_BINDING_HEADER_BIND_RESULT_ERROR)); + IF_FAIL_GO(WriteLog(m_hLogFile, temp.GetUnicode())); + } + IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_NEW_LINE)); + + GetHrResultInfo(temp, m_HrResult[dwLogCategory]); + + IF_FAIL_GO(format.LoadResourceAndReturnHR(CCompRC::Debugging, ID_FUSLOG_BINDING_HEADER_BIND_RESULT)); + info.Printf(format.GetUnicode(), temp.GetUnicode()); + IF_FAIL_GO(WriteLog(m_hLogFile, info.GetUnicode())); + IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_NEW_LINE DEBUG_LOG_NEW_LINE)); + + // TODO: Assembly Manager info + Executable info. + + IF_FAIL_GO(info.LoadResourceAndReturnHR(CCompRC::Debugging, ID_FUSLOG_BINDING_HEADER_END)); + IF_FAIL_GO(WriteLog(m_hLogFile, info.GetUnicode())); + IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_NEW_LINE DEBUG_LOG_NEW_LINE)); + + Exit: + BINDER_LOG_LEAVE_HR(L"CDebugLog::LogHeader", hr); + return hr; + } + + HRESULT CDebugLog::LogFooter(DWORD) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"CDebugLog::LogFooter"); + + IF_FAIL_GO(WriteLog(m_hLogFile, DEBUG_LOG_HTML_END)); + + Exit: + BINDER_LOG_LEAVE_HR(L"CDebugLog::LogFooter", hr); + return hr; + } +}; + +#endif // FEATURE_VERSIONING_LOG diff --git a/src/binder/clrprivbinderassemblyloadcontext.cpp b/src/binder/clrprivbinderassemblyloadcontext.cpp new file mode 100644 index 0000000000..0ea1ea3c6c --- /dev/null +++ b/src/binder/clrprivbinderassemblyloadcontext.cpp @@ -0,0 +1,276 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#include "common.h" +#include "assemblybinder.hpp" +#include "clrprivbindercoreclr.h" +#include "clrprivbinderassemblyloadcontext.h" +#include "clrprivbinderutil.h" + +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + +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 + // MSCORLIB should be bound using BindToSystem + _ASSERTE(!pAssemblyName->IsMscorlib()); +#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, + 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); + + // DevDiv #933506: Exceptions thrown during AssemblyLoadContext.Load should propagate + // EX_TRY + { + // Check if the assembly is in the TPA list or not. + // + // HAR_TODO: For Bing scenarios, we should be able to tell the TPA Binder + // to not consult the AppPaths/App_ni_Paths. + _ASSERTE(m_pTPABinder != NULL); + + ReleaseHolder<BINDER_SPACE::Assembly> pCoreCLRFoundAssembly; + ReleaseHolder<AssemblyName> pAssemblyName; + + SAFE_NEW(pAssemblyName, AssemblyName); + IF_FAIL_GO(pAssemblyName->Init(pIAssemblyName)); + + hr = m_pTPABinder->BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly); + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + // If we could not find the assembly in the TPA list, + // then bind to it in the context of the current binder. + // If we find it already loaded, we will return the reference. + 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(this, pAssemblyName, pIAssemblyName, &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:; + } + // EX_CATCH_HRESULT(hr); + + 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)); + } + + // Ensure we are not being asked to bind to a TPA assembly + // + // Easy out for mscorlib + if (pAssemblyName->IsMscorlib()) + { + IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + } + + { + SString& simpleName = pAssemblyName->GetSimpleName(); + ApplicationContext *pTPAApplicationContext = m_pTPABinder->GetAppContext(); + SimpleNameToFileNameMap * tpaMap = pTPAApplicationContext->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 or not. + hr = m_pTPABinder->BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly); + if (SUCCEEDED(hr)) + { + if (pCoreCLRFoundAssembly->GetIsInGAC()) + { + // If we were able to bind to a TPA assembly, then fail the load + 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::VerifyBind(IAssemblyName *AssemblyName, + ICLRPrivAssembly *pAssembly, + ICLRPrivAssemblyInfo *pAssemblyInfo) +{ + return E_FAIL; +} + +HRESULT CLRPrivBinderAssemblyLoadContext::GetBinderFlags(DWORD *pBinderFlags) +{ + if (pBinderFlags == NULL) + return E_INVALIDARG; + *pBinderFlags = BINDER_NONE; + return S_OK; +} + +HRESULT CLRPrivBinderAssemblyLoadContext::GetBinderID( + UINT_PTR *pBinderId) +{ + *pBinderId = reinterpret_cast<UINT_PTR>(this); + return S_OK; +} + +HRESULT CLRPrivBinderAssemblyLoadContext::FindAssemblyBySpec( + LPVOID pvAppDomain, + LPVOID pvAssemblySpec, + HRESULT *pResult, + ICLRPrivAssembly **ppAssembly) +{ + // We are not using a cache at this level + // However, assemblies bound by the CoreCLR binder is already cached in the + // AppDomain and will be resolved from there if required + return E_FAIL; +} + +//============================================================================= +// 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, + UINT_PTR ptrAssemblyLoadContext, + CLRPrivBinderAssemblyLoadContext **ppBindContext) +{ + HRESULT hr = E_FAIL; + EX_TRY + { + if(ppBindContext != NULL) + { + ReleaseHolder<CLRPrivBinderAssemblyLoadContext> pBinder; + + SAFE_NEW(pBinder, CLRPrivBinderAssemblyLoadContext); + hr = pBinder->m_appContext.Init(); + if(SUCCEEDED(hr)) + { + // Save the reference to the AppDomain in which the binder lives + pBinder->m_appContext.SetAppDomainId(dwAppDomainId); + + // Mark that this binder can explicitly bind to native images + pBinder->m_appContext.SetExplicitBindToNativeImages(true); + + // 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; + + // Return reference to the allocated Binder instance + *ppBindContext = clr::SafeAddRef(pBinder.Extract()); + } + } + } + EX_CATCH_HRESULT(hr); + +Exit: + return hr; +} + +CLRPrivBinderAssemblyLoadContext::CLRPrivBinderAssemblyLoadContext() +{ + m_pTPABinder = NULL; +} + +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) diff --git a/src/binder/clrprivbindercoreclr.cpp b/src/binder/clrprivbindercoreclr.cpp new file mode 100644 index 0000000000..b791c9baca --- /dev/null +++ b/src/binder/clrprivbindercoreclr.cpp @@ -0,0 +1,198 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#include "common.h" +#include "assemblybinder.hpp" +#include "clrprivbindercoreclr.h" +#include "clrprivbinderutil.h" + +using namespace BINDER_SPACE; + +//============================================================================= +// Helper functions +//----------------------------------------------------------------------------- + +HRESULT CLRPrivBinderCoreCLR::BindAssemblyByNameWorker(BINDER_SPACE::AssemblyName *pAssemblyName, + BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly) +{ + VALIDATE_ARG_RET(pAssemblyName != nullptr && ppCoreCLRFoundAssembly != nullptr); + HRESULT hr = S_OK; + +#ifdef _DEBUG + // MSCORLIB should be bound using BindToSystem + _ASSERTE(!pAssemblyName->IsMscorlib()); +#endif + + hr = AssemblyBinder::BindAssembly(&m_appContext, + pAssemblyName, + NULL, + NULL, + FALSE, //fNgenExplicitBind, + FALSE, //fExplicitBindToNativeImage, + 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); + + EX_TRY + { + *ppAssembly = nullptr; + + ReleaseHolder<BINDER_SPACE::Assembly> pCoreCLRFoundAssembly; + ReleaseHolder<AssemblyName> pAssemblyName; + + SAFE_NEW(pAssemblyName, AssemblyName); + IF_FAIL_GO(pAssemblyName->Init(pIAssemblyName)); + + hr = BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly); + IF_FAIL_GO(hr); + + *ppAssembly = pCoreCLRFoundAssembly.Extract(); + +Exit:; + } + EX_CATCH_HRESULT(hr); + + return hr; +} + +HRESULT CLRPrivBinderCoreCLR::VerifyBind(IAssemblyName *AssemblyName, + ICLRPrivAssembly *pAssembly, + ICLRPrivAssemblyInfo *pAssemblyInfo) +{ + return E_FAIL; +} + +HRESULT CLRPrivBinderCoreCLR::GetBinderFlags(DWORD *pBinderFlags) +{ + if (pBinderFlags == NULL) + return E_INVALIDARG; + *pBinderFlags = BINDER_NONE; + return S_OK; +} + +HRESULT CLRPrivBinderCoreCLR::GetBinderID( + UINT_PTR *pBinderId) +{ + *pBinderId = reinterpret_cast<UINT_PTR>(this); + return S_OK; +} + +HRESULT CLRPrivBinderCoreCLR::FindAssemblyBySpec( + LPVOID pvAppDomain, + LPVOID pvAssemblySpec, + HRESULT *pResult, + ICLRPrivAssembly **ppAssembly) +{ + // We are not using a cache at this level + // However, assemblies bound by the CoreCLR binder is already cached in the + // AppDomain and will be resolved from there if required + return E_FAIL; +} + +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; +} + +bool CLRPrivBinderCoreCLR::IsInTpaList(const SString &sFileName) +{ + bool fIsFileOnTpaList = false; + + TpaFileNameHash * tpaFileNameMap = m_appContext.GetTpaFileNameList(); + if (tpaFileNameMap != nullptr) + { + const FileNameMapEntry *pTpaEntry = tpaFileNameMap->LookupPtr(sFileName.GetUnicode()); + fIsFileOnTpaList = (pTpaEntry != nullptr); + } + + return fIsFileOnTpaList; +} + +// 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, + &pAsm); + if(SUCCEEDED(hr)) + { + _ASSERTE(pAsm != NULL); + pAsm->SetBinder(this); + *ppAssembly = pAsm.Extract(); + } + } + EX_CATCH_HRESULT(hr); + +Exit: + return hr; +} + +#ifndef CROSSGEN_COMPILE +HRESULT CLRPrivBinderCoreCLR::PreBindByteArray(PEImage *pPEImage, BOOL fInspectionOnly) +{ + HRESULT hr = S_OK; + VALIDATE_ARG_RET(pPEImage != NULL); + + EX_TRY + { + hr = AssemblyBinder::PreBindByteArray(&m_appContext, pPEImage, fInspectionOnly); + } + EX_CATCH_HRESULT(hr); + + return hr; +} +#endif // CROSSGEN_COMPILE diff --git a/src/binder/compatibility.cpp b/src/binder/compatibility.cpp new file mode 100644 index 0000000000..5b64fb7565 --- /dev/null +++ b/src/binder/compatibility.cpp @@ -0,0 +1,256 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// Compatibility.cpp +// + + +// +// Implements the V2 Compatibility class +// +// ============================================================ + +#include "compatibility.hpp" +#include "assemblyname.hpp" +#include "utils.hpp" +#include "ndpversion.h" + +#define ECMAKeyToken W("B77A5C561934E089") // The ECMA key used by some framework assemblies: mscorlib, system, etc. +#define FXKeyToken W("b03f5f7f11d50a3a") // The FX key used by other framework assemblies: System.Web, System.Drawing, etc. +#define CoreClrKeyToken W("7CEC85D7BEA7798E") // The silverlight platform key used by CoreClr framework assemblies: mscorlib, system, etc +#define SilverlightKeyToken W("31bf3856ad364e35") + +#define NETCF_PUBLIC_KEY_TOKEN_3 W("969db8053d3322ac") + +#ifdef FEATURE_LEGACYNETCF +extern BOOL RuntimeIsLegacyNetCF(DWORD adid); +#endif + +namespace BINDER_SPACE +{ + typedef struct + { + LPCWSTR pwzSimpleName; + LPCWSTR pwzPublicKeyToken; + LPCWSTR pwzVersion; + // Newline + LPCWSTR pwzNewSimpleName; + LPCWSTR pwzNewPublicKeyToken; + LPCWSTR pwzNewVersion; +#ifdef FEATURE_LEGACYNETCF + BOOL fMangoOnly; +#endif + } RetargetConfig; + + namespace + { + // Hard-coded retargeting table from legacy Fusion + + static RetargetConfig arRetargetConfig[] = + { + // Example entry + // {W("System.Data.SqlServerCe"), SQL_MOBILE_PUBLIC_KEY_TOKEN, W("3.0.3600.0"), + // NULL, SQL_PUBLIC_KEY_TOKEN, VER_SQL_ASSEMBLYVERSION_STR_L} + {W("Microsoft.CSharp"), SilverlightKeyToken, W("1.0.0.0-99.0.0.0"), + NULL, FXKeyToken, VER_ASSEMBLYVERSION_STR_L +#ifdef FEATURE_LEGACYNETCF + , FALSE +#endif + }, + {W("System.Xml"), NETCF_PUBLIC_KEY_TOKEN_3, W("1.0.0.0-99.0.0.0"), + NULL, CoreClrKeyToken, VER_ASSEMBLYVERSION_STR_L +#ifdef FEATURE_LEGACYNETCF + ,TRUE +#endif + }, + {W("System"), NETCF_PUBLIC_KEY_TOKEN_3, W("1.0.0.0-99.0.0.0"), + NULL, CoreClrKeyToken, VER_ASSEMBLYVERSION_STR_L +#ifdef FEATURE_LEGACYNETCF + , TRUE +#endif + }, + {W("Microsoft.VisualBasic"), NETCF_PUBLIC_KEY_TOKEN_3, W("1.0.0.0-99.0.0.0"), + NULL, CoreClrKeyToken, VER_ASSEMBLYVERSION_STR_L +#ifdef FEATURE_LEGACYNETCF + , TRUE +#endif + }, + {W("System.Core"), NETCF_PUBLIC_KEY_TOKEN_3, W("1.0.0.0-99.0.0.0"), + NULL, CoreClrKeyToken, VER_ASSEMBLYVERSION_STR_L +#ifdef FEATURE_LEGACYNETCF + , TRUE +#endif + }, + {W("System.Runtime.Serialization"), NETCF_PUBLIC_KEY_TOKEN_3, W("1.0.0.0-99.0.0.0"), + NULL, CoreClrKeyToken, VER_ASSEMBLYVERSION_STR_L +#ifdef FEATURE_LEGACYNETCF + , TRUE +#endif + }, + {W("System.ServiceModel"), NETCF_PUBLIC_KEY_TOKEN_3, W("1.0.0.0-99.0.0.0"), + NULL, CoreClrKeyToken, VER_ASSEMBLYVERSION_STR_L +#ifdef FEATURE_LEGACYNETCF + , TRUE +#endif + }, + {W("System.ServiceModel.Web"), NETCF_PUBLIC_KEY_TOKEN_3, W("1.0.0.0-99.0.0.0"), + NULL, CoreClrKeyToken, VER_ASSEMBLYVERSION_STR_L +#ifdef FEATURE_LEGACYNETCF + , TRUE +#endif + } + }; + + + BOOL IsMatchingString(/* in */ SString &sValue, + /* in */ LPCWSTR pwzValue) + { + SString value(SString::Literal, pwzValue); + + return EqualsCaseInsensitive(sValue, value); + } + + BOOL IsMatchingVersion(/* in */ AssemblyVersion *pAssemblyVersion, + /* in */ LPCWSTR pwzAssemblyVersion) + { + SmallStackSString assemblyVersionStr(pwzAssemblyVersion); + assemblyVersionStr.Normalize(); + SString::CIterator pos = assemblyVersionStr.Begin(); + + if (assemblyVersionStr.Find(pos, W('-'))) + { + SmallStackSString beginVersionStr(assemblyVersionStr, + assemblyVersionStr.Begin(), + pos++); + SmallStackSString endVersionStr(assemblyVersionStr, pos, assemblyVersionStr.End()); + + BINDER_LOG_STRING(W("begin"), beginVersionStr); + BINDER_LOG_STRING(W("end"), endVersionStr); + + AssemblyVersion beginVersion; + AssemblyVersion endVersion; + BOOL fIsValidBeginVersion = beginVersion.SetVersion(beginVersionStr.GetUnicode()); + BOOL fIsValidEndVersion = endVersion.SetVersion(endVersionStr.GetUnicode()); + _ASSERTE(fIsValidBeginVersion && fIsValidEndVersion); + + return (pAssemblyVersion->IsLargerOrEqual(&beginVersion) && + pAssemblyVersion->IsSmallerOrEqual(&endVersion)); + } + else + { + AssemblyVersion assemblyVersion; + BOOL fIsValidVersion = assemblyVersion.SetVersion(pwzAssemblyVersion); + _ASSERTE(fIsValidVersion); + + return pAssemblyVersion->Equals(&assemblyVersion); + } + } + }; + + + /* static */ + HRESULT Compatibility::Retarget(AssemblyName *pAssemblyName, + AssemblyName **ppRetargetedAssemblyName, + BOOL *pfIsRetargeted) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("Compatibility::Retarget")); + + IF_FALSE_GO(pAssemblyName != NULL); + IF_FALSE_GO(ppRetargetedAssemblyName != NULL); + + BINDER_LOG_ASSEMBLY_NAME(W("source"), pAssemblyName); + + if (pfIsRetargeted) + { + *pfIsRetargeted = FALSE; + } +#ifdef FEATURE_CORESYSTEM + // Apply retargeting only for strong-named culture neutral assemblies + if (pAssemblyName->IsStronglyNamed() && + pAssemblyName->GetDeNormalizedCulture().IsEmpty()) + { + ReleaseHolder<AssemblyName> pRetargetedAssemblyName; + SString &simpleName = pAssemblyName->GetSimpleName(); + AssemblyVersion *pAssemblyVersion = pAssemblyName->GetVersion(); + SString publicKeyToken; + + TextualIdentityParser::BlobToHex(pAssemblyName->GetPublicKeyTokenBLOB(), + publicKeyToken); + + // Perform linear search for matching assembly. Legacy Fusion also does that + for (unsigned int i = 0; i < LENGTH_OF(arRetargetConfig); i++) + { +#ifdef FEATURE_LEGACYNETCF + if (!RuntimeIsLegacyNetCF(0) && arRetargetConfig[i].fMangoOnly == TRUE) + continue; +#endif + if (IsMatchingString(simpleName, arRetargetConfig[i].pwzSimpleName) && + IsMatchingVersion(pAssemblyVersion, arRetargetConfig[i].pwzVersion) && + IsMatchingString(publicKeyToken, arRetargetConfig[i].pwzPublicKeyToken)) + { + AssemblyVersion newAssemblyVersion; + IF_FALSE_GO(newAssemblyVersion.SetVersion(arRetargetConfig[i].pwzNewVersion)); + + SAFE_NEW(pRetargetedAssemblyName, AssemblyName); + + if (arRetargetConfig[i].pwzNewSimpleName != NULL) + { + pRetargetedAssemblyName-> + GetSimpleName().Set(arRetargetConfig[i].pwzNewSimpleName); + } + else + { + pRetargetedAssemblyName->GetSimpleName().Set(simpleName); + } + pRetargetedAssemblyName->SetVersion(&newAssemblyVersion); + + SBuffer newPublicKeyTokenBlob; + SmallStackSString newPublicKeyToken(arRetargetConfig[i].pwzNewPublicKeyToken); + TextualIdentityParser::HexToBlob(newPublicKeyToken, + FALSE /* fValidateHex */, + TRUE /* fIsToken */, + newPublicKeyTokenBlob); + + pRetargetedAssemblyName->GetPublicKeyTokenBLOB().Set(newPublicKeyTokenBlob); + + BINDER_LOG_ASSEMBLY_NAME(W("retargeted"), pRetargetedAssemblyName); + + *ppRetargetedAssemblyName = pRetargetedAssemblyName.Extract(); + + if (pfIsRetargeted) + { + *pfIsRetargeted = TRUE; + } + + GO_WITH_HRESULT(S_OK); + } + } + + // Create a clone without retargetable flag + if (pAssemblyName->GetIsRetargetable()) + { + IF_FAIL_GO(pAssemblyName->Clone(&pRetargetedAssemblyName)); + pRetargetedAssemblyName->SetIsRetargetable(FALSE); + *ppRetargetedAssemblyName = pRetargetedAssemblyName.Extract(); + } else + { + pAssemblyName->AddRef(); + *ppRetargetedAssemblyName = pAssemblyName; + } + } + else +#endif // FEATURE_CORESYSTEM + { + pAssemblyName->AddRef(); + *ppRetargetedAssemblyName = pAssemblyName; + } + + Exit: + BINDER_LOG_LEAVE_HR(W("Compatibility::Retarget"), hr); + return hr; + } +}; diff --git a/src/binder/coreclrbindercommon.cpp b/src/binder/coreclrbindercommon.cpp new file mode 100644 index 0000000000..cec0d8e0b3 --- /dev/null +++ b/src/binder/coreclrbindercommon.cpp @@ -0,0 +1,188 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +#include "common.h" +#include "assemblybinder.hpp" +#include "coreclrbindercommon.h" +#include "clrprivbindercoreclr.h" +#include "clrprivbinderutil.h" + +using namespace BINDER_SPACE; + +//============================================================================= +// Init code +//----------------------------------------------------------------------------- +/* static */ +HRESULT CCoreCLRBinderHelper::Init() +{ + STANDARD_VM_CONTRACT; + HRESULT hr = S_OK; + EX_TRY + { + hr = AssemblyBinder::Startup(); + } + EX_CATCH_HRESULT(hr); + + return hr; +} + +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(); + hr = pApplicationContext->Init(); + if(SUCCEEDED(hr)) + { + pApplicationContext->SetAppDomainId(dwAppDomainId); + *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; +#ifdef FEATURE_CORECLR + StackSString systemPath(SystemDomain::System()->SystemDirectory()); + hr = AssemblyBinder::BindToSystem(systemPath, &pAsm, fBindToNativeImage); +#else + AssemblySpec::BindToSystem(&pAsm); +#endif + 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; +} + +HRESULT CCoreCLRBinderHelper::GetAssemblyFromImage(PEImage *pPEImage, + PEImage *pNativePEImage, + ICLRPrivAssembly **ppAssembly) +{ + HRESULT hr = S_OK; + VALIDATE_ARG_RET(pPEImage != NULL && ppAssembly != NULL); + + EX_TRY + { + ReleaseHolder<BINDER_SPACE::Assembly> pAsm; + hr = AssemblyBinder::GetAssemblyFromImage(pPEImage, pNativePEImage, &pAsm); + if(SUCCEEDED(hr)) + { + _ASSERTE(pAsm != nullptr); + *ppAssembly = pAsm.Extract(); + } + } + EX_CATCH_HRESULT(hr); + + return hr; +} + +//============================================================================= +// Explicitly bind to an assembly by filepath +//============================================================================= +/* static */ +HRESULT CCoreCLRBinderHelper::GetAssembly(/* in */ SString &assemblyPath, + /* in */ BOOL fInspectionOnly, + /* in */ BOOL fIsInGAC, + /* in */ BOOL fExplicitBindToNativeImage, + /* out */ BINDER_SPACE::Assembly **ppAssembly) +{ + return AssemblyBinder::GetAssembly(assemblyPath, + fInspectionOnly, + fIsInGAC, + fExplicitBindToNativeImage, + ppAssembly + ); +} diff --git a/src/binder/debuglog.cpp b/src/binder/debuglog.cpp new file mode 100644 index 0000000000..4f82b7bed9 --- /dev/null +++ b/src/binder/debuglog.cpp @@ -0,0 +1,418 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// DebugLog.cpp +// + + +// +// Implements the DebugLog class +// +// ============================================================ + +#if defined(BINDER_DEBUG_LOG) + +#include "debuglog.hpp" +#include "assemblyname.hpp" +#include "utils.hpp" +#include "variables.hpp" + +#include "ex.h" + +namespace BINDER_SPACE +{ + namespace + { + void GetStringFromHR(HRESULT hr, + SString &info) + { + switch(hr) + { + case S_OK: + info.Append(L"S_OK"); + break; + case S_FALSE: + info.Append(L"S_FALSE"); + break; + case E_FAIL: + info.Append(L"E_FAIL"); + break; + case __HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): + info.Append(L"HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)"); + break; + case __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER): + info.Append(L"HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)"); + break; + case FUSION_E_REF_DEF_MISMATCH: + info.Append(L"FUSION_E_REF_DEF_MISMATCH"); + break; + case FUSION_E_CODE_DOWNLOAD_DISABLED: + info.Append(L"FUSION_E_CODE_DOWNLOAD_DISABLED"); + break; + default: + info.AppendPrintf(L"%p", hr); + break; + } + } + + HRESULT GetLogFilePath(PathString &logFileDir, + PathString &logFilePath) + { + HRESULT hr = S_OK; + + BOOL fFileExists = TRUE; + + do + { + LARGE_INTEGER kCount1; + LARGE_INTEGER kCount2; + + logFilePath.Clear(); + + if (!QueryPerformanceCounter(&kCount1)) + { + hr = HRESULT_FROM_GetLastError(); + } + else if(!QueryPerformanceCounter(&kCount2)) + { + hr = HRESULT_FROM_GetLastError(); + } + else + { + logFilePath.Printf(L"%s\\Log_%u%u_%u%u.tmp", + logFileDir.GetUnicode(), + static_cast<UINT32>(kCount1.u.LowPart), + kCount1.u.HighPart, + static_cast<UINT32>(kCount2.u.LowPart), + kCount2.u.HighPart); + + PlatformPath(logFilePath); + CanonicalizePath(logFilePath); + } + + fFileExists = (FileOrDirectoryExists(logFilePath) == S_OK); + } + while (fFileExists == TRUE); + + return hr; + } + + HRESULT WriteToFile(HANDLE hFile, + const BYTE *pbBuffer, + DWORD dwcbBuffer) + { + HRESULT hr = S_OK; + DWORD dwNumberOfBytesWritten = 0; + + while ((dwcbBuffer != 0) && (dwNumberOfBytesWritten < dwcbBuffer)) + { + if (WriteFile(hFile, pbBuffer, dwcbBuffer, &dwNumberOfBytesWritten, NULL)) + { + dwcbBuffer -= dwNumberOfBytesWritten; + pbBuffer += dwNumberOfBytesWritten; + } + else + { + hr = HRESULT_FROM_GetLastError(); + goto Exit; + } + } + + Exit: + return hr; + } + }; + + /* static */ + HRESULT DebugLog::Startup() + { + HRESULT hr = S_OK; + + PathString logFileDir; + PathString logFilePath; + + REGUTIL::CORConfigLevel kCorConfigLevel = + static_cast<REGUTIL::CORConfigLevel>(REGUTIL::COR_CONFIG_ENV | + REGUTIL::COR_CONFIG_FUSION); + NewArrayHolder<WCHAR> pwzLogDirectory = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_CoreClrBinderLog); + + + g_BinderVariables->m_logCS = ClrCreateCriticalSection(CrstCoreCLRBinderLog, CRST_REENTRANCY); + if (!g_BinderVariables->m_logCS) + { + IF_FAIL_GO(E_OUTOFMEMORY); + } + + + if (pwzLogDirectory == NULL) + { + goto Exit; + } + + logFileDir.Set(pwzLogDirectory); + + if (WszCreateDirectory(logFileDir.GetUnicode(), NULL) || + ((hr = HRESULT_FROM_GetLastError()) == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))) + { + hr = S_OK; + } + + IF_FAIL_GO(GetLogFilePath(logFileDir, logFilePath)); + + g_BinderVariables->m_hDebugLogFile = WszCreateFile(logFilePath.GetUnicode(), + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (g_BinderVariables->m_hDebugLogFile == INVALID_HANDLE_VALUE) + { + IF_FAIL_GO(E_FAIL); + } + Exit: + return hr; + } + + // This is not multi-thread aware by any means. That said, neither is any of this logging mechanism. + static int s_scopeLevel = 0; + static const ANSI s_szScopeIndent[3] = " "; + + /* static */ + void DebugLog::Enter(WCHAR *pwzScope) + { + HRESULT hr = S_OK; + + EX_TRY + { + PathString info; + + info.Append(pwzScope); + info.Append(L": Enter"); + Log(info); + } + EX_CATCH_HRESULT(hr); + + s_scopeLevel++; + } + + /* static */ + void DebugLog::Leave(WCHAR *pwzScope) + { + HRESULT hr = S_OK; + + s_scopeLevel--; + + EX_TRY + { + PathString info; + + info.Append(pwzScope); + info.Append(L": Leave(void)"); + Log(info); + } + EX_CATCH_HRESULT(hr); + } + + /* static */ + void DebugLog::LeaveHR(WCHAR *pwzScope, + HRESULT hrLog) + { + HRESULT hr = S_OK; + + s_scopeLevel--; + + EX_TRY + { + PathString info; + + info.Append(pwzScope); + info.Append(L": Leave(hr="); + GetStringFromHR(hrLog, info); + info.Append(L")"); + + Log(info); + } + EX_CATCH_HRESULT(hr); + } + + /* static */ + void DebugLog::LeaveBool(WCHAR *pwzScope, + BOOL fResult) + { + HRESULT hr = S_OK; + + s_scopeLevel--; + + EX_TRY + { + PathString info; + + info.Append(pwzScope); + info.Append(L": Leave(fResult="); + info.Append(fResult ? L"TRUE)" : L"FALSE)"); + Log(info); + } + EX_CATCH_HRESULT(hr); + } + + /* static */ + void DebugLog::Log(WCHAR *pwzComment) + { + HRESULT hr = S_OK; + + EX_TRY + { + PathString info; + + info.Append(pwzComment); + Log(info); + } + EX_CATCH_HRESULT(hr); + } + + /* static */ + void DebugLog::Log(WCHAR *pwzComment, + SString &value) + { + HRESULT hr = S_OK; + + EX_TRY + { + PathString info; + + info.Append(pwzComment); + info.Append(L" = '"); + info.Append(value); + info.Append(L"'"); + + Log(info); + } + EX_CATCH_HRESULT(hr); + } + + /* static */ + void DebugLog::Log(WCHAR *pwzComment, + const WCHAR *value) + { + HRESULT hr = S_OK; + + EX_TRY + { + PathString info; + + info.Append(pwzComment); + info.Append(L" = '"); + info.Append(value); + info.Append(L"'"); + + Log(info); + } + EX_CATCH_HRESULT(hr) + } + + /* static */ + void DebugLog::Log(WCHAR *pwzComment, + HRESULT hrLog) + { + HRESULT hr = S_OK; + + EX_TRY + { + PathString info; + + info.Append(pwzComment); + info.Append(L" = "); + GetStringFromHR(hrLog, info); + + Log(info); + } + EX_CATCH_HRESULT(hr); + } + + /* static */ + void DebugLog::Log(WCHAR *pwzComment, + AssemblyName *pAssemblyName) + { + EX_TRY + { + PathString assemblyDisplayName; + PathString info; + + if (pAssemblyName != NULL) + { + pAssemblyName->GetDisplayName(assemblyDisplayName, + AssemblyName::INCLUDE_VERSION | + AssemblyName::INCLUDE_ARCHITECTURE | + AssemblyName::INCLUDE_RETARGETABLE); + } + else + { + assemblyDisplayName.Set(L"<NULL>"); + } + + info.Printf(L"(%d):", static_cast<INT32>(assemblyDisplayName.GetCount())); + info.Append(assemblyDisplayName); + + Log(pwzComment, info); + } + EX_CATCH + { + Log(L"<AssemblyDisplayName: Failure>"); + } + EX_END_CATCH(SwallowAllExceptions); + } + + /* static */ + void DebugLog::Log(WCHAR *pwzComment, + void *pData) + { + HRESULT hr = S_OK; + + EX_TRY + { + PathString info; + + info.Append(pwzComment); + info.AppendPrintf(L" = %p", pData); + Log(info); + } + EX_CATCH_HRESULT(hr); + } + + /* static */ + void DebugLog::Log(SString &info) + { + HRESULT hr = S_OK; + + StackScratchBuffer scratchBuffer; + const BYTE *pbRawBuffer = reinterpret_cast<const BYTE *>(info.GetANSI(scratchBuffer)); + DWORD dwcbRawBuffer = static_cast<DWORD>(info.GetCount()); + // Work around SString issue + const ANSI ansiCRLF[] = { 0x0d, 0x0a }; + DWORD dwcbAnsiCRLF = 2 * sizeof(ANSI); + s_scopeLevel; + for (int iScope = 0; iScope < s_scopeLevel; iScope++) + { + IF_FAIL_GO(WriteToFile(g_BinderVariables->m_hDebugLogFile, reinterpret_cast<const BYTE *>(&s_szScopeIndent[0]), sizeof(s_szScopeIndent))); + } + IF_FAIL_GO(WriteToFile(g_BinderVariables->m_hDebugLogFile, pbRawBuffer, dwcbRawBuffer)); + IF_FAIL_GO(WriteToFile(g_BinderVariables->m_hDebugLogFile, + reinterpret_cast<const BYTE *>(ansiCRLF), + dwcbAnsiCRLF)); + // Don't cache anything + if (!FlushFileBuffers(g_BinderVariables->m_hDebugLogFile)) + { + WszOutputDebugString(L"DebugLog::Log(info): FlushFileBuffers failed!\n"); + } + + Exit: + return; + } +}; + +#endif diff --git a/src/binder/dirs.proj b/src/binder/dirs.proj new file mode 100644 index 0000000000..cb7c0b4d02 --- /dev/null +++ b/src/binder/dirs.proj @@ -0,0 +1,20 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + <PropertyGroup> + <BuildInPhase1>true</BuildInPhase1> + <BuildInPhaseDefault>false</BuildInPhaseDefault> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + </PropertyGroup> + + <!--The following projects will build during PHASE 1 of the Desktop build --> + <ItemGroup Condition="'$(BuildExePhase)' == '1'"> + <ProjectFile Include="assemblyname\assemblyname.nativeproj" /> + </ItemGroup> + + <ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(FeatureCoreclr)' == 'true'"> + <ProjectFile Include="v3binder\v3binder.nativeproj" /> + </ItemGroup> + + <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" /> +</Project> diff --git a/src/binder/failurecache.cpp b/src/binder/failurecache.cpp new file mode 100644 index 0000000000..931a56ec1a --- /dev/null +++ b/src/binder/failurecache.cpp @@ -0,0 +1,86 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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; + BINDER_LOG_ENTER(L"FailureCache::Add"); + + NewHolder<FailureCacheEntry> pFailureCacheEntry; + SAFE_NEW(pFailureCacheEntry, FailureCacheEntry); + + // No error occured; report the original error + hr = hrBindingResult; + + pFailureCacheEntry->GetAssemblyNameOrPath().Set(assemblyNameorPath); + pFailureCacheEntry->SetBindingResult(hrBindingResult); + + Hash::Add(pFailureCacheEntry); + pFailureCacheEntry.SuppressRelease(); + + Exit: + BINDER_LOG_LEAVE_HR(L"FailureCache::Add", hr); + return hr; + } + + HRESULT FailureCache::Lookup(SString &assemblyNameorPath) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(L"FailureCache::Lookup"); + FailureCacheEntry *pFailureCachEntry = Hash::Lookup(assemblyNameorPath); + + if (pFailureCachEntry != NULL) + { + hr = pFailureCachEntry->GetBindingResult(); + } + + BINDER_LOG_LEAVE_HR(L"FailureCache::Lookup", hr); + return hr; + } + + void FailureCache::Remove(SString &assemblyName) + { + BINDER_LOG_ENTER(L"FailureCache::Remove"); + + FailureCacheEntry *pFailureCachEntry = Hash::Lookup(assemblyName); + + // Hash::Remove does not clean up entries + Hash::Remove(assemblyName); + SAFE_DELETE(pFailureCachEntry); + + BINDER_LOG_LEAVE(L"FailureCache::Remove"); + } +}; diff --git a/src/binder/fusionassemblyname.cpp b/src/binder/fusionassemblyname.cpp new file mode 100644 index 0000000000..0605116fa5 --- /dev/null +++ b/src/binder/fusionassemblyname.cpp @@ -0,0 +1,1858 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// FusionAssemblyName.cpp +// +// Implements the CAssemblyName class +// +// ============================================================ + +#include <windows.h> +#include <winerror.h> +#include "strongname.h" + +#include "fusionhelpers.hpp" +#include "fusionassemblyname.hpp" + +#include <strsafe.h> +#include "shlwapi.h" + +#include "binderinterface.hpp" +#include "assemblyidentity.hpp" +#include "textualidentityparser.hpp" + +#define DISPLAY_NAME_DELIMITER W(',') +#define DISPLAY_NAME_DELIMITER_STRING W(",") +#define VERSION_STRING_SEGMENTS 4 +#define REMAINING_BUFFER_SIZE ((*pccDisplayName) - (pszBuf - szDisplayName)) + +// --------------------------------------------------------------------------- +// Private Helpers +// --------------------------------------------------------------------------- +namespace +{ + HRESULT GetPublicKeyTokenFromPKBlob(LPBYTE pbPublicKeyToken, DWORD cbPublicKeyToken, + LPBYTE *ppbSN, LPDWORD pcbSN) + { + HRESULT hr = S_OK; + + // Generate the hash of the public key. + if (!StrongNameTokenFromPublicKey(pbPublicKeyToken, cbPublicKeyToken, ppbSN, pcbSN)) + { + hr = StrongNameErrorInfo(); + } + + return hr; + } +}; + +// --------------------------------------------------------------------------- +// 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) + 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::Finalize +// --------------------------------------------------------------------------- +STDMETHODIMP +CAssemblyName::Finalize() +{ + BEGIN_ENTRYPOINT_NOTHROW; + + _fIsFinalized = TRUE; + END_ENTRYPOINT_NOTHROW; + + return S_OK; +} +// --------------------------------------------------------------------------- +// CAssemblyName::GetDisplayName +// --------------------------------------------------------------------------- +STDMETHODIMP +CAssemblyName::GetDisplayName( __out_ecount_opt(*pccDisplayName) LPOLESTR szDisplayName, + __inout LPDWORD pccDisplayName, + DWORD dwDisplayFlags) +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + if (!dwDisplayFlags) { + dwDisplayFlags = ASM_DISPLAYF_DEFAULT; + } + + // Validate input buffer. + if(!pccDisplayName || (!szDisplayName && *pccDisplayName)) { + hr = E_INVALIDARG; + goto exit; + } + + EX_TRY + { + NewHolder<BINDER_SPACE::AssemblyIdentity> pAssemblyIdentity = new BINDER_SPACE::AssemblyIdentity(); + FusionProperty prop; + StackSString textualIdentity; + + // Name required + prop = _rProp[ASM_NAME_NAME]; + if (prop.cb == 0) { + hr = FUSION_E_INVALID_NAME; + goto exit; + } + else { + _ASSERTE(prop.cb >= sizeof(WCHAR)); + + pAssemblyIdentity->m_simpleName.Set((const WCHAR *) prop.pv, + (prop.cb - sizeof(WCHAR)) / sizeof(WCHAR)); + pAssemblyIdentity->SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_SIMPLE_NAME); + } + + // Display version + if (dwDisplayFlags & ASM_DISPLAYF_VERSION) { + prop = _rProp[ASM_NAME_MAJOR_VERSION]; + + // Set version if we have it + if (prop.cb != 0) { + DWORD dwVersionParts[4]; + + for(DWORD i = 0; i < 4; i++) { + prop = _rProp[ASM_NAME_MAJOR_VERSION + i]; + + // Normalize non-existing version parts to zero + if (prop.cb == sizeof(WORD)) { + dwVersionParts[i] = (DWORD) (* ((WORD *) prop.pv)); + } + else { + dwVersionParts[i] = 0; + } + } + + pAssemblyIdentity->m_version.SetFeatureVersion(dwVersionParts[0], dwVersionParts[1]); + pAssemblyIdentity->m_version.SetServiceVersion(dwVersionParts[2], dwVersionParts[3]); + pAssemblyIdentity->SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_VERSION); + } + } + + // Display culture + if (dwDisplayFlags & ASM_DISPLAYF_CULTURE) { + prop = _rProp[ASM_NAME_CULTURE]; + + if (prop.cb != 0) { + _ASSERTE(prop.cb >= sizeof(WCHAR)); + + if (((const WCHAR *) prop.pv)[0] != 0x00) { + pAssemblyIdentity->m_cultureOrLanguage. + Set((const WCHAR *) prop.pv, + (prop.cb - sizeof(WCHAR)) / sizeof(WCHAR)); + } + + pAssemblyIdentity->SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CULTURE); + } + } + + // Display public key token + if ((dwDisplayFlags & ASM_DISPLAYF_PUBLIC_KEY_TOKEN) && _fPublicKeyToken) { + prop = _rProp[ASM_NAME_PUBLIC_KEY_TOKEN]; + + if (prop.cb != 0) { + pAssemblyIdentity->m_publicKeyOrTokenBLOB.Set((const BYTE *) prop.pv, prop.cb); + pAssemblyIdentity->SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN); + } + else { + pAssemblyIdentity-> + SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL); + } + } + + // Display processor architecture + if (dwDisplayFlags & ASM_DISPLAYF_PROCESSORARCHITECTURE) { + if (_rProp[ASM_NAME_ARCHITECTURE].cb != 0) { + DWORD PeKind = *((LPDWORD)_rProp[ASM_NAME_ARCHITECTURE].pv); + + if (PeKind != peNone) { + pAssemblyIdentity->m_kProcessorArchitecture = (PEKIND) PeKind; + pAssemblyIdentity-> + SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE); + } + } + } + + // Display retarget flag + if (dwDisplayFlags & ASM_DISPLAYF_RETARGET) { + prop = _rProp[ASM_NAME_RETARGET]; + + if (prop.cb != 0) { + BOOL fRetarget = *((LPBOOL) prop.pv); + + if (fRetarget) + { + pAssemblyIdentity->SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE); + } + } + } + + // Display content type + if (dwDisplayFlags & ASM_DISPLAYF_CONTENT_TYPE) + { + prop = _rProp[ASM_NAME_CONTENT_TYPE]; + if (prop.cb != 0) + { + DWORD dwContentType = *((LPDWORD)prop.pv); + if (dwContentType != AssemblyContentType_Default) + { + pAssemblyIdentity->SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE); + pAssemblyIdentity->m_kContentType = (AssemblyContentType)dwContentType; + } + } + } + + // Display custom flag + if ((dwDisplayFlags & ASM_DISPLAYF_CUSTOM) && _fCustom) { + prop = _rProp[ASM_NAME_CUSTOM]; + + if (prop.cb != 0) { + pAssemblyIdentity->m_customBLOB.Set((const BYTE *) prop.pv, prop.cb); + pAssemblyIdentity->SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CUSTOM); + } + else { + pAssemblyIdentity->SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CUSTOM_NULL); + } + } + + // Create the textual identity + hr = BINDER_SPACE::TextualIdentityParser::ToString(pAssemblyIdentity, + pAssemblyIdentity->m_dwIdentityFlags, + textualIdentity); + if (FAILED(hr)) { + goto exit; + } + + // Determine required buffer size + DWORD dwGivenSize = *pccDisplayName; + DWORD dwRequiredSize = textualIdentity.GetCount() + 1; + + *pccDisplayName = dwRequiredSize; + + if (dwRequiredSize > dwGivenSize) { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + + if (szDisplayName) { + szDisplayName[0] = 0x00; + } + goto exit; + } + else { + hr = S_OK; + memcpy(szDisplayName, textualIdentity.GetUnicode(), dwRequiredSize * sizeof(WCHAR)); + } + } + EX_CATCH_HRESULT(hr); + + exit: + END_ENTRYPOINT_NOTHROW; + return hr; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::GetName +// --------------------------------------------------------------------------- +STDMETHODIMP +CAssemblyName::GetName( + __inout LPDWORD lpcwBuffer, + __out_ecount_opt(*lpcwBuffer) LPOLESTR pwzBuffer) +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + DWORD cbBuffer = *lpcwBuffer * sizeof(TCHAR); + hr = GetProperty(ASM_NAME_NAME, pwzBuffer, &cbBuffer); + *lpcwBuffer = cbBuffer / sizeof(TCHAR); + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::GetVersion +// --------------------------------------------------------------------------- +STDMETHODIMP +CAssemblyName::GetVersion( + /* [out] */ LPDWORD pdwVersionHi, + /* [out] */ LPDWORD pdwVersionLow) +{ + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + // Get Assembly Version + hr = GetVersion( ASM_NAME_MAJOR_VERSION, pdwVersionHi, pdwVersionLow); + + END_ENTRYPOINT_NOTHROW; + return hr; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::IsEqual +// --------------------------------------------------------------------------- +STDMETHODIMP +CAssemblyName::IsEqual(LPASSEMBLYNAME pName, DWORD dwCmpFlags) +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + DWORD dwPartialCmpMask = 0; + BOOL fIsPartial = FALSE; + CAssemblyName *pCName = static_cast<CAssemblyName *>(pName); + + const DWORD SIMPLE_VERSION_MASK = ASM_CMPF_VERSION; + + FusionProperty propThis; + FusionProperty propPara; + + if(!pName) { + hr = S_FALSE; + goto Exit; + } + + // Get the ref partial comparison mask, if any. + fIsPartial = CAssemblyName::IsPartial(this, &dwPartialCmpMask); + + if (dwCmpFlags == ASM_CMPF_DEFAULT) { + // Set all comparison flags. + dwCmpFlags = ASM_CMPF_IL_ALL | ASM_CMPF_ARCHITECTURE; + + // don't compare architecture if ref does not have architecture. + if (!(dwPartialCmpMask & ASM_CMPF_ARCHITECTURE)) { + dwCmpFlags &= ~ASM_CMPF_ARCHITECTURE; + } + + // Otherwise, if ref is simple (possibly partial) + // we mask off all version bits. + if (!CAssemblyName::IsStronglyNamed(this)) + { + // we don't have a public key token, but we don't know + // it is because we are simply named assembly or we are + // just partial on public key token. + if (dwPartialCmpMask & ASM_CMPF_PUBLIC_KEY_TOKEN) + { + // now we know we are simply named assembly since we + // have a public key token, but it is NULL. + dwCmpFlags &= ~SIMPLE_VERSION_MASK; + } + // If neither of these two cases then public key token + // is not set in ref , but def may be simple or strong. + // The comparison mask is chosen based on def. + else + { + if (!CAssemblyName::IsStronglyNamed(pName)) + dwCmpFlags &= ~SIMPLE_VERSION_MASK; + } + } + } + + // Mask off flags (either passed in or generated + // by default flag with the comparison mask generated + // from the ref. + dwCmpFlags &= dwPartialCmpMask; + + + // The individual name fields can now be compared.. + + // Compare name + + if (dwCmpFlags & ASM_CMPF_NAME) + { + propThis = _rProp[ASM_NAME_NAME]; + propPara = pCName->_rProp[ASM_NAME_NAME]; + + if (propThis.cb != propPara.cb) + { + hr = S_FALSE; + goto Exit; + } + + if (propThis.cb && FusionCompareStringI((LPWSTR)propThis.pv, (LPWSTR)propPara.pv)) + { + hr = S_FALSE; + goto Exit; + } + } + + // Compare version + + if (dwCmpFlags & ASM_CMPF_MAJOR_VERSION) + { + propThis = _rProp[ASM_NAME_MAJOR_VERSION]; + propPara = pCName->_rProp[ASM_NAME_MAJOR_VERSION]; + + if (*((LPWORD) propThis.pv) != *((LPWORD)propPara.pv)) + { + hr = S_FALSE; + goto Exit; + } + } + + if (dwCmpFlags & ASM_CMPF_MINOR_VERSION) + { + propThis = _rProp[ASM_NAME_MINOR_VERSION]; + propPara = pCName->_rProp[ASM_NAME_MINOR_VERSION]; + + if (*((LPWORD) propThis.pv) != *((LPWORD)propPara.pv)) + { + hr = S_FALSE; + goto Exit; + } + } + + if (dwCmpFlags & ASM_CMPF_REVISION_NUMBER) + { + propThis = _rProp[ASM_NAME_REVISION_NUMBER]; + propPara = pCName->_rProp[ASM_NAME_REVISION_NUMBER]; + + if (*((LPWORD) propThis.pv) != *((LPWORD)propPara.pv)) + { + hr = S_FALSE; + goto Exit; + } + } + + if (dwCmpFlags & ASM_CMPF_BUILD_NUMBER) + { + propThis = _rProp[ASM_NAME_BUILD_NUMBER]; + propPara = pCName->_rProp[ASM_NAME_BUILD_NUMBER]; + + if (*((LPWORD) propThis.pv) != *((LPWORD)propPara.pv)) + { + hr = S_FALSE; + goto Exit; + } + } + + // Compare public key token + + if (dwCmpFlags & ASM_CMPF_PUBLIC_KEY_TOKEN) + { + // compare public key if both of them have public key set. + propThis = _rProp[ASM_NAME_PUBLIC_KEY]; + propPara = pCName->_rProp[ASM_NAME_PUBLIC_KEY]; + if (!propThis.cb || !propPara.cb) { + // otherwise, compare public key token + propThis = _rProp[ASM_NAME_PUBLIC_KEY_TOKEN]; + propPara = pCName->_rProp[ASM_NAME_PUBLIC_KEY_TOKEN]; + } + + if (propThis.cb != propPara.cb) { + hr = S_FALSE; + goto Exit; + } + + if (propThis.cb && memcmp(propThis.pv, propPara.pv, propThis.cb)) { + hr = S_FALSE; + goto Exit; + } + } + + // Compare Culture + + if (dwCmpFlags & ASM_CMPF_CULTURE) + { + propThis = _rProp[ASM_NAME_CULTURE]; + propPara = pCName->_rProp[ASM_NAME_CULTURE]; + + if (propThis.cb != propPara.cb) + { + hr = S_FALSE; + goto Exit; + } + + if (propThis.cb && FusionCompareStringI((LPWSTR)propThis.pv, (LPWSTR)propPara.pv)) + { + hr = S_FALSE; + goto Exit; + } + } + + // Compare Custom attribute. + + if (dwCmpFlags & ASM_CMPF_CUSTOM) + { + propThis = _rProp[ASM_NAME_PUBLIC_KEY_TOKEN]; + propPara = pCName->_rProp[ASM_NAME_PUBLIC_KEY_TOKEN]; + + if (propThis.cb != propPara.cb) { + hr = S_FALSE; + goto Exit; + } + + if (propThis.cb && memcmp(propThis.pv, propPara.pv, propThis.cb)) { + hr = S_FALSE; + goto Exit; + } + } + + // Compare Retarget flag + if (dwCmpFlags & ASM_CMPF_RETARGET) + { + propThis = _rProp[ASM_NAME_RETARGET]; + propPara = pCName->_rProp[ASM_NAME_RETARGET]; + + if (*((LPDWORD) propThis.pv) != *((LPDWORD)propPara.pv)) + { + hr = S_FALSE; + goto Exit; + } + } + + // compare config mask + if (dwCmpFlags & ASM_CMPF_CONFIG_MASK) + { + propThis = _rProp[ASM_NAME_CONFIG_MASK]; + propPara = pCName->_rProp[ASM_NAME_CONFIG_MASK]; + + if (*((LPDWORD) propThis.pv) != *((LPDWORD)propPara.pv)) + { + hr = S_FALSE; + goto Exit; + } + + } + + // compare architecture + if (dwCmpFlags & ASM_CMPF_ARCHITECTURE) + { + propThis = _rProp[ASM_NAME_ARCHITECTURE]; + propPara = pCName->_rProp[ASM_NAME_ARCHITECTURE]; + + if (propThis.cb != propPara.cb) { + hr = S_FALSE; + goto Exit; + } + + if (propThis.cb) { + if (*((LPDWORD) propThis.pv) != *((LPDWORD)propPara.pv)) { + hr = S_FALSE; + goto Exit; + } + } + } + + // Compare content type + if (dwCmpFlags & ASM_CMPF_CONTENT_TYPE) + { + propThis = _rProp[ASM_NAME_CONTENT_TYPE]; + propPara = pCName->_rProp[ASM_NAME_CONTENT_TYPE]; + + if (*((LPDWORD)propThis.pv) != *((LPDWORD)propPara.pv)) + { + hr = S_FALSE; + goto Exit; + } + } + + // compare MVID + if (dwCmpFlags & ASM_CMPF_MVID) + { + propThis = _rProp[ASM_NAME_MVID]; + propPara = pCName->_rProp[ASM_NAME_MVID]; + + if (propThis.cb != propPara.cb) { + hr = S_FALSE; + goto Exit; + } + + if (propThis.cb && memcmp(propThis.pv, propPara.pv, propThis.cb)) { + hr = S_FALSE; + goto Exit; + } + } + + // compare Signature + if (dwCmpFlags & ASM_CMPF_SIGNATURE) + { + propThis = _rProp[ASM_NAME_SIGNATURE_BLOB]; + propPara = pCName->_rProp[ASM_NAME_SIGNATURE_BLOB]; + + if (propThis.cb != propPara.cb) { + hr = S_FALSE; + goto Exit; + } + + if (propThis.cb && memcmp(propThis.pv, propPara.pv, propThis.cb)) { + hr = S_FALSE; + goto Exit; + } + } + + hr = S_OK; +Exit: + END_ENTRYPOINT_NOTHROW; + return hr; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::Reserved +// --------------------------------------------------------------------------- +STDMETHODIMP +CAssemblyName::Reserved( + /* in */ REFIID refIID, + /* in */ IUnknown *pUnkBindSink, + /* in */ IUnknown *pUnkAppCtx, + /* in */ LPCOLESTR szCodebaseIn, + /* in */ LONGLONG llFlags, + /* in */ LPVOID pvReserved, + /* in */ DWORD cbReserved, + /* out */ VOID **ppv) +{ + return E_NOTIMPL; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::Clone +// --------------------------------------------------------------------------- +HRESULT CAssemblyName::Clone(IAssemblyName **ppName) +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + CAssemblyName *pClone = NULL; + + if (!ppName) { + hr = E_INVALIDARG; + goto Exit; + } + + *ppName = NULL; + + pClone = NEW(CAssemblyName); + if( !pClone ) { + hr = E_OUTOFMEMORY; + goto Exit; + } + + hr = CopyProperties(this, pClone, NULL, 0); + if (FAILED(hr)) { + goto Exit; + } + + *ppName = pClone; + (*ppName)->AddRef(); + +Exit: + SAFERELEASE(pClone); + + 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; + + // Fail if finalized. + if (_fIsFinalized) + { + _ASSERTE(!"SetProperty on a IAssemblyName while the name is finalized!"); + hr = E_UNEXPECTED; + goto exit; + } + + 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 = GetPublicKeyTokenFromPKBlob((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: + if (SUCCEEDED(hr)) { + LPWSTR pwzOld; + + // Clear cache + + pwzOld = InterlockedExchangeT(&_pwzTextualIdentity, NULL); + SAFEDELETEARRAY(pwzOld); + pwzOld = InterlockedExchangeT(&_pwzTextualIdentityILFull, NULL); + SAFEDELETEARRAY(pwzOld); + } + + // Free memory allocated by crypto wrapper. + if (pbSN) { + StrongNameFreeBuffer(pbSN); + } + + return hr; +} + + + +// --------------------------------------------------------------------------- +// CheckFieldsForFriendAssembly +// --------------------------------------------------------------------------- +STDAPI +CheckFieldsForFriendAssembly( + LPASSEMBLYNAME pAssemblyName) +{ + HRESULT hr = S_OK; + DWORD dwSize=0; + + // Let's look at the information they gave us in the friends declaration. + // If they put in a Processor Architecture, Culture, or Version, then we'll return an error. + + if (FAILED(hr = pAssemblyName->GetProperty(ASM_NAME_MAJOR_VERSION, NULL, &dwSize)) || + FAILED(hr = pAssemblyName->GetProperty(ASM_NAME_MINOR_VERSION, NULL, &dwSize)) || + FAILED(hr = pAssemblyName->GetProperty(ASM_NAME_BUILD_NUMBER, NULL, &dwSize)) || + FAILED(hr = pAssemblyName->GetProperty(ASM_NAME_REVISION_NUMBER, NULL, &dwSize)) || + FAILED(hr = pAssemblyName->GetProperty(ASM_NAME_CULTURE, NULL, &dwSize)) || + FAILED(hr = pAssemblyName->GetProperty(ASM_NAME_ARCHITECTURE, NULL, &dwSize))) + { + // If any of these calls failed due to an insufficient buffer, then that means + // the assembly name contained them + if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) + hr = META_E_CA_BAD_FRIENDS_ARGS; + } else { + if (FAILED(hr = pAssemblyName->GetProperty(ASM_NAME_PUBLIC_KEY_TOKEN, NULL, &dwSize))) { + + // + // Public Key token should not be passed to InternalsVisibleTo + // attribute. This translates to the ASM_NAME_PUBLIC_KEY_TOKEN + // property being set, while the full public key is not. + // + + if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { + + dwSize = 0; + + if (FAILED(hr = pAssemblyName->GetProperty(ASM_NAME_PUBLIC_KEY, NULL, &dwSize))) { + if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) + hr = S_OK; + } else { + hr = META_E_CA_BAD_FRIENDS_ARGS; + } + + } + } else { + hr = S_OK; + } + } + + + return hr; +} + +// --------------------------------------------------------------------------- +// CreateAssemblyNameObject +// --------------------------------------------------------------------------- + +// This is not external for CoreCLR +STDAPI +CreateAssemblyNameObject( + LPASSEMBLYNAME *ppAssemblyName, + LPCOLESTR szAssemblyName, + DWORD dwFlags, + LPVOID pvReserved) +{ + + 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; + } + + if (dwFlags & CANOF_PARSE_DISPLAY_NAME) + { + hr = pName->Init(NULL, NULL); + if (FAILED(hr)) { + goto exit; + } + + hr = pName->Parse((LPWSTR)szAssemblyName); + } + else + { + hr = pName->Init(szAssemblyName, NULL); + } + + + if (SUCCEEDED(hr) && ((dwFlags & CANOF_VERIFY_FRIEND_ASSEMBLYNAME))) + { + hr = CheckFieldsForFriendAssembly(pName); + } + + + if (FAILED(hr)) + { + SAFERELEASE(pName); + goto exit; + } + + *ppAssemblyName = pName; + +exit: + END_ENTRYPOINT_NOTHROW; + return hr; +} + +// --------------------------------------------------------------------------- +// CreateAssemblyNameObjectFromMetaData +// --------------------------------------------------------------------------- +STDAPI +CreateAssemblyNameObjectFromMetaData( + LPASSEMBLYNAME *ppAssemblyName, + LPCOLESTR szAssemblyName, + ASSEMBLYMETADATA *pamd, + LPVOID pvReserved) +{ + + HRESULT hr = S_OK; + CAssemblyName *pName = NULL; + + pName = NEW(CAssemblyName); + if (!pName) + { + hr = E_OUTOFMEMORY; + goto exit; + } + + hr = pName->Init(szAssemblyName, pamd); + + if (FAILED(hr)) + { + SAFERELEASE(pName); + goto exit; + } + + *ppAssemblyName = pName; + +exit: + return hr; +} + +// --------------------------------------------------------------------------- +// CAssemblyName constructor +// --------------------------------------------------------------------------- +CAssemblyName::CAssemblyName() +{ + _dwSig = 0x454d414e; /* 'EMAN' */ + _fIsFinalized = FALSE; + _fPublicKeyToken = FALSE; + _fCustom = TRUE; + _cRef = 1; + _pwzPathModifier = NULL; + _pwzTextualIdentity = NULL; + _pwzTextualIdentityILFull = NULL; +} + +// --------------------------------------------------------------------------- +// CAssemblyName destructor +// --------------------------------------------------------------------------- +CAssemblyName::~CAssemblyName() +{ + SAFEDELETEARRAY(_pwzPathModifier); + SAFEDELETEARRAY(_pwzTextualIdentity); + SAFEDELETEARRAY(_pwzTextualIdentityILFull); +} + +// --------------------------------------------------------------------------- +// CAssemblyName::IsStronglyNamed +// --------------------------------------------------------------------------- +BOOL CAssemblyName::IsStronglyNamed(IAssemblyName *pName) +{ + CAssemblyName *pCName = static_cast<CAssemblyName *> (pName); + _ASSERTE(pCName); + + return (pCName->_rProp[ASM_NAME_PUBLIC_KEY_TOKEN].cb != 0); +} + +// --------------------------------------------------------------------------- +// CAssemblyName::IsPartial +// --------------------------------------------------------------------------- +BOOL CAssemblyName::IsPartial(IAssemblyName *pIName, + LPDWORD pdwCmpMask) +{ + DWORD dwCmpMask = 0; + BOOL fPartial = FALSE; + + static const ASM_NAME rNameFlags[] ={ASM_NAME_NAME, + ASM_NAME_CULTURE, + ASM_NAME_PUBLIC_KEY_TOKEN, + ASM_NAME_MAJOR_VERSION, + ASM_NAME_MINOR_VERSION, + ASM_NAME_BUILD_NUMBER, + ASM_NAME_REVISION_NUMBER, + ASM_NAME_CUSTOM + }; + + static const ASM_CMP_FLAGS rCmpFlags[] = {ASM_CMPF_NAME, + ASM_CMPF_CULTURE, + ASM_CMPF_PUBLIC_KEY_TOKEN, + ASM_CMPF_MAJOR_VERSION, + ASM_CMPF_MINOR_VERSION, + ASM_CMPF_BUILD_NUMBER, + ASM_CMPF_REVISION_NUMBER, + ASM_CMPF_CUSTOM + }; + + CAssemblyName *pName = static_cast<CAssemblyName*> (pIName); // dynamic_cast + _ASSERTE(pName); + + DWORD iNumOfComparison = sizeof(rNameFlags) / sizeof(rNameFlags[0]); + + for (DWORD i = 0; i < iNumOfComparison; i++) + { + if (pName->_rProp[rNameFlags[i]].cb + || (rNameFlags[i] == ASM_NAME_PUBLIC_KEY_TOKEN + && pName->_fPublicKeyToken) + || (rNameFlags[i] == ASM_NAME_CUSTOM + && pName->_fCustom)) + { + dwCmpMask |= rCmpFlags[i]; + } + else { + fPartial = TRUE; + } + } + + if(pName->_rProp[ASM_NAME_ARCHITECTURE].cb) { + dwCmpMask |= ASM_CMPF_ARCHITECTURE; + } + + if (pName->_rProp[ASM_NAME_RETARGET].cb) { + dwCmpMask |= ASM_CMPF_RETARGET; + } + + if (pName->_rProp[ASM_NAME_CONTENT_TYPE].cb != 0) + { + dwCmpMask |= ASM_CMPF_CONTENT_TYPE; + } + + if (pName->_rProp[ASM_NAME_CONFIG_MASK].cb) { + dwCmpMask |= ASM_CMPF_CONFIG_MASK; + } + + if (pName->_rProp[ASM_NAME_MVID].cb) { + dwCmpMask |= ASM_CMPF_MVID; + } + + if (pName->_rProp[ASM_NAME_SIGNATURE_BLOB].cb) { + dwCmpMask |= ASM_CMPF_SIGNATURE; + } + + if (pdwCmpMask) + *pdwCmpMask = dwCmpMask; + + return fPartial; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::Init +// --------------------------------------------------------------------------- +HRESULT +CAssemblyName::Init(LPCTSTR pszAssemblyName, ASSEMBLYMETADATA *pamd) +{ + HRESULT hr = S_OK; + + // Name + if (pszAssemblyName) + { + hr = SetProperty(ASM_NAME_NAME, (LPTSTR) pszAssemblyName, + (lstrlenW(pszAssemblyName)+1) * sizeof(TCHAR)); + if (FAILED(hr)) + goto exit; + } + + if (pamd) { + // Major version + if (FAILED(hr = SetProperty(ASM_NAME_MAJOR_VERSION, + &pamd->usMajorVersion, sizeof(WORD))) + + // Minor version + || FAILED(hr = SetProperty(ASM_NAME_MINOR_VERSION, + &pamd->usMinorVersion, sizeof(WORD))) + + // Revision number + || FAILED(hr = SetProperty(ASM_NAME_REVISION_NUMBER, + &pamd->usRevisionNumber, sizeof(WORD))) + + // Build number + || FAILED(hr = SetProperty(ASM_NAME_BUILD_NUMBER, + &pamd->usBuildNumber, sizeof(WORD))) + + // Culture + || FAILED(hr = SetProperty(ASM_NAME_CULTURE, + pamd->szLocale, pamd->cbLocale * sizeof(WCHAR))) + ) + { + goto exit; + } + } + +exit: + return hr; +} + +// --------------------------------------------------------------------------- +// 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; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::GetVersion +// --------------------------------------------------------------------------- +HRESULT +CAssemblyName::GetVersion( + /* [in] */ DWORD dwMajorVersionEnumValue, + /* [out] */ LPDWORD pdwVersionHi, + /* [out] */ LPDWORD pdwVersionLow) +{ + HRESULT hr = S_OK; + DWORD cb = sizeof(WORD); + WORD wVerMajor = 0, wVerMinor = 0, wRevNo = 0, wBldNo = 0; + + if(!pdwVersionHi || !pdwVersionLow) { + hr = E_INVALIDARG; + goto Exit; + } + + *pdwVersionHi = *pdwVersionLow = 0; + + if(FAILED( (hr = GetProperty(dwMajorVersionEnumValue, &wVerMajor, &(cb = sizeof(WORD)))))) + goto Exit; + if (cb == 0) { + hr = FUSION_E_INVALID_NAME; + goto Exit; + } + + if(FAILED( (hr = GetProperty(dwMajorVersionEnumValue+1, &wVerMinor, &(cb = sizeof(WORD)))))) + goto Exit; + + if (cb == 0) { + hr = FUSION_E_INVALID_NAME; + goto Exit; + } + + if(FAILED( (hr = GetProperty(dwMajorVersionEnumValue+2, &wBldNo, &(cb = sizeof(WORD)))))) + goto Exit; + if (cb == 0) { + hr = FUSION_E_INVALID_NAME; + goto Exit; + } + + if(FAILED( (hr = GetProperty(dwMajorVersionEnumValue+3, &wRevNo, &(cb = sizeof(WORD)))))) + goto Exit; + + if (cb == 0) { + hr = FUSION_E_INVALID_NAME; + goto Exit; + } + + *pdwVersionHi = MAKELONG(wVerMinor, wVerMajor); + *pdwVersionLow = MAKELONG(wRevNo, wBldNo); + +Exit: + return hr; +} + +// --------------------------------------------------------------------------- +// CAssemblyName::CopyProperties +// --------------------------------------------------------------------------- +HRESULT +CAssemblyName::CopyProperties(CAssemblyName *pSource, + CAssemblyName *pTarget, + const DWORD properties[], + DWORD dwSize) +{ + HRESULT hr = S_OK; + DWORD i = 0; + FusionProperty prop; + + _ASSERTE(pSource && pTarget); + + if (!dwSize) { + for( i = 0; i < ASM_NAME_MAX_PARAMS; i ++) { + prop = pSource->_rProp[i]; + + if (prop.cb) { + if (FAILED(hr = pTarget->SetProperty(i, prop.pv, prop.cb))) { + goto Exit; + } + } + } + } + else { + for (i = 0; i<dwSize; i++) { + _ASSERTE(properties[i] < ASM_NAME_MAX_PARAMS); + prop = pSource->_rProp[properties[i]]; + if (prop.cb) { + if (FAILED(hr = pTarget->SetProperty(properties[i], prop.pv, prop.cb))) { + goto Exit; + } + } + } + } + + pTarget->_fPublicKeyToken = pSource->_fPublicKeyToken; + pTarget->_fCustom = pSource->_fCustom; + + if (pSource->_pwzPathModifier) { + pTarget->_pwzPathModifier = WSTRDupDynamic(pSource->_pwzPathModifier); + if(!pTarget->_pwzPathModifier) { + hr = E_OUTOFMEMORY; + goto Exit; + } + } + +Exit: + return hr; +} + +namespace LegacyFusion +{ + HRESULT SetStringProperty(IAssemblyName *pIAssemblyName, + DWORD dwPropertyId, + SString &value) + { + CAssemblyName *pAssemblyName = static_cast<CAssemblyName *>(pIAssemblyName); + const WCHAR *pValue = value.GetUnicode(); + DWORD dwCBValue = (value.GetCount() + 1) * sizeof(WCHAR); + + return pAssemblyName->SetPropertyInternal(dwPropertyId, + const_cast<WCHAR *>(pValue), + dwCBValue); + } + + HRESULT SetBufferProperty(IAssemblyName *pIAssemblyName, + DWORD dwPropertyId, + SBuffer &value) + { + CAssemblyName *pAssemblyName = static_cast<CAssemblyName *>(pIAssemblyName); + const BYTE *pValue = value; // special operator + DWORD dwCBValue = value.GetSize() * sizeof(BYTE); + + return pAssemblyName->SetPropertyInternal(dwPropertyId, + const_cast<BYTE *>(pValue), + dwCBValue); + } + + HRESULT SetWordProperty(IAssemblyName *pIAssemblyName, + DWORD dwPropertyId, + DWORD dwValue) + { + CAssemblyName *pAssemblyName = static_cast<CAssemblyName *>(pIAssemblyName); + WORD wValue = static_cast<WORD>(dwValue); + DWORD wCBValue = sizeof(WORD); + + // This file-internal function is and must be only used to set version fields + PREFIX_ASSUME((dwPropertyId == ASM_NAME_MAJOR_VERSION) || + (dwPropertyId == ASM_NAME_MINOR_VERSION) || + (dwPropertyId == ASM_NAME_BUILD_NUMBER) || + (dwPropertyId == ASM_NAME_REVISION_NUMBER)); + + return pAssemblyName->SetPropertyInternal(dwPropertyId, &wValue, wCBValue); + } + + HRESULT SetDwordProperty(IAssemblyName *pIAssemblyName, + DWORD dwPropertyId, + DWORD dwValue) + { + CAssemblyName *pAssemblyName = static_cast<CAssemblyName *>(pIAssemblyName); + DWORD dwCBValue = sizeof(DWORD); + + return pAssemblyName->SetPropertyInternal(dwPropertyId, &dwValue, dwCBValue); + } +}; +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; + } + + HRESULT ConvertToUtf8(PCWSTR wzStr, __deref_out UTF8** pszStr) + { + HRESULT hr = S_OK; + + _ASSERTE(wzStr != nullptr && pszStr != nullptr); + if (wzStr == nullptr || pszStr == nullptr) + { + return E_INVALIDARG; + } + + DWORD cbSize = WszWideCharToMultiByte(CP_UTF8, 0, wzStr, -1, NULL, 0, NULL, NULL); + if(cbSize == 0) + { + return SUCCEEDED(hr = HRESULT_FROM_GetLastError()) ? E_UNEXPECTED : hr; + } + + NewArrayHolder<UTF8> szStr = new (nothrow) UTF8[cbSize]; + IfNullRet(szStr); + + cbSize = WszWideCharToMultiByte(CP_UTF8, 0, wzStr, -1, static_cast<LPSTR>(szStr), cbSize, NULL, NULL); + if(cbSize == 0) + { + return SUCCEEDED(hr = HRESULT_FROM_GetLastError()) ? E_UNEXPECTED : hr; + } + + *pszStr = szStr.Extract(); + return S_OK; + } + } + + // 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; + } + + HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, __deref_out WCHAR ** pwzVal) + { + LIMITED_METHOD_CONTRACT; + HRESULT hr = S_OK; + + _ASSERTE(pName != nullptr && pwzVal != nullptr); + if (pName == nullptr || pwzVal == nullptr) + { + return E_INVALIDARG; + } + + DWORD cbSize = 0; + hr = pName->GetProperty(dwProperty, NULL, &cbSize); + + if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) + { + NewArrayHolder<WCHAR> wzVal = reinterpret_cast<PWSTR>(new (nothrow) BYTE[cbSize]); + IfNullRet(wzVal); + hr = pName->GetProperty(dwProperty, reinterpret_cast<PBYTE>(static_cast<PWSTR>(wzVal)), &cbSize); + IfFailRet(hr); + *pwzVal = wzVal.Extract(); + } + + return hr; + } + + HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, __deref_out UTF8 **pwzOut) + { + LIMITED_METHOD_CONTRACT; + HRESULT hr = S_OK; + + if (pwzOut == nullptr) + return E_INVALIDARG; + + SmallStackSString ssStr; + hr = GetProperty(pName, dwProperty, ssStr); + IfFailRet(hr); + hr = priv::ConvertToUtf8(ssStr, pwzOut); + IfFailRet(hr); + return hr; + } + + + } +} + diff --git a/src/binder/fusionhelpers.cpp b/src/binder/fusionhelpers.cpp new file mode 100644 index 0000000000..40ef745743 --- /dev/null +++ b/src/binder/fusionhelpers.cpp @@ -0,0 +1,115 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// FusionHelpers.cpp +// +// Implements various helper functions +// +// ============================================================ + +#include "fusionhelpers.hpp" + +#include "shlwapi.h" +#include "newapis.h" + +#define IS_UPPER_A_TO_Z(x) (((x) >= L'A') && ((x) <= L'Z')) +#define IS_LOWER_A_TO_Z(x) (((x) >= L'a') && ((x) <= L'z')) +#define IS_0_TO_9(x) (((x) >= L'0') && ((x) <= L'9')) +#define CAN_SIMPLE_UPCASE(x) (((x)&~0x7f) == 0) +#define SIMPLE_UPCASE(x) (IS_LOWER_A_TO_Z(x) ? ((x) - L'a' + L'A') : (x)) +#define CAN_SIMPLE_LOWERCASE(x) (((x)&~0x7f) == 0) +#define SIMPLE_LOWERCASE(x) (IS_UPPER_A_TO_Z(x) ? ((x) - L'A' + L'a') : (x)) + +// --------------------------------------------------------------------------- +// Private Helpers +// --------------------------------------------------------------------------- +namespace +{ + WCHAR FusionMapChar(WCHAR wc) + { + WCHAR wTmp; + +#ifndef FEATURE_PAL + +#ifdef FEATURE_USE_LCID + int iRet = WszLCMapString(g_lcid, LCMAP_UPPERCASE, &wc, 1, &wTmp, 1); +#else + int iRet = NewApis::LCMapStringEx(g_lcid, LCMAP_UPPERCASE, &wc, 1, &wTmp, 1, NULL, NULL, 0); +#endif + if (!iRet) { + _ASSERTE(!"LCMapString failed!"); + iRet = GetLastError(); + wTmp = wc; + } +#else // !FEATURE_PAL + // For PAL, no locale specific processing is done + wTmp = toupper(wc); +#endif // !FEATURE_PAL + + return wTmp; + } +}; + +// --------------------------------------------------------------------------- +// FusionCompareStringN +// --------------------------------------------------------------------------- +// if nChar < 0, compare the whole string +int FusionCompareStringN(LPCWSTR pwz1, LPCWSTR pwz2, int nChar, BOOL bCaseSensitive) +{ + int iRet = 0; + int nCount = 0; + WCHAR ch1; + WCHAR ch2; + _ASSERTE(pwz1 && pwz2); + + // same point always return equal. + if (pwz1 == pwz2) { + return 0; + } + + // Case sensitive comparison + if (bCaseSensitive) { + if (nChar >= 0) + return wcsncmp(pwz1, pwz2, nChar); + else + return wcscmp(pwz1, pwz2); + } + + for (;;) { + ch1 = *pwz1++; + ch2 = *pwz2++; + + if (ch1 == L'\0' || ch2 == L'\0') { + break; + } + + // We use OS mapping table + ch1 = (CAN_SIMPLE_UPCASE(ch1)) ? (SIMPLE_UPCASE(ch1)) : (FusionMapChar(ch1)); + ch2 = (CAN_SIMPLE_UPCASE(ch2)) ? (SIMPLE_UPCASE(ch2)) : (FusionMapChar(ch2)); + nCount++; + + if (ch1 != ch2 || (nChar >= 0 && nCount >= nChar)) { + break; + } + } + + if (ch1 > ch2) { + iRet = 1; + } + else if (ch1 < ch2) { + iRet = -1; + } + + return iRet; +} + +// --------------------------------------------------------------------------- +// FusionCompareStringI +// --------------------------------------------------------------------------- +int FusionCompareStringI(LPCWSTR pwz1, LPCWSTR pwz2) +{ + return FusionCompareStringN(pwz1, pwz2, -1, FALSE); +} diff --git a/src/binder/inc/.gitmirror b/src/binder/inc/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/binder/inc/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/binder/inc/applicationcontext.hpp b/src/binder/inc/applicationcontext.hpp new file mode 100644 index 0000000000..f92cc8da16 --- /dev/null +++ b/src/binder/inc/applicationcontext.hpp @@ -0,0 +1,198 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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" +#ifdef FEATURE_VERSIONING_LOG +#include "bindinglog.hpp" +#endif // FEATURE_VERSIONING_LOG +#include "stringarraylist.h" + +namespace BINDER_SPACE +{ + //============================================================================================= + // Data structures for Simple Name -> File Name hash + struct FileNameMapEntry + { + LPWSTR m_wszFileName; + }; + + class FileNameHashTraits : public NoRemoveSHashTraits< DefaultSHashTraits< FileNameMapEntry > > + { + public: + typedef PCWSTR key_t; + static const FileNameMapEntry Null() { FileNameMapEntry e; e.m_wszFileName = nullptr; return e; } + static bool IsNull(const FileNameMapEntry & e) { return e.m_wszFileName == nullptr; } + static const key_t GetKey(const FileNameMapEntry & e) + { + key_t key; + key = e.m_wszFileName; + return key; + } + static count_t Hash(const key_t &str) { return HashiString(str); } + static BOOL Equals(const key_t &lhs, const key_t &rhs) { LIMITED_METHOD_CONTRACT; return (_wcsicmp(lhs, rhs) == 0); } + }; + + typedef SHash<FileNameHashTraits> TpaFileNameHash; + + // 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 const key_t GetKey(const SimpleNameToFileNameMapEntry & e) + { + key_t key; + key = e.m_wszSimpleName; + return key; + } + static count_t Hash(const key_t &str) { return HashiString(str); } + static BOOL Equals(const key_t &lhs, const key_t &rhs) { LIMITED_METHOD_CONTRACT; return (_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(); + + 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 InspectionContext *GetInspectionContext(); + inline FailureCache *GetFailureCache(); + inline HRESULT AddToFailureCache(SString &assemblyNameOrPath, + HRESULT hrBindResult); + inline StringArrayList *GetAppPaths(); + inline SimpleNameToFileNameMap *GetTpaList(); + inline TpaFileNameHash *GetTpaFileNameList(); + 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(); + +#ifdef FEATURE_VERSIONING_LOG + inline BindingLog *GetBindingLog(); + inline void ClearBindingLog(); +#endif // FEATURE_VERSIONING_LOG + + protected: + LONG m_cRef; + Volatile<LONG> m_cVersion; + SString m_applicationName; + DWORD m_dwAppDomainId; + ExecutionContext *m_pExecutionContext; + InspectionContext *m_pInspectionContext; + FailureCache *m_pFailureCache; + CRITSEC_COOKIE m_contextCS; +#ifdef FEATURE_VERSIONING_LOG + BindingLog m_bindingLog; +#endif // FEATURE_VERSIONING_LOG + + AssemblyIdentityCache m_assemblyIdentityCache; + + StringArrayList m_platformResourceRoots; + StringArrayList m_appPaths; + StringArrayList m_appNiPaths; + + SimpleNameToFileNameMap * m_pTrustedPlatformAssemblyMap; + TpaFileNameHash * m_pFileNameHash; + +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) + bool m_fCanExplicitlyBindToNativeImages; +public: + inline void SetExplicitBindToNativeImages(bool fCanExplicitlyBindToNativeImages) + { + m_fCanExplicitlyBindToNativeImages = fCanExplicitlyBindToNativeImages; + } + + inline bool CanExplicitlyBindToNativeImages() + { + return m_fCanExplicitlyBindToNativeImages; + } +protected: +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) + }; + +#include "applicationcontext.inl" + +}; + +#endif diff --git a/src/binder/inc/applicationcontext.inl b/src/binder/inc/applicationcontext.inl new file mode 100644 index 0000000000..5b756860f5 --- /dev/null +++ b/src/binder/inc/applicationcontext.inl @@ -0,0 +1,111 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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; +} + +InspectionContext *ApplicationContext::GetInspectionContext() +{ + return m_pInspectionContext; +} + +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; +} + +TpaFileNameHash * ApplicationContext::GetTpaFileNameList() +{ + return m_pFileNameHash; +} + +StringArrayList * ApplicationContext::GetPlatformResourceRoots() +{ + return &m_platformResourceRoots; +} + +StringArrayList * ApplicationContext::GetAppNiPaths() +{ + return &m_appNiPaths; +} + +CRITSEC_COOKIE ApplicationContext::GetCriticalSectionCookie() +{ + return m_contextCS; +} + +#ifdef FEATURE_VERSIONING_LOG +BindingLog *ApplicationContext::GetBindingLog() +{ + return &m_bindingLog; +} + +void ApplicationContext::ClearBindingLog() +{ + m_bindingLog.SetDebugLog(NULL); +} +#endif // FEATURE_VERSIONING_LOG + + +#endif diff --git a/src/binder/inc/assembly.hpp b/src/binder/inc/assembly.hpp new file mode 100644 index 0000000000..81ff1e465c --- /dev/null +++ b/src/binder/inc/assembly.hpp @@ -0,0 +1,223 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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" + +#if !defined(FEATURE_FUSION) +#include "clrprivbindercoreclr.h" +#endif // !defined(FEATURE_FUSION) + +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) +#include "clrprivbinderassemblyloadcontext.h" +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + +STDAPI BinderAcquirePEImage(LPCTSTR szAssemblyPath, + PEImage **ppPEImage, + PEImage **ppNativeImage, + BOOL fExplicitBindToNativeImage); + +STDAPI BinderAcquireImport(PEImage *pPEImage, + IMDInternalImport **pIMetaDataAssemblyImport, + DWORD *pdwPAFlags, + BOOL bNativeImage); + +STDAPI BinderHasNativeHeader(PEImage *pPEImage, + BOOL *result); + +STDAPI BinderGetImagePath(PEImage *pPEImage, + SString &imagePath); + +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(IsShareable)(BOOL * pbIsShareable); + + STDMETHOD(GetAvailableImageTypes)(PDWORD pdwImageTypes); + + STDMETHOD(GetImageResource)( + DWORD dwImageType, + DWORD *pdwImageType, + ICLRPrivResource ** ppIResource); + + STDMETHOD(VerifyBind)( + IAssemblyName * pIAssemblyName, + ICLRPrivAssembly *pAssembly, + ICLRPrivAssemblyInfo *pAssemblyInfo); + + STDMETHOD(GetBinderID)(UINT_PTR *pBinderId); + + STDMETHOD(FindAssemblyBySpec)( + LPVOID pvAppDomain, + LPVOID pvAssemblySpec, + HRESULT * pResult, + ICLRPrivAssembly ** ppAssembly); + + STDMETHOD(GetBinderFlags)(DWORD *pBinderFlags); + + // -------------------------------------------------------------------- + // Assembly methods + // -------------------------------------------------------------------- + Assembly(); + virtual ~Assembly(); + + HRESULT Init(/* in */ IMDInternalImport *pIMetaDataAssemblyImport, + /* in */ PEKIND PeKind, + /* in */ PEImage *pPEImage, + /* in */ PEImage *pPENativeImage, + /* in */ SString &assemblyPath, + /* in */ BOOL fInspectionOnly, + /* in */ BOOL fIsInGAC); + + // Enumerates dependent assemblies + HRESULT GetNextAssemblyNameRef(/* in */ DWORD nIndex, + /* out */ AssemblyName **ppAssemblyName); + + inline AssemblyName *GetAssemblyName(BOOL fAddRef = FALSE); + inline BOOL GetIsInGAC(); + inline BOOL GetIsDynamicBind(); + inline void SetIsDynamicBind(BOOL fIsDynamicBind); + inline BOOL GetIsByteArray(); + inline void SetIsByteArray(BOOL fIsByteArray); + inline BOOL GetIsSharable(); + inline void SetIsSharable(BOOL fIsSharable); + 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); + +#ifndef CROSSGEN_COMPILE + protected: +#endif + // Asssembly Flags + enum + { + FLAG_NONE = 0x00, + FLAG_INSPECTION_ONLY = 0x01, + FLAG_IS_IN_GAC = 0x02, + FLAG_IS_DYNAMIC_BIND = 0x04, + FLAG_IS_BYTE_ARRAY = 0x08, + FLAG_IS_SHARABLE = 0x10 + }; + + inline void SetPEImage(PEImage *pPEImage); + inline void SetNativePEImage(PEImage *pNativePEImage); + + inline void SetAssemblyName(AssemblyName *pAssemblyName, + BOOL fAddRef = TRUE); + inline BOOL GetInspectionOnly(); + inline void SetInspectionOnly(BOOL fInspectionOnly); + inline void SetIsInGAC(BOOL fIsInGAC); + + inline IMDInternalImport *GetMDImport(); + inline void SetMDImport(IMDInternalImport *pMDImport); + inline mdAssembly *GetAssemblyRefTokens(); + + inline DWORD GetNbAssemblyRefTokens(); + inline void SetNbAsssemblyRefTokens(DWORD dwCAssemblyRefTokens); + + LONG m_cRef; + PEImage *m_pPEImage; + PEImage *m_pNativePEImage; + IMDInternalImport *m_pMDImport; + mdAssembly *m_pAssemblyRefTokens; + DWORD m_dwCAssemblyRefTokens; + AssemblyName *m_pAssemblyName; + SString m_assemblyPath; + DWORD m_dwAssemblyFlags; + ICLRPrivBinder *m_pBinder; + + // Nested class used to implement ICLRPriv binder related interfaces + class CLRPrivResourceAssembly : + public ICLRPrivResource, public ICLRPrivResourceAssembly + { +public: + STDMETHOD(QueryInterface)(REFIID riid, void ** ppv); + STDMETHOD_(ULONG, AddRef)(); + STDMETHOD_(ULONG, Release)(); + STDMETHOD(GetResourceType)(IID *pIID); + STDMETHOD(GetAssembly)(LPVOID *ppAssembly); + } m_clrPrivRes; + + inline void SetBinder(ICLRPrivBinder *pBinder) + { + _ASSERTE(m_pBinder == NULL || m_pBinder == pBinder); + m_pBinder = pBinder; + } + + inline ICLRPrivBinder* GetBinder() + { + return m_pBinder; + } + +#if !defined(FEATURE_FUSION) + friend class ::CLRPrivBinderCoreCLR; +#endif // !defined(FEATURE_FUSION) + +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + friend class ::CLRPrivBinderAssemblyLoadContext; +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + }; + + // 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/binder/inc/assembly.inl b/src/binder/inc/assembly.inl new file mode 100644 index 0000000000..606b7a2c35 --- /dev/null +++ b/src/binder/inc/assembly.inl @@ -0,0 +1,219 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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::GetInspectionOnly() +{ + return ((m_dwAssemblyFlags & FLAG_INSPECTION_ONLY) != 0); +} + +void Assembly::SetInspectionOnly(BOOL fInspectionOnly) +{ + if (fInspectionOnly) + { + m_dwAssemblyFlags |= FLAG_INSPECTION_ONLY; + } + else + { + m_dwAssemblyFlags &= ~FLAG_INSPECTION_ONLY; + } +} + +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; + } +} + +BOOL Assembly::GetIsDynamicBind() +{ + return ((m_dwAssemblyFlags & FLAG_IS_DYNAMIC_BIND) != 0); +} + +void Assembly::SetIsDynamicBind(BOOL fIsDynamicBind) +{ + if (fIsDynamicBind) + { + m_dwAssemblyFlags |= FLAG_IS_DYNAMIC_BIND; + } + else + { + m_dwAssemblyFlags &= ~FLAG_IS_DYNAMIC_BIND; + } +} + +BOOL Assembly::GetIsByteArray() +{ + return ((m_dwAssemblyFlags & FLAG_IS_BYTE_ARRAY) != 0); +} + +void Assembly::SetIsByteArray(BOOL fIsByteArray) +{ + if (fIsByteArray) + { + m_dwAssemblyFlags |= FLAG_IS_BYTE_ARRAY; + } + else + { + m_dwAssemblyFlags &= ~FLAG_IS_BYTE_ARRAY; + } +} + +BOOL Assembly::GetIsSharable() +{ + return ((m_dwAssemblyFlags & FLAG_IS_SHARABLE) != 0); +} + +void Assembly::SetIsSharable(BOOL fIsSharable) +{ + if (fIsSharable) + { + m_dwAssemblyFlags |= FLAG_IS_SHARABLE; + } + else + { + m_dwAssemblyFlags &= ~FLAG_IS_SHARABLE; + } +} + +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(); +} + +mdAssembly *Assembly::GetAssemblyRefTokens() +{ + return m_pAssemblyRefTokens; +} + +DWORD Assembly::GetNbAssemblyRefTokens() +{ + return m_dwCAssemblyRefTokens; +} + +void Assembly::SetNbAsssemblyRefTokens(DWORD dwCAssemblyRefTokens) +{ + m_dwCAssemblyRefTokens = dwCAssemblyRefTokens; +} + +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/binder/inc/assemblybinder.hpp b/src/binder/inc/assemblybinder.hpp new file mode 100644 index 0000000000..6780710a7a --- /dev/null +++ b/src/binder/inc/assemblybinder.hpp @@ -0,0 +1,173 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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" + +class CLRPrivBinderAssemblyLoadContext; +class CLRPrivBinderCoreCLR; + +namespace BINDER_SPACE +{ + typedef enum + { + kBindingStoreGAC = 0x01, + kBindingStoreManifest = 0x02, + kBindingStoreHost = 0x04, + kBindingStoreContext = 0x08 + } BindingStore; + + 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, + /* 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 GetAssemblyFromImage(/* in */ PEImage *pPEImage, + /* in */ PEImage *pNativePEImage, + /* out */ Assembly **ppAssembly); + + // Special assembly binder entry point for byte arrays + static HRESULT PreBindByteArray(/* in */ ApplicationContext *pApplicationContext, + /* in */ PEImage *pPEImage, + /* in */ BOOL fInspectionOnly); + + static HRESULT GetAssembly(/* in */ SString &assemblyPath, + /* in */ BOOL fInspectionOnly, + /* in */ BOOL fIsInGAC, + /* in */ BOOL fExplicitBindToNativeImage, + /* out */ Assembly **ppAssembly, + /* in */ LPCTSTR szMDAssemblyPath = NULL); + +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + static HRESULT BindUsingHostAssemblyResolver (/* in */ CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin, + /* in */ AssemblyName *pAssemblyName, + /* in */ IAssemblyName *pIAssemblyName, + /* 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(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + + static HRESULT TranslatePEToArchitectureType(DWORD *pdwPAFlags, PEKIND *PeKind); + + protected: + enum + { + BIND_NONE = 0x00, + BIND_CACHE_FAILURES = 0x01, + BIND_CACHE_RERUN_BIND = 0x02, + BIND_IGNORE_DYNAMIC_BINDS = 0x04 +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + , + BIND_IGNORE_REFDEF_MATCH = 0x8 +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + }; + + static BOOL IgnoreDynamicBinds(DWORD dwBindFlags) + { + return ((dwBindFlags & BIND_IGNORE_DYNAMIC_BINDS) != 0); + } + + static BOOL CacheBindFailures(DWORD dwBindFlags) + { + return ((dwBindFlags & BIND_CACHE_FAILURES) != 0); + } + + static BOOL RerunBind(DWORD dwBindFlags) + { + return ((dwBindFlags & BIND_CACHE_RERUN_BIND) != 0); + } + +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + static BOOL IgnoreRefDefMatch(DWORD dwBindFlags) + { + return ((dwBindFlags & BIND_IGNORE_REFDEF_MATCH) != 0); + } +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + + static HRESULT BindByName(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* in */ DWORD dwBindFlags, + /* 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, + /* out */ BindResult *pBindResult); + + static HRESULT BindLocked(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* in */ DWORD dwBindFlags, + /* out */ BindResult *pBindResult); + static HRESULT BindLockedOrService(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* 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 fInspectionOnly, + /* out */ BindResult *pBindResult, + /* out */ bool *pfUnifiedAppAssemblyToPlatform); + + static HRESULT Register(/* in */ ApplicationContext *pApplicationContext, + /* in */ BOOL fInspectionOnly, + /* 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/binder/inc/assemblyentry.hpp b/src/binder/inc/assemblyentry.hpp new file mode 100644 index 0000000000..9e51ac55cc --- /dev/null +++ b/src/binder/inc/assemblyentry.hpp @@ -0,0 +1,63 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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/binder/inc/assemblyhashtraits.hpp b/src/binder/inc/assemblyhashtraits.hpp new file mode 100644 index 0000000000..605e868d9c --- /dev/null +++ b/src/binder/inc/assemblyhashtraits.hpp @@ -0,0 +1,60 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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 const element_t Null() + { + return NULL; + } + static bool IsNull(const element_t &assemblyEntry) + { + return (assemblyEntry == NULL); + } + }; +}; + +#endif diff --git a/src/binder/inc/assemblyidentity.hpp b/src/binder/inc/assemblyidentity.hpp new file mode 100644 index 0000000000..1fedf3642b --- /dev/null +++ b/src/binder/inc/assemblyidentity.hpp @@ -0,0 +1,150 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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) + } IdentityFlags; + + 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; + } + + void CloneInto(AssemblyIdentity *pAssemblyIdentity) + { + pAssemblyIdentity->m_simpleName.Set(m_simpleName); + pAssemblyIdentity->m_simpleName.Normalize(); + pAssemblyIdentity->m_version.SetVersion(&m_version); + pAssemblyIdentity->m_cultureOrLanguage.Set(m_cultureOrLanguage); + pAssemblyIdentity->m_cultureOrLanguage.Normalize(); + pAssemblyIdentity->m_publicKeyOrTokenBLOB.Set(m_publicKeyOrTokenBLOB); + pAssemblyIdentity->m_kProcessorArchitecture = m_kProcessorArchitecture; + pAssemblyIdentity->m_kContentType = m_kContentType; + pAssemblyIdentity->m_customBLOB.Set(m_customBLOB); + pAssemblyIdentity->m_dwIdentityFlags = m_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/binder/inc/assemblyidentitycache.hpp b/src/binder/inc/assemblyidentitycache.hpp new file mode 100644 index 0000000000..644414cff6 --- /dev/null +++ b/src/binder/inc/assemblyidentitycache.hpp @@ -0,0 +1,117 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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 const 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/binder/inc/assemblyname.hpp b/src/binder/inc/assemblyname.hpp new file mode 100644 index 0000000000..ac648586e3 --- /dev/null +++ b/src/binder/inc/assemblyname.hpp @@ -0,0 +1,113 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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, + EXCLUDE_PUBLIC_KEY_TOKEN_IF_MISSING = 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); + HRESULT CreateFusionName(/* out */ IAssemblyName **ppIAssemblyName); + + 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); + + inline BOOL HaveAssemblyVersion(); + inline BOOL HaveNeutralCulture(); + + SString &GetDeNormalizedCulture(); + BOOL IsStronglyNamed(); + + BOOL IsMscorlib(); + + // Translate textual identity into appropriate PEKIND + HRESULT SetArchitecture(SString &architecture); + + ULONG Hash(/* in */ DWORD dwIncludeFlags); + BOOL Equals(/* in */ AssemblyName *pAssemblyName, + /* in */ DWORD dwIncludeFlags); + + // Compare assembly ref with assembly def ignoring assembly version + BOOL RefEqualsDef(/* in */ AssemblyName *pAssemblyNameDef, + /* in */ BOOL fInspectionOnly); + + HRESULT Clone(/* out */ AssemblyName **ppAssemblyName); + + void GetDisplayName(/* out */ PathString &displayName, + /* in */ DWORD dwIncludeFlags); + + static SString &ArchitectureToString(PEKIND kArchitecture); + 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/binder/inc/assemblyname.inl b/src/binder/inc/assemblyname.inl new file mode 100644 index 0000000000..5744592c00 --- /dev/null +++ b/src/binder/inc/assemblyname.inl @@ -0,0 +1,145 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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); +} + +BOOL AssemblyName::HaveAssemblyVersion() +{ + return (m_version.GetMajor() != static_cast<DWORD>(-1)); +} + +BOOL AssemblyName::HaveNeutralCulture() +{ + return GetDeNormalizedCulture().IsEmpty(); +} + +#endif diff --git a/src/binder/inc/assemblyversion.hpp b/src/binder/inc/assemblyversion.hpp new file mode 100644 index 0000000000..1d5a6bb34c --- /dev/null +++ b/src/binder/inc/assemblyversion.hpp @@ -0,0 +1,61 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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 + { + public: + inline AssemblyVersion(); + inline ~AssemblyVersion(); + + 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 BOOL SetServiceVersion(/* in */ LPCWSTR pwzVersionStr); + inline BOOL SetVersion(/* in */ LPCWSTR pwzVersionStr); + inline void SetVersion(AssemblyVersion *pAssemblyVersion); + + inline BOOL IsLargerFeatureVersion(/* in */ AssemblyVersion *pAssemblyVersion); + inline BOOL IsEqualFeatureVersion(/* in */ AssemblyVersion *pAssemblyVersion); + inline BOOL IsSmallerFeatureVersion(/* in */ AssemblyVersion *pAssemblyVersion); + inline BOOL IsEqualServiceVersion(/* in */ AssemblyVersion *pAssemblyVersion); + inline BOOL IsLargerServiceVersion(/* in */ AssemblyVersion *pAssemblyVersion); + inline BOOL Equals(AssemblyVersion *pAssemblyVersion); + inline BOOL IsSmallerOrEqual(AssemblyVersion *pAssemblyVersion); + inline BOOL IsLargerOrEqual(AssemblyVersion *pAssemblyVersion); + protected: + DWORD m_dwMajor; + DWORD m_dwMinor; + DWORD m_dwBuild; + DWORD m_dwRevision; + }; + +#include "assemblyversion.inl" +}; + +#endif diff --git a/src/binder/inc/assemblyversion.inl b/src/binder/inc/assemblyversion.inl new file mode 100644 index 0000000000..a42990934d --- /dev/null +++ b/src/binder/inc/assemblyversion.inl @@ -0,0 +1,184 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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 +} + +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) +{ + m_dwMajor = dwMajor; + m_dwMinor = dwMinor; +} + +void AssemblyVersion::SetServiceVersion(DWORD dwBuild, + DWORD dwRevision) +{ + m_dwBuild = dwBuild; + m_dwRevision = dwRevision; +} + +BOOL AssemblyVersion::SetVersion(LPCWSTR pwzVersionStr) +{ + SmallStackSString versionString(pwzVersionStr); + + return TextualIdentityParser::ParseVersion(versionString, this); +} + +void AssemblyVersion::SetVersion(AssemblyVersion *pAssemblyVersion) +{ + m_dwMajor = pAssemblyVersion->GetMajor(); + m_dwMinor = pAssemblyVersion->GetMinor(); + m_dwBuild = pAssemblyVersion->GetBuild(); + m_dwRevision = pAssemblyVersion->GetRevision(); +} + +BOOL AssemblyVersion::IsLargerFeatureVersion(AssemblyVersion *pAssemblyVersion) +{ + BOOL result = FALSE; + + if (GetMajor() > pAssemblyVersion->GetMajor()) + { + result = TRUE; + } + else if ((GetMajor() == pAssemblyVersion->GetMajor()) && + (GetMinor() > pAssemblyVersion->GetMinor())) + { + result = TRUE; + } + + return result; +} + +BOOL AssemblyVersion::IsEqualFeatureVersion(AssemblyVersion *pAssemblyVersion) +{ + BOOL result = FALSE; + + if ((GetMajor() == pAssemblyVersion->GetMajor()) && + (GetMinor() == pAssemblyVersion->GetMinor())) + { + result = TRUE; + } + + return result; +} + +BOOL AssemblyVersion::IsSmallerFeatureVersion(AssemblyVersion *pAssemblyVersion) +{ + BOOL result = FALSE; + + if (GetMajor() < pAssemblyVersion->GetMajor()) + { + result = TRUE; + } + else if ((GetMajor() == pAssemblyVersion->GetMajor()) && + (GetMinor() < pAssemblyVersion->GetMinor())) + { + result = TRUE; + } + + return result; +} + +BOOL AssemblyVersion::IsEqualServiceVersion(AssemblyVersion *pAssemblyVersion) +{ + BOOL result = FALSE; + + if ((GetBuild() == pAssemblyVersion->GetBuild()) && + (GetRevision() == pAssemblyVersion->GetRevision())) + { + result = TRUE; + } + + return result; +} + +BOOL AssemblyVersion::IsLargerServiceVersion(AssemblyVersion *pAssemblyVersion) +{ + BOOL result = FALSE; + + if (GetBuild() > pAssemblyVersion->GetBuild()) + { + result = TRUE; + } + else if ((GetBuild() == pAssemblyVersion->GetBuild()) && + (GetRevision() > pAssemblyVersion->GetRevision())) + { + result = TRUE; + } + + return result; +} + +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; +} + +BOOL AssemblyVersion::IsSmallerOrEqual(AssemblyVersion *pAssemblyVersion) +{ + return (Equals(pAssemblyVersion) || + IsSmallerFeatureVersion(pAssemblyVersion) || + (IsEqualFeatureVersion(pAssemblyVersion) && + !IsLargerServiceVersion(pAssemblyVersion))); +} + +BOOL AssemblyVersion::IsLargerOrEqual(AssemblyVersion *pAssemblyVersion) +{ + return (Equals(pAssemblyVersion) || + IsLargerFeatureVersion(pAssemblyVersion) || + (IsEqualFeatureVersion(pAssemblyVersion) && + IsLargerServiceVersion(pAssemblyVersion))); +} + +#endif diff --git a/src/binder/inc/binderinterface.hpp b/src/binder/inc/binderinterface.hpp new file mode 100644 index 0000000000..381dc740af --- /dev/null +++ b/src/binder/inc/binderinterface.hpp @@ -0,0 +1,70 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// BinderInterface.hpp +// + + +// +// Defines the public AssemblyBinder interface +// +// ============================================================ + +#ifndef __BINDER_INTERFACE_HPP__ +#define __BINDER_INTERFACE_HPP__ + +class PEImage; +class PEAssembly; +class StringArrayList; + +namespace BINDER_SPACE +{ + class Assembly; + class AssemblyIdentityUTF8; +}; + +namespace BinderInterface +{ + HRESULT Init(); + + HRESULT SetupContext(/* in */ LPCWSTR wszApplicationBase, + /* in */ DWORD dwAppDomainId, + /* out */ IUnknown **ppIApplicationContext); + + // 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 Bind(/* in */ IUnknown *pIApplicationContext, + /* in */ SString &assemblyDisplayName, + /* in */ LPCWSTR wszCodeBase, + /* in */ PEAssembly *pParentAssembly, + /* in */ BOOL fNgenExplicitBind, + /* in */ BOOL fExplicitBindToNativeImage, + /*out */ BINDER_SPACE::Assembly **ppAssembly); + + // + // Called via managed AppDomain.ExecuteAssembly variants and during binding host setup + // + HRESULT SetupBindingPaths(/* in */ IUnknown *pIApplicationContext, + /* in */ SString &sTrustedPlatformAssemblies, + /* in */ SString &sPlatformResourceRoots, + /* in */ SString &sAppPaths, + /* in */ SString &sAppNiPaths); + + // + // Called via CoreAssemblySpec::BindToSystem + // + HRESULT BindToSystem(/* in */ SString &sSystemDirectory, + /* out */ BINDER_SPACE::Assembly **ppSystemAssembly, + /* in */ bool fBindToNativeImage); + +#ifdef BINDER_DEBUG_LOG + HRESULT Log(/* in */ LPCWSTR wszMessage); +#endif // BINDER_DEBUG_LOG + +}; + +#endif diff --git a/src/binder/inc/bindertypes.hpp b/src/binder/inc/bindertypes.hpp new file mode 100644 index 0000000000..97ea1db432 --- /dev/null +++ b/src/binder/inc/bindertypes.hpp @@ -0,0 +1,135 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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 +{ + typedef InlineSString<MAX_PATH + 1> PathString; + + class AssemblyVersion; + class AssemblyName; + class Assembly; + + class GACEntry; + class GACVersionIterator; + class GAC; + + class ContextEntry; + class ExecutionContext; + class InspectionContext; + + class PropertyMap; + class ApplicationContext; + + class BindResult; + class FailureCache; + class AssemblyBinder; + +#if defined(BINDER_DEBUG_LOG) + class DebugLog; +#endif + +#if defined(FEATURE_VERSIONING_LOG) + class BindingLog; + class CDebugLog; +#endif // FEATURE_VERSIONING_LOG + + namespace Tests + { + HRESULT Run(); + }; +}; + +#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])) + +#include "debuglog.hpp" + +#endif diff --git a/src/binder/inc/bindinglog.hpp b/src/binder/inc/bindinglog.hpp new file mode 100644 index 0000000000..b2917f80dd --- /dev/null +++ b/src/binder/inc/bindinglog.hpp @@ -0,0 +1,83 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// BindingLog.hpp +// + + +// +// Defines the BindingLog class +// +// ============================================================ + +#ifndef __BINDER__BINDING_LOG_HPP__ +#define __BINDER__BINDING_LOG_HPP__ + +#include "bindertypes.hpp" + +namespace BINDER_SPACE +{ + class BindingLog + { + public: + BindingLog(); + ~BindingLog(); + + // + // These functions will create a new log together with pre-bind state + // information if needed and store it into the application context. + // This is to avoid endlessly passing around the debug log. + // + static HRESULT CreateInContext(/* in */ ApplicationContext *pApplicationContext, + /* in */ SString &assemblyPath, + /* in */ PEAssembly *pParentAssembly); + static HRESULT CreateInContext(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* in */ PEAssembly *pParentAssembly); + + HRESULT Log(SString &info); + + inline HRESULT Log(LPCWSTR pwzInfo); + inline HRESULT Log(/* in */ LPCWSTR pwzPrefix, + /* in */ SString &info); + + HRESULT LogAssemblyName(/* in */ LPCWSTR pwzPrefix, + /* in */ AssemblyName *pAssemblyName); + + HRESULT LogHR(/* in */ HRESULT logHR); + HRESULT LogResult(/* in */ BindResult *pBindResult); + + HRESULT Flush(); + + inline BOOL CanLog(); + + void SetDebugLog(CDebugLog *pCDebugLog); + + protected: + static BOOL IsLoggingNeeded(); + + static HRESULT CreateInContext(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* in */ SString &assemblyPath, + /* in */ PEAssembly *pParentAssembly); + + HRESULT LogPreBindState(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* in */ SString &assemblyPath, + /* in */ PEAssembly *pParentAssembly); + + + inline CDebugLog *GetDebugLog(); + + HRESULT LogUser(); + + CDebugLog *m_pCDebugLog; + }; + +#include "bindinglog.inl" +}; + +#endif diff --git a/src/binder/inc/bindinglog.inl b/src/binder/inc/bindinglog.inl new file mode 100644 index 0000000000..822f0e3b53 --- /dev/null +++ b/src/binder/inc/bindinglog.inl @@ -0,0 +1,47 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// BindingLog.inl +// + + +// +// Implements inlined methods of BindingLog +// +// ============================================================ + +#ifndef __BINDER__BINDING_LOG_INL__ +#define __BINDER__BINDING_LOG_INL__ + +BOOL BindingLog::CanLog() +{ + return (m_pCDebugLog != NULL); +} + +CDebugLog *BindingLog::GetDebugLog() +{ + _ASSERTE(m_pCDebugLog != NULL); + return m_pCDebugLog; +} + +HRESULT BindingLog::Log(LPCWSTR pwzInfo) +{ + PathString info(pwzInfo); + return BindingLog::Log(info); +} + +HRESULT BindingLog::Log(LPCWSTR pwzPrefix, + SString &info) +{ + PathString message; + + message.Append(pwzPrefix); + message.Append(info); + + return Log(message); +} + +#endif diff --git a/src/binder/inc/bindresult.hpp b/src/binder/inc/bindresult.hpp new file mode 100644 index 0000000000..83a9b8cf26 --- /dev/null +++ b/src/binder/inc/bindresult.hpp @@ -0,0 +1,65 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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 AssemblyName *GetRetargetedAssemblyName(); + inline void SetRetargetedAssemblyName(AssemblyName *pRetargetedAssemblyName); + + inline BOOL GetIsDynamicBind(); + inline void SetIsDynamicBind(BOOL fIsDynamicBind); + 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 BOOL GetIsSharable(); + inline void SetIsSharable(BOOL fIsSharable); + + 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 IUnknown *ExtractAssembly(); + inline void Reset(); + + protected: + DWORD m_dwResultFlags; + AssemblyName *m_pAssemblyName; + AssemblyName *m_pRetargetedAssemblyName; + ReleaseHolder<IUnknown> m_pIUnknownAssembly; + }; +}; + +#endif diff --git a/src/binder/inc/bindresult.inl b/src/binder/inc/bindresult.inl new file mode 100644 index 0000000000..a9bee03360 --- /dev/null +++ b/src/binder/inc/bindresult.inl @@ -0,0 +1,230 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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_pRetargetedAssemblyName = NULL; + m_pIUnknownAssembly = NULL; +} + +BindResult::~BindResult() +{ + SAFE_RELEASE(m_pAssemblyName); + SAFE_RELEASE(m_pRetargetedAssemblyName); +} + +AssemblyName *BindResult::GetAssemblyName(BOOL fAddRef /* = FALSE */) +{ + AssemblyName *pAssemblyName = m_pAssemblyName; + + if (fAddRef && (pAssemblyName != NULL)) + { + pAssemblyName->AddRef(); + } + + return pAssemblyName; +} + +AssemblyName *BindResult::GetRetargetedAssemblyName() +{ + return m_pRetargetedAssemblyName; +} + +void BindResult::SetRetargetedAssemblyName(AssemblyName *pRetargetedAssemblyName) +{ + SAFE_RELEASE(m_pRetargetedAssemblyName); + + if (pRetargetedAssemblyName) + { + pRetargetedAssemblyName->AddRef(); + } + + m_pRetargetedAssemblyName = pRetargetedAssemblyName; +} + +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::GetIsDynamicBind() +{ + return ((m_dwResultFlags & ContextEntry::RESULT_FLAG_IS_DYNAMIC_BIND) != 0); +} + +void BindResult::SetIsDynamicBind(BOOL fIsDynamicBind) +{ + if (fIsDynamicBind) + { + m_dwResultFlags |= ContextEntry::RESULT_FLAG_IS_DYNAMIC_BIND; + } + else + { + m_dwResultFlags &= ~ContextEntry::RESULT_FLAG_IS_DYNAMIC_BIND; + } +} + +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; + } +} + +BOOL BindResult::GetIsSharable() +{ + return ((m_dwResultFlags & ContextEntry::RESULT_FLAG_IS_SHARABLE) != 0); +} + +void BindResult::SetIsSharable(BOOL fIsSharable) +{ + if (fIsSharable) + { + m_dwResultFlags |= ContextEntry::RESULT_FLAG_IS_SHARABLE; + } + else + { + m_dwResultFlags &= ~ContextEntry::RESULT_FLAG_IS_SHARABLE; + } +} + +void BindResult::SetResult(ContextEntry *pContextEntry, BOOL fIsContextBound /* = TRUE */) +{ + _ASSERTE(pContextEntry != NULL); + + SetIsDynamicBind(pContextEntry->GetIsDynamicBind()); + SetIsInGAC(pContextEntry->GetIsInGAC()); + SetIsContextBound(fIsContextBound); + SetIsSharable(pContextEntry->GetIsSharable()); + SAFE_RELEASE(m_pAssemblyName); + m_pAssemblyName = pContextEntry->GetAssemblyName(TRUE /* fAddRef */); + m_pIUnknownAssembly = pContextEntry->GetAssembly(TRUE /* fAddRef */); +} + +void BindResult::SetResult(Assembly *pAssembly) +{ + _ASSERTE(pAssembly != NULL); + + SetIsDynamicBind(pAssembly->GetIsDynamicBind()); + SetIsInGAC(pAssembly->GetIsInGAC()); + SetIsSharable(pAssembly->GetIsSharable()); + 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 */); +} + +void BindResult::SetNoResult() +{ + m_pAssemblyName = NULL; +} + +BOOL BindResult::HaveResult() +{ + return (GetAssemblyName() != NULL); +} + +IUnknown *BindResult::ExtractAssembly() +{ + return m_pIUnknownAssembly.Extract(); +} + +void BindResult::Reset() +{ + SAFE_RELEASE(m_pAssemblyName); + SAFE_RELEASE(m_pRetargetedAssemblyName); + m_pIUnknownAssembly = NULL; + m_dwResultFlags = ContextEntry::RESULT_FLAG_NONE; +} + +} + +#endif diff --git a/src/binder/inc/cdebuglog.hpp b/src/binder/inc/cdebuglog.hpp new file mode 100644 index 0000000000..ff3092e418 --- /dev/null +++ b/src/binder/inc/cdebuglog.hpp @@ -0,0 +1,68 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// CDebugLog.hpp +// + + +// +// Defines the CDebugLog class +// +// ============================================================ + +#ifndef __BINDER__C_DEBUG_LOG_HPP__ +#define __BINDER__C_DEBUG_LOG_HPP__ + +#include "bindertypes.hpp" +#include "list.hpp" + +#define FUSION_BIND_LOG_CATEGORY_DEFAULT 0 +#define FUSION_BIND_LOG_CATEGORY_NGEN 1 +#define FUSION_BIND_LOG_CATEGORY_MAX 2 + +namespace BINDER_SPACE +{ + class CDebugLog + { + public: + CDebugLog(); + ~CDebugLog(); + + static HRESULT Create(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* in */ SString &sCodeBase, + /* out */ CDebugLog **ppCDebugLog); + + ULONG AddRef(); + ULONG Release(); + + HRESULT SetResultCode(/* in */ DWORD dwLogCategory, + /* in */ HRESULT hrResult); + + HRESULT LogMessage(/* in */ DWORD dwDetailLevel, + /* in */ DWORD dwLogCategory, + /* in */ SString &sDebugString); + + HRESULT Flush(/* in */ DWORD dwDetailLevel, + /* in */ DWORD dwLogCategory); + protected: + HRESULT Init(/* in */ ApplicationContext *pApplicationContext, + /* in */ AssemblyName *pAssemblyName, + /* in */ SString &sCodeBase); + + HRESULT LogHeader(/* in */ DWORD dwLogCategory); + HRESULT LogFooter(/* in */ DWORD dwLogCategory); + + LONG m_cRef; + FileHandleHolder m_hLogFile; + List<SString> m_content[FUSION_BIND_LOG_CATEGORY_MAX]; + SString m_applicationName; + SString m_logFileName; + HRESULT m_HrResult[FUSION_BIND_LOG_CATEGORY_MAX]; + }; +}; + +#endif diff --git a/src/binder/inc/clrprivbinderassemblyloadcontext.h b/src/binder/inc/clrprivbinderassemblyloadcontext.h new file mode 100644 index 0000000000..c6f8a6ecfe --- /dev/null +++ b/src/binder/inc/clrprivbinderassemblyloadcontext.h @@ -0,0 +1,87 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +#ifndef __CLRPRIVBINDERASSEMBLYLOADCONTEXT_H__ +#define __CLRPRIVBINDERASSEMBLYLOADCONTEXT_H__ + +#include "coreclrbindercommon.h" +#include "applicationcontext.hpp" +#include "clrprivbindercoreclr.h" + +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) + +namespace BINDER_SPACE +{ + class AssemblyIdentityUTF8; +}; + +class CLRPrivBinderAssemblyLoadContext : public IUnknownCommon<ICLRPrivBinder> +{ +public: + + //========================================================================= + // ICLRPrivBinder functions + //------------------------------------------------------------------------- + STDMETHOD(BindAssemblyByName)( + /* [in] */ IAssemblyName *pIAssemblyName, + /* [retval][out] */ ICLRPrivAssembly **ppAssembly); + + STDMETHOD(VerifyBind)( + /* [in] */ IAssemblyName *pIAssemblyName, + /* [in] */ ICLRPrivAssembly *pAssembly, + /* [in] */ ICLRPrivAssemblyInfo *pAssemblyInfo); + + STDMETHOD(GetBinderFlags)( + /* [retval][out] */ DWORD *pBinderFlags); + + STDMETHOD(GetBinderID)( + /* [retval][out] */ UINT_PTR *pBinderId); + + STDMETHOD(FindAssemblyBySpec)( + /* [in] */ LPVOID pvAppDomain, + /* [in] */ LPVOID pvAssemblySpec, + /* [out] */ HRESULT *pResult, + /* [out] */ ICLRPrivAssembly **ppAssembly); + +public: + //========================================================================= + // Class functions + //------------------------------------------------------------------------- + + static HRESULT SetupContext(DWORD dwAppDomainId, CLRPrivBinderCoreCLR *pTPABinder, + UINT_PTR ptrAssemblyLoadContext, CLRPrivBinderAssemblyLoadContext **ppBindContext); + + CLRPrivBinderAssemblyLoadContext(); + + inline BINDER_SPACE::ApplicationContext *GetAppContext() + { + return &m_appContext; + } + + inline INT_PTR GetManagedAssemblyLoadContext() + { + return m_ptrManagedAssemblyLoadContext; + } + + 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; + + INT_PTR m_ptrManagedAssemblyLoadContext; +}; + +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN) +#endif // __CLRPRIVBINDERASSEMBLYLOADCONTEXT_H__ diff --git a/src/binder/inc/clrprivbindercoreclr.h b/src/binder/inc/clrprivbindercoreclr.h new file mode 100644 index 0000000000..2aa1283448 --- /dev/null +++ b/src/binder/inc/clrprivbindercoreclr.h @@ -0,0 +1,82 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +#ifndef __CLR_PRIV_BINDER_CORECLR_H__ +#define __CLR_PRIV_BINDER_CORECLR_H__ + +#include "coreclrbindercommon.h" +#include "applicationcontext.hpp" + +namespace BINDER_SPACE +{ + class AssemblyIdentityUTF8; +}; + +class CLRPrivBinderCoreCLR : public IUnknownCommon<ICLRPrivBinder> +{ +public: + + //========================================================================= + // ICLRPrivBinder functions + //------------------------------------------------------------------------- + STDMETHOD(BindAssemblyByName)( + /* [in] */ IAssemblyName *pIAssemblyName, + /* [retval][out] */ ICLRPrivAssembly **ppAssembly); + + STDMETHOD(VerifyBind)( + /* [in] */ IAssemblyName *pIAssemblyName, + /* [in] */ ICLRPrivAssembly *pAssembly, + /* [in] */ ICLRPrivAssemblyInfo *pAssemblyInfo); + + STDMETHOD(GetBinderFlags)( + /* [retval][out] */ DWORD *pBinderFlags); + + STDMETHOD(GetBinderID)( + /* [retval][out] */ UINT_PTR *pBinderId); + + STDMETHOD(FindAssemblyBySpec)( + /* [in] */ LPVOID pvAppDomain, + /* [in] */ LPVOID pvAssemblySpec, + /* [out] */ HRESULT *pResult, + /* [out] */ ICLRPrivAssembly **ppAssembly); + +public: + + HRESULT SetupBindingPaths(SString &sTrustedPlatformAssemblies, + SString &sPlatformResourceRoots, + SString &sAppPaths, + SString &sAppNiPaths); + + bool IsInTpaList(const SString &sFileName); + + inline BINDER_SPACE::ApplicationContext *GetAppContext() + { + return &m_appContext; + } + + HRESULT Bind(SString &assemblyDisplayName, + LPCWSTR wszCodeBase, + PEAssembly *pParentAssembly, + BOOL fNgenExplicitBind, + BOOL fExplicitBindToNativeImage, + ICLRPrivAssembly **ppAssembly); + +#ifndef CROSSGEN_COMPILE + HRESULT PreBindByteArray(PEImage *pPEImage, BOOL fInspectionOnly); +#endif // CROSSGEN_COMPILE + + HRESULT BindAssemblyByNameWorker( + BINDER_SPACE::AssemblyName *pAssemblyName, + BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly); + + //========================================================================= + // Internal implementation details + //------------------------------------------------------------------------- +private: + BINDER_SPACE::ApplicationContext m_appContext; +}; + +#endif // __CLR_PRIV_BINDER_CORECLR_H__ diff --git a/src/binder/inc/compatibility.hpp b/src/binder/inc/compatibility.hpp new file mode 100644 index 0000000000..7e7000ebf1 --- /dev/null +++ b/src/binder/inc/compatibility.hpp @@ -0,0 +1,32 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// Compatibility.hpp +// + + +// +// Defines the V2 Compatibility class +// +// ============================================================ + +#ifndef __BINDER__COMPATIBLITY_HPP__ +#define __BINDER__COMPATIBLITY_HPP__ + +#include "bindertypes.hpp" + +namespace BINDER_SPACE +{ + class Compatibility + { + public: + static HRESULT Retarget(/* in */ AssemblyName *pAssemblyName, + /* out */ AssemblyName **ppRetargetedAssemblyName, + /* out */ BOOL *pFIsRetargeted); + }; +}; + +#endif diff --git a/src/binder/inc/contextentry.hpp b/src/binder/inc/contextentry.hpp new file mode 100644 index 0000000000..4a9c9277c5 --- /dev/null +++ b/src/binder/inc/contextentry.hpp @@ -0,0 +1,147 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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, + RESULT_FLAG_IS_SHARABLE = 0x20 + } ResultFlags; + + ContextEntry() : AssemblyEntry() + { + m_dwResultFlags = RESULT_FLAG_NONE; + m_pIUnknownAssembly = NULL; + } + + ~ContextEntry() + { + SAFE_RELEASE(m_pIUnknownAssembly); + } + + BOOL GetIsDynamicBind() + { + return ((m_dwResultFlags & RESULT_FLAG_IS_DYNAMIC_BIND) != 0); + } + + void SetIsDynamicBind(BOOL fIsDynamicBind) + { + if (fIsDynamicBind) + { + m_dwResultFlags |= RESULT_FLAG_IS_DYNAMIC_BIND; + } + else + { + m_dwResultFlags &= ~RESULT_FLAG_IS_DYNAMIC_BIND; + } + } + + 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; + } + } + + BOOL GetIsSharable() + { + return ((m_dwResultFlags & RESULT_FLAG_IS_SHARABLE) != 0); + } + + void SetIsSharable(BOOL fIsSharable) + { + if (fIsSharable) + { + m_dwResultFlags |= RESULT_FLAG_IS_SHARABLE; + } + else + { + m_dwResultFlags &= ~RESULT_FLAG_IS_SHARABLE; + } + } + + 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/binder/inc/coreclrbindercommon.h b/src/binder/inc/coreclrbindercommon.h new file mode 100644 index 0000000000..3049d7fc9b --- /dev/null +++ b/src/binder/inc/coreclrbindercommon.h @@ -0,0 +1,59 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + + +#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); + + static HRESULT GetAssemblyFromImage(PEImage *pPEImage, + PEImage *pNativePEImage, + ICLRPrivAssembly **ppAssembly); + + static HRESULT GetAssembly(/* in */ SString &assemblyPath, + /* in */ BOOL fInspectionOnly, + /* in */ BOOL fIsInGAC, + /* in */ BOOL fExplicitBindToNativeImage, + /* out */ BINDER_SPACE::Assembly **ppAssembly); +}; + +#endif // __CORECLR_BINDER_COMMON_H__ diff --git a/src/binder/inc/debuglog.hpp b/src/binder/inc/debuglog.hpp new file mode 100644 index 0000000000..710b528f16 --- /dev/null +++ b/src/binder/inc/debuglog.hpp @@ -0,0 +1,119 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// DebugLog.hpp +// + + +// +// Defines the DebugLog class +// +// ============================================================ + +#ifndef __BINDER__DEBUG_LOG_HPP__ +#define __BINDER__DEBUG_LOG_HPP__ + +#include "bindertypes.hpp" +#include "variables.hpp" + +namespace BINDER_SPACE +{ + +// When defined at the top of a source file, DISABLE_BINDER_DEBUG_LOGGING will cause +// binder debug logging to be disabled only in the scope of that file by defining all the +// binder logging macros as NOPs. + +#if defined(BINDER_DEBUG_LOG) && !defined(DISABLE_BINDER_DEBUG_LOGGING) + +#define BINDER_LOG_STARTUP() \ + IF_FAIL_GO(DebugLog::Startup()); + +#define BINDER_LOG_LOCK() \ + CRITSEC_Holder logLock(g_BinderVariables->m_logCS); + +#define BINDER_LOG_ENTER(scope) \ + DebugLog::Enter(scope); + +#define BINDER_LOG_LEAVE(scope) \ + DebugLog::Leave(scope); + +#define BINDER_LOG_LEAVE_HR(scope, hr) \ + DebugLog::LeaveHR(scope, hr); + +#define BINDER_LOG_LEAVE_BOOL(scope, fResult) \ + DebugLog::LeaveBool(scope, fResult); + +#define BINDER_LOG(comment) \ + DebugLog::Log(comment); + +#define BINDER_LOG_STRING(comment, value) \ + DebugLog::Log(comment, value); + +#define BINDER_LOG_HRESULT(comment, hr) \ + DebugLog::Log(comment, hr); + +#define BINDER_LOG_ASSEMBLY_NAME(comment, assemblyName) \ + DebugLog::Log(comment, assemblyName); + +#define BINDER_LOG_I_ASSEMBLY_NAME(comment, assemblyName) \ + DebugLog::Log(comment, assemblyName); + +#define BINDER_LOG_POINTER(comment, pData) \ + DebugLog::Log(comment, (void *) (pData)); + + class DebugLog + { + public: + static HRESULT Startup(); + + static void Enter(/* in */ WCHAR *pwzScope); + static void Leave(/* in */ WCHAR *pwzScope); + static void LeaveHR(/* in */ WCHAR *pwzScope, + /* in */ HRESULT hrLog); + static void LeaveBool(/* in */ WCHAR *pwzScope, + /* in */ BOOL fResult); + + static void Log(/* in */ WCHAR *pwzComment); + static void Log(/* in */ WCHAR *pwzComment, + /* in */ SString &value); + static void Log(/* in */ WCHAR *pwzComment, + /* in */ const WCHAR *value); + static void Log(/* in */ WCHAR *pwzComment, + /* in */ HRESULT hrLog); + static void Log(/* in */ WCHAR *pwzComment, + /* in */ AssemblyName *pAssemblyName); + static void Log(/* in */ WCHAR *pwzComment, + /* in */ void *pData); + protected: + static void Log(/* in */ SString &info); + }; +#else + class DebugLog + { + public: + static void Empty() {}; + }; + +#define BINDER_LOG_STARTUP() DebugLog::Empty(); + +#define BINDER_LOG_LOCK() DebugLog::Empty(); + +#define BINDER_LOG_ENTER(scope) DebugLog::Empty(); +#define BINDER_LOG_LEAVE(scope) DebugLog::Empty(); +#define BINDER_LOG_LEAVE_HR(scope, hr) DebugLog::Empty(); +#define BINDER_LOG_LEAVE_BOOL(scope, fResult) DebugLog::Empty(); + +#define BINDER_LOG(comment) DebugLog::Empty(); +#define BINDER_LOG_STRING(comment, value) DebugLog::Empty(); +#define BINDER_LOG_HRESULT(comment, hr) DebugLog::Empty(); +#define BINDER_LOG_ASSEMBLY_NAME(comment, assemblyName) DebugLog::Empty(); +#define BINDER_LOG_I_ASSEMBLY_NAME(comment, assemblyName) DebugLog::Empty(); +#define BINDER_LOG_POINTER(comment, pData) DebugLog::Empty(); + +#endif +}; + +#endif diff --git a/src/binder/inc/failurecache.hpp b/src/binder/inc/failurecache.hpp new file mode 100644 index 0000000000..da695ca0d3 --- /dev/null +++ b/src/binder/inc/failurecache.hpp @@ -0,0 +1,39 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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/binder/inc/failurecachehashtraits.hpp b/src/binder/inc/failurecachehashtraits.hpp new file mode 100644 index 0000000000..5298280e79 --- /dev/null +++ b/src/binder/inc/failurecachehashtraits.hpp @@ -0,0 +1,89 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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 const element_t Null() + { + return NULL; + } + static bool IsNull(const element_t &propertyEntry) + { + return (propertyEntry == NULL); + } + + }; +}; + +#endif diff --git a/src/binder/inc/fusionassemblyname.hpp b/src/binder/inc/fusionassemblyname.hpp new file mode 100644 index 0000000000..8dfb554817 --- /dev/null +++ b/src/binder/inc/fusionassemblyname.hpp @@ -0,0 +1,218 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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_t* 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 : public IAssemblyName +{ +private: + DWORD _dwSig; + Volatile<LONG> _cRef; + CPropertyArray _rProp; + BOOL _fIsFinalized; + BOOL _fPublicKeyToken; + BOOL _fCustom; + LPWSTR _pwzPathModifier; + LPWSTR _pwzTextualIdentity; + LPWSTR _pwzTextualIdentityILFull; + + DWORD _dw; + +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); + + STDMETHOD(Finalize)(); + + STDMETHOD(GetDisplayName)( + __out_ecount_opt(*pccDisplayName) LPOLESTR szDisplayName, + __inout LPDWORD pccDisplayName, + __in DWORD dwDisplayFlags); + + STDMETHOD(GetName)( + __inout LPDWORD lpcwBuffer, + __out_ecount_opt(*lpcwBuffer) LPOLESTR pwzBuffer); + + STDMETHOD(GetVersion)( + /* [out] */ LPDWORD pwVersionHi, + /* [out] */ LPDWORD pwVersionLow); + + STDMETHOD (IsEqual)( + /* [in] */ LPASSEMBLYNAME pName, + /* [in] */ DWORD dwCmpFlags); + + STDMETHOD(Reserved)( + /* in */ REFIID refIID, + /* in */ IUnknown *pUnkBindSink, + /* in */ IUnknown *pUnkAppCtx, + /* in */ LPCOLESTR szCodebase, + /* in */ LONGLONG llFlags, + /* in */ LPVOID pvReserved, + /* in */ DWORD cbReserved, + /* out */ VOID **ppv); + + STDMETHODIMP Clone(IAssemblyName **ppName); + + HRESULT SetPropertyInternal(/* in */ DWORD PropertyId, + /* in */ LPCVOID pvProperty, + /* in */ DWORD cbProperty); + + CAssemblyName(); + ~CAssemblyName(); + + HRESULT Init(LPCTSTR pszAssemblyName, ASSEMBLYMETADATA *pamd); + HRESULT Parse(LPCWSTR szDisplayName); + + static BOOL IsStronglyNamed(IAssemblyName *pName); + static BOOL IsPartial(IAssemblyName *pName, + LPDWORD pdwCmpMask = NULL); + +protected: + HRESULT GetVersion(DWORD dwMajorVersionEnumValue, + LPDWORD pwVersionHi, + LPDWORD pwVersionLow); + + HRESULT CopyProperties(CAssemblyName *pSource, + CAssemblyName *pTarget, + const DWORD properties[], + DWORD dwSize); +}; + +STDAPI +CreateAssemblyNameObject( + LPASSEMBLYNAME *ppAssemblyName, + LPCOLESTR szAssemblyName, + DWORD dwFlags, + LPVOID pvReserved); + +STDAPI +CreateAssemblyNameObjectFromMetaData( + LPASSEMBLYNAME *ppAssemblyName, + LPCOLESTR szAssemblyName, + ASSEMBLYMETADATA *pamd, + LPVOID pvReserved); + +namespace LegacyFusion +{ + HRESULT SetStringProperty(IAssemblyName *pIAssemblyName, + DWORD dwPropertyId, + SString &value); + HRESULT SetBufferProperty(IAssemblyName *pIAssemblyName, + DWORD dwPropertyId, + SBuffer &value); + HRESULT SetWordProperty(IAssemblyName *pIAssemblyName, + DWORD dwPropertyId, + DWORD dwValue); + HRESULT SetDwordProperty(IAssemblyName *pIAssemblyName, + DWORD dwPropertyId, + DWORD dwValue); +}; + +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); + + // Returns an allocated buffer with the contents of the property. + // + // Returns S_FALSE if the property has not been set. + HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, __deref_out LPWSTR * pwzVal); + + inline HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, LPCWSTR *pwzOut) + { return GetProperty(pName, dwProperty, const_cast<LPWSTR*>(pwzOut)); } + + // Returns an allocated buffer with the contents of the property. + // + // Returns S_FALSE if the property has not been set. + HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, __deref_out LPSTR *pwzOut); + + inline HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, LPCSTR *pwzOut) + { return GetProperty(pName, dwProperty, const_cast<LPSTR*>(pwzOut)); } + + template <typename T> inline + typename std::enable_if<!std::is_pointer< typename std::remove_cv< T >::type >::value, HRESULT>::type + GetProperty(IAssemblyName * pName, DWORD dwProperty, T * pVal) + { + DWORD cbBuf = sizeof(T); + HRESULT hr = GetProperty(pName, dwProperty, pVal, &cbBuf); + if (hr == S_OK && cbBuf != sizeof(T)) + hr = E_UNEXPECTED; + return hr; + } + + inline HRESULT GetSimpleName(IAssemblyName * pName, SString & ssName) + { return GetProperty(pName, ASM_NAME_NAME, ssName); } + } // namespace fusion.util +} // namespace fusion + + +#endif diff --git a/src/binder/inc/fusionhelpers.hpp b/src/binder/inc/fusionhelpers.hpp new file mode 100644 index 0000000000..cb749559e6 --- /dev/null +++ b/src/binder/inc/fusionhelpers.hpp @@ -0,0 +1,96 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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" + +#ifndef FEATURE_VERSIONING +// Rename the fusion bind result to avoid collision with core bind result +#define IBindResult IBindResult_Fusion +#include "binderngen.h" +#undef IBindResult +#endif + +#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 + +inline +WCHAR* +WSTRDupDynamic(LPCWSTR pwszSrc) +{ + LPWSTR pwszDest = NULL; + if (pwszSrc != NULL) + { + const size_t dwLen = wcslen(pwszSrc) + 1; + pwszDest = FUSION_NEW_ARRAY(WCHAR, dwLen); + + if( pwszDest ) + memcpy(pwszDest, pwszSrc, dwLen * sizeof(WCHAR)); + } + + return pwszDest; +} + +#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)))) + +int FusionCompareStringI(LPCWSTR pwz1, LPCWSTR pwz2); + +// CLR lobal culture ID +extern LocaleID g_lcid; + +#endif diff --git a/src/binder/inc/list.hpp b/src/binder/inc/list.hpp new file mode 100644 index 0000000000..e436484670 --- /dev/null +++ b/src/binder/inc/list.hpp @@ -0,0 +1,359 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// List.hpp +// + + +// +// Defines the List class +// +// ============================================================ + +#ifndef __BINDER__LIST_HPP__ +#define __BINDER__LIST_HPP__ + +#include "bindertypes.hpp" +#include "ex.h" + +namespace BINDER_SPACE +{ + // Ripped from Fusion + + // + // ListNode + // + + typedef void *LISTNODE; + + template <class Type> class ListNode + { + public: + ListNode(Type item); + virtual ~ListNode(); + + void SetNext(ListNode *pNode); + void SetPrev(ListNode *pNode); + Type GetItem(); + ListNode *GetNext(); + ListNode *GetPrev(); + + private: + Type _type; + ListNode *_pNext; + ListNode *_pPrev; + }; + + // + // List + // + + template <class Type> class List + { + public: + List(); + ~List(); + + LISTNODE AddHead(const Type &item); + LISTNODE AddTail(const Type &item); + + LISTNODE GetHeadPosition(); + LISTNODE GetTailPosition(); + void RemoveAt(LISTNODE pNode); + void RemoveAll(); + LISTNODE Find(const Type &item); + int GetCount(); + Type GetNext(LISTNODE &pNode); + Type GetAt(LISTNODE pNode); + LISTNODE AddSorted(const Type &item, LPVOID pfn); + + public: + DWORD _dwSig; + + private: + ListNode<Type> *_pHead; + ListNode<Type> *_pTail; + int _iCount; + }; + + // + // ListNode Implementation + // + + template <class Type> ListNode<Type>::ListNode(Type item) + : _type(item) + , _pNext(NULL) + , _pPrev(NULL) + { + } + + template <class Type> ListNode<Type>::~ListNode() + { + } + + template <class Type> void ListNode<Type>::SetNext(ListNode *pNode) + { + _pNext = pNode; + } + + template <class Type> void ListNode<Type>::SetPrev(ListNode *pNode) + { + _pPrev = pNode; + } + + template <class Type> Type ListNode<Type>::GetItem() + { + return _type; + } + + template <class Type> ListNode<Type> *ListNode<Type>::GetNext() + { + return _pNext; + } + + template <class Type> ListNode<Type> *ListNode<Type>::GetPrev() + { + return _pPrev; + } + + + // + // List Implementation + // + + template <class Type> List<Type>::List() + : _pHead(NULL) + , _pTail(NULL) + , _iCount(0) + { + _dwSig = 0x5453494c; /* 'TSIL' */ + } + + template <class Type> List<Type>::~List() + { + HRESULT hr = S_OK; + + EX_TRY + { + RemoveAll(); + } + EX_CATCH_HRESULT(hr); + } + + template <class Type> LISTNODE List<Type>::AddHead(const Type &item) + { + ListNode<Type> *pNode = NULL; + + NEW_CONSTR(pNode, ListNode<Type>(item)); + if (pNode) { + _iCount++; + pNode->SetNext(_pHead); + pNode->SetPrev(NULL); + if (_pHead == NULL) { + _pTail = pNode; + } + else { + _pHead->SetPrev(pNode); + } + _pHead = pNode; + } + + return (LISTNODE)pNode; + } + + template <class Type> LISTNODE List<Type>::AddSorted(const Type &item, + LPVOID pfn) + { + ListNode<Type> *pNode = NULL; + LISTNODE pCurrNode = NULL; + LISTNODE pPrevNode = NULL; + int i; + Type curItem; + + LONG (*pFN) (const Type item1, const Type item2); + + pFN = (LONG (*) (const Type item1, const Type item2))pfn; + + if(_pHead == NULL) { + return AddHead(item); + } + else { + pCurrNode = GetHeadPosition(); + curItem = ((ListNode<Type> *) pCurrNode)->GetItem(); + for (i = 0; i < _iCount; i++) { + if (pFN(item, curItem) < 1) { + NEW_CONSTR(pNode, ListNode<Type>(item)); + if (pNode) { + pNode->SetPrev((ListNode<Type> *)pPrevNode); + pNode->SetNext((ListNode<Type> *)pCurrNode); + // update pPrevNode + if(pPrevNode) { + ((ListNode<Type> *)pPrevNode)->SetNext(pNode); + } + else { + _pHead = pNode; + } + // update pCurrNode + ((ListNode<Type> *)pCurrNode)->SetPrev(pNode); + _iCount++; + } + break; + } + pPrevNode = pCurrNode; + GetNext(pCurrNode); + if(i+1 == _iCount) { + return AddTail(item); + } + else { + _ASSERTE(pCurrNode); + curItem = GetAt(pCurrNode); + } + } + } + + return (LISTNODE)pNode; + } + + template <class Type> LISTNODE List<Type>::AddTail(const Type &item) + { + ListNode<Type> *pNode = NULL; + + NEW_CONSTR(pNode, ListNode<Type>(item)); + if (pNode) { + _iCount++; + if (_pTail) { + pNode->SetPrev(_pTail); + _pTail->SetNext(pNode); + _pTail = pNode; + } + else { + _pHead = _pTail = pNode; + } + } + + return (LISTNODE)pNode; + } + + template <class Type> int List<Type>::GetCount() + { + return _iCount; + } + + template <class Type> LISTNODE List<Type>::GetHeadPosition() + { + return (LISTNODE)_pHead; + } + + template <class Type> LISTNODE List<Type>::GetTailPosition() + { + return (LISTNODE)_pTail; + } + + template <class Type> Type List<Type>::GetNext(LISTNODE &pNode) + { + ListNode<Type> *pListNode = (ListNode<Type> *)pNode; + + // Faults if you pass NULL + _ASSERTE(pNode); + + Type item = pListNode->GetItem(); + pNode = (LISTNODE)(pListNode->GetNext()); + + return item; + } + + template <class Type> void List<Type>::RemoveAll() + { + int i; + LISTNODE listNode = NULL; + ListNode<Type> *pDelNode = NULL; + + listNode = GetHeadPosition(); + + for (i = 0; i < _iCount; i++) { + pDelNode = (ListNode<Type> *)listNode; + GetNext(listNode); + SAFE_DELETE(pDelNode); + } + + _iCount = 0; + _pHead = NULL; + _pTail = NULL; + } + + template <class Type> void List<Type>::RemoveAt(LISTNODE pNode) + { + ListNode<Type> *pListNode = (ListNode<Type> *)pNode; + ListNode<Type> *pPrevNode = NULL; + ListNode<Type> *pNextNode = NULL; + + if (pNode) { + pPrevNode = pListNode->GetPrev(); + pNextNode = pListNode->GetNext(); + + if (pPrevNode) { + pPrevNode->SetNext(pNextNode); + if (pNextNode) { + pNextNode->SetPrev(pPrevNode); + } + else { + // We're removing the last node, so we have a new tail + _pTail = pPrevNode; + } + SAFE_DELETE(pListNode); + } + else { + // No previous, so we are the head of the list + _pHead = pNextNode; + if (pNextNode) { + pNextNode->SetPrev(NULL); + } + else { + // No previous, or next. There was only one node. + _pHead = NULL; + _pTail = NULL; + } + SAFE_DELETE(pListNode); + } + + _iCount--; + } + } + + + template <class Type> LISTNODE List<Type>::Find(const Type &item) + { + int i; + Type curItem; + LISTNODE pNode = NULL; + LISTNODE pMatchNode = NULL; + ListNode<Type> * pListNode = NULL; + + pNode = GetHeadPosition(); + for (i = 0; i < _iCount; i++) { + pListNode = (ListNode<Type> *)pNode; + curItem = GetNext(pNode); + if (curItem == item) { + pMatchNode = (LISTNODE)pListNode; + break; + } + } + + return pMatchNode; + } + + template <class Type> Type List<Type>::GetAt(LISTNODE pNode) + { + ListNode<Type> *pListNode = (ListNode<Type> *)pNode; + + // Faults if you pass NULL + _ASSERTE(pNode); + + return pListNode->GetItem(); + } +}; + +#endif diff --git a/src/binder/inc/loadcontext.hpp b/src/binder/inc/loadcontext.hpp new file mode 100644 index 0000000000..c4e0fc6361 --- /dev/null +++ b/src/binder/inc/loadcontext.hpp @@ -0,0 +1,50 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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 InspectionContext : + public LoadContext<AssemblyName::INCLUDE_VERSION | AssemblyName::INCLUDE_ARCHITECTURE> {}; + class ExecutionContext : public LoadContext<AssemblyName::INCLUDE_DEFAULT> {}; +}; + +#endif diff --git a/src/binder/inc/loadcontext.inl b/src/binder/inc/loadcontext.inl new file mode 100644 index 0000000000..b8002e03cc --- /dev/null +++ b/src/binder/inc/loadcontext.inl @@ -0,0 +1,91 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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->SetIsDynamicBind(pBindResult->GetIsDynamicBind()); + pContextEntry->SetIsInGAC(pBindResult->GetIsInGAC()); + pContextEntry->SetIsSharable(pBindResult->GetIsSharable()); + 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/binder/inc/propertyhashtraits.hpp b/src/binder/inc/propertyhashtraits.hpp new file mode 100644 index 0000000000..0616db3f89 --- /dev/null +++ b/src/binder/inc/propertyhashtraits.hpp @@ -0,0 +1,96 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// PropertyMap.hpp +// + + +// +// Defines the PropertyMap class +// +// ============================================================ + +#ifndef __BINDER__PROPERTY_HASH_TRAITS_HPP__ +#define __BINDER__PROPERTY_HASH_TRAITS_HPP__ + +#include "bindertypes.hpp" +#include "sstring.h" +#include "shash.h" + +namespace BINDER_SPACE +{ + class PropertyEntry + { + public: + inline PropertyEntry() + { + m_pPropertyName = NULL; + m_pPropertyValue = NULL; + } + inline ~PropertyEntry() + { + SAFE_DELETE(m_pPropertyName); + SAFE_DELETE(m_pPropertyValue); + } + + // Getters/Setters + inline SString *GetPropertyName() + { + return m_pPropertyName; + } + inline void SetPropertyName(SString *pPropertyName) + { + SAFE_DELETE(m_pPropertyName); + m_pPropertyName = pPropertyName; + } + inline SBuffer *GetPropertyValue() + { + return m_pPropertyValue; + } + inline void SetPropertyValue(SBuffer *pPropertyValue) + { + SAFE_DELETE(m_pPropertyValue); + m_pPropertyValue = pPropertyValue; + } + + protected: + SString *m_pPropertyName; + SBuffer *m_pPropertyValue; + }; + + class PropertyHashTraits : public DefaultSHashTraits<PropertyEntry *> + { + 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 pPropertyEntry) + { + return pPropertyEntry->GetPropertyName(); + } + static BOOL Equals(key_t pPropertyName1, key_t pPropertyName2) + { + return pPropertyName1->Equals(*pPropertyName2); + } + static count_t Hash(key_t pPropertyName) + { + return pPropertyName->Hash(); + } + static const element_t Null() + { + return NULL; + } + static bool IsNull(const element_t &propertyEntry) + { + return (propertyEntry == NULL); + } + + }; +}; + +#endif diff --git a/src/binder/inc/propertymap.hpp b/src/binder/inc/propertymap.hpp new file mode 100644 index 0000000000..d2ac2e6575 --- /dev/null +++ b/src/binder/inc/propertymap.hpp @@ -0,0 +1,36 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// PropertyMap.hpp +// + + +// +// Defines the PropertyMap class +// +// ============================================================ + + +#ifndef __BINDER__PROPERTY_MAP_HPP__ +#define __BINDER__PROPERTY_MAP_HPP__ + +#include "propertyhashtraits.hpp" + +namespace BINDER_SPACE +{ + class PropertyMap : protected SHash<PropertyHashTraits> + { + public: + PropertyMap(); + ~PropertyMap(); + + HRESULT Add(/* in */ SString *pPropertyName, + /* in */ SBuffer *pPropertyValue); + SBuffer *Lookup(/* in */ SString *pPropertyName); + }; +}; + +#endif diff --git a/src/binder/inc/stringlexer.hpp b/src/binder/inc/stringlexer.hpp new file mode 100644 index 0000000000..4dca5a6593 --- /dev/null +++ b/src/binder/inc/stringlexer.hpp @@ -0,0 +1,101 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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/binder/inc/stringlexer.inl b/src/binder/inc/stringlexer.inl new file mode 100644 index 0000000000..134345d874 --- /dev/null +++ b/src/binder/inc/stringlexer.inl @@ -0,0 +1,267 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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; + BINDER_LOG_ENTER(L"StringLexer::PopCharacter"); + + if (wcCurrentChar != INVALID_CHARACTER) + { + BINDER_LOG(L"HAVE wcCurrentChar"); + m_wcCurrentChar = INVALID_CHARACTER; + *pfIsEscaped = m_fCurrentCharIsEscaped; + } + else + { + BINDER_LOG(L"GET wcCurrentChar"); + wcCurrentChar = GetNextCharacter(pfIsEscaped); + } + +#ifdef BINDER_DEBUG_LOG + PathString info; + + info.Printf(L"wcCurrentChar=%p", (void *) wcCurrentChar); + BINDER_LOG((WCHAR *) info.GetUnicode()); +#endif + + BINDER_LOG_LEAVE(L"StringLexer::PopCharacter"); + return wcCurrentChar; +} + +void StringLexer::PushCharacter(WCHAR wcCurrentChar, + BOOL fIsEscaped) +{ + BINDER_LOG_ENTER(L"StringLexer::PushCharacter"); + +#ifdef BINDER_DEBUG_LOG + PathString info; + + info.Printf(L"wcCurrentChar=%p, fIsEscaped=%d", (void *) wcCurrentChar, fIsEscaped); + BINDER_LOG((WCHAR *) info.GetUnicode()); +#endif + + _ASSERTE(m_wcCurrentChar == INVALID_CHARACTER); + + m_wcCurrentChar = wcCurrentChar; + m_fCurrentCharIsEscaped = fIsEscaped; + + BINDER_LOG_LEAVE(L"StringLexer::PushCharacter"); +} + +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; + SIZE_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/binder/inc/textualidentityparser.hpp b/src/binder/inc/textualidentityparser.hpp new file mode 100644 index 0000000000..fb9ff4d81a --- /dev/null +++ b/src/binder/inc/textualidentityparser.hpp @@ -0,0 +1,71 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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/binder/inc/utils.hpp b/src/binder/inc/utils.hpp new file mode 100644 index 0000000000..7bd0bf2f4f --- /dev/null +++ b/src/binder/inc/utils.hpp @@ -0,0 +1,58 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// Utils.hpp +// + + +// +// Declares a bunch of binder auxilary functions +// +// ============================================================ + +#ifndef __BINDER_UTILS_HPP__ +#define __BINDER_UTILS_HPP__ + +#include "bindertypes.hpp" + +extern LocaleID g_lcid; + +namespace BINDER_SPACE +{ + inline BOOL EqualsCaseInsensitive(SString &a, SString &b) + { + return a.EqualsCaseInsensitive(b, g_lcid); + } + + inline ULONG HashCaseInsensitive(SString &string) + { + return string.HashCaseInsensitive(g_lcid); + } + + HRESULT FileOrDirectoryExists(PathString &path); + HRESULT FileOrDirectoryExistsLog(PathString &path); + + BOOL IsURL(SString &urlOrPath); + + void MutateUrlToPath(SString &urlOrPath); + void MutatePathToUrl(SString &pathOrUrl); + + // Mutates path + void PlatformPath(SString &path); + void CanonicalizePath(SString &path, BOOL fAppendPathSeparator = FALSE); + + // 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); +}; + +#endif diff --git a/src/binder/inc/variables.hpp b/src/binder/inc/variables.hpp new file mode 100644 index 0000000000..3a681edc97 --- /dev/null +++ b/src/binder/inc/variables.hpp @@ -0,0 +1,67 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// 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(); + + // ApplicationContext string constants + SString AppBaseURL; + SString DynamicDirectory; + SString DynamicBaseDirectory; + SString AppName; + SString AppConfigFile; + SString AppDomainId; + + // AssemblyBinder string constants + SString httpURLPrefix; + + // AssemblyName string constants + SString architectureMSIL; + SString architectureX86; + SString architectureAMD64; + SString architectureARM; + SString architectureARM64; + SString cultureNeutral; + SString emptyString; + SString mscorlib; + +#ifdef FEATURE_VERSIONING_LOG + BOOL fLoggingNeeded; + SString logPath; +#endif // FEATURE_VERSIONING_LOG +#ifndef DACCESS_COMPILE +#ifdef BINDER_DEBUG_LOG + CRITSEC_COOKIE m_logCS; + HandleHolder m_hDebugLogFile; +#endif // BINDER_DEBUG_LOG +#endif + }; + + extern Variables *g_BinderVariables; +}; + +#endif diff --git a/src/binder/propertymap.cpp b/src/binder/propertymap.cpp new file mode 100644 index 0000000000..36c47ac7a8 --- /dev/null +++ b/src/binder/propertymap.cpp @@ -0,0 +1,63 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// PropertyMap.cpp +// + + +// +// Implements the PropertyMap class +// +// ============================================================ + +#include "propertymap.hpp" + +namespace BINDER_SPACE +{ + PropertyMap::PropertyMap() : SHash<PropertyHashTraits>::SHash() { + // Nothing to do here + } + + PropertyMap::~PropertyMap() { + // Nothing to do here + } + + HRESULT PropertyMap::Add(SString *pPropertyName, + SBuffer *pPropertyValue) + { + _ASSERTE(pPropertyName != NULL); + _ASSERTE(pPropertyValue != NULL); + + HRESULT hr = S_OK; + + NewHolder<PropertyEntry> pPropertyEntry; + SAFE_NEW(pPropertyEntry, PropertyEntry); + + pPropertyEntry->SetPropertyName(pPropertyName); + pPropertyEntry->SetPropertyValue(pPropertyValue); + + SHash<PropertyHashTraits>::Add(pPropertyEntry); + pPropertyEntry.SuppressRelease(); + + Exit: + return hr; + } + + SBuffer *PropertyMap::Lookup(SString *pPropertyName) + { + _ASSERTE(pPropertyName != NULL); + + SBuffer *pPropertyValue = NULL; + PropertyEntry *pPropertyEntry = SHash<PropertyHashTraits>::Lookup(pPropertyName); + + if (pPropertyEntry != NULL) + { + pPropertyValue = pPropertyEntry->GetPropertyValue(); + } + + return pPropertyValue; + } +}; diff --git a/src/binder/stringlexer.cpp b/src/binder/stringlexer.cpp new file mode 100644 index 0000000000..78c26dfee2 --- /dev/null +++ b/src/binder/stringlexer.cpp @@ -0,0 +1,174 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// StringLexer.cpp +// + + +// +// Implements the StringLexer class +// +// ============================================================ + +#define DISABLE_BINDER_DEBUG_LOGGING + +#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; + BINDER_LOG_ENTER(L"StringLexer::GetNextLexeme"); + + // Remove any white spaces + do + { + wcCurrentChar = PopCharacter(&fIsEscaped); + } + while (IsWhitespace(wcCurrentChar)); + + // Determine lexeme type + LEXEME_TYPE kLexemeType = LEXEME_TYPE_INVALID; + if (!fIsEscaped) + { + kLexemeType = GetLexemeType(wcCurrentChar); + + if (kLexemeType != LEXEME_TYPE_STRING) + { + return kLexemeType; + } + } + + // First character of string lexeme; push it back + PushCharacter(wcCurrentChar, fIsEscaped); + kLexemeType = ParseString(currentString, fPermitUnescapedQuotes); + if (kLexemeType == LEXEME_TYPE_STRING) + { + BINDER_LOG_LEAVE_HR(L"StringLexer::GetNextLexeme(LEXEME_TYPE_STRING)", S_OK); + } + else + { + BINDER_LOG_LEAVE_HR(L"StringLexer::GetNextLexeme(LEXEME_TYPE_INVALID)", + S_FALSE); + } + + return kLexemeType; + } + + 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 + BINDER_LOG(L"StringLexer::ParseString: Invalid character encoding"); + return LEXEME_TYPE_INVALID; + } + + if (IsEOS(wcCurrentChar)) + { + if (IsQuoteCharacter(wcOpeningQuote)) + { + // EOS and unclosed quotes is an error + BINDER_LOG(L"StringLexer::ParseString: EOS and unclosed quotes"); + 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 + BINDER_LOG(L"StringLexer::ParseString: Quote in the middle of a string"); + 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 + BINDER_LOG(L"StringLexer::ParseString: Trimming string"); + TrimTrailingWhiteSpaces(currentString); + } + + BINDER_LOG_STRING(L"string", 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/binder/textualidentityparser.cpp b/src/binder/textualidentityparser.cpp new file mode 100644 index 0000000000..e1ca4a53f5 --- /dev/null +++ b/src/binder/textualidentityparser.cpp @@ -0,0 +1,777 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// TextualIdentityParser.cpp +// + + +// +// Implements the TextualIdentityParser class +// +// ============================================================ + +#define DISABLE_BINDER_DEBUG_LOGGING + +#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 iPublicKeyMinLength = 0; + const int iPublicKeyMaxLength = 2048; + + const int iVersionMax = 65535; + const int iRequiredVersionParts = 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 ((publicKey.GetCount() >= (iPublicKeyMinLength * 2)) && + (publicKey.GetCount() <= (iPublicKeyMaxLength * 2)) && + 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 *pkProcessorAchitecture) + { + for (int i = LENGTH_OF(wszKnownArchitectures); i--;) + { + if (EqualsCaseInsensitive(processorArchitecture, wszKnownArchitectures[i].strValue)) + { + *pkProcessorAchitecture = 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; + } + + BOOL ValidateAndConvertContentType( + SString & ssContentType, + AssemblyContentType * pkContentType) + { + if (EqualsCaseInsensitive(ssContentType, W("WindowsRuntime"))) + { + *pkContentType = AssemblyContentType_WindowsRuntime; + return TRUE; + } + + return FALSE; + } + + 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; + BINDER_LOG_ENTER(W("TextualIdentityParser::Parse")); + + IF_FALSE_GO(pAssemblyIdentity != NULL); + + BINDER_LOG_STRING(W("textualIdentity"), textualIdentity); + + EX_TRY + { + TextualIdentityParser identityParser(pAssemblyIdentity); + + if (!identityParser.Parse(textualIdentity, fPermitUnescapedQuotes)) + { + IF_FAIL_GO(FUSION_E_INVALID_NAME); + } + } + EX_CATCH_HRESULT(hr); + + Exit: + BINDER_LOG_LEAVE_HR(W("TextualIdentityParser::Parse"), hr); + return hr; + } + + /* static */ + HRESULT TextualIdentityParser::ToString(AssemblyIdentity *pAssemblyIdentity, + DWORD dwIdentityFlags, + SString &textualIdentity) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("TextualIdentityParser::ToString")); + + 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"), + pAssemblyIdentity->m_version.GetMajor(), + pAssemblyIdentity->m_version.GetMinor(), + pAssemblyIdentity->m_version.GetBuild(), + 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: + BINDER_LOG_LEAVE_HR(W("TextualIdentityParser::ToString"), hr); + return hr; + } + + /* static */ + BOOL TextualIdentityParser::ParseVersion(SString &versionString, + AssemblyVersion *pAssemblyVersion) + { + BOOL fIsValid = FALSE; + DWORD dwFoundNumbers = 0; + DWORD dwCurrentNumber = 0; + DWORD dwNumbers[iRequiredVersionParts]; + + BINDER_LOG_ENTER(W("TextualIdentityParser::ParseVersion")); + + 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>(iRequiredVersionParts)) + { + goto Exit; + } + else if (wcCurrentChar == W('.') || wcCurrentChar == 0x00) + { + dwNumbers[dwFoundNumbers++] = dwCurrentNumber; + dwCurrentNumber = 0; + } + else if ((wcCurrentChar >= W('0')) && (wcCurrentChar <= W('9'))) + { + dwCurrentNumber = (dwCurrentNumber * 10) + (wcCurrentChar - W('0')); + + if (dwCurrentNumber > static_cast<DWORD>(iVersionMax)) + { + goto Exit; + } + } + else + { + goto Exit; + } + + cursor++; + } + + if (dwFoundNumbers == static_cast<DWORD>(iRequiredVersionParts)) + { + pAssemblyVersion->SetFeatureVersion(dwNumbers[0], dwNumbers[1]); + pAssemblyVersion->SetServiceVersion(dwNumbers[2], dwNumbers[3]); + fIsValid = TRUE; + } + } + + Exit: + BINDER_LOG_LEAVE(W("TextualIdentityParser::ParseVersion")); + 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; + BINDER_LOG_ENTER(W("TextualIdentityParser::Parse(textualIdentity)")); + 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: + BINDER_LOG_LEAVE_BOOL(W("TextualIdentityParser::Parse(textualIdentity)"), fIsValid); + return fIsValid; + } + + BOOL TextualIdentityParser::ParseString(SString &textualString, + SString &contentString) + { + BOOL fIsValid = TRUE; + BINDER_LOG_ENTER(W("TextualIdentityParser::ParseString")); + 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: + BINDER_LOG_LEAVE_BOOL(W("TextualIdentityParser::ParseString"), fIsValid); + return fIsValid; + } + + BOOL TextualIdentityParser::PopulateAssemblyIdentity(SString &attributeString, + SString &valueString) + { + BINDER_LOG_ENTER(W("TextualIdentityParser::PopulateAssemblyIdentity")); + 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); + } + } + else + { + // Fusion compat: Silently drop unknown attribute/value pair + BINDER_LOG_STRING(W("unknown attribute"), attributeString); + BINDER_LOG_STRING(W("unknown value"), valueString); + } + + Exit: + BINDER_LOG_LEAVE_HR(W("TextualIdentityParser::PopulateAssemblyIdentity"), + (fIsValid ? S_OK : S_FALSE)); + return fIsValid; + } + + /* static */ + void TextualIdentityParser::EscapeString(SString &input, + SString &result) + { + BINDER_LOG_ENTER(W("TextualIdentityParser::EscapeString")); + + BINDER_LOG_STRING(W("input"), input); + + 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); + } + + BINDER_LOG_LEAVE(W("TextualIdentityParser::EscapeString")); + } +}; diff --git a/src/binder/utils.cpp b/src/binder/utils.cpp new file mode 100644 index 0000000000..3069802700 --- /dev/null +++ b/src/binder/utils.cpp @@ -0,0 +1,352 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// Utils.cpp +// + + +// +// Implements a bunch of binder auxilary functions +// +// ============================================================ + +#define DISABLE_BINDER_DEBUG_LOGGING + +#include "utils.hpp" + +#include <shlwapi.h> + +#include "strongname.h" +#include "corpriv.h" + +namespace BINDER_SPACE +{ + namespace + { + inline BOOL IsPathSeparator(WCHAR wcChar) + { + // Invariant: Valid only for MutateUrlToPath treated pathes + return (wcChar == W('\\')); + } + + inline const WCHAR *GetPlatformPathSeparator() + { +#ifdef PLATFORM_UNIX + return W("/"); +#else + return W("\\"); +#endif // PLATFORM_UNIX + } + + inline WCHAR ToPlatformPathSepator(WCHAR wcChar) + { +#ifdef PLATFORM_UNIX + if (IsPathSeparator(wcChar)) + { + wcChar = W('/'); + } +#endif // PLATFORM_UNIX + + return wcChar; + } + + inline BOOL IsDoublePathSeparator(SString::CIterator &cur) + { + return (IsPathSeparator(cur[0]) && IsPathSeparator(cur[1])); + } + + bool NeedToRemoveDoubleAndNormalizePathSeparators(SString const &path) + { +#ifdef PLATFORM_UNIX + return true; +#else + SString::CIterator begin = path.Begin(); + SString::CIterator end = path.End(); + SString::CIterator cur = path.Begin(); + + while (cur < end) + { + if ((cur != begin) && ((cur + 2) < end) && IsDoublePathSeparator(cur)) + { + return true; + } + + cur++; + } + + return false; +#endif + } + + void RemoveDoubleAndNormalizePathSeparators(SString &path) + { + BINDER_LOG_ENTER(W("Utils::RemoveDoubleAndNormalizePathSeparators")); + + SString::Iterator begin = path.Begin(); + SString::Iterator end = path.End(); + SString::Iterator cur = path.Begin(); + PathString resultPath; + + BINDER_LOG_STRING(W("path"), path); + + while (cur < end) + { + if ((cur != begin) && ((cur + 2) < end) && IsDoublePathSeparator(cur)) + { + // Skip the doublette + cur++; + } + + resultPath.Append(ToPlatformPathSepator(cur[0])); + cur++; + } + + BINDER_LOG_STRING(W("resultPath"), resultPath); + + path.Set(resultPath); + + BINDER_LOG_LEAVE(W("Utils::RemoveDoubleAndNormalizePathSeparators")); + } + } + + HRESULT FileOrDirectoryExists(PathString &path) + { + HRESULT hr = S_FALSE; + + DWORD dwFileAttributes = WszGetFileAttributes(path.GetUnicode()); + if (dwFileAttributes == INVALID_FILE_ATTRIBUTES) + { + hr = HRESULT_FROM_GetLastError(); + + if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || + (hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))) + { + hr = S_FALSE; + } + } + else + { + hr = S_TRUE; + } + + return hr; + } + + HRESULT FileOrDirectoryExistsLog(PathString &path) + { + HRESULT hr = S_FALSE; + BINDER_LOG_ENTER(W("Utils::FileOrDirectoryExistsLog")); + BINDER_LOG_STRING(W("path"), path); + + hr = FileOrDirectoryExists(path); + + BINDER_LOG_LEAVE_HR(W("Utils::FileOrDirectoryExistsLog"), hr); + return hr; + } + + BOOL IsURL(SString &urlOrPath) + { + // This is also in defined rotor pal + return PathIsURLW(urlOrPath); + } + + void MutateUrlToPath(SString &urlOrPath) + { + BINDER_LOG_ENTER(W("Utils::MutateUrlToPath")); + const SString fileUrlPrefix(SString::Literal, W("file://")); + SString::Iterator i = urlOrPath.Begin(); + + BINDER_LOG_STRING(W("URL"), urlOrPath); + + 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(PLATFORM_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('\\')); + } + + BINDER_LOG_STRING(W("Path"), urlOrPath); + BINDER_LOG_LEAVE(W("Utils::MutateUrlToPath")); + } + + void MutatePathToUrl(SString &pathOrUrl) + { + BINDER_LOG_ENTER(W("Utils::MutatePathToUrl")); + SString::Iterator i = pathOrUrl.Begin(); + + BINDER_LOG_STRING(W("Path"), pathOrUrl); + +#if !defined(PLATFORM_UNIX) + // Network path \\server --> file://server + // Disk path c:\dir --> file:///c:/dir + if (i[0] == W('\\')) + { + const SString networkUrlPrefix(SString::Literal, W("file:")); + + // Network path + pathOrUrl.Insert(i, networkUrlPrefix); + pathOrUrl.Skip(i, networkUrlPrefix); + } + else + { + const SString diskPathUrlPrefix(SString::Literal, W("file:///")); + + // Disk path + pathOrUrl.Insert(i, diskPathUrlPrefix); + pathOrUrl.Skip(i, diskPathUrlPrefix); + } +#else + // Unix doesn't have a distinction between a network or a local path + _ASSERTE(i[0] == W('\\') || i[0] == W('/')); + const SString fileUrlPrefix(SString::Literal, W("file://")); + + pathOrUrl.Insert(i, fileUrlPrefix); + pathOrUrl.Skip(i, fileUrlPrefix); +#endif + + while (pathOrUrl.Find(i, W('\\'))) + { + pathOrUrl.Replace(i, W('/')); + } + + BINDER_LOG_STRING(W("URL"), pathOrUrl); + BINDER_LOG_LEAVE(W("Utils::MutatePathToUrl")); + } + + void PlatformPath(SString &path) + { + BINDER_LOG_ENTER(W("Utils::PlatformPath")); + BINDER_LOG_STRING(W("input path"), path); + + // Create platform representation + MutateUrlToPath(path); + if (NeedToRemoveDoubleAndNormalizePathSeparators(path)) + RemoveDoubleAndNormalizePathSeparators(path); + + BINDER_LOG_STRING(W("platform path"), path); + + BINDER_LOG_LEAVE(W("Utils::PlatformPath")); + } + + void CanonicalizePath(SString &path, BOOL fAppendPathSeparator) + { + BINDER_LOG_ENTER(W("Utils::CanonicalizePath")); + BINDER_LOG_STRING(W("input path"), path); + + if (!path.IsEmpty()) + { + WCHAR wszCanonicalPath[MAX_PATH]; + PlatformPath(path); + + // This is also defined in rotor pal + if (PathCanonicalizeW(wszCanonicalPath, path)) + { + path.Set(wszCanonicalPath); + } + + if (fAppendPathSeparator) + { + SString platformPathSeparator(SString::Literal, GetPlatformPathSeparator()); + + if (!path.EndsWith(platformPathSeparator)) + { + path.Append(platformPathSeparator); + } + } + } + + BINDER_LOG_STRING(W("canonicalized path"), path); + BINDER_LOG_LEAVE(W("Utils::CanonicalizePath")); + } + + void CombinePath(SString &pathA, + SString &pathB, + SString &combinedPath) + { + BINDER_LOG_ENTER(W("Utils::CombinePath")); + + BINDER_LOG_STRING(W("path A"), pathA); + BINDER_LOG_STRING(W("path B"), pathB); + + WCHAR tempResultPath[MAX_PATH]; + if (PathCombineW(tempResultPath, pathA, pathB)) + { + combinedPath.Set(tempResultPath); + BINDER_LOG_STRING(W("combined path"), tempResultPath); + } + else + { + combinedPath.Clear(); + } + + BINDER_LOG_LEAVE(W("Utils::CombinePath")); + } + + HRESULT GetTokenFromPublicKey(SBuffer &publicKeyBLOB, + SBuffer &publicKeyTokenBLOB) + { + HRESULT hr = S_OK; + BINDER_LOG_ENTER(W("GetTokenFromPublicKey")); + + const BYTE *pByteKey = publicKeyBLOB; + DWORD dwKeyLen = publicKeyBLOB.GetSize(); + BYTE *pByteToken = NULL; + DWORD dwTokenLen = 0; + + if (!StrongNameTokenFromPublicKey(const_cast<BYTE *>(pByteKey), + dwKeyLen, + &pByteToken, + &dwTokenLen)) + { + BINDER_LOG(W("StrongNameTokenFromPublicKey failed!")); + IF_FAIL_GO(StrongNameErrorInfo()); + } + else + { + _ASSERTE(pByteToken != NULL); + publicKeyTokenBLOB.Set(pByteToken, dwTokenLen); + StrongNameFreeBuffer(pByteToken); + } + + Exit: + BINDER_LOG_LEAVE_HR(W("GetTokenFromPublicKey"), hr); + return hr; + } + + BOOL IsFileNotFound(HRESULT hr) + { + return RuntimeFileNotFound(hr); + } +}; diff --git a/src/binder/v3binder/.gitmirror b/src/binder/v3binder/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/binder/v3binder/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/binder/v3binder/v3binder.nativeproj b/src/binder/v3binder/v3binder.nativeproj new file mode 100644 index 0000000000..5412d69020 --- /dev/null +++ b/src/binder/v3binder/v3binder.nativeproj @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\Debug\SetDebugTargetLocal.props" /> + <!--Leaf project Properties--> + <PropertyGroup> + <BuildCoreBinaries>true</BuildCoreBinaries> + <BuildSysBinaries>true</BuildSysBinaries> + <OutputName>v3binder</OutputName> + </PropertyGroup> + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\Binder\binder.targets" /> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> diff --git a/src/binder/v3binder_crossgen/.gitmirror b/src/binder/v3binder_crossgen/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/binder/v3binder_crossgen/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror.
\ No newline at end of file diff --git a/src/binder/v3binder_crossgen/v3binder_crossgen.nativeproj b/src/binder/v3binder_crossgen/v3binder_crossgen.nativeproj new file mode 100644 index 0000000000..61348e3f62 --- /dev/null +++ b/src/binder/v3binder_crossgen/v3binder_crossgen.nativeproj @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood"> + <!--Import the settings--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetCrossGen.props" /> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" /> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\Debug\SetDebugTargetLocal.props" /> + + <!--Leaf project Properties--> + <PropertyGroup> + <OutputName>v3binder_crossgen</OutputName> + </PropertyGroup> + <!--Import the targets--> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\Binder\binder.targets" /> + <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" /> +</Project> diff --git a/src/binder/variables.cpp b/src/binder/variables.cpp new file mode 100644 index 0000000000..c2915244fc --- /dev/null +++ b/src/binder/variables.cpp @@ -0,0 +1,132 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// ============================================================ +// +// Variables.cpp +// + + +// +// Implements the Variables class +// +// ============================================================ + +#include "variables.hpp" + +#include "ex.h" + +namespace BINDER_SPACE +{ + namespace + { + HRESULT CheckFileExistence(LPCWSTR pwzFile, LPDWORD pdwAttrib) + { + HRESULT hr = S_FALSE; + DWORD dwRet = 0; + + _ASSERTE(pwzFile && pdwAttrib); + + *pdwAttrib = 0; + + dwRet = WszGetFileAttributes(pwzFile); + if (dwRet == INVALID_FILE_ATTRIBUTES) + { + hr = HRESULT_FROM_GetLastError(); + + if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || + (hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))) + { + GO_WITH_HRESULT(S_FALSE); + } + } + else + { + *pdwAttrib = dwRet; + GO_WITH_HRESULT(S_OK); + } + + Exit: + return hr; + } + }; + + 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 + { + // ApplicationContext string constants + + // AssemblyBinder string constants + httpURLPrefix.SetLiteral(W("http://")); + + // AssemblyName string constants + architectureMSIL.SetLiteral(W("MSIL")); + architectureX86.SetLiteral(W("x86")); + architectureAMD64.SetLiteral(W("AMD64")); + architectureARM.SetLiteral(W("ARM")); + architectureARM64.SetLiteral(W("ARM64")); + cultureNeutral.SetLiteral(W("neutral")); + mscorlib.SetLiteral(W("mscorlib")); + emptyString.Clear(); + +#ifdef FEATURE_VERSIONING_LOG + REGUTIL::CORConfigLevel kCorConfigLevel = + static_cast<REGUTIL::CORConfigLevel>(REGUTIL::COR_CONFIG_ENV | + REGUTIL::COR_CONFIG_FUSION); + + DWORD dwLoggingNeeded = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_ForceLog, + 0, + kCorConfigLevel, + TRUE); + fLoggingNeeded = (dwLoggingNeeded ? TRUE : FALSE); + + NewArrayHolder<WCHAR> pwzLogDirectory = REGUTIL::GetConfigString_DontUse_(CLRConfig::INTERNAL_LogPath, + TRUE, + kCorConfigLevel, + FALSE /* fUsePerfCache */); + + // When no directory is specified, we can't log. + if (pwzLogDirectory == NULL) + { + fLoggingNeeded = FALSE; + } + else + { + DWORD dwAttr = 0; + + // If we do not get a regular directory, then we can't log either + hr = CheckFileExistence(pwzLogDirectory, &dwAttr); + if ((hr == S_OK) && ((dwAttr & FILE_ATTRIBUTE_DIRECTORY) != 0)) + { + logPath.Set(pwzLogDirectory); + } + else + { + // Any failure here simply yields no logging. + hr = S_OK; + fLoggingNeeded = FALSE; + } + } +#endif // FEATURE_VERSIONING_LOG + } + EX_CATCH_HRESULT(hr); + + return hr; + } +}; |