diff options
Diffstat (limited to 'src/vm/assembly.cpp')
-rw-r--r-- | src/vm/assembly.cpp | 5070 |
1 files changed, 5070 insertions, 0 deletions
diff --git a/src/vm/assembly.cpp b/src/vm/assembly.cpp new file mode 100644 index 0000000000..b3e7611205 --- /dev/null +++ b/src/vm/assembly.cpp @@ -0,0 +1,5070 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Header: Assembly.cpp +** +** +** Purpose: Implements assembly (loader domain) architecture +** +** +===========================================================*/ + +#include "common.h" + +#include <stdlib.h> + +#include "assembly.hpp" +#include "appdomain.hpp" +#include "security.h" +#include "perfcounters.h" +#include "assemblyname.hpp" + +#ifdef FEATURE_FUSION +#include "fusion.h" +#include "assemblysink.h" +#include "ngenoptout.h" +#endif + +#if !defined(FEATURE_CORECLR) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) +#include "assemblyusagelogmanager.h" +#include "policy.h" +#endif + +#include "eeprofinterfaces.h" +#include "reflectclasswriter.h" +#include "comdynamic.h" + +#include <wincrypt.h> +#include "urlmon.h" +#include "sha1.h" + +#include "eeconfig.h" +#include "strongname.h" + +#include "ceefilegenwriter.h" +#include "assemblynative.hpp" +#include "threadsuspend.h" + +#ifdef FEATURE_PREJIT +#include "corcompile.h" +#endif + +#include "appdomainnative.hpp" +#ifdef FEATURE_REMOTING +#include "remoting.h" +#include "appdomainhelper.h" +#endif +#include "customattribute.h" +#include "winnls.h" + +#include "constrainedexecutionregion.h" +#include "caparser.h" +#include "../md/compiler/custattr.h" +#include "mdaassistants.h" + +#include "peimagelayout.inl" + +#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) +#include <shlobj.h> +#include "eventmsg.h" +#endif + +#ifdef FEATURE_TRACELOGGING +#include "clrtracelogging.h" +#endif // FEATURE_TRACELOGGING + + +// Define these macro's to do strict validation for jit lock and class init entry leaks. +// This defines determine if the asserts that verify for these leaks are defined or not. +// These asserts can sometimes go off even if no entries have been leaked so this defines +// should be used with caution. +// +// If we are inside a .cctor when the application shut's down then the class init lock's +// head will be set and this will cause the assert to go off., +// +// If we are jitting a method when the application shut's down then the jit lock's head +// will be set causing the assert to go off. + +//#define STRICT_JITLOCK_ENTRY_LEAK_DETECTION +//#define STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION + + +#ifndef DACCESS_COMPILE + +// This value is to make it easier to diagnose Assembly Loader "grant set" crashes. +// See Dev11 bug 358184 for more details. + +// This value is not thread safe and is not intended to be. It is just a best +// effort to collect more data on the problem. Is is possible, though unlikely, +// that thread A would record a reason for an upcoming crash, +// thread B would then record a different reason, and we would then +// crash on thread A, thus ending up with the recorded reason not matching +// the thread we crash in. Be aware of this when using this value +// to help your debugging. +DWORD g_dwLoaderReasonForNotSharing = 0; // See code:DomainFile::m_dwReasonForRejectingNativeImage for a similar variable. + +// These will sometimes result in a crash with error code 0x80131401 SECURITY_E_INCOMPATIBLE_SHARE +// "Loading this assembly would produce a different grant set from other instances." +enum ReasonForNotSharing +{ + ReasonForNotSharing_NoInfoRecorded = 0x1, + ReasonForNotSharing_NullDomainassembly = 0x2, + ReasonForNotSharing_DebuggerFlagMismatch = 0x3, + ReasonForNotSharing_NullPeassembly = 0x4, + ReasonForNotSharing_MissingAssemblyClosure1 = 0x5, + ReasonForNotSharing_MissingAssemblyClosure2 = 0x6, + ReasonForNotSharing_MissingDependenciesResolved = 0x7, + ReasonForNotSharing_ClosureComparisonFailed = 0x8, +}; + +#define NO_FRIEND_ASSEMBLIES_MARKER ((FriendAssemblyDescriptor *)S_FALSE) + +//---------------------------------------------------------------------------------------------- +// The ctor's job is to initialize the Assembly enough so that the dtor can safely run. +// It cannot do any allocations or operations that might fail. Those operations should be done +// in Assembly::Init() +//---------------------------------------------------------------------------------------------- +Assembly::Assembly(BaseDomain *pDomain, PEAssembly* pFile, DebuggerAssemblyControlFlags debuggerFlags, BOOL fIsCollectible) : + m_FreeFlag(0), +#ifdef FEATURE_MULTIMODULE_ASSEMBLIES + m_pAllowedFiles(NULL), + m_crstAllowedFiles(CrstAllowedFiles), +#endif + m_pDomain(pDomain), + m_pClassLoader(NULL), + m_pEntryPoint(NULL), + m_pManifest(NULL), + m_pManifestFile(clr::SafeAddRef(pFile)), + m_pOnDiskManifest(NULL), + m_pFriendAssemblyDescriptor(NULL), + m_pbStrongNameKeyPair(NULL), + m_pwStrongNameKeyContainer(NULL), + m_isDynamic(false), +#ifdef FEATURE_COLLECTIBLE_TYPES + m_isCollectible(fIsCollectible), +#endif + m_needsToHideManifestForEmit(FALSE), + m_dwDynamicAssemblyAccess(ASSEMBLY_ACCESS_RUN), + m_nextAvailableModuleIndex(1), + m_pLoaderAllocator(NULL), + m_isDisabledPrivateReflection(0), +#ifdef FEATURE_COMINTEROP + m_pITypeLib(NULL), + m_winMDStatus(WinMDStatus_Unknown), + m_pManifestWinMDImport(NULL), +#endif // FEATURE_COMINTEROP + m_pSharedSecurityDesc(NULL), + m_pTransparencyBehavior(NULL), + m_fIsDomainNeutral(pDomain == SharedDomain::GetDomain()), +#ifdef FEATURE_LOADER_OPTIMIZATION + m_bMissingDependenciesCheckDone(FALSE), +#ifdef FEATURE_FUSION + m_pBindingClosure(NULL), +#endif +#endif // FEATURE_LOADER_OPTIMIZATION + m_debuggerFlags(debuggerFlags), + m_fTerminated(FALSE), + m_HostAssemblyId(0) +#ifdef FEATURE_COMINTEROP + , m_InteropAttributeStatus(INTEROP_ATTRIBUTE_UNSET) +#endif +#ifndef FEATURE_CORECLR + , m_fSupportsAutoNGen(FALSE) +#endif +{ + STANDARD_VM_CONTRACT; +} + +// This name needs to stay in sync with AssemblyBuilder.MANIFEST_MODULE_NAME +// which is used in AssemblyBuilder.InitManifestModule +#define REFEMIT_MANIFEST_MODULE_NAME W("RefEmit_InMemoryManifestModule") + + +#ifdef FEATURE_TRACELOGGING +//---------------------------------------------------------------------------------------------- +// Reads and logs the TargetFramework attribute for an assembly. For example: [assembly: TargetFramework(".NETFramework,Version=v4.0")] +//---------------------------------------------------------------------------------------------- +void Assembly::TelemetryLogTargetFrameworkAttribute() +{ + const BYTE *pbAttr; // Custom attribute data as a BYTE*. + ULONG cbAttr; // Size of custom attribute data. + HRESULT hr = GetManifestImport()->GetCustomAttributeByName(GetManifestToken(), TARGET_FRAMEWORK_TYPE, (const void**)&pbAttr, &cbAttr); + bool dataLogged = false; + if (hr == S_OK) + { + CustomAttributeParser cap(pbAttr, cbAttr); + LPCUTF8 lpTargetFramework; + ULONG cbTargetFramework; + if (SUCCEEDED(cap.ValidateProlog())) + { + if (SUCCEEDED(cap.GetString(&lpTargetFramework, &cbTargetFramework))) + { + if ((lpTargetFramework != NULL) && (cbTargetFramework != 0)) + { + SString s(SString::Utf8, lpTargetFramework, cbTargetFramework); + CLRTraceLog::Logger::LogTargetFrameworkAttribute(s.GetUnicode(), GetSimpleName()); + dataLogged = true; + } + } + } + } + if (!dataLogged) + { + CLRTraceLog::Logger::LogTargetFrameworkAttribute(L"", GetSimpleName()); + } +} + +#endif // FEATURE_TRACELOGGING + +//---------------------------------------------------------------------------------------------- +// Does most Assembly initialization tasks. It can assume the ctor has already run +// and the assembly is safely destructable. Whether this function throws or succeeds, +// it must leave the Assembly in a safely destructable state. +//---------------------------------------------------------------------------------------------- +void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocator) +{ + STANDARD_VM_CONTRACT; + + if (IsSystem()) + { + _ASSERTE(pLoaderAllocator == NULL); // pLoaderAllocator may only be non-null for collectible types + m_pLoaderAllocator = SystemDomain::GetGlobalLoaderAllocator(); + } + else + { + if (!IsDomainNeutral()) + { + if (!IsCollectible()) + { + // pLoaderAllocator will only be non-null for reflection emit assemblies + _ASSERTE((pLoaderAllocator == NULL) || (pLoaderAllocator == GetDomain()->AsAppDomain()->GetLoaderAllocator())); + m_pLoaderAllocator = GetDomain()->AsAppDomain()->GetLoaderAllocator(); + } + else + { + _ASSERTE(pLoaderAllocator != NULL); // ppLoaderAllocator must be non-null for collectible assemblies + + m_pLoaderAllocator = pLoaderAllocator; + } + } + else + { + _ASSERTE(pLoaderAllocator == NULL); // pLoaderAllocator may only be non-null for collectible types + // use global loader heaps + m_pLoaderAllocator = SystemDomain::GetGlobalLoaderAllocator(); + } + } + _ASSERTE(m_pLoaderAllocator != NULL); + + m_pClassLoader = new ClassLoader(this); + m_pClassLoader->Init(pamTracker); + + m_pSharedSecurityDesc = Security::CreateSharedSecurityDescriptor(this); + +#ifdef FEATURE_MULTIMODULE_ASSEMBLIES + m_pAllowedFiles = new EEUtf8StringHashTable(); +#endif + + COUNTER_ONLY(GetPerfCounters().m_Loading.cAssemblies++); + +#ifndef CROSSGEN_COMPILE + if (GetManifestFile()->IsDynamic()) + // manifest modules of dynamic assemblies are always transient + m_pManifest = ReflectionModule::Create(this, GetManifestFile(), pamTracker, REFEMIT_MANIFEST_MODULE_NAME, TRUE); + else +#endif + m_pManifest = Module::Create(this, mdFileNil, GetManifestFile(), pamTracker); + + PrepareModuleForAssembly(m_pManifest, pamTracker); + + CacheManifestFiles(); + + if (!m_pManifest->IsReadyToRun()) + CacheManifestExportedTypes(pamTracker); + +#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) + GenerateBreadcrumbForServicing(); + + m_fSupportsAutoNGen = SupportsAutoNGenWorker(); + + ReportAssemblyUse(); +#endif + +#ifdef FEATURE_TRACELOGGING + + TelemetryLogTargetFrameworkAttribute(); + +#endif // FEATURE_TRACELOGGING + + + // Check for the assemblies that contain SIMD Vector types. + // If we encounter a non-trusted assembly with these names, we will simply not recognize any of its + // methods as intrinsics. + LPCUTF8 assemblyName = GetSimpleName(); + const int length = sizeof("System.Numerics") - 1; + if ((strncmp(assemblyName, "System.Numerics", length) == 0) && + ((assemblyName[length] == '\0') || (strcmp(assemblyName+length, ".Vectors") == 0))) + { + m_fIsSIMDVectorAssembly = true; + } + else + { + m_fIsSIMDVectorAssembly = false; + } + + // We'll load the friend assembly information lazily. For the ngen case we should avoid + // loading it entirely. + //CacheFriendAssemblyInfo(); + + { + CANNOTTHROWCOMPLUSEXCEPTION(); + FAULT_FORBID(); + //Cannot fail after this point. + + PublishModuleIntoAssembly(m_pManifest); + + return; // Explicit return to let you know you are NOT welcome to add code after the CANNOTTHROW/FAULT_FORBID expires + } +} + +BOOL Assembly::IsDisabledPrivateReflection() +{ + CONTRACTL + { + THROWS; + } + CONTRACTL_END; + + enum { UNINITIALIZED, ENABLED, DISABLED}; + + if (m_isDisabledPrivateReflection == UNINITIALIZED) + { + IMDInternalImport *pImport = GetManifestImport(); + HRESULT hr = pImport->GetCustomAttributeByName(GetManifestToken(), DISABLED_PRIVATE_REFLECTION_TYPE, NULL, 0); + IfFailThrow(hr); + + if (hr == S_OK) + { + m_isDisabledPrivateReflection = DISABLED; + } + else + { + m_isDisabledPrivateReflection = ENABLED; + } + } + + return m_isDisabledPrivateReflection == DISABLED; +} + +#ifndef CROSSGEN_COMPILE +Assembly::~Assembly() +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + DISABLED(FORBID_FAULT); //Must clean up some profiler stuff + } + CONTRACTL_END + + Terminate(); + + if (m_pFriendAssemblyDescriptor != NULL && m_pFriendAssemblyDescriptor != NO_FRIEND_ASSEMBLIES_MARKER) + delete m_pFriendAssemblyDescriptor; + + if (m_pbStrongNameKeyPair && (m_FreeFlag & FREE_KEY_PAIR)) + delete[] m_pbStrongNameKeyPair; + if (m_pwStrongNameKeyContainer && (m_FreeFlag & FREE_KEY_CONTAINER)) + delete[] m_pwStrongNameKeyContainer; + +#ifdef FEATURE_MULTIMODULE_ASSEMBLIES + if (m_pAllowedFiles) + delete(m_pAllowedFiles); +#endif +#ifdef FEATURE_FUSION + if (m_pBindingClosure) + { + m_pBindingClosure->Release(); + } +#endif + if (IsDynamic()) { + if (m_pOnDiskManifest) + // clear the on disk manifest if it is not cleared yet. + m_pOnDiskManifest = NULL; + } + + if (m_pManifestFile) + { + m_pManifestFile->Release(); + } + +#ifdef FEATURE_COMINTEROP + if (m_pManifestWinMDImport) + { + m_pManifestWinMDImport->Release(); + } +#endif // FEATURE_COMINTEROP +} + +#ifdef FEATURE_PREJIT +void Assembly::DeleteNativeCodeRanges() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + FORBID_FAULT; + } + CONTRACTL_END + + ModuleIterator i = IterateModules(); + while (i.Next()) + i.GetModule()->DeleteNativeCodeRanges(); +} +#endif + +#ifdef PROFILING_SUPPORTED +void ProfilerCallAssemblyUnloadStarted(Assembly* assemblyUnloaded) +{ + WRAPPER_NO_CONTRACT; + { + BEGIN_PIN_PROFILER(CORProfilerPresent()); + GCX_PREEMP(); + g_profControlBlock.pProfInterface->AssemblyUnloadStarted((AssemblyID)assemblyUnloaded); + END_PIN_PROFILER(); + } +} + +void ProfilerCallAssemblyUnloadFinished(Assembly* assemblyUnloaded) +{ + WRAPPER_NO_CONTRACT; + { + BEGIN_PIN_PROFILER(CORProfilerPresent()); + GCX_PREEMP(); + g_profControlBlock.pProfInterface->AssemblyUnloadFinished((AssemblyID) assemblyUnloaded, S_OK); + END_PIN_PROFILER(); + } +} +#endif + +void Assembly::StartUnload() +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_FORBID_FAULT; +#ifdef PROFILING_SUPPORTED + if (CORProfilerTrackAssemblyLoads()) + { + ProfilerCallAssemblyUnloadStarted(this); + } +#endif + + // we need to release tlb files eagerly +#ifdef FEATURE_COMINTEROP + if(g_fProcessDetach == FALSE) + { + DefaultCatchFilterParam param; param.pv = COMPLUS_EXCEPTION_EXECUTE_HANDLER; + PAL_TRY(Assembly *, pThis, this) + { + if (pThis->m_pITypeLib && pThis->m_pITypeLib != (ITypeLib*)-1) { + pThis->m_pITypeLib->Release(); + pThis->m_pITypeLib = NULL; + } + } + PAL_EXCEPT_FILTER(DefaultCatchFilter) + { + } + PAL_ENDTRY + } +#endif // FEATURE_COMINTEROP + +} + +void Assembly::Terminate( BOOL signalProfiler ) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_TRIGGERS; + + STRESS_LOG1(LF_LOADER, LL_INFO100, "Assembly::Terminate (this = 0x%p)\n", reinterpret_cast<void *>(this)); + + if (this->m_fTerminated) + return; + + Security::DeleteSharedSecurityDescriptor(m_pSharedSecurityDesc); + m_pSharedSecurityDesc = NULL; + + if (m_pClassLoader != NULL) + { + GCX_PREEMP(); + delete m_pClassLoader; + m_pClassLoader = NULL; + } + + if (m_pLoaderAllocator != NULL) + { + if (IsCollectible()) + { + // This cleanup code starts resembling parts of AppDomain::Terminate too much. + // It would be useful to reduce duplication and also establish clear responsibilites + // for LoaderAllocator::Destroy, Assembly::Terminate, LoaderAllocator::Terminate + // and LoaderAllocator::~LoaderAllocator. We need to establish how these + // cleanup paths interact with app-domain unload and process tear-down, too. + + if (!IsAtProcessExit()) + { + // Suspend the EE to do some clean up that can only occur + // while no threads are running. + GCX_COOP (); // SuspendEE may require current thread to be in Coop mode + // SuspendEE cares about the reason flag only when invoked for a GC + // Other values are typically ignored. If using SUSPEND_FOR_APPDOMAIN_SHUTDOWN + // is inappropriate, we can introduce a new flag or hijack an unused one. + ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_APPDOMAIN_SHUTDOWN); + } + + ExecutionManager::Unload(m_pLoaderAllocator); + + m_pLoaderAllocator->UninitVirtualCallStubManager(); + MethodTable::ClearMethodDataCache(); + _ASSERTE(m_pDomain->IsAppDomain()); + AppDomain *pAppDomain = m_pDomain->AsAppDomain(); + ClearJitGenericHandleCache(pAppDomain); + + if (!IsAtProcessExit()) + { + // Resume the EE. + ThreadSuspend::RestartEE(FALSE, TRUE); + } + + // Once the manifest file is tenured, the managed LoaderAllocatorScout is responsible for cleanup. + if (m_pManifest != NULL && m_pManifest->IsTenured()) + { + pAppDomain->RegisterLoaderAllocatorForDeletion(m_pLoaderAllocator); + } + } + m_pLoaderAllocator = NULL; + } + + COUNTER_ONLY(GetPerfCounters().m_Loading.cAssemblies--); + + +#ifdef PROFILING_SUPPORTED + if (CORProfilerTrackAssemblyLoads()) + { + ProfilerCallAssemblyUnloadFinished(this); + } +#endif // PROFILING_SUPPORTED + + this->m_fTerminated = TRUE; +} +#endif // CROSSGEN_COMPILE + +Assembly * Assembly::Create( + BaseDomain * pDomain, + PEAssembly * pFile, + DebuggerAssemblyControlFlags debuggerFlags, + BOOL fIsCollectible, + AllocMemTracker * pamTracker, + LoaderAllocator * pLoaderAllocator) +{ + STANDARD_VM_CONTRACT; + + NewHolder<Assembly> pAssembly (new Assembly(pDomain, pFile, debuggerFlags, fIsCollectible)); + + // If there are problems that arise from this call stack, we'll chew up a lot of stack + // with the various EX_TRY/EX_HOOKs that we will encounter. + INTERIOR_STACK_PROBE_FOR(GetThread(), DEFAULT_ENTRY_PROBE_SIZE); +#ifdef PROFILING_SUPPORTED + { + BEGIN_PIN_PROFILER(CORProfilerTrackAssemblyLoads()); + GCX_COOP(); + g_profControlBlock.pProfInterface->AssemblyLoadStarted((AssemblyID)(Assembly *) pAssembly); + END_PIN_PROFILER(); + } + + // Need TRY/HOOK instead of holder so we can get HR of exception thrown for profiler callback + EX_TRY +#endif + { + pAssembly->Init(pamTracker, pLoaderAllocator); + } +#ifdef PROFILING_SUPPORTED + EX_HOOK + { + { + BEGIN_PIN_PROFILER(CORProfilerTrackAssemblyLoads()); + GCX_COOP(); + g_profControlBlock.pProfInterface->AssemblyLoadFinished((AssemblyID)(Assembly *) pAssembly, + GET_EXCEPTION()->GetHR()); + END_PIN_PROFILER(); + } + } + EX_END_HOOK; +#endif + pAssembly.SuppressRelease(); + END_INTERIOR_STACK_PROBE; + + return pAssembly; +} // Assembly::Create + + +#ifndef CROSSGEN_COMPILE +Assembly *Assembly::CreateDynamic(AppDomain *pDomain, CreateDynamicAssemblyArgs *args) +{ + // WARNING: not backout clean + CONTRACT(Assembly *) + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(args)); + } + CONTRACT_END; + + // This must be before creation of the AllocMemTracker so that the destructor for the AllocMemTracker happens before the destructor for pLoaderAllocator. + // That is necessary as the allocation of Assembly objects and other related details is done on top of heaps located in + // the loader allocator objects. + NewHolder<LoaderAllocator> pLoaderAllocator; + + AllocMemTracker amTracker; + AllocMemTracker *pamTracker = &amTracker; + + Assembly *pRetVal = NULL; + + AppDomain *pCallersDomain; + MethodDesc *pmdEmitter = SystemDomain::GetCallersMethod(args->stackMark, &pCallersDomain); + + // Called either from interop or async delegate invocation. Rejecting because we don't + // know how to set the correct permission on the new dynamic assembly. + if (!pmdEmitter) + COMPlusThrow(kInvalidOperationException); + + Assembly *pCallerAssembly = pmdEmitter->GetAssembly(); + + // First, we set up a pseudo-manifest file for the assembly. + + // Set up the assembly name + + STRINGREF strRefName = (STRINGREF) args->assemblyName->GetSimpleName(); + + if (strRefName == NULL) + COMPlusThrow(kArgumentException, W("ArgumentNull_AssemblyNameName")); + + StackSString name; + strRefName->GetSString(name); + + if (name.GetCount() == 0) + COMPlusThrow(kArgumentException, W("ArgumentNull_AssemblyNameName")); + + SString::Iterator i = name.Begin(); + if (COMCharacter::nativeIsWhiteSpace(*i) + || name.Find(i, '\\') + || name.Find(i, ':') + || name.Find(i, '/')) + { + COMPlusThrow(kArgumentException, W("Argument_InvalidAssemblyName")); + } + + // Set up the assembly manifest metadata + // When we create dynamic assembly, we always use a working copy of IMetaDataAssemblyEmit + // to store temporary runtime assembly information. This is to preserve the invariant that + // an assembly must have a PEFile with proper metadata. + // This working copy of IMetaDataAssemblyEmit will store every AssemblyRef as a simple name + // reference as we must have an instance of Assembly(can be dynamic assembly) before we can + // add such a reference. Also because the referenced assembly if dynamic strong name, it may + // not be ready to be hashed! + + SafeComHolder<IMetaDataAssemblyEmit> pAssemblyEmit; + PEFile::DefineEmitScope( + IID_IMetaDataAssemblyEmit, + &pAssemblyEmit); + + // remember the hash algorithm + ULONG ulHashAlgId = args->assemblyName->GetAssemblyHashAlgorithm(); + if (ulHashAlgId == 0) + ulHashAlgId = CALG_SHA1; + + ASSEMBLYMETADATA assemData; + memset(&assemData, 0, sizeof(assemData)); + + // get the version info (default to 0.0.0.0 if none) + VERSIONREF versionRef = (VERSIONREF) args->assemblyName->GetVersion(); + if (versionRef != NULL) + { + assemData.usMajorVersion = (USHORT)versionRef->GetMajor(); + assemData.usMinorVersion = (USHORT)versionRef->GetMinor(); + assemData.usBuildNumber = (USHORT)versionRef->GetBuild(); + assemData.usRevisionNumber = (USHORT)versionRef->GetRevision(); + } + + struct _gc + { + OBJECTREF granted; + OBJECTREF denied; + OBJECTREF cultureinfo; + STRINGREF pString; + OBJECTREF orArrayOrContainer; + OBJECTREF throwable; + OBJECTREF strongNameKeyPair; + } gc; + ZeroMemory(&gc, sizeof(gc)); + + GCPROTECT_BEGIN(gc); + + StackSString culture; + + gc.cultureinfo = args->assemblyName->GetCultureInfo(); + if (gc.cultureinfo != NULL) + { + MethodDescCallSite getName(METHOD__CULTURE_INFO__GET_NAME, &gc.cultureinfo); + + ARG_SLOT args2[] = + { + ObjToArgSlot(gc.cultureinfo) + }; + + // convert culture info into a managed string form + gc.pString = getName.Call_RetSTRINGREF(args2); + gc.pString->GetSString(culture); + + assemData.szLocale = (LPWSTR) (LPCWSTR) culture; + } + + SBuffer publicKey; + if (args->assemblyName->GetPublicKey() != NULL) + { + publicKey.Set(args->assemblyName->GetPublicKey()->GetDataPtr(), + args->assemblyName->GetPublicKey()->GetNumComponents()); + } + + + // get flags + DWORD dwFlags = args->assemblyName->GetFlags(); + + // Now create a dynamic PE file out of the name & metadata + PEAssemblyHolder pFile; + + { + GCX_PREEMP(); + + mdAssembly ma; + IfFailThrow(pAssemblyEmit->DefineAssembly(publicKey, publicKey.GetSize(), ulHashAlgId, + name, &assemData, dwFlags, + &ma)); + pFile = PEAssembly::Create(pCallerAssembly->GetManifestFile(), pAssemblyEmit, args->access & ASSEMBLY_ACCESS_REFLECTION_ONLY); + +#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) + // Dynamically created modules (aka RefEmit assemblies) do not have a LoadContext associated with them since they are not bound + // using an actual binder. As a result, we will assume the same binding/loadcontext information for the dynamic assembly as its + // caller/creator to ensure that any assembly loads triggered by the dynamic assembly are resolved using the intended load context. + // + // If the creator assembly has a HostAssembly associated with it, then use it for binding. Otherwise, the creator is dynamic + // and will have a fallback load context binder associated with it. + ICLRPrivBinder *pFallbackLoadContextBinder = nullptr; + + // There is always a manifest file - wehther working with static or dynamic assemblies. + PEFile *pCallerAssemblyManifestFile = pCallerAssembly->GetManifestFile(); + _ASSERTE(pCallerAssemblyManifestFile != NULL); + + if (!pCallerAssemblyManifestFile->IsDynamic()) + { + // Static assemblies with do not have fallback load context + _ASSERTE(pCallerAssemblyManifestFile->GetFallbackLoadContextBinder() == nullptr); + + if (pCallerAssemblyManifestFile->IsSystem()) + { + // CoreLibrary is always bound to TPA binder + pFallbackLoadContextBinder = pDomain->GetTPABinderContext(); + } + else + { + // Fetch the binder from the host assembly + PTR_ICLRPrivAssembly pCallerAssemblyHostAssembly = pCallerAssemblyManifestFile->GetHostAssembly(); + _ASSERTE(pCallerAssemblyHostAssembly != nullptr); + + UINT_PTR assemblyBinderID = 0; + IfFailThrow(pCallerAssemblyHostAssembly->GetBinderID(&assemblyBinderID)); + pFallbackLoadContextBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID); + } + } + else + { + // Creator assembly is dynamic too, so use its fallback load context for the one + // we are creating. + pFallbackLoadContextBinder = pCallerAssemblyManifestFile->GetFallbackLoadContextBinder(); + } + + // At this point, we should have a fallback load context binder to work with + _ASSERTE(pFallbackLoadContextBinder != nullptr); + + // Set it as the fallback load context binder for the dynamic assembly being created + pFile->SetFallbackLoadContextBinder(pFallbackLoadContextBinder); +#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) + + } + + AssemblyLoadSecurity loadSecurity; +#ifndef FEATURE_CORECLR + DWORD dwSpecialFlags = 0xFFFFFFFF; + + // Don't bother with setting up permissions if this isn't allowed to run + // This doesn't apply in CoreCLR because you cannot specify evidence when creating a dynamic assembly + if ((args->identity != NULL) && + (args->access & ASSEMBLY_ACCESS_RUN)) + { + loadSecurity.m_pAdditionalEvidence = &args->identity; + } + else + { + if (pCallerAssembly != NULL) // can be null if caller is interop + { + if (args->securityContextSource == kCurrentAssembly) + { + IAssemblySecurityDescriptor *pCallerSecDesc = pCallerAssembly->GetSecurityDescriptor(pCallersDomain); + gc.granted = pCallerSecDesc->GetGrantedPermissionSet(&(gc.denied)); + dwSpecialFlags = pCallerSecDesc->GetSpecialFlags(); + } + else + { + IApplicationSecurityDescriptor *pCallersDomainSecDesc = pCallersDomain->GetSecurityDescriptor(); + +#ifdef FEATURE_CAS_POLICY + // We only want to propigate the identity of homogenous domains, since heterogenous domains tend + // to be fully trusted even if they are housing partially trusted code - which could lead to an + // elevation of privilege if we allow the grant set to be pushed to assemblies partially trusted + // code is loading. + if (!pCallersDomainSecDesc->IsHomogeneous()) + { + COMPlusThrow(kNotSupportedException, W("NotSupported_SecurityContextSourceAppDomainInHeterogenous")); + } +#endif // FEATURE_CAS_POLICY + + gc.granted = pCallersDomainSecDesc->GetGrantedPermissionSet(); + dwSpecialFlags = pCallersDomainSecDesc->GetSpecialFlags(); + } + + // Caller may be in another appdomain context, in which case we'll + // need to marshal/unmarshal the grant and deny sets across. +#ifdef FEATURE_REMOTING // should not happen without remoting + if (pCallersDomain != ::GetAppDomain()) + { + gc.granted = AppDomainHelper::CrossContextCopyFrom(pCallersDomain->GetId(), &(gc.granted)); + if (gc.denied != NULL) + { + gc.denied = AppDomainHelper::CrossContextCopyFrom(pCallersDomain->GetId(), &(gc.denied)); + } + } +#else // !FEATURE_REMOTING + _ASSERTE(pCallersDomain == ::GetAppDomain()); +#endif // FEATURE_REMOTING + } + } +#else // FEATURE_CORECLR + // In SilverLight all dynamic assemblies should be transparent and partially trusted, even if they are + // created by platform assemblies. Thus they should inherit the grant sets from the appdomain not the + // parent assembly. + IApplicationSecurityDescriptor *pCurrentDomainSecDesc = ::GetAppDomain()->GetSecurityDescriptor(); + gc.granted = pCurrentDomainSecDesc->GetGrantedPermissionSet(); + DWORD dwSpecialFlags = pCurrentDomainSecDesc->GetSpecialFlags(); +#endif // !FEATURE_CORECLR + + // If the dynamic assembly creator did not specify evidence for the newly created assembly, then it + // should inherit the grant set of the creation assembly. + if (loadSecurity.m_pAdditionalEvidence == NULL) + { +#ifdef FEATURE_CAS_POLICY + // If we're going to inherit the grant set of an anonymously hosted dynamic method, it will be + // full trust/transparent. In that case, we should demand full trust. + if(args->securityContextSource == kCurrentAssembly && + pCallerAssembly != NULL && + pCallersDomain != NULL && + pCallerAssembly->GetDomainAssembly(pCallersDomain) == pCallersDomain->GetAnonymouslyHostedDynamicMethodsAssembly()) + { + loadSecurity.m_fPropagatingAnonymouslyHostedDynamicMethodGrant = true; + } +#endif // FEATURE_CAS_POLICY + + loadSecurity.m_pGrantSet = &gc.granted; + loadSecurity.m_pRefusedSet = &gc.denied; + loadSecurity.m_dwSpecialFlags = dwSpecialFlags; + } + + NewHolder<DomainAssembly> pDomainAssembly; + + { + GCX_PREEMP(); + + // Create a new LoaderAllocator if appropriate + if ((args->access & ASSEMBLY_ACCESS_COLLECT) != 0) + { + AssemblyLoaderAllocator *pAssemblyLoaderAllocator = new AssemblyLoaderAllocator(); + pLoaderAllocator = pAssemblyLoaderAllocator; + + // Some of the initialization functions are not virtual. Call through the derived class + // to prevent calling the base class version. + pAssemblyLoaderAllocator->Init(pDomain); + + // Setup the managed proxy now, but do not actually transfer ownership to it. + // Once everything is setup and nothing can fail anymore, the ownership will be + // atomically transfered by call to LoaderAllocator::ActivateManagedTracking(). + pAssemblyLoaderAllocator->SetupManagedTracking(&args->loaderAllocator); + } + else + { + pLoaderAllocator = pDomain->GetLoaderAllocator(); + pLoaderAllocator.SuppressRelease(); + } + + // Create a domain assembly + pDomainAssembly = new DomainAssembly(pDomain, pFile, &loadSecurity, pLoaderAllocator); + } + + // Start loading process + +#ifdef FEATURE_CAS_POLICY + // Get the security descriptor for the assembly. + IAssemblySecurityDescriptor *pSecDesc = pDomainAssembly->GetSecurityDescriptor(); + + // Propagate identity and permission request information into the assembly's + // security descriptor. Then when policy is resolved we'll end up with the + // correct grant set. + // If identity has not been provided then the caller's assembly will be + // calculated instead and we'll just copy the granted permissions from the + // caller to the new assembly and mark policy as resolved (done + // automatically by SetGrantedPermissionSet). + pSecDesc->SetRequestedPermissionSet(args->requiredPset, + args->optionalPset, + args->refusedPset); +#endif // FEATURE_CAS_POLICY + + { + // Create a concrete assembly + // (!Do not remove scoping brace: order is important here: the Assembly holder must destruct before the AllocMemTracker!) + NewHolder<Assembly> pAssem; + + { + GCX_PREEMP(); + // Assembly::Create will call SuppressRelease on the NewHolder that holds the LoaderAllocator when it transfers ownership + pAssem = Assembly::Create(pDomain, pFile, pDomainAssembly->GetDebuggerInfoBits(), args->access & ASSEMBLY_ACCESS_COLLECT ? TRUE : FALSE, pamTracker, pLoaderAllocator); + + ReflectionModule* pModule = (ReflectionModule*) pAssem->GetManifestModule(); + pModule->SetCreatingAssembly( pCallerAssembly ); + + + if ((args->access & ASSEMBLY_ACCESS_COLLECT) != 0) + { + // Initializing the virtual call stub manager is delayed to remove the need for the LoaderAllocator destructor to properly handle + // uninitializing the VSD system. (There is a need to suspend the runtime, and that's tricky) + pLoaderAllocator->InitVirtualCallStubManager(pDomain, TRUE); + } + } + + pAssem->m_isDynamic = true; + + pAssem->m_dwDynamicAssemblyAccess = args->access; + +#ifdef FEATURE_CAS_POLICY + // If a legacy assembly is emitting an assembly, then we implicitly add the legacy attribute. If the legacy + // assembly is also in partial trust, we implicitly make the emitted assembly transparent. + ModuleSecurityDescriptor *pEmittingMSD = ModuleSecurityDescriptor::GetModuleSecurityDescriptor(pCallerAssembly); + if (pEmittingMSD->GetSecurityRuleSet() == SecurityRuleSet_Level1) + { + IAssemblySecurityDescriptor *pCallerSecDesc = pCallerAssembly->GetSecurityDescriptor(pCallersDomain); + if (!pCallerSecDesc->IsFullyTrusted()) + { + args->flags = kTransparentAssembly; + } + } + + // If the code emitting the dynamic assembly is transparent and it is attempting to emit a non-transparent + // assembly, then we need to do a demand for the grant set of the emitting assembly (which should also be + // is the grant set of the dynamic assembly). + if (Security::IsMethodTransparent(pmdEmitter) && !(args->flags & kTransparentAssembly)) + { + Security::DemandGrantSet(pCallerAssembly->GetSecurityDescriptor(pCallersDomain)); + } +#else // FEATURE_CORECLR + // Making the dynamic assembly opportunistically critical in full trust CoreCLR and transparent otherwise. + if (!GetAppDomain()->GetSecurityDescriptor()->IsFullyTrusted()) + { + args->flags = kTransparentAssembly; + } +#endif //!FEATURE_CORECLR + + // Fake up a module security descriptor for the assembly. + TokenSecurityDescriptorFlags tokenFlags = TokenSecurityDescriptorFlags_None; + if (args->flags & kAllCriticalAssembly) + tokenFlags |= TokenSecurityDescriptorFlags_AllCritical; + if (args->flags & kAptcaAssembly) + tokenFlags |= TokenSecurityDescriptorFlags_APTCA; + if (args->flags & kCriticalAssembly) + tokenFlags |= TokenSecurityDescriptorFlags_Critical; + if (args->flags & kTransparentAssembly) + tokenFlags |= TokenSecurityDescriptorFlags_Transparent; + if (args->flags & kTreatAsSafeAssembly) + tokenFlags |= TokenSecurityDescriptorFlags_TreatAsSafe; + +#ifdef FEATURE_APTCA + if (args->aptcaBlob != NULL) + { + tokenFlags |= ParseAptcaAttribute(args->aptcaBlob->GetDirectPointerToNonObjectElements(), + args->aptcaBlob->GetNumComponents()); + } + +#endif // FEATURE_APTCA + +#ifndef FEATURE_CORECLR + // Use the security rules given to us if the emitting code has selected a specific one. Otherwise, + // inherit the security rules of the emitting assembly. + if (args->securityRulesBlob != NULL) + { + tokenFlags |= ParseSecurityRulesAttribute(args->securityRulesBlob->GetDirectPointerToNonObjectElements(), + args->securityRulesBlob->GetNumComponents()); + } + else + { + // Ensure that dynamic assemblies created by mscorlib always specify a rule set, since we want to + // make sure that creating a level 2 assembly was an explicit decision by the emitting code, + // rather than an implicit decision because mscorlib is level 2 itself. + // + // If you're seeing this assert, it means that you've created a dynamic assembly from mscorlib, + // but did not pass a CustomAttributeBuilder for the SecurityRulesAttribute to the + // DefineDynamicAssembly call. + _ASSERTE(!pCallerAssembly->IsSystem()); + + // Use the creating assembly's security rule set for the emitted assembly + SecurityRuleSet callerRuleSet = + ModuleSecurityDescriptor::GetModuleSecurityDescriptor(pCallerAssembly)->GetSecurityRuleSet(); + tokenFlags |= EncodeSecurityRuleSet(callerRuleSet); + + tokenFlags |= TokenSecurityDescriptorFlags_SecurityRules; + } +#endif // !FEATURE_CORECLR + + _ASSERTE(pAssem->GetManifestModule()->m_pModuleSecurityDescriptor != NULL); + pAssem->GetManifestModule()->m_pModuleSecurityDescriptor->OverrideTokenFlags(tokenFlags); + + // Set the additional strong name information + + pAssem->SetStrongNameLevel(Assembly::SN_NONE); + + if (publicKey.GetSize() > 0) + { + pAssem->SetStrongNameLevel(Assembly::SN_PUBLIC_KEY); +#ifndef FEATURE_CORECLR + gc.strongNameKeyPair = args->assemblyName->GetStrongNameKeyPair(); + // If there's a public key, there might be a strong name key pair. + if (gc.strongNameKeyPair != NULL) + { + MethodDescCallSite getKeyPair(METHOD__STRONG_NAME_KEY_PAIR__GET_KEY_PAIR, &gc.strongNameKeyPair); + + ARG_SLOT arglist[] = + { + ObjToArgSlot(gc.strongNameKeyPair), + PtrToArgSlot(&gc.orArrayOrContainer) + }; + + BOOL bKeyInArray; + bKeyInArray = (BOOL)getKeyPair.Call_RetBool(arglist); + + if (bKeyInArray) + { + U1ARRAYREF orArray = (U1ARRAYREF)gc.orArrayOrContainer; + pAssem->m_cbStrongNameKeyPair = orArray->GetNumComponents(); + pAssem->m_pbStrongNameKeyPair = new BYTE[pAssem->m_cbStrongNameKeyPair]; + + pAssem->m_FreeFlag |= pAssem->FREE_KEY_PAIR; + memcpy(pAssem->m_pbStrongNameKeyPair, orArray->GetDataPtr(), pAssem->m_cbStrongNameKeyPair); + pAssem->SetStrongNameLevel(Assembly::SN_FULL_KEYPAIR_IN_ARRAY); + } + else + { + STRINGREF orContainer = (STRINGREF)gc.orArrayOrContainer; + DWORD cchContainer = orContainer->GetStringLength(); + pAssem->m_pwStrongNameKeyContainer = new WCHAR[cchContainer + 1]; + + pAssem->m_FreeFlag |= pAssem->FREE_KEY_CONTAINER; + memcpy(pAssem->m_pwStrongNameKeyContainer, orContainer->GetBuffer(), cchContainer * sizeof(WCHAR)); + pAssem->m_pwStrongNameKeyContainer[cchContainer] = W('\0'); + + pAssem->SetStrongNameLevel(Assembly::SN_FULL_KEYPAIR_IN_CONTAINER); + } + } + else +#endif // FEATURE_CORECLR + { + // Since we have no way to validate the public key of a dynamic assembly we don't allow + // partial trust code to emit a dynamic assembly with an arbitrary public key. + // Ideally we shouldn't allow anyone to emit a dynamic assembly with only a public key, + // but we allow a couple of exceptions to reduce the compat risk: full trust, caller's own key. + // As usual we treat anonymously hosted dynamic methods as partial trust code. + DomainAssembly* pCallerDomainAssembly = pCallerAssembly->GetDomainAssembly(pCallersDomain); + if (!pCallerDomainAssembly->GetSecurityDescriptor()->IsFullyTrusted() || + pCallerDomainAssembly == pCallersDomain->GetAnonymouslyHostedDynamicMethodsAssembly()) + { + DWORD cbKey = 0; + const void* pKey = pCallerAssembly->GetPublicKey(&cbKey); + + if (!publicKey.Equals((const BYTE *)pKey, cbKey)) + COMPlusThrow(kInvalidOperationException, W("InvalidOperation_StrongNameKeyPairRequired")); + } + } + } + + //we need to suppress release for pAssem to avoid double release + pAssem.SuppressRelease (); + + { + GCX_PREEMP(); + + // Finish loading process + // <TODO> would be REALLY nice to unify this with main loading loop </TODO> + pDomainAssembly->Begin(); + pDomainAssembly->SetAssembly(pAssem); + pDomainAssembly->m_level = FILE_LOAD_ALLOCATE; + pDomainAssembly->DeliverSyncEvents(); + pDomainAssembly->DeliverAsyncEvents(); + pDomainAssembly->FinishLoad(); + pDomainAssembly->ClearLoading(); + pDomainAssembly->m_level = FILE_ACTIVE; + } + + // Force the transparency of the module to be computed now, so that we can catch any errors due to + // inconsistent assembly level attributes during the assembly creation call, rather than at some + // later point. + pAssem->GetManifestModule()->m_pModuleSecurityDescriptor->VerifyDataComputed(); + + { + CANNOTTHROWCOMPLUSEXCEPTION(); + FAULT_FORBID(); + + //Cannot fail after this point + + pDomainAssembly.SuppressRelease(); // This also effectively suppresses the release of the pAssem + pamTracker->SuppressRelease(); + + // Once we reach this point, the loader allocator lifetime is controlled by the Assembly object. + if ((args->access & ASSEMBLY_ACCESS_COLLECT) != 0) + { + // Atomically transfer ownership to the managed heap + pLoaderAllocator->ActivateManagedTracking(); + pLoaderAllocator.SuppressRelease(); + } + + pAssem->SetIsTenured(); + pRetVal = pAssem; + } + } + GCPROTECT_END(); + + RETURN pRetVal; +} // Assembly::CreateDynamic + +#ifdef FEATURE_MULTIMODULE_ASSEMBLIES +ReflectionModule *Assembly::CreateDynamicModule(LPCWSTR wszModuleName, LPCWSTR wszFileName, BOOL fIsTransient, INT32* ptkFile) +{ + CONTRACT(ReflectionModule *) + { + STANDARD_VM_CHECK; + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + AllocMemTracker amTracker; + + // Add a manifest entry for the module + mdFile token; + IMetaDataAssemblyEmit *pAssemblyEmit = GetManifestFile()->GetAssemblyEmitter(); + IfFailThrow(pAssemblyEmit->DefineFile(wszFileName, NULL, 0, 0, &token)); + + if (ptkFile) + *ptkFile = (INT32)token; + + GetManifestModule()->UpdateDynamicMetadataIfNeeded(); + + // Define initial metadata for the module + SafeComHolder<IMetaDataEmit> pEmit; + PEFile::DefineEmitScope(IID_IMetaDataEmit, (void **)&pEmit); + + // the module name will be set later when we create the ReflectionModule + + // Create the PEFile for the module + PEModuleHolder pFile(PEModule::Create(GetManifestFile(), token, pEmit)); + + // Create the DomainModule + NewHolder<DomainModule> pDomainModule(new DomainModule(::GetAppDomain(), GetDomainAssembly(), pFile)); + + // Create the module itself + ReflectionModuleHolder pWrite(ReflectionModule::Create(this, pFile, &amTracker, wszModuleName, fIsTransient)); + + amTracker.SuppressRelease(); //@todo: OOM: is this the right place to commit the tracker? + pWrite->SetIsTenured(); + + // Modules take the DebuggerAssemblyControlFlags down from its parent Assembly initially. + // By default, this turns on JIT optimization. + + pWrite->SetDebuggerInfoBits(GetDebuggerInfoBits()); + + // Associate the two + pDomainModule->SetModule(pWrite); + m_pManifest->StoreFileThrowing(token, pWrite); + + // Simulate loading process + pDomainModule->Begin(); + pDomainModule->DeliverSyncEvents(); + pDomainModule->DeliverAsyncEvents(); + pDomainModule->FinishLoad(); + pDomainModule->ClearLoading(); + pDomainModule->m_level = FILE_ACTIVE; + + pDomainModule.SuppressRelease(); + ReflectionModule *pModule = pWrite.Extract(); + + LPCSTR szUTF8FileName; + CQuickBytes qbLC; + + // Get the UTF8 file name + IfFailThrow(m_pManifest->GetMDImport()->GetFileProps(token, &szUTF8FileName, NULL, NULL, NULL)); + UTF8_TO_LOWER_CASE(szUTF8FileName, qbLC); + LPCSTR szUTF8FileNameLower = (LPUTF8) qbLC.Ptr(); + + CrstHolder lock(&m_crstAllowedFiles); + + // insert the value into manifest's look up table. + // Need to perform case insensitive hashing as well. + m_pAllowedFiles->InsertValue(szUTF8FileName, (HashDatum)(size_t)token, TRUE); + m_pAllowedFiles->InsertValue(szUTF8FileNameLower, (HashDatum)(size_t)token, TRUE); + + // Now make file token associate with the loaded module + m_pManifest->StoreFileThrowing(token, pModule); + + RETURN pModule; +} // Assembly::CreateDynamicModule +#endif // FEATURE_MULTIMODULE_ASSEMBLIES + +#endif // CROSSGEN_COMPILE + +void Assembly::SetDomainAssembly(DomainAssembly *pDomainAssembly) +{ + CONTRACTL + { + PRECONDITION(CheckPointer(pDomainAssembly)); + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + GetManifestModule()->SetDomainFile(pDomainAssembly); + + IAssemblySecurityDescriptor *pSec = pDomainAssembly->GetSecurityDescriptor(); + + GCX_COOP(); + pSec->ResolvePolicy(GetSharedSecurityDescriptor(), pDomainAssembly->ShouldSkipPolicyResolution()); + +} // Assembly::SetDomainAssembly + +#endif // #ifndef DACCESS_COMPILE + +DomainAssembly *Assembly::GetDomainAssembly(AppDomain *pDomain) +{ + CONTRACT(DomainAssembly *) + { + PRECONDITION(CheckPointer(pDomain, NULL_NOT_OK)); + POSTCONDITION(CheckPointer(RETVAL)); + THROWS; + GC_TRIGGERS; + } + CONTRACT_END; + + RETURN GetManifestModule()->GetDomainAssembly(pDomain); +} + +DomainAssembly *Assembly::FindDomainAssembly(AppDomain *pDomain) +{ + CONTRACT(DomainAssembly *) + { + PRECONDITION(CheckPointer(pDomain)); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + SO_TOLERANT; + SUPPORTS_DAC; + } + CONTRACT_END; + + PREFIX_ASSUME (GetManifestModule() !=NULL); + RETURN GetManifestModule()->FindDomainAssembly(pDomain); +} + +BOOL Assembly::IsIntrospectionOnly() +{ + WRAPPER_NO_CONTRACT; + return m_pManifestFile->IsIntrospectionOnly(); +} + +PTR_LoaderHeap Assembly::GetLowFrequencyHeap() +{ + WRAPPER_NO_CONTRACT; + + return GetLoaderAllocator()->GetLowFrequencyHeap(); +} + +PTR_LoaderHeap Assembly::GetHighFrequencyHeap() +{ + WRAPPER_NO_CONTRACT; + + return GetLoaderAllocator()->GetHighFrequencyHeap(); +} + + +PTR_LoaderHeap Assembly::GetStubHeap() +{ + WRAPPER_NO_CONTRACT; + + return GetLoaderAllocator()->GetStubHeap(); +} + + +PTR_BaseDomain Assembly::GetDomain() +{ + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + _ASSERTE(m_pDomain); + return (m_pDomain); +} +IAssemblySecurityDescriptor *Assembly::GetSecurityDescriptor(AppDomain *pDomain) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END + + IAssemblySecurityDescriptor* pSecDesc; + + if (pDomain == NULL) + { +#ifndef DACCESS_COMPILE + pDomain = ::GetAppDomain(); +#else //DACCESS_COMPILE + DacNotImpl(); +#endif //DACCESS_COMPILE + } + + PREFIX_ASSUME(FindDomainAssembly(pDomain) != NULL); + pSecDesc = FindDomainAssembly(pDomain)->GetSecurityDescriptor(); + + CONSISTENCY_CHECK(pSecDesc != NULL); + + return pSecDesc; +} + +#ifndef DACCESS_COMPILE + +const SecurityTransparencyBehavior *Assembly::GetSecurityTransparencyBehavior() +{ + CONTRACT(const SecurityTransparencyBehavior *) + { + THROWS; + GC_TRIGGERS; + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + if (m_pTransparencyBehavior == NULL) + { + ModuleSecurityDescriptor *pModuleSecurityDescriptor = ModuleSecurityDescriptor::GetModuleSecurityDescriptor(this); + SetSecurityTransparencyBehavior(SecurityTransparencyBehavior::GetTransparencyBehavior(pModuleSecurityDescriptor->GetSecurityRuleSet())); + } + + RETURN(m_pTransparencyBehavior); +} + +// This method is like GetTransparencyBehavior, but will not attempt to get the transparency behavior if we +// don't already know it, and therefore may return NULL +const SecurityTransparencyBehavior *Assembly::TryGetSecurityTransparencyBehavior() +{ + LIMITED_METHOD_CONTRACT; + return m_pTransparencyBehavior; +} + + +// The transparency behavior object passed to this method must have a lifetime of at least as long +// as the assembly itself. +void Assembly::SetSecurityTransparencyBehavior(const SecurityTransparencyBehavior *pTransparencyBehavior) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(CheckPointer(pTransparencyBehavior)); + PRECONDITION(m_pTransparencyBehavior == NULL || m_pTransparencyBehavior == pTransparencyBehavior); + } + CONTRACTL_END; + + m_pTransparencyBehavior = pTransparencyBehavior; +} + +void Assembly::SetParent(BaseDomain* pParent) +{ + LIMITED_METHOD_CONTRACT; + + m_pDomain = pParent; +} + +#endif // !DACCCESS_COMPILE + +mdFile Assembly::GetManifestFileToken(LPCSTR name) +{ + +#ifdef FEATURE_MULTIMODULE_ASSEMBLIES + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + HashDatum datum; + // Note: We're doing a case sensitive lookup + // This is OK because the lookup string and the string we insert into the hashtable + // are obtained from the same place. + + // m_pAllowedFiles only grows - entries are never deleted from it. So we do not take + // a lock around GetValue. If the code is modified such that we delete entries from m_pAllowedFiles, + // reconsider whether the callers that consume the mdFile should take the m_crstAllowedFiles lock. + if (m_pAllowedFiles->GetValue(name, &datum)) { + + if (datum != NULL) // internal module + return (mdFile)(size_t)PTR_TO_TADDR(datum); + else // manifest file + return mdFileNil; + } + else + return mdTokenNil; // not found +#else + return mdFileNil; +#endif +} + +mdFile Assembly::GetManifestFileToken(IMDInternalImport *pImport, mdFile kFile) +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + LPCSTR name; + if ((TypeFromToken(kFile) != mdtFile) || + !pImport->IsValidToken(kFile)) + { + BAD_FORMAT_NOTHROW_ASSERT(!"Invalid File token"); + return mdTokenNil; + } + + if (FAILED(pImport->GetFileProps(kFile, &name, NULL, NULL, NULL))) + { + BAD_FORMAT_NOTHROW_ASSERT(!"Invalid File token"); + return mdTokenNil; + } + + return GetManifestFileToken(name); +} + +Module *Assembly::FindModuleByExportedType(mdExportedType mdType, + Loader::LoadFlag loadFlag, + mdTypeDef mdNested, + mdTypeDef* pCL) +{ + CONTRACT(Module *) + { + if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; + if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS; + if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else INJECT_FAULT(COMPlusThrowOM();); + MODE_ANY; + POSTCONDITION(CheckPointer(RETVAL, loadFlag==Loader::Load ? NULL_NOT_OK : NULL_OK)); + SUPPORTS_DAC; + } + CONTRACT_END + + mdToken mdLinkRef; + mdToken mdBinding; + + IMDInternalImport *pManifestImport = GetManifestImport(); + + IfFailThrow(pManifestImport->GetExportedTypeProps( + mdType, + NULL, + NULL, + &mdLinkRef, // Impl + &mdBinding, // Hint + NULL)); // dwflags + + // Don't trust the returned tokens. + if (!pManifestImport->IsValidToken(mdLinkRef)) + { + if (loadFlag != Loader::Load) + { + RETURN NULL; + } + else + { + ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN); + } + } + + switch(TypeFromToken(mdLinkRef)) { + case mdtAssemblyRef: + { + *pCL = mdTypeDefNil; // We don't trust the mdBinding token + + Assembly *pAssembly = NULL; + switch(loadFlag) + { + case Loader::Load: + { +#ifndef DACCESS_COMPILE + // LoadAssembly never returns NULL + DomainAssembly * pDomainAssembly = + GetManifestModule()->LoadAssembly(::GetAppDomain(), mdLinkRef); + PREFIX_ASSUME(pDomainAssembly != NULL); + + RETURN pDomainAssembly->GetCurrentModule(); +#else + _ASSERTE(!"DAC shouldn't attempt to trigger loading"); + return NULL; +#endif // !DACCESS_COMPILE + }; + case Loader::DontLoad: + pAssembly = GetManifestModule()->GetAssemblyIfLoaded(mdLinkRef); + break; + case Loader::SafeLookup: + pAssembly = GetManifestModule()->LookupAssemblyRef(mdLinkRef); + break; + default: + _ASSERTE(FALSE); + } + + if (pAssembly) + RETURN pAssembly->GetManifestModule(); + else + RETURN NULL; + + } + + case mdtFile: + { + // We may not want to trust this TypeDef token, since it + // was saved in a scope other than the one it was defined in + if (mdNested == mdTypeDefNil) + *pCL = mdBinding; + else + *pCL = mdNested; + + // Note that we don't want to attempt a LoadModule if a GetModuleIfLoaded will + // succeed, because it has a stronger contract. + Module *pModule = GetManifestModule()->GetModuleIfLoaded(mdLinkRef, TRUE, FALSE); +#ifdef DACCESS_COMPILE + return pModule; +#else + if (pModule != NULL) + RETURN pModule; + + if(loadFlag==Loader::SafeLookup) + return NULL; + + // We should never get here in the GC case - the above should have succeeded. + CONSISTENCY_CHECK(!FORBIDGC_LOADER_USE_ENABLED()); + + DomainFile * pDomainModule = GetManifestModule()->LoadModule(::GetAppDomain(), mdLinkRef, FALSE, loadFlag!=Loader::Load); + + if (pDomainModule == NULL) + RETURN NULL; + else + { + pModule = pDomainModule->GetCurrentModule(); + if (pModule == NULL) + { + _ASSERTE(loadFlag!=Loader::Load); + } + + RETURN pModule; + } +#endif // DACCESS_COMPILE + } + + case mdtExportedType: + // Only override the nested type token if it hasn't been set yet. + if (mdNested != mdTypeDefNil) + mdBinding = mdNested; + + RETURN FindModuleByExportedType(mdLinkRef, loadFlag, mdBinding, pCL); + + default: + ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN_TYPE); + } +} // Assembly::FindModuleByExportedType + + +// The returned Module is non-NULL unless you prevented the load by setting loadFlag=Loader::DontLoad. +/* static */ +Module * Assembly::FindModuleByTypeRef( + Module * pModule, + mdTypeRef tkType, + Loader::LoadFlag loadFlag, + BOOL * pfNoResolutionScope) +{ + CONTRACT(Module *) + { + if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; + if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS; + if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM();); } + + MODE_ANY; + + PRECONDITION(CheckPointer(pModule)); + PRECONDITION(TypeFromToken(tkType) == mdtTypeRef); + PRECONDITION(CheckPointer(pfNoResolutionScope)); + POSTCONDITION( CheckPointer(RETVAL, loadFlag==Loader::Load ? NULL_NOT_OK : NULL_OK) ); + SUPPORTS_DAC; + } + CONTRACT_END + + // WARNING! Correctness of the type forwarder detection algorithm in code:ClassLoader::ResolveTokenToTypeDefThrowing + // relies on this function not performing any form of type forwarding itself. + + IMDInternalImport * pImport; + mdTypeRef tkTopLevelEncloserTypeRef; + + pImport = pModule->GetMDImport(); + if (TypeFromToken(tkType) != mdtTypeRef) + { + ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN_TYPE); + } + + { + // Find the top level encloser + GCX_NOTRIGGER(); + + // If nested, get top level encloser's impl + int iter = 0; + int maxIter = 1000; + do + { + _ASSERTE(TypeFromToken(tkType) == mdtTypeRef); + tkTopLevelEncloserTypeRef = tkType; + + if (!pImport->IsValidToken(tkType) || iter >= maxIter) + { + break; + } + + IfFailThrow(pImport->GetResolutionScopeOfTypeRef(tkType, &tkType)); + + // nil-scope TR okay if there's an ExportedType + // Return manifest file + if (IsNilToken(tkType)) + { + *pfNoResolutionScope = TRUE; + RETURN(pModule); + } + iter++; + } + while (TypeFromToken(tkType) == mdtTypeRef); + } + + *pfNoResolutionScope = FALSE; + +#ifndef DACCESS_COMPILE + if (!pImport->IsValidToken(tkType)) // redundant check only when invalid token already found. + { + THROW_BAD_FORMAT(BFA_BAD_TYPEREF_TOKEN, pModule); + } +#endif //!DACCESS_COMPILE + + switch (TypeFromToken(tkType)) + { + case mdtModule: + { + // Type is in the referencing module. + GCX_NOTRIGGER(); + CANNOTTHROWCOMPLUSEXCEPTION(); + RETURN( pModule ); + } + + case mdtModuleRef: + { + if ((loadFlag != Loader::Load) || IsGCThread() || IsStackWalkerThread()) + { + // Either we're not supposed to load, or we're doing a GC or stackwalk + // in which case we shouldn't need to load. So just look up the module + // and return what we find. + RETURN(pModule->LookupModule(tkType,FALSE)); + } + +#ifndef DACCESS_COMPILE + DomainFile * pActualDomainFile = pModule->LoadModule(::GetAppDomain(), tkType, FALSE, loadFlag!=Loader::Load); + if (pActualDomainFile == NULL) + { + RETURN NULL; + } + else + { + RETURN(pActualDomainFile->GetModule()); + } + +#else //DACCESS_COMPILE + _ASSERTE(loadFlag!=Loader::Load); + DacNotImpl(); + RETURN NULL; +#endif //DACCESS_COMPILE + } + break; + + case mdtAssemblyRef: + { + // Do this first because it has a strong contract + Assembly * pAssembly = NULL; + +#if defined(FEATURE_COMINTEROP) || !defined(DACCESS_COMPILE) + LPCUTF8 szNamespace = NULL; + LPCUTF8 szClassName = NULL; +#endif + +#ifdef FEATURE_COMINTEROP + if (pModule->HasBindableIdentity(tkType)) +#endif// FEATURE_COMINTEROP + { + _ASSERTE(!IsAfContentType_WindowsRuntime(pModule->GetAssemblyRefFlags(tkType))); + if (loadFlag == Loader::SafeLookup) + { + pAssembly = pModule->LookupAssemblyRef(tkType); + } + else + { + pAssembly = pModule->GetAssemblyIfLoaded(tkType); + } + } +#ifdef FEATURE_COMINTEROP + else + { + _ASSERTE(IsAfContentType_WindowsRuntime(pModule->GetAssemblyRefFlags(tkType))); + + if (FAILED(pImport->GetNameOfTypeRef( + tkTopLevelEncloserTypeRef, + &szNamespace, + &szClassName))) + { + THROW_BAD_FORMAT(BFA_BAD_TYPEREF_TOKEN, pModule); + } + + pAssembly = pModule->GetAssemblyIfLoaded( + tkType, + szNamespace, + szClassName, + NULL); // pMDImportOverride + } +#endif // FEATURE_COMINTEROP + + if (pAssembly != NULL) + { + RETURN pAssembly->m_pManifest; + } + +#ifdef DACCESS_COMPILE + RETURN NULL; +#else + if (loadFlag != Loader::Load) + { + RETURN NULL; + } + +#ifndef FEATURE_CORECLR + // Event Tracing for Windows is used to log data for performance and functional testing purposes. + // The events below are used to help measure the performance of assembly loading of a static reference. + FireEtwLoaderPhaseStart((::GetAppDomain() ? ::GetAppDomain()->GetId().m_dwId : ETWAppDomainIdNotAvailable), ETWLoadContextNotAvailable, ETWFieldUnused, ETWLoaderStaticLoad, NULL, NULL, GetClrInstanceId()); +#endif //!FEATURE_CORECLR + + DomainAssembly * pDomainAssembly = pModule->LoadAssembly( + ::GetAppDomain(), + tkType, + szNamespace, + szClassName); + +#ifndef FEATURE_CORECLR + if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_PRIVATEBINDING_KEYWORD)) + { + StackSString assemblySimpleName; + EX_TRY + { + if ((pDomainAssembly != NULL) && (pDomainAssembly->GetCurrentAssembly() != NULL)) + { + assemblySimpleName.AppendUTF8(pDomainAssembly->GetCurrentAssembly()->GetSimpleName()); + assemblySimpleName.Normalize(); // Ensures that the later cast to LPCWSTR does not throw. + } + } + EX_CATCH + { + assemblySimpleName.Clear(); + } + EX_END_CATCH(RethrowTransientExceptions) + + FireEtwLoaderPhaseEnd(::GetAppDomain() ? ::GetAppDomain()->GetId().m_dwId : ETWAppDomainIdNotAvailable, ETWLoadContextNotAvailable, ETWFieldUnused, ETWLoaderStaticLoad, NULL, assemblySimpleName.IsEmpty() ? NULL : (LPCWSTR)assemblySimpleName, GetClrInstanceId()); + } +#endif //!FEATURE_CORECLR + + if (pDomainAssembly == NULL) + RETURN NULL; + + pAssembly = pDomainAssembly->GetCurrentAssembly(); + if (pAssembly == NULL) + { + RETURN NULL; + } + else + { + RETURN pAssembly->m_pManifest; + } +#endif //!DACCESS_COMPILE + } + + default: + ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN_TYPE); + } +} // Assembly::FindModuleByTypeRef + +#ifndef DACCESS_COMPILE + +Module *Assembly::FindModuleByName(LPCSTR pszModuleName) +{ + CONTRACT(Module *) + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + MODE_ANY; + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + CQuickBytes qbLC; + + // Need to perform case insensitive hashing. + UTF8_TO_LOWER_CASE(pszModuleName, qbLC); + pszModuleName = (LPUTF8) qbLC.Ptr(); + + mdFile kFile = GetManifestFileToken(pszModuleName); + if (kFile == mdTokenNil) + ThrowHR(COR_E_UNAUTHORIZEDACCESS); + + if (this == SystemDomain::SystemAssembly()) + RETURN m_pManifest->GetModuleIfLoaded(kFile, TRUE, TRUE); + else + RETURN m_pManifest->LoadModule(::GetAppDomain(), kFile)->GetModule(); +} + +void Assembly::CacheManifestExportedTypes(AllocMemTracker *pamTracker) +{ + CONTRACT_VOID + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACT_END; + + // Prejitted assemblies are expected to have their table prebuilt. + // If not, we do it here at load time (as if we would jit the assembly). + + if (m_pManifest->IsPersistedObject(m_pManifest->m_pAvailableClasses)) + RETURN; + + mdToken mdExportedType; + + HENUMInternalHolder phEnum(GetManifestImport()); + phEnum.EnumInit(mdtExportedType, + mdTokenNil); + + ClassLoader::AvailableClasses_LockHolder lh(m_pClassLoader); + + for(int i = 0; GetManifestImport()->EnumNext(&phEnum, &mdExportedType); i++) + m_pClassLoader->AddExportedTypeHaveLock(GetManifestModule(), + mdExportedType, + pamTracker); + + RETURN; +} +void Assembly::CacheManifestFiles() +{ +#ifdef FEATURE_MULTIMODULE_ASSEMBLIES + CONTRACT_VOID + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACT_END; + + mdToken tkFile; + LPCSTR pszFileName; + CQuickBytes qbLC; + + HENUMInternalHolder phEnum(GetManifestImport()); + phEnum.EnumInit(mdtFile, + mdTokenNil); + + + DWORD dwCount = GetManifestImport()->EnumGetCount(&phEnum); + LockOwner lockOwner = { &m_crstAllowedFiles, IsOwnerOfCrst }; + if (!m_pAllowedFiles->Init(dwCount+1, &lockOwner)) + ThrowOutOfMemory(); + + CrstHolder lock(&m_crstAllowedFiles); + + m_nextAvailableModuleIndex = dwCount+1; + + while (GetManifestImport()->EnumNext(&phEnum, &tkFile)) + { + if (TypeFromToken(tkFile) == mdtFile) + { + IfFailThrow(GetManifestImport()->GetFileProps( + tkFile, + &pszFileName, + NULL, // hash + NULL, // hash len + NULL)); // flags + + // Add to hash table + m_pAllowedFiles->InsertValue(pszFileName, (HashDatum)(size_t)tkFile, TRUE); + + // Need to perform case insensitive hashing as well. + { + UTF8_TO_LOWER_CASE(pszFileName, qbLC); + pszFileName = (LPUTF8) qbLC.Ptr(); + } + + // Add each internal module + m_pAllowedFiles->InsertValue(pszFileName, (HashDatum)(size_t)tkFile, TRUE); + } + } + + HENUMInternalHolder phEnumModules(GetManifestImport()); + phEnumModules.EnumInit(mdtModuleRef, mdTokenNil); + mdToken tkModuleRef; + + while (GetManifestImport()->EnumNext(&phEnumModules, &tkModuleRef)) + { + LPCSTR pszModuleRefName, pszModuleRefNameLower; + + if (TypeFromToken(tkModuleRef) == mdtModuleRef) + { + IfFailThrow(GetManifestImport()->GetModuleRefProps(tkModuleRef, &pszModuleRefName)); + + // Convert to lower case and lookup + { + UTF8_TO_LOWER_CASE(pszModuleRefName, qbLC); + pszModuleRefNameLower = (LPUTF8) qbLC.Ptr(); + } + + HashDatum datum; + if (m_pAllowedFiles->GetValue(pszModuleRefNameLower, &datum)) + { + mdFile tkFileForModuleRef = (mdFile)(size_t)datum; + m_pAllowedFiles->InsertValue(pszModuleRefName, (HashDatum)(size_t)tkFileForModuleRef); + } + } + } + + // Add the manifest file + if (!GetManifestImport()->IsValidToken(GetManifestImport()->GetModuleFromScope())) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + IfFailThrow(GetManifestImport()->GetScopeProps(&pszFileName, NULL)); + + // Add to hash table + m_pAllowedFiles->InsertValue(pszFileName, NULL, TRUE); + + // Need to perform case insensitive hashing as well. + { + UTF8_TO_LOWER_CASE(pszFileName, qbLC); + pszFileName = (LPUTF8) qbLC.Ptr(); + } + + m_pAllowedFiles->InsertValue(pszFileName, NULL, TRUE); + + RETURN; +#endif +} + + +//<TODO>@TODO: if module is not signed it needs to acquire the +//permissions from the assembly.</TODO> +void Assembly::PrepareModuleForAssembly(Module* module, AllocMemTracker *pamTracker) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + PRECONDITION(CheckPointer(module)); + } + CONTRACTL_END; + + if (module->m_pAvailableClasses != NULL && !module->IsPersistedObject(module->m_pAvailableClasses)) + { + // ! We intentionally do not take the AvailableClass lock here. It creates problems at + // startup and we haven't yet published the module yet so nobody should be searching it. + m_pClassLoader->PopulateAvailableClassHashTable(module, pamTracker); + } + + +#ifdef DEBUGGING_SUPPORTED + // Modules take the DebuggerAssemblyControlFlags down from its + // parent Assembly initially. + module->SetDebuggerInfoBits(GetDebuggerInfoBits()); + + LOG((LF_CORDB, LL_INFO10, "Module %s: bits=0x%x\n", + module->GetFile()->GetSimpleName(), + module->GetDebuggerInfoBits())); +#endif // DEBUGGING_SUPPORTED + + m_pManifest->EnsureFileCanBeStored(module->GetModuleRef()); +} + +// This is the final step of publishing a Module into an Assembly. This step cannot fail. +void Assembly::PublishModuleIntoAssembly(Module *module) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + FORBID_FAULT; + } + CONTRACTL_END + + GetManifestModule()->EnsuredStoreFile(module->GetModuleRef(), module); + FastInterlockIncrement((LONG*)&m_pClassLoader->m_cUnhashedModules); +} + + + +#ifdef FEATURE_MULTIMODULE_ASSEMBLIES +Module* Assembly::FindModule(PEFile *pFile, BOOL includeLoading) +{ + CONTRACT(Module *) + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACT_END; + + DomainFile *pModule = GetDomainAssembly()->FindModule(pFile, includeLoading); + + if (pModule == NULL) + RETURN NULL; + else + RETURN pModule->GetModule(); +} +#endif // FEATURE_MULTIMODULE_ASSEMBLIES + +#ifdef FEATURE_MIXEDMODE +DomainFile* Assembly::FindIJWDomainFile(HMODULE hMod, const SString &path) +{ + CONTRACT (DomainFile*) + { + INSTANCE_CHECK; + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(GetManifestModule())); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + ModuleIterator i = IterateModules(); + while (i.Next()) + { + PEFile *pFile = i.GetModule()->GetFile(); + + if ( !pFile->IsResource() + && !pFile->IsDynamic() + && !pFile->IsILOnly()) + { + if ( (pFile->GetLoadedIL()!= NULL && pFile->GetIJWBase() == hMod) + || PEImage::PathEquals(pFile->GetPath(), path)) + RETURN i.GetModule()->GetDomainFile(); + } + } + RETURN NULL; +} +#endif // FEATURE_MIXEDMODE + +//***************************************************************************** +// Set up the list of names of any friend assemblies +void Assembly::CacheFriendAssemblyInfo() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END + + if (m_pFriendAssemblyDescriptor == NULL) + { + FriendAssemblyDescriptor *pFriendAssemblies = FriendAssemblyDescriptor::CreateFriendAssemblyDescriptor(this->GetManifestFile()); + if (pFriendAssemblies == NULL) + { + pFriendAssemblies = NO_FRIEND_ASSEMBLIES_MARKER; + } + + void *pvPreviousDescriptor = InterlockedCompareExchangeT(&m_pFriendAssemblyDescriptor, + pFriendAssemblies, + NULL); + + if (pvPreviousDescriptor != NULL && pFriendAssemblies != NO_FRIEND_ASSEMBLIES_MARKER) + { + if (pFriendAssemblies != NO_FRIEND_ASSEMBLIES_MARKER) + { + delete pFriendAssemblies; + } + } + } +} // void Assembly::CacheFriendAssemblyInfo() + +//***************************************************************************** +// Is the given assembly a friend of this assembly? +bool Assembly::GrantsFriendAccessTo(Assembly *pAccessingAssembly, FieldDesc *pFD) +{ + WRAPPER_NO_CONTRACT; + + CacheFriendAssemblyInfo(); + + if (m_pFriendAssemblyDescriptor == NO_FRIEND_ASSEMBLIES_MARKER) + { + return false; + } + + return m_pFriendAssemblyDescriptor->GrantsFriendAccessTo(pAccessingAssembly, pFD); +} + +bool Assembly::GrantsFriendAccessTo(Assembly *pAccessingAssembly, MethodDesc *pMD) +{ + WRAPPER_NO_CONTRACT; + + CacheFriendAssemblyInfo(); + + if (m_pFriendAssemblyDescriptor == NO_FRIEND_ASSEMBLIES_MARKER) + { + return false; + } + + return m_pFriendAssemblyDescriptor->GrantsFriendAccessTo(pAccessingAssembly, pMD); +} + +bool Assembly::GrantsFriendAccessTo(Assembly *pAccessingAssembly, MethodTable *pMT) +{ + WRAPPER_NO_CONTRACT; + + CacheFriendAssemblyInfo(); + + if (m_pFriendAssemblyDescriptor == NO_FRIEND_ASSEMBLIES_MARKER) + { + return false; + } + + return m_pFriendAssemblyDescriptor->GrantsFriendAccessTo(pAccessingAssembly, pMT); +} + +bool Assembly::IgnoresAccessChecksTo(Assembly *pAccessedAssembly) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + PRECONDITION(CheckPointer(pAccessedAssembly)); + } + CONTRACTL_END; + + CacheFriendAssemblyInfo(); + + if (m_pFriendAssemblyDescriptor == NO_FRIEND_ASSEMBLIES_MARKER) + { + return false; + } + + if (pAccessedAssembly->IsDisabledPrivateReflection()) + { + return false; + } + + if (!m_fIsDomainNeutral && !GetSecurityDescriptor(GetDomain()->AsAppDomain())->IsFullyTrusted()) + { + return false; + } + + return m_pFriendAssemblyDescriptor->IgnoresAccessChecksTo(pAccessedAssembly); +} + + +#ifndef CROSSGEN_COMPILE + +enum CorEntryPointType +{ + EntryManagedMain, // void main(String[]) + EntryCrtMain // unsigned main(void) +}; + +#ifdef STRESS_THREAD + +struct Stress_Thread_Param +{ + MethodDesc *pFD; + GlobalStrongHandleHolder argHandle; + short numSkipArgs; + CorEntryPointType EntryType; + Thread* pThread; + +public: + Stress_Thread_Param() + : pFD(NULL), + argHandle(), + numSkipArgs(0), + EntryType(EntryManagedMain), + pThread(NULL) + { LIMITED_METHOD_CONTRACT; } + + Stress_Thread_Param* Clone () + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + NewHolder<Stress_Thread_Param> retVal= new Stress_Thread_Param; + + retVal->pFD = pFD; + if (argHandle.GetValue()!=NULL) + { + GCX_COOP(); + retVal->argHandle.Assign(CreateDuplicateHandle(argHandle.GetValue())); + } + retVal->numSkipArgs = numSkipArgs; + retVal->EntryType = EntryType; + retVal->pThread = pThread; + return retVal.Extract(); + } +}; + +struct Stress_Thread_Worker_Param +{ + Stress_Thread_Param *lpParameter; + ULONG retVal; +}; + +static void Stress_Thread_Proc_Worker_Impl(Stress_Thread_Worker_Param * args) +{ + STATIC_CONTRACT_THROWS; + + args->retVal = E_FAIL; + + Stress_Thread_Param* lpParam = (Stress_Thread_Param *)args->lpParameter; + + ARG_SLOT stackVar = 0; + + MethodDescCallSite threadStart(lpParam->pFD); + + // Build the parameter array and invoke the method. + if (lpParam->EntryType == EntryManagedMain) + { + PTRARRAYREF StrArgArray = (PTRARRAYREF)ObjectFromHandle(lpParam->argHandle.GetValue()); + stackVar = ObjToArgSlot(StrArgArray); + } + + if (lpParam->pFD->IsVoid()) + { + threadStart.Call(&stackVar); + args->retVal = GetLatchedExitCode(); + } + else + { + // We are doing the same cast as in RunMain. Main is required to return INT32 if it returns. + ARG_SLOT retVal = (INT32)threadStart.Call_RetArgSlot(&stackVar); + args->retVal = static_cast<ULONG>(retVal); + } +} + +// wrap into EX_TRY_NOCATCH and call the real thing +static void Stress_Thread_Proc_Worker (LPVOID ptr) +{ + STATIC_CONTRACT_THROWS; + + EX_TRY_NOCATCH(Stress_Thread_Worker_Param *, args, (Stress_Thread_Worker_Param *) ptr) + { + Stress_Thread_Proc_Worker_Impl(args); + //<TODO> + // When we get mainCRTStartup from the C++ then this should be able to go away.</TODO> + fflush(stdout); + fflush(stderr); + } + EX_END_NOCATCH +} + +static DWORD WINAPI __stdcall Stress_Thread_Proc (LPVOID lpParameter) +{ + STATIC_CONTRACT_THROWS; + + Stress_Thread_Worker_Param args = {(Stress_Thread_Param*)lpParameter,0}; + Stress_Thread_Param *lpParam = (Stress_Thread_Param *)lpParameter; + Thread *pThread = lpParam->pThread; + if (!pThread->HasStarted()) + return 0; + + _ASSERTE(::GetAppDomain() != NULL); + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return E_FAIL); + EX_TRY + { + + ADID KickOffDomain = pThread->GetKickOffDomainId(); + + // should always have a kickoff domain - a thread should never start in a domain that is unloaded + // because otherwise it would have been collected because nobody can hold a reference to thread object + // in a domain that has been unloaded. But it is possible that we started the unload, in which + // case this thread wouldn't be allowed in or would be punted anyway. + if (KickOffDomain != lpParam->pThread->GetDomain()->GetId()) + pThread->DoADCallBack(KickOffDomain, Stress_Thread_Proc_Worker, &args); + else + Stress_Thread_Proc_Worker(&args); + + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); + + delete (Stress_Thread_Param *) lpParameter; + // Enable preemptive GC so a GC thread can suspend me. + GCX_PREEMP_NO_DTOR(); + DestroyThread(pThread); + + END_SO_INTOLERANT_CODE; + return args.retVal; +} + +static void Stress_Thread_Start (LPVOID lpParameter) +{ + CONTRACT_VOID + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM()); + MODE_ANY; + } + CONTRACT_END; + + Thread *pCurThread = GetThread(); + if (pCurThread->m_stressThreadCount == -1) { + pCurThread->m_stressThreadCount = g_pConfig->GetStressThreadCount(); + } + DWORD dwThreads = pCurThread->m_stressThreadCount; + if (dwThreads <= 1) + RETURN; + + Thread ** threads = new Thread* [dwThreads-1]; + + DWORD n; + for (n = 0; n < dwThreads-1; n ++) + { + threads[n] = SetupUnstartedThread(); + + threads[n]->m_stressThreadCount = dwThreads/2; + Stress_Thread_Param *param = ((Stress_Thread_Param*)lpParameter)->Clone(); + param->pThread = threads[n]; + if (!threads[n]->CreateNewThread(0, Stress_Thread_Proc, param)) + { + delete param; + threads[n]->DecExternalCount(FALSE); + ThrowOutOfMemory(); + } + threads[n]->SetThreadPriority (THREAD_PRIORITY_NORMAL); + } + + for (n = 0; n < dwThreads-1; n ++) + { + threads[n]->StartThread(); + } + __SwitchToThread (0, CALLER_LIMITS_SPINNING); + + RETURN; +} + +void Stress_Thread_RunMain(MethodDesc* pFD, CorEntryPointType EntryType, short numSkipArgs, OBJECTHANDLE argHandle) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + Stress_Thread_Param Param; + Param.pFD = pFD; + Param.argHandle.Assign(argHandle); + Param.numSkipArgs = numSkipArgs; + Param.EntryType = EntryType; + Param.pThread = NULL; + Stress_Thread_Start (&Param); +} + + +#endif // STRESS_THREAD + +void DECLSPEC_NORETURN ThrowMainMethodException(MethodDesc* pMD, UINT resID) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + } + CONTRACTL_END; + + DefineFullyQualifiedNameForClassW(); + LPCWSTR szClassName = GetFullyQualifiedNameForClassW(pMD->GetMethodTable()); + LPCUTF8 szUTFMethodName; + if (FAILED(pMD->GetMDImport()->GetNameOfMethodDef(pMD->GetMemberDef(), &szUTFMethodName))) + { + szUTFMethodName = "Invalid MethodDef record"; + } + PREFIX_ASSUME(szUTFMethodName!=NULL); + MAKE_WIDEPTR_FROMUTF8(szMethodName, szUTFMethodName); + COMPlusThrowHR(COR_E_METHODACCESS, resID, szClassName, szMethodName); +} + +// Returns true if this is a valid main method? +void ValidateMainMethod(MethodDesc * pFD, CorEntryPointType *pType) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + + PRECONDITION(CheckPointer(pType)); + } + CONTRACTL_END; + + // Must be static, but we don't care about accessibility + if ((pFD->GetAttrs() & mdStatic) == 0) + ThrowMainMethodException(pFD, IDS_EE_MAIN_METHOD_MUST_BE_STATIC); + + if (pFD->GetNumGenericClassArgs() != 0 || pFD->GetNumGenericMethodArgs() != 0) + ThrowMainMethodException(pFD, IDS_EE_LOAD_BAD_MAIN_SIG); + + // Check for types + SigPointer sig(pFD->GetSigPointer()); + + ULONG nCallConv; + if (FAILED(sig.GetData(&nCallConv))) + ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE); + + if (nCallConv != IMAGE_CEE_CS_CALLCONV_DEFAULT) + ThrowMainMethodException(pFD, IDS_EE_LOAD_BAD_MAIN_SIG); + + ULONG nParamCount; + if (FAILED(sig.GetData(&nParamCount))) + ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE); + + + CorElementType nReturnType; + if (FAILED(sig.GetElemType(&nReturnType))) + ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE); + + if ((nReturnType != ELEMENT_TYPE_VOID) && (nReturnType != ELEMENT_TYPE_I4) && (nReturnType != ELEMENT_TYPE_U4)) + ThrowMainMethodException(pFD, IDS_EE_MAIN_METHOD_HAS_INVALID_RTN); + + if (nParamCount == 0) + *pType = EntryCrtMain; + else { + *pType = EntryManagedMain; + + if (nParamCount != 1) + ThrowMainMethodException(pFD, IDS_EE_TO_MANY_ARGUMENTS_IN_MAIN); + + CorElementType argType; + CorElementType argType2 = ELEMENT_TYPE_END; + + if (FAILED(sig.GetElemType(&argType))) + ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE); + + if (argType == ELEMENT_TYPE_SZARRAY) + if (FAILED(sig.GetElemType(&argType2))) + ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE); + + if (argType != ELEMENT_TYPE_SZARRAY || argType2 != ELEMENT_TYPE_STRING) + ThrowMainMethodException(pFD, IDS_EE_LOAD_BAD_MAIN_SIG); + } +} + +/* static */ +HRESULT RunMain(MethodDesc *pFD , + short numSkipArgs, + INT32 *piRetVal, + PTRARRAYREF *stringArgs /*=NULL*/) +{ + STATIC_CONTRACT_THROWS; + _ASSERTE(piRetVal); + + DWORD cCommandArgs = 0; // count of args on command line + LPWSTR *wzArgs = NULL; // command line args + HRESULT hr = S_OK; + + *piRetVal = -1; + + // The exit code for the process is communicated in one of two ways. If the + // entrypoint returns an 'int' we take that. Otherwise we take a latched + // process exit code. This can be modified by the app via setting + // Environment's ExitCode property. + // + // When we're executing the default exe main in the default domain, set the latched exit code to + // zero as a default. If it gets set to something else by user code then that value will be returned. + // + // StringArgs appears to be non-null only when the main method is explicitly invoked via the hosting api + // or through creating a subsequent domain and running an exe within it. In those cases we don't + // want to reset the (global) latched exit code. + if (stringArgs == NULL) + SetLatchedExitCode(0); + + if (!pFD) { + _ASSERTE(!"Must have a function to call!"); + return E_FAIL; + } + + CorEntryPointType EntryType = EntryManagedMain; + ValidateMainMethod(pFD, &EntryType); + + if ((EntryType == EntryManagedMain) && + (stringArgs == NULL)) { + #ifndef FEATURE_CORECLR + // If you look at the DIFF on this code then you will see a major change which is that we + // no longer accept all the different types of data arguments to main. We now only accept + // an array of strings. + + wzArgs = CorCommandLine::GetArgvW(&cCommandArgs); + // In the WindowsCE case where the app has additional args the count will come back zero. + if (cCommandArgs > 0) { + if (!wzArgs) + return E_INVALIDARG; + } +#else // !FEATURE_CORECLR + return E_INVALIDARG; +#endif // !FEATURE_CORECLR + } + + ETWFireEvent(Main_V1); + + struct Param + { + MethodDesc *pFD; + short numSkipArgs; + INT32 *piRetVal; + PTRARRAYREF *stringArgs; + CorEntryPointType EntryType; + DWORD cCommandArgs; + LPWSTR *wzArgs; + } param; + param.pFD = pFD; + param.numSkipArgs = numSkipArgs; + param.piRetVal = piRetVal; + param.stringArgs = stringArgs; + param.EntryType = EntryType; + param.cCommandArgs = cCommandArgs; + param.wzArgs = wzArgs; + + EX_TRY_NOCATCH(Param *, pParam, ¶m) + { + MethodDescCallSite threadStart(pParam->pFD); + + PTRARRAYREF StrArgArray = NULL; + GCPROTECT_BEGIN(StrArgArray); + + // Build the parameter array and invoke the method. + if (pParam->EntryType == EntryManagedMain) { + if (pParam->stringArgs == NULL) { + // Allocate a COM Array object with enough slots for cCommandArgs - 1 + StrArgArray = (PTRARRAYREF) AllocateObjectArray((pParam->cCommandArgs - pParam->numSkipArgs), g_pStringClass); + + // Create Stringrefs for each of the args + for (DWORD arg = pParam->numSkipArgs; arg < pParam->cCommandArgs; arg++) { + STRINGREF sref = StringObject::NewString(pParam->wzArgs[arg]); + StrArgArray->SetAt(arg - pParam->numSkipArgs, (OBJECTREF) sref); + } + } + else + StrArgArray = *pParam->stringArgs; + } + +#ifdef STRESS_THREAD + OBJECTHANDLE argHandle = (StrArgArray != NULL) ? CreateGlobalStrongHandle (StrArgArray) : NULL; + Stress_Thread_RunMain(pParam->pFD, pParam->EntryType, pParam->numSkipArgs, argHandle); +#endif + + ARG_SLOT stackVar = ObjToArgSlot(StrArgArray); + + if (pParam->pFD->IsVoid()) + { + // Set the return value to 0 instead of returning random junk + *pParam->piRetVal = 0; + threadStart.Call(&stackVar); + } + else + { + *pParam->piRetVal = (INT32)threadStart.Call_RetArgSlot(&stackVar); + if (pParam->stringArgs == NULL) + { + SetLatchedExitCode(*pParam->piRetVal); + } + } + + GCPROTECT_END(); + + //<TODO> + // When we get mainCRTStartup from the C++ then this should be able to go away.</TODO> + fflush(stdout); + fflush(stderr); + } + EX_END_NOCATCH + + ETWFireEvent(MainEnd_V1); + + return hr; +} + +static void RunMainPre() +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE(GetThread() != 0); + g_fWeControlLifetime = TRUE; +} + +static void RunMainPost() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + PRECONDITION(CheckPointer(GetThread())); + } + CONTRACTL_END + + GCX_PREEMP(); + ThreadStore::s_pThreadStore->WaitForOtherThreads(); + + DWORD dwSecondsToSleep = g_pConfig->GetSleepOnExit(); + + // if dwSeconds is non-zero then we will sleep for that many seconds + // before we exit this allows the vaDumpCmd to detect that our process + // has gone idle and this allows us to get a vadump of our process at + // this point in it's execution + // + if (dwSecondsToSleep != 0) + { + ClrSleepEx(dwSecondsToSleep * 1000, FALSE); + } +} + +INT32 Assembly::ExecuteMainMethod(PTRARRAYREF *stringArgs, BOOL waitForOtherThreads) +{ + CONTRACTL + { + INSTANCE_CHECK; + THROWS; + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + INJECT_FAULT(COMPlusThrowOM()); + } + CONTRACTL_END; + + // reset the error code for std C + errno=0; + + HRESULT hr = S_OK; + INT32 iRetVal = 0; + + BEGIN_ENTRYPOINT_THROWS; + + Thread *pThread = GetThread(); + MethodDesc *pMeth; + { + // This thread looks like it wandered in -- but actually we rely on it to keep the process alive. + pThread->SetBackground(FALSE); + + GCX_COOP(); + + pMeth = GetEntryPoint(); + if (pMeth) { + RunMainPre(); + +#if defined(FEATURE_APPX_BINDER) && defined(FEATURE_MULTICOREJIT) + if (AppX::IsAppXProcess()) + { + GCX_PREEMP(); + + // we call this to obtain and cache the PRAID value which is used + // by multicore JIT manager and watson bucket params generation. + + // NOTE: this makes a COM call into WinRT so we must do this after we've + // set the thread's apartment state which will do CoInitializeEx(). + LPCWSTR praid; + hr = AppX::GetApplicationId(praid); + _ASSERTE(SUCCEEDED(hr)); + + if (!pMeth->GetModule()->HasNativeImage()) + { + // For Appx, multicore JIT is only needed when root assembly does not have NI image + // When it has NI image, we can't generate profile, and do not need to playback profile + AppDomain * pDomain = pThread->GetDomain(); + pDomain->GetMulticoreJitManager().AutoStartProfileAppx(pDomain); + } + } +#endif // FEATURE_APPX_BINDER && FEATURE_MULTICOREJIT + +#ifdef FEATURE_CORECLR + // Set the root assembly as the assembly that is containing the main method + // The root assembly is used in the GetEntryAssembly method that on CoreCLR is used + // to get the TargetFrameworkMoniker for the app + AppDomain * pDomain = pThread->GetDomain(); + pDomain->SetRootAssembly(pMeth->GetAssembly()); +#endif + hr = RunMain(pMeth, 1, &iRetVal, stringArgs); + } + } + + //RunMainPost is supposed to be called on the main thread of an EXE, + //after that thread has finished doing useful work. It contains logic + //to decide when the process should get torn down. So, don't call it from + // AppDomain.ExecuteAssembly() + if (pMeth) { + if (waitForOtherThreads) + RunMainPost(); + } + else { + StackSString displayName; + GetDisplayName(displayName); + COMPlusThrowHR(COR_E_MISSINGMETHOD, IDS_EE_FAILED_TO_FIND_MAIN, displayName); + } + + IfFailThrow(hr); + + END_ENTRYPOINT_THROWS; + return iRetVal; +} +#endif // CROSSGEN_COMPILE + +MethodDesc* Assembly::GetEntryPoint() +{ + CONTRACT(MethodDesc*) + { + THROWS; + INJECT_FAULT(COMPlusThrowOM();); + MODE_ANY; + + // Can return NULL if no entry point. + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + if (m_pEntryPoint) + RETURN m_pEntryPoint; + + mdToken mdEntry = m_pManifestFile->GetEntryPointToken(); + if (IsNilToken(mdEntry)) + RETURN NULL; + + Module *pModule = NULL; + switch(TypeFromToken(mdEntry)) { + case mdtFile: + pModule = m_pManifest->LoadModule(::GetAppDomain(), mdEntry, FALSE)->GetModule(); + + mdEntry = pModule->GetEntryPointToken(); + if ( (TypeFromToken(mdEntry) != mdtMethodDef) || + (!pModule->GetMDImport()->IsValidToken(mdEntry)) ) + pModule = NULL; + break; + + case mdtMethodDef: + if (m_pManifestFile->GetPersistentMDImport()->IsValidToken(mdEntry)) + pModule = m_pManifest; + break; + } + + // May be unmanaged entrypoint + if (!pModule) + RETURN NULL; + + // We need to get its properties and the class token for this MethodDef token. + mdToken mdParent; + if (FAILED(pModule->GetMDImport()->GetParentToken(mdEntry, &mdParent))) { + StackSString displayName; + GetDisplayName(displayName); + COMPlusThrowHR(COR_E_BADIMAGEFORMAT, IDS_EE_ILLEGAL_TOKEN_FOR_MAIN, displayName); + } + + // For the entrypoint, also validate if the paramList is valid or not. We do this check + // by asking for the return-value (sequence 0) parameter to MDInternalRO::FindParamOfMethod. + // Incase the parameter list is invalid, CLDB_E_FILE_CORRUPT will be returned + // byMDInternalRO::FindParamOfMethod and we will bail out. + // + // If it does not exist (return value CLDB_E_RECORD_NOTFOUND) or if it is found (S_OK), + // we do not bother as the values would have come upon ensurin a valid parameter record + // list. + mdParamDef pdParam; + HRESULT hrValidParamList = pModule->GetMDImport()->FindParamOfMethod(mdEntry, 0, &pdParam); + if (hrValidParamList == CLDB_E_FILE_CORRUPT) + { + // Throw an exception for bad_image_format (because of corrupt metadata) + StackSString displayName; + GetDisplayName(displayName); + COMPlusThrowHR(COR_E_BADIMAGEFORMAT, IDS_EE_ILLEGAL_TOKEN_FOR_MAIN, displayName); + } + + if (mdParent != COR_GLOBAL_PARENT_TOKEN) { + GCX_COOP(); + // This code needs a class init frame, because without it, the + // debugger will assume any code that results from searching for a + // type handle (ie, loading an assembly) is the first line of a program. + FrameWithCookie<DebuggerClassInitMarkFrame> __dcimf; + + MethodTable * pInitialMT = ClassLoader::LoadTypeDefOrRefThrowing(pModule, mdParent, + ClassLoader::ThrowIfNotFound, + ClassLoader::FailIfUninstDefOrRef).GetMethodTable(); + + m_pEntryPoint = MemberLoader::FindMethod(pInitialMT, mdEntry); + + __dcimf.Pop(); + } + else + { + m_pEntryPoint = pModule->FindMethod(mdEntry); + } + + RETURN m_pEntryPoint; +} + +#ifndef CROSSGEN_COMPILE +OBJECTREF Assembly::GetExposedObject() +{ + CONTRACT(OBJECTREF) + { + GC_TRIGGERS; + THROWS; + INJECT_FAULT(COMPlusThrowOM();); + MODE_COOPERATIVE; + } + CONTRACT_END; + + RETURN GetDomainAssembly()->GetExposedAssemblyObject(); +} +#endif // CROSSGEN_COMPILE + +/* static */ +BOOL Assembly::FileNotFound(HRESULT hr) +{ + LIMITED_METHOD_CONTRACT; + return IsHRESULTForExceptionKind(hr, kFileNotFoundException) || +#ifdef FEATURE_COMINTEROP + (hr == RO_E_METADATA_NAME_NOT_FOUND) || +#endif //FEATURE_COMINTEROP + (hr == CLR_E_BIND_TYPE_NOT_FOUND); +} + +#ifdef FEATURE_MULTIMODULE_ASSEMBLIES +PEModule * Assembly::LoadModule_AddRef(mdFile kFile, BOOL fLoadResource) +{ + CONTRACT(PEModule *) + { + INSTANCE_CHECK; + THROWS; + GC_TRIGGERS; + MODE_ANY; + POSTCONDITION(CheckPointer(RETVAL, fLoadResource ? NULL_NOT_OK : NULL_OK)); + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACT_END + + if (! ((TypeFromToken(kFile) == mdtFile) && + GetManifestImport()->IsValidToken(kFile)) ) + { + ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_FILE_TOKEN); + } + + LPCSTR psModuleName; + DWORD dwFlags; + IfFailThrow(GetManifestImport()->GetFileProps( + kFile, + &psModuleName, + NULL, + NULL, + &dwFlags)); + + if (! (IsFfContainsMetaData(dwFlags) || fLoadResource) ) + RETURN NULL; + + SString name(SString::Utf8, psModuleName); + PEModule * pModule = NULL; + + if (AssemblySpec::VerifyBindingString((LPCWSTR)name)) + { + EX_TRY + { + GCX_PREEMP(); + +#ifdef FEATURE_FUSION // specific to remote modules + if (GetFusionAssembly()) { + StackSString path; + ::GetAppDomain()->GetFileFromFusion(GetFusionAssembly(), + (LPCWSTR)name, path); + pModule = PEModule::Open(m_pManifestFile, kFile, path); + goto lDone; + } + + if (GetIHostAssembly()) { + pModule = PEModule::Open(m_pManifestFile, kFile, name); + goto lDone; + } +#endif + if (!m_pManifestFile->GetPath().IsEmpty()) { + StackSString path = m_pManifestFile->GetPath(); + + SString::Iterator i = path.End()-1; + + if (PEAssembly::FindLastPathSeparator(path, i)) { + path.Truncate(++i); + path.Insert(i, name); + } + pModule = PEModule::Open(m_pManifestFile, kFile, path); + } +#ifdef FEATURE_FUSION + lDone: ; +#endif + } + EX_CATCH + { + Exception *ex = GET_EXCEPTION(); + if (FileNotFound(ex->GetHR()) || + (ex->GetHR() == FUSION_E_INVALID_NAME)) + pModule = RaiseModuleResolveEvent_AddRef(psModuleName, kFile); + + if (pModule == NULL) + { + EEFileLoadException::Throw(name, ex->GetHR(), ex); + } + } + EX_END_CATCH(SwallowAllExceptions) + } + + if (pModule == NULL) + { + pModule = RaiseModuleResolveEvent_AddRef(psModuleName, kFile); + if (pModule == NULL) + { + EEFileLoadException::Throw(name, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + } + } + + RETURN pModule; +} + +PEModule * Assembly::RaiseModuleResolveEvent_AddRef(LPCSTR szName, mdFile kFile) +{ + CONTRACT(PEModule *) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + Module* pModule = NULL; + +#ifndef CROSSGEN_COMPILE + GCX_COOP(); + + struct _gc { + OBJECTREF AssemblyRef; + STRINGREF str; + } gc; + ZeroMemory(&gc, sizeof(gc)); + + GCPROTECT_BEGIN(gc); + if ((gc.AssemblyRef = GetExposedObject()) != NULL) + { + MethodDescCallSite onModuleResolve(METHOD__ASSEMBLY__ON_MODULE_RESOLVE, &gc.AssemblyRef); + gc.str = StringObject::NewString(szName); + ARG_SLOT args[2] = { + ObjToArgSlot(gc.AssemblyRef), + ObjToArgSlot(gc.str) + }; + + REFLECTMODULEBASEREF ResultingModuleRef = + (REFLECTMODULEBASEREF) onModuleResolve.Call_RetOBJECTREF(args); + + if (ResultingModuleRef != NULL) + { + pModule = ResultingModuleRef->GetModule(); + } + } + GCPROTECT_END(); + + if (pModule && ( (!(pModule->IsIntrospectionOnly())) != !(IsIntrospectionOnly()) )) + { + COMPlusThrow(kFileLoadException, IDS_CLASSLOAD_MODULE_RESOLVE_INTROSPECTION_MISMATCH); + } + + if ((pModule != NULL) && + (pModule == m_pManifest->LookupFile(kFile))) + { + RETURN clr::SafeAddRef((PEModule *)pModule->GetFile()); + } +#endif // CROSSGEN_COMPILE + + RETURN NULL; +} +#endif // FEATURE_MULTIMODULE_ASSEMBLIES + +BOOL Assembly::GetResource(LPCSTR szName, DWORD *cbResource, + PBYTE *pbInMemoryResource, Assembly** pAssemblyRef, + LPCSTR *szFileName, DWORD *dwLocation, + StackCrawlMark *pStackMark, BOOL fSkipSecurityCheck, + BOOL fSkipRaiseResolveEvent) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + DomainAssembly *pAssembly = NULL; + BOOL result = GetDomainAssembly()->GetResource(szName, cbResource, + pbInMemoryResource, &pAssembly, + szFileName, dwLocation, pStackMark, fSkipSecurityCheck, + fSkipRaiseResolveEvent); + if (result && pAssemblyRef != NULL && pAssembly!=NULL) + *pAssemblyRef = pAssembly->GetAssembly(); + + return result; +} + +#ifdef FEATURE_PREJIT +BOOL Assembly::IsInstrumented() +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_FAULT; + + BOOL isInstrumented = false; + + EX_TRY + { + FAULT_NOT_FATAL(); + + isInstrumented = IsInstrumentedHelper(); + } + EX_CATCH + { + isInstrumented = false; + } + EX_END_CATCH(RethrowTerminalExceptions); + + return isInstrumented; +} + +BOOL Assembly::IsInstrumentedHelper() +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_FAULT; + + // Dynamic Assemblies cannot be instrumented + if (IsDynamic()) + return false; + + // We must have a native image in order to perform IBC instrumentation + if (!GetManifestFile()->HasNativeImage()) + return false; + + // @Consider using the full name instead of the short form + // (see GetFusionAssemblyName()->IsEqual). + + LPCUTF8 szZapBBInstr = g_pConfig->GetZapBBInstr(); + LPCUTF8 szAssemblyName = GetSimpleName(); + + if (!szZapBBInstr || !szAssemblyName || + (*szZapBBInstr == '\0') || (*szAssemblyName == '\0')) + return false; + + // Convert to unicode so that we can do a case insensitive comparison + + SString instrumentedAssemblyNamesList(SString::Utf8, szZapBBInstr); + SString assemblyName(SString::Utf8, szAssemblyName); + + const WCHAR *wszInstrumentedAssemblyNamesList = instrumentedAssemblyNamesList.GetUnicode(); + const WCHAR *wszAssemblyName = assemblyName.GetUnicode(); + + // wszInstrumentedAssemblyNamesList is a space separated list of assembly names. + // We need to determine if wszAssemblyName is in this list. + // If there is a "*" in the list, then all assemblies match. + + const WCHAR * pCur = wszInstrumentedAssemblyNamesList; + + do + { + _ASSERTE(pCur[0] != W('\0')); + const WCHAR * pNextSpace = wcschr(pCur, W(' ')); + _ASSERTE(pNextSpace == NULL || pNextSpace[0] == W(' ')); + + if (pCur != pNextSpace) + { + // pCur is not pointing to a space + _ASSERTE(pCur[0] != W(' ')); + + if (pCur[0] == W('*') && (pCur[1] == W(' ') || pCur[1] == W('\0'))) + return true; + + if (pNextSpace == NULL) + { + // We have reached the last name in the list. There are no more spaces. + return (SString::_wcsicmp(wszAssemblyName, pCur) == 0); + } + else + { + if (SString::_wcsnicmp(wszAssemblyName, pCur, static_cast<COUNT_T>(pNextSpace - pCur)) == 0) + return true; + } + } + + pCur = pNextSpace + 1; + } + while (pCur[0] != W('\0')); + + return false; +} +#endif // FEATURE_PREJIT + +//*********************************************************** +// Add an assembly to the assemblyref list. pAssemEmitter specifies where +// the AssemblyRef is emitted to. +//*********************************************************** +mdAssemblyRef Assembly::AddAssemblyRef(Assembly *refedAssembly, IMetaDataAssemblyEmit *pAssemEmitter, BOOL fUsePublicKeyToken) +{ + CONTRACT(mdAssemblyRef) + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + PRECONDITION(CheckPointer(refedAssembly)); +#ifdef FEATURE_CORECLR + PRECONDITION(CheckPointer(pAssemEmitter, NULL_NOT_OK)); +#else + PRECONDITION(CheckPointer(pAssemEmitter, NULL_OK)); +#endif //FEATURE_CORECLR + POSTCONDITION(!IsNilToken(RETVAL)); + POSTCONDITION(TypeFromToken(RETVAL) == mdtAssemblyRef); + } + CONTRACT_END; + + SafeComHolder<IMetaDataAssemblyEmit> emitHolder; +#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) + if (pAssemEmitter == NULL) + { + pAssemEmitter = GetOnDiskMDAssemblyEmitter(); + emitHolder.Assign(pAssemEmitter); + } +#endif // FEATURE_CORECLR && !CROSSGEN_COMPILE + + AssemblySpec spec; + spec.InitializeSpec(refedAssembly->GetManifestFile()); + + if (refedAssembly->IsCollectible()) + { + if (this->IsCollectible()) + this->GetLoaderAllocator()->EnsureReference(refedAssembly->GetLoaderAllocator()); + else + COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleBoundNonCollectible")); + } + + mdAssemblyRef ar; + IfFailThrow(spec.EmitToken(pAssemEmitter, &ar, fUsePublicKeyToken)); + + RETURN ar; +} // Assembly::AddAssemblyRef + +//*********************************************************** +// Add a typedef to the runtime TypeDef table of this assembly +//*********************************************************** +void Assembly::AddType( + Module *pModule, + mdTypeDef cl) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END + + AllocMemTracker amTracker; + + if (pModule->GetAssembly() != this) + { + // you cannot add a typedef outside of the assembly to the typedef table + _ASSERTE(!"Bad usage!"); + } + m_pClassLoader->AddAvailableClassDontHaveLock(pModule, + cl, + &amTracker); + amTracker.SuppressRelease(); +} + +//*********************************************************** +// Add an ExportedType to the runtime TypeDef table of this assembly +//*********************************************************** +void Assembly::AddExportedType(mdExportedType cl) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END + + AllocMemTracker amTracker; + m_pClassLoader->AddExportedTypeDontHaveLock(GetManifestModule(), + cl, + &amTracker); + amTracker.SuppressRelease(); +} + + +#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) + +//*********************************************************** +// +// get the IMetaDataAssemblyEmit for the on disk manifest. +// Note that the pointer returned is AddRefed. It is the caller's +// responsibility to release the reference. +// +//*********************************************************** +IMetaDataAssemblyEmit *Assembly::GetOnDiskMDAssemblyEmitter() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END + + IMetaDataAssemblyEmit *pAssemEmitter = NULL; + IMetaDataEmit *pEmitter; + RefClassWriter *pRCW; + + _ASSERTE(m_pOnDiskManifest); + + pRCW = m_pOnDiskManifest->GetClassWriter(); + _ASSERTE(pRCW); + + // If the RefClassWriter has a on disk emitter, then use it rather than the in-memory emitter. + pEmitter = pRCW->GetOnDiskEmitter(); + + if (pEmitter == NULL) + pEmitter = m_pOnDiskManifest->GetEmitter(); + + _ASSERTE(pEmitter != NULL); + + IfFailThrow(pEmitter->QueryInterface(IID_IMetaDataAssemblyEmit, (void**) &pAssemEmitter)); + + if (pAssemEmitter == NULL) + { + // the manifest is not writable + _ASSERTE(!"Bad usage!"); + } + return pAssemEmitter; +} + +//*********************************************************** +// +// prepare saving manifest to disk. +// +//*********************************************************** +void Assembly::PrepareSavingManifest(ReflectionModule *pAssemblyModule) +{ + STANDARD_VM_CONTRACT; + + if (pAssemblyModule) + { + // embedded assembly + m_pOnDiskManifest = pAssemblyModule; + m_fEmbeddedManifest = true; + } + else + { + m_fEmbeddedManifest = false; + + StackSString name(SString::Utf8, GetSimpleName()); + + // Create the module + m_pOnDiskManifest = CreateDynamicModule(name, name, FALSE /*fIsTransient*/); + // store the fact this on disk manifest is temporary and can be hidden from the user + m_needsToHideManifestForEmit = TRUE; + } + + NonVMComHolder<IMetaDataAssemblyEmit> pAssemblyEmit(GetOnDiskMDAssemblyEmitter()); + + // Copy assembly metadata to emit scope + //<TODO>@todo: add Title, Description, Alias as CA</TODO> + // <TODO>@todo: propagate all of the information</TODO> + // <TODO>@todo: introduce a helper in metadata to take the ansi version of string.</TODO> + + IMetaDataAssemblyImport *pAssemblyImport = GetManifestFile()->GetAssemblyImporter(); + + const void *pbPublicKey; + ULONG cbPublicKey; + ULONG ulHashAlgId; + LPWSTR szName; + ULONG chName; + ASSEMBLYMETADATA MetaData; + DWORD dwAssemblyFlags; + + MetaData.cbLocale = 0; + MetaData.ulProcessor = 0; + MetaData.ulOS = 0; + IfFailThrow(pAssemblyImport->GetAssemblyProps(TokenFromRid(1, mdtAssembly), + NULL, NULL, NULL, + NULL, 0, &chName, + &MetaData, NULL)); + StackSString name; + szName = name.OpenUnicodeBuffer(chName); + + SString locale; + MetaData.szLocale = locale.OpenUnicodeBuffer(MetaData.cbLocale); + + SBuffer proc; + MetaData.rProcessor = (DWORD *) proc.OpenRawBuffer(MetaData.ulProcessor*sizeof(*MetaData.rProcessor)); + + SBuffer os; + MetaData.rOS = (OSINFO *) os.OpenRawBuffer(MetaData.ulOS*sizeof(*MetaData.rOS)); + + IfFailThrow(pAssemblyImport->GetAssemblyProps(TokenFromRid(1, mdtAssembly), + &pbPublicKey, &cbPublicKey, &ulHashAlgId, + szName, chName, &chName, + &MetaData, &dwAssemblyFlags)); + + mdAssembly ad; + IfFailThrow(pAssemblyEmit->DefineAssembly(pbPublicKey, cbPublicKey, ulHashAlgId, + szName, &MetaData, dwAssemblyFlags, &ad)); + + SafeComHolder<IMetaDataImport> pImport; + IfFailThrow(pAssemblyEmit->QueryInterface(IID_IMetaDataImport, (void**)&pImport)); + ULONG cExistingName = 0; + if (FAILED(pImport->GetScopeProps(NULL, 0, &cExistingName, NULL)) || cExistingName == 0) + { + SafeComHolder<IMetaDataEmit> pEmit; + IfFailThrow(pAssemblyEmit->QueryInterface(IID_IMetaDataEmit, (void**)&pEmit)); + IfFailThrow(pEmit->SetModuleProps(szName)); + } + + name.CloseBuffer(); + locale.CloseBuffer(); + proc.CloseRawBuffer(); + os.CloseRawBuffer(); +} // Assembly::PrepareSavingManifest + + +//*********************************************************** +// +// add a file name to the file list of this assembly. On disk only. +// +//*********************************************************** +mdFile Assembly::AddFile(LPCWSTR wszFileName) +{ + STANDARD_VM_CONTRACT; + + SafeComHolder<IMetaDataAssemblyEmit> pAssemEmitter(GetOnDiskMDAssemblyEmitter()); + mdFile fl; + + // Define File. + IfFailThrow( pAssemEmitter->DefineFile( + wszFileName, // [IN] Name of the file. + 0, // [IN] Hash Blob. + 0, // [IN] Count of bytes in the Hash Blob. + 0, // [IN] Flags. + &fl) ); // [OUT] Returned File token. + + return fl; +} // Assembly::AddFile + + +//*********************************************************** +// +// Set the hash value on a file table entry. +// +//*********************************************************** +void Assembly::SetFileHashValue(mdFile tkFile, LPCWSTR wszFullFileName) +{ + STANDARD_VM_CONTRACT; + + SafeComHolder<IMetaDataAssemblyEmit> pAssemEmitter(GetOnDiskMDAssemblyEmitter()); + + // Get the hash value. + SBuffer buffer; + PEImageHolder map(PEImage::OpenImage(StackSString(wszFullFileName))); + map->ComputeHash(GetHashAlgId(), buffer); + + // Set the hash blob. + IfFailThrow( pAssemEmitter->SetFileProps( + tkFile, // [IN] File Token. + buffer, // [IN] Hash Blob. + buffer.GetSize(), // [IN] Count of bytes in the Hash Blob. + (DWORD) -1)); // [IN] Flags. + +} // Assembly::SetHashValue + +//***************************************************************************** +// Add a Type name to the ExportedType table in the on-disk assembly manifest. +//***************************************************************************** +mdExportedType Assembly::AddExportedTypeOnDisk(LPCWSTR wszExportedType, mdToken tkImpl, mdToken tkTypeDef, CorTypeAttr flags) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(TypeFromToken(tkTypeDef) == mdtTypeDef); + + // The on-disk assembly manifest + SafeComHolder<IMetaDataAssemblyEmit> pAssemEmitter(GetOnDiskMDAssemblyEmitter()); + + mdExportedType ct; + + IfFailThrow( pAssemEmitter->DefineExportedType( + wszExportedType, // [IN] Name of the COMType. + tkImpl, // [IN] mdFile or mdAssemblyRef that provides the ExportedType. + tkTypeDef, // [IN] TypeDef token within the file. + flags, // [IN] Flags. + &ct) ); // [OUT] Returned ExportedType token. + + return ct; +} // Assembly::AddExportedTypeOnDisk + +//******************************************************************************* +// Add a Type name to the ExportedType table in the in-memory assembly manifest. +//******************************************************************************* +mdExportedType Assembly::AddExportedTypeInMemory(LPCWSTR wszExportedType, mdToken tkImpl, mdToken tkTypeDef, CorTypeAttr flags) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(TypeFromToken(tkTypeDef) == mdtTypeDef); + + // The in-memory assembly manifest + IMetaDataAssemblyEmit* pAssemEmitter = GetManifestFile()->GetAssemblyEmitter(); + + mdExportedType ct; + + IfFailThrow( pAssemEmitter->DefineExportedType( + wszExportedType, // [IN] Name of the COMType. + tkImpl, // [IN] mdFile or mdAssemblyRef that provides the ExportedType. + tkTypeDef, // [IN] TypeDef token within the file. + flags, // [IN] Flags. + &ct) ); // [OUT] Returned ExportedType token. + + return ct; +} // Assembly::AddExportedTypeInMemory + + +//*********************************************************** +// add an entry to ManifestResource table for a stand alone managed resource. On disk only. +//*********************************************************** +void Assembly::AddStandAloneResource(LPCWSTR wszName, LPCWSTR wszDescription, LPCWSTR wszMimeType, LPCWSTR wszFileName, LPCWSTR wszFullFileName, int iAttribute) +{ + STANDARD_VM_CONTRACT; + + SafeComHolder<IMetaDataAssemblyEmit> pAssemEmitter(GetOnDiskMDAssemblyEmitter()); + mdFile tkFile; + mdManifestResource mr; + SBuffer hash; + + // Get the hash value; + if (GetHashAlgId()) + { + PEImageHolder pImage(PEImage::OpenImage(StackSString(wszFullFileName))); + pImage->ComputeHash(GetHashAlgId(), hash); + } + + IfFailThrow( pAssemEmitter->DefineFile( + wszFileName, // [IN] Name of the file. + hash, // [IN] Hash Blob. + hash.GetSize(), // [IN] Count of bytes in the Hash Blob. + ffContainsNoMetaData, // [IN] Flags. + &tkFile) ); // [OUT] Returned File token. + + + IfFailThrow( pAssemEmitter->DefineManifestResource( + wszName, // [IN] Name of the resource. + tkFile, // [IN] mdFile or mdAssemblyRef that provides the resource. + 0, // [IN] Offset to the beginning of the resource within the file. + iAttribute, // [IN] Flags. + &mr) ); // [OUT] Returned ManifestResource token. + +} // Assembly::AddStandAloneResource + + +//*********************************************************** +// Save security permission requests. +//*********************************************************** +void Assembly::AddDeclarativeSecurity(DWORD dwAction, void const *pValue, DWORD cbValue) +{ + STANDARD_VM_CONTRACT; + + mdAssembly tkAssembly = 0x20000001; + + SafeComHolder<IMetaDataAssemblyEmit> pAssemEmitter(GetOnDiskMDAssemblyEmitter()); + _ASSERTE( pAssemEmitter ); + + SafeComHolder<IMetaDataEmitHelper> pEmitHelper; + IfFailThrow( pAssemEmitter->QueryInterface(IID_IMetaDataEmitHelper, (void**)&pEmitHelper) ); + + IfFailThrow(pEmitHelper->AddDeclarativeSecurityHelper(tkAssembly, + dwAction, + pValue, + cbValue, + NULL)); +} + + +//*********************************************************** +// Allocate space for a strong name signature in the manifest +//*********************************************************** +HRESULT Assembly::AllocateStrongNameSignature(ICeeFileGen *pCeeFileGen, + HCEEFILE ceeFile) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_PREEMPTIVE; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + HRESULT hr; + HCEESECTION TData; + DWORD dwDataOffset; + DWORD dwDataLength; + DWORD dwDataRVA; + VOID *pvBuffer; + const void *pbPublicKey; + ULONG cbPublicKey; + + // Determine size of signature blob. + + IfFailRet(GetManifestImport()->GetAssemblyProps(TokenFromRid(1, mdtAssembly), + &pbPublicKey, &cbPublicKey, NULL, + NULL, NULL, NULL)); + + if (!StrongNameSignatureSize((BYTE *) pbPublicKey, cbPublicKey, &dwDataLength)) { + hr = StrongNameErrorInfo(); + return hr; + } + + // Allocate space for the signature in the text section and update the COM+ + // header to point to the space. + IfFailRet(pCeeFileGen->GetIlSection(ceeFile, &TData)); + IfFailRet(pCeeFileGen->GetSectionDataLen(TData, &dwDataOffset)); + IfFailRet(pCeeFileGen->GetSectionBlock(TData, dwDataLength, 4, &pvBuffer)); + IfFailRet(pCeeFileGen->GetMethodRVA(ceeFile, dwDataOffset, &dwDataRVA)); + IfFailRet(pCeeFileGen->SetStrongNameEntry(ceeFile, dwDataLength, dwDataRVA)); + + return S_OK; +} + + +//*********************************************************** +// Strong name sign a manifest already persisted to disk +//*********************************************************** +HRESULT Assembly::SignWithStrongName(LPCWSTR wszFileName) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + HRESULT hr = S_OK; + + // If we're going to do a full signing we have a key pair either + // in a key container or provided directly in a byte array. + + switch (m_eStrongNameLevel) { + case SN_FULL_KEYPAIR_IN_ARRAY: + if (!StrongNameSignatureGeneration(wszFileName, NULL, m_pbStrongNameKeyPair, m_cbStrongNameKeyPair, NULL, NULL)) + hr = StrongNameErrorInfo(); + break; + + case SN_FULL_KEYPAIR_IN_CONTAINER: + if (!StrongNameSignatureGeneration(wszFileName, m_pwStrongNameKeyContainer, NULL, 0, NULL, NULL)) + hr = StrongNameErrorInfo(); + break; + + default: + break; + } + + return hr; +} + + +//*********************************************************** +// save the manifest to disk! +//*********************************************************** +void Assembly::SaveManifestToDisk(LPCWSTR wszFileName, int entrypoint, int fileKind, DWORD corhFlags, DWORD peFlags) +{ + STANDARD_VM_CONTRACT; + + HRESULT hr = NOERROR; + HCEEFILE ceeFile = NULL; + ICeeFileGen *pCeeFileGen = NULL; + RefClassWriter *pRCW; + IMetaDataEmit *pEmitter; + + _ASSERTE( m_fEmbeddedManifest == false ); + + pRCW = m_pOnDiskManifest->GetClassWriter(); + _ASSERTE(pRCW); + + IfFailGo( pRCW->EnsureCeeFileGenCreated(corhFlags, peFlags) ); + + pCeeFileGen = pRCW->GetCeeFileGen(); + ceeFile = pRCW->GetHCEEFILE(); + _ASSERTE(ceeFile && pCeeFileGen); + + //Emit the MetaData + pEmitter = m_pOnDiskManifest->GetClassWriter()->GetEmitter(); + IfFailGo( pCeeFileGen->EmitMetaDataEx(ceeFile, pEmitter) ); + + // Allocate space for a strong name signature if a public key was supplied + // (this doesn't strong name the assembly, but it makes it possible to do so + // as a post processing step). + if (IsStrongNamed()) + IfFailGo(AllocateStrongNameSignature(pCeeFileGen, ceeFile)); + + IfFailGo( pCeeFileGen->SetOutputFileName(ceeFile, (LPWSTR)wszFileName) ); + + // the entryPoint for an assembly is a tkFile token if exist. + if (RidFromToken(entrypoint) != mdTokenNil) + IfFailGo( pCeeFileGen->SetEntryPoint(ceeFile, entrypoint) ); + if (fileKind == Dll) + { + pCeeFileGen->SetDllSwitch(ceeFile, true); + } + else + { + // should have a valid entry point for applications + if (fileKind == WindowApplication) + { + IfFailGo( pCeeFileGen->SetSubsystem(ceeFile, IMAGE_SUBSYSTEM_WINDOWS_GUI, CEE_IMAGE_SUBSYSTEM_MAJOR_VERSION, CEE_IMAGE_SUBSYSTEM_MINOR_VERSION) ); + } + else + { + _ASSERTE(fileKind == ConsoleApplication); + IfFailGo( pCeeFileGen->SetSubsystem(ceeFile, IMAGE_SUBSYSTEM_WINDOWS_CUI, CEE_IMAGE_SUBSYSTEM_MAJOR_VERSION, CEE_IMAGE_SUBSYSTEM_MINOR_VERSION) ); + } + + } + + //Generate the CeeFile + IfFailGo(pCeeFileGen->GenerateCeeFile(ceeFile) ); + + // Strong name sign the resulting assembly if required. + if (IsStrongNamed()) + IfFailGo(SignWithStrongName(wszFileName)); + + // now release the m_pOnDiskManifest +ErrExit: + pRCW->DestroyCeeFileGen(); + + // we keep the on disk manifest so that the GetModules code can skip over this ad-hoc module when modules are enumerated. + // Need to see if we can remove the creation of this module alltogether + //m_pOnDiskManifest = NULL; + + if (FAILED(hr)) + { + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + { + if (IsWin32IOError(HRESULT_CODE(hr))) + { + COMPlusThrowHR(COR_E_IO); + } + else + { + COMPlusThrowHR(hr); + } + } + if (hr == CEE_E_CVTRES_NOT_FOUND) + COMPlusThrow(kIOException, W("Argument_cvtres_NotFound")); + COMPlusThrowHR(hr); + } +} // Assembly::SaveManifestToDisk + +#endif // FEATURE_CORECLR && !CROSSGEN_COMPILE + + +HRESULT STDMETHODCALLTYPE +GetAssembliesByName(LPCWSTR szAppBase, + LPCWSTR szPrivateBin, + LPCWSTR szAssemblyName, + IUnknown *ppIUnk[], + ULONG cMax, + ULONG *pcAssemblies) +{ + CONTRACTL + { + NOTHROW; + MODE_PREEMPTIVE; + GC_TRIGGERS; + INJECT_FAULT(return E_OUTOFMEMORY;); + } + CONTRACTL_END + + HRESULT hr = S_OK; + + if (g_fEEInit) { + // Cannot call this during EE startup + return MSEE_E_ASSEMBLYLOADINPROGRESS; + } + + if (!(szAssemblyName && ppIUnk && pcAssemblies)) + return E_POINTER; + +#if defined(FEATURE_CORECLR) || defined(CROSSGEN_COMPILE) + hr = COR_E_NOTSUPPORTED; +#else + AppDomain *pDomain = NULL; + + BEGIN_EXTERNAL_ENTRYPOINT(&hr) + if(szAppBase || szPrivateBin) + { + GCX_COOP_THREAD_EXISTS(GET_THREAD()); + MethodDescCallSite createDomainEx(METHOD__APP_DOMAIN__CREATE_DOMAINEX); + struct _gc { + STRINGREF pFriendlyName; + STRINGREF pAppBase; + STRINGREF pPrivateBin; + } gc; + ZeroMemory(&gc, sizeof(gc)); + + GCPROTECT_BEGIN(gc); + gc.pFriendlyName = StringObject::NewString(W("GetAssembliesByName")); + + if(szAppBase) + { + gc.pAppBase = StringObject::NewString(szAppBase); + } + + if(szPrivateBin) + { + gc.pPrivateBin = StringObject::NewString(szPrivateBin); + } + + ARG_SLOT args[5] = + { + ObjToArgSlot(gc.pFriendlyName), + NULL, + ObjToArgSlot(gc.pAppBase), + ObjToArgSlot(gc.pPrivateBin), + BoolToArgSlot(false) + }; + APPDOMAINREF pDom = (APPDOMAINREF) createDomainEx.Call_RetOBJECTREF(args); + if (pDom == NULL) + { + hr = E_FAIL; + } + else + { + Context *pContext = CRemotingServices::GetServerContextForProxy((OBJECTREF) pDom); + _ASSERTE(pContext); + pDomain = pContext->GetDomain(); + } + + GCPROTECT_END(); + } + else + pDomain = SystemDomain::System()->DefaultDomain(); + + Assembly *pFoundAssembly; + if (SUCCEEDED(hr)) { + pFoundAssembly = pDomain->LoadAssemblyHelper(szAssemblyName, + NULL); + if (SUCCEEDED(hr)) { + if (cMax < 1) + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + else { + ppIUnk[0] = (IUnknown *)pFoundAssembly->GetManifestAssemblyImporter(); + ppIUnk[0]->AddRef(); + } + *pcAssemblies = 1; + } + } + + END_EXTERNAL_ENTRYPOINT; +#endif // FEATURE_CORECLR + + return hr; +}// Used by the IMetadata API's to access an assemblies metadata. + +#ifdef FEATURE_LOADER_OPTIMIZATION + +void Assembly::SetMissingDependenciesCheckDone() +{ + LIMITED_METHOD_CONTRACT; + m_bMissingDependenciesCheckDone=TRUE; +}; + +BOOL Assembly::MissingDependenciesCheckDone() +{ + LIMITED_METHOD_CONTRACT; + return m_bMissingDependenciesCheckDone; +}; + + +#ifdef FEATURE_FUSION +void Assembly::SetBindingClosure(IAssemblyBindingClosure* pClosure) // Addrefs. It is assumed the caller did not addref pClosure for us. +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + _ASSERTE(m_pBindingClosure == NULL); + _ASSERTE(pClosure != NULL); + + m_pBindingClosure = pClosure; + pClosure->AddRef(); // It is assumed the caller did not addref pBindingClosure for us. +} + +IAssemblyBindingClosure * Assembly::GetBindingClosure() +{ + LIMITED_METHOD_CONTRACT; + return m_pBindingClosure; +} + + +// The shared module list is effectively an extension of the shared domain assembly hash table. +// It is the canonical list and aribiter of modules loaded from this assembly by any app domain. +// Modules are stored here immediately on creating (to prevent duplicate creation), as opposed to +// in the rid map, where they are only placed upon load completion. + +BOOL Assembly::CanBeShared(DomainAssembly *pDomainAssembly) +{ + CONTRACTL + { + PRECONDITION(CheckPointer(pDomainAssembly)); + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + LOG((LF_CODESHARING, + LL_INFO100, + "Checking if we can share: \"%S\" in domain 0x%x.\n", + GetDebugName(), pDomainAssembly->GetAppDomain())); + + STRESS_LOG2(LF_CODESHARING, LL_INFO1000,"Checking whether DomainAssembly %p is compatible with Assembly %p", + pDomainAssembly,this); + + // We must always share the same system assemblies + if (IsSystem()) + { + STRESS_LOG0(LF_CODESHARING, LL_INFO1000,"System assembly - sharing"); + return TRUE; + } + + if ((pDomainAssembly->GetDebuggerInfoBits()&~(DACF_PDBS_COPIED|DACF_IGNORE_PDBS|DACF_OBSOLETE_TRACK_JIT_INFO)) + != (m_debuggerFlags&~(DACF_PDBS_COPIED|DACF_IGNORE_PDBS|DACF_OBSOLETE_TRACK_JIT_INFO))) + { + LOG((LF_CODESHARING, + LL_INFO100, + "We can't share it, desired debugging flags %x are different than %x\n", + pDomainAssembly->GetDebuggerInfoBits(), (m_debuggerFlags&~(DACF_PDBS_COPIED|DACF_IGNORE_PDBS|DACF_OBSOLETE_TRACK_JIT_INFO)))); + STRESS_LOG2(LF_CODESHARING, LL_INFO100,"Flags diff= %08x [%08x/%08x]",pDomainAssembly->GetDebuggerInfoBits(), + m_debuggerFlags); + g_dwLoaderReasonForNotSharing = ReasonForNotSharing_DebuggerFlagMismatch; + return FALSE; + } + + PEAssembly * pDomainAssemblyFile = pDomainAssembly->GetFile(); + if (pDomainAssemblyFile == NULL) + { + g_dwLoaderReasonForNotSharing = ReasonForNotSharing_NullPeassembly; + return FALSE; + } + + IAssemblyBindingClosure * pContext = GetBindingClosure(); + if (pContext == NULL) + { + STRESS_LOG1(LF_CODESHARING, LL_INFO1000,"No context 1 - status=%d",pDomainAssemblyFile->IsSystem()); + if (pDomainAssemblyFile->IsSystem()) + return TRUE; + else + { + g_dwLoaderReasonForNotSharing = ReasonForNotSharing_MissingAssemblyClosure1; + return FALSE; + } + } + + IAssemblyBindingClosure * pCurrentContext = pDomainAssembly->GetAssemblyBindingClosure(LEVEL_STARTING); + if (pCurrentContext == NULL) + { + STRESS_LOG1(LF_CODESHARING, LL_INFO1000,"No context 2 - status=%d",pDomainAssemblyFile->IsSystem()); + if (pDomainAssemblyFile->IsSystem()) + return TRUE; + else + { + g_dwLoaderReasonForNotSharing = ReasonForNotSharing_MissingAssemblyClosure2; + return FALSE; + } + } + + // ensure the closures are walked + { + ReleaseHolder<IBindResult> pWinRTBindResult; + + IUnknown * pUnk; + if (pDomainAssembly->GetFile()->IsWindowsRuntime()) + { // It is .winmd file (WinRT assembly) + IfFailThrow(CLRPrivAssemblyWinRT::GetIBindResult(pDomainAssembly->GetFile()->GetHostAssembly(), &pWinRTBindResult)); + pUnk = pWinRTBindResult; + } + else + { + pUnk = pDomainAssembly->GetFile()->GetFusionAssembly(); + } + + GCX_PREEMP(); + IfFailThrow(pCurrentContext->EnsureWalked(pUnk, ::GetAppDomain()->GetFusionContext(), LEVEL_COMPLETE)); + } + + if ((pContext->HasBeenWalked(LEVEL_COMPLETE) != S_OK) || !MissingDependenciesCheckDone()) + { + GCX_COOP(); + + BOOL fMissingDependenciesResolved = FALSE; + + ENTER_DOMAIN_PTR(SystemDomain::System()->DefaultDomain(), ADV_DEFAULTAD); + { + { + ReleaseHolder<IBindResult> pWinRTBindResult; + + IUnknown * pUnk; + if (GetManifestFile()->IsWindowsRuntime()) + { // It is .winmd file (WinRT assembly) + IfFailThrow(CLRPrivAssemblyWinRT::GetIBindResult(GetManifestFile()->GetHostAssembly(), &pWinRTBindResult)); + pUnk = pWinRTBindResult; + } + else + { + pUnk = GetManifestFile()->GetFusionAssembly(); + } + + GCX_PREEMP(); + IfFailThrow(pContext->EnsureWalked(pUnk, ::GetAppDomain()->GetFusionContext(), LEVEL_COMPLETE)); + } + DomainAssembly * domainAssembly = ::GetAppDomain()->FindDomainAssembly(this); + if (domainAssembly != NULL) + { + if (domainAssembly->CheckMissingDependencies() == CMD_Resolved) + { + //cannot share + fMissingDependenciesResolved = TRUE; + } + } + } + END_DOMAIN_TRANSITION; + + if (fMissingDependenciesResolved) + { + STRESS_LOG0(LF_CODESHARING, LL_INFO1000,"Missing dependencies resolved - not sharing"); + g_dwLoaderReasonForNotSharing = ReasonForNotSharing_MissingDependenciesResolved; + return FALSE; + } + } + + HRESULT hr = pContext->IsEqual(pCurrentContext); + IfFailThrow(hr); + if (hr != S_OK) + { + STRESS_LOG1(LF_CODESHARING, LL_INFO1000,"Closure comparison returned %08x - not sharing",hr); + g_dwLoaderReasonForNotSharing = ReasonForNotSharing_ClosureComparisonFailed; + return FALSE; + } + + LOG((LF_CODESHARING, LL_INFO100, "We can share it : \"%S\"\n", GetDebugName())); + STRESS_LOG0(LF_CODESHARING, LL_INFO1000,"Everything is fine - sharing"); + return TRUE; +} +#endif + +#ifdef FEATURE_VERSIONING + +BOOL Assembly::CanBeShared(DomainAssembly *pDomainAssembly) +{ + PTR_PEAssembly pFile=pDomainAssembly->GetFile(); + + if(pFile == NULL) + return FALSE; + + if(pFile->IsDynamic()) + return FALSE; + + if(IsSystem() && pFile->IsSystem()) + return TRUE; + + if ((pDomainAssembly->GetDebuggerInfoBits()&~(DACF_PDBS_COPIED|DACF_IGNORE_PDBS|DACF_OBSOLETE_TRACK_JIT_INFO)) + != (m_debuggerFlags&~(DACF_PDBS_COPIED|DACF_IGNORE_PDBS|DACF_OBSOLETE_TRACK_JIT_INFO))) + { + LOG((LF_CODESHARING, + LL_INFO100, + "We can't share it, desired debugging flags %x are different than %x\n", + pDomainAssembly->GetDebuggerInfoBits(), (m_debuggerFlags&~(DACF_PDBS_COPIED|DACF_IGNORE_PDBS|DACF_OBSOLETE_TRACK_JIT_INFO)))); + STRESS_LOG2(LF_CODESHARING, LL_INFO100,"Flags diff= %08x [%08x/%08x]",pDomainAssembly->GetDebuggerInfoBits(), + m_debuggerFlags); + return FALSE; + } + + return TRUE; +} + +#endif // FEATURE_VERSIONING + +#endif // FEATURE_LOADER_OPTIMIZATION + +#if defined(FEATURE_APTCA) || defined(FEATURE_CORESYSTEM) +BOOL Assembly::AllowUntrustedCaller() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END + + return ModuleSecurityDescriptor::GetModuleSecurityDescriptor(this)->IsAPTCA(); +} +#endif // defined(FEATURE_APTCA) || defined(FEATURE_CORESYSTEM) + +void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(LPCUTF8 pszFullName, UINT resIDWhy) +{ + WRAPPER_NO_CONTRACT; + ThrowTypeLoadException(NULL, pszFullName, NULL, + resIDWhy); +} + +void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(LPCUTF8 pszNameSpace, LPCUTF8 pszTypeName, + UINT resIDWhy) +{ + WRAPPER_NO_CONTRACT; + ThrowTypeLoadException(pszNameSpace, pszTypeName, NULL, + resIDWhy); + +} + +void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(NameHandle *pName, UINT resIDWhy) +{ + STATIC_CONTRACT_THROWS; + + if (pName->GetName()) { + ThrowTypeLoadException(pName->GetNameSpace(), + pName->GetName(), + NULL, + resIDWhy); + } + else + ThrowTypeLoadException(pName->GetTypeModule()->GetMDImport(), + pName->GetTypeToken(), + resIDWhy); + +} + +void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(IMDInternalImport *pInternalImport, + mdToken token, + UINT resIDWhy) +{ + WRAPPER_NO_CONTRACT; + ThrowTypeLoadException(pInternalImport, token, NULL, resIDWhy); +} + +void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(IMDInternalImport *pInternalImport, + mdToken token, + LPCUTF8 pszFieldOrMethodName, + UINT resIDWhy) +{ + STATIC_CONTRACT_THROWS; + char pszBuff[32]; + LPCUTF8 pszClassName = (LPCUTF8)pszBuff; + LPCUTF8 pszNameSpace = "Invalid_Token"; + + if(pInternalImport->IsValidToken(token)) + { + switch (TypeFromToken(token)) { + case mdtTypeRef: + if (FAILED(pInternalImport->GetNameOfTypeRef(token, &pszNameSpace, &pszClassName))) + { + pszNameSpace = pszClassName = "Invalid TypeRef record"; + } + break; + case mdtTypeDef: + if (FAILED(pInternalImport->GetNameOfTypeDef(token, &pszClassName, &pszNameSpace))) + { + pszNameSpace = pszClassName = "Invalid TypeDef record"; + } + break; + case mdtTypeSpec: + + // If you see this assert, you need to make sure the message for + // this resID is appropriate for TypeSpecs + _ASSERTE((resIDWhy == IDS_CLASSLOAD_GENERAL) || + (resIDWhy == IDS_CLASSLOAD_BADFORMAT) || + (resIDWhy == IDS_CLASSLOAD_TYPESPEC)); + + resIDWhy = IDS_CLASSLOAD_TYPESPEC; + } + } + else + sprintf_s(pszBuff, sizeof(pszBuff), "0x%8.8X", token); + + ThrowTypeLoadException(pszNameSpace, pszClassName, + pszFieldOrMethodName, resIDWhy); +} + + + +void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(LPCUTF8 pszNameSpace, + LPCUTF8 pszTypeName, + LPCUTF8 pszMethodName, + UINT resIDWhy) +{ + STATIC_CONTRACT_THROWS; + + StackSString displayName; + GetDisplayName(displayName); + + ::ThrowTypeLoadException(pszNameSpace, pszTypeName, displayName, + pszMethodName, resIDWhy); +} + +void DECLSPEC_NORETURN Assembly::ThrowBadImageException(LPCUTF8 pszNameSpace, + LPCUTF8 pszTypeName, + UINT resIDWhy) +{ + STATIC_CONTRACT_THROWS; + + StackSString displayName; + GetDisplayName(displayName); + + StackSString fullName; + SString sNameSpace(SString::Utf8, pszNameSpace); + SString sTypeName(SString::Utf8, pszTypeName); + fullName.MakeFullNamespacePath(sNameSpace, sTypeName); + + COMPlusThrowHR(COR_E_BADIMAGEFORMAT, resIDWhy, fullName, displayName); +} + + +#ifdef FEATURE_COMINTEROP +// +// Manage an ITypeLib pointer for this Assembly. +// +ITypeLib* Assembly::GetTypeLib() +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + FORBID_FAULT; + } + CONTRACTL_END + + // Get the value we are going to return. + ITypeLib *pResult = m_pITypeLib; + // If there is a value, AddRef() it. + if (pResult && pResult != (ITypeLib*)-1) + pResult->AddRef(); + return pResult; +} // ITypeLib* Assembly::GetTypeLib() + +void Assembly::SetTypeLib(ITypeLib *pNew) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + FORBID_FAULT; + } + CONTRACTL_END + + ITypeLib *pOld; + pOld = InterlockedExchangeT(&m_pITypeLib, pNew); + // TypeLibs are refcounted pointers. + if (pNew != pOld) + { + if (pNew && pNew != (ITypeLib*)-1) + pNew->AddRef(); + if (pOld && pOld != (ITypeLib*)-1) + pOld->Release(); + } +} // void Assembly::SetTypeLib() + +Assembly::WinMDStatus Assembly::GetWinMDStatus() +{ + LIMITED_METHOD_CONTRACT; + + if (m_winMDStatus == WinMDStatus_Unknown) + { + IWinMDImport *pWinMDImport = GetManifestWinMDImport(); + if (pWinMDImport != NULL) + { + BOOL bIsWinMDExp; + VERIFY(SUCCEEDED(pWinMDImport->IsScenarioWinMDExp(&bIsWinMDExp))); + + if (bIsWinMDExp) + { + // this is a managed backed WinMD + m_winMDStatus = WinMDStatus_IsManagedWinMD; + } + else + { + // this is a pure WinMD + m_winMDStatus = WinMDStatus_IsPureWinMD; + } + } + else + { + // this is not a WinMD at all + m_winMDStatus = WinMDStatus_IsNotWinMD; + } + } + + return m_winMDStatus; +} + +bool Assembly::IsWinMD() +{ + LIMITED_METHOD_CONTRACT; + return GetWinMDStatus() != WinMDStatus_IsNotWinMD; +} + +bool Assembly::IsManagedWinMD() +{ + LIMITED_METHOD_CONTRACT; + return GetWinMDStatus() == WinMDStatus_IsManagedWinMD; +} + +IWinMDImport *Assembly::GetManifestWinMDImport() +{ + LIMITED_METHOD_CONTRACT; + + if (m_pManifestWinMDImport == NULL) + { + ReleaseHolder<IWinMDImport> pWinMDImport; + if (SUCCEEDED(m_pManifest->GetMDImport()->QueryInterface(IID_IWinMDImport, (void **)&pWinMDImport))) + { + if (InterlockedCompareExchangeT<IWinMDImport *>(&m_pManifestWinMDImport, pWinMDImport, NULL) == NULL) + { + pWinMDImport.SuppressRelease(); + } + } + } + + return m_pManifestWinMDImport; +} + +#endif // FEATURE_COMINTEROP + +#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) +void Assembly::GenerateBreadcrumbForServicing() +{ + STANDARD_VM_CONTRACT; + + if (AppX::IsAppXProcess() || IsIntrospectionOnly() || GetManifestFile()->IsDynamic()) + { + return; + } + + if (HasServiceableAttribute() || IsExistingOobAssembly()) + { + StackSString ssDisplayName; + GetDisplayName(ssDisplayName); + + WriteBreadcrumb(ssDisplayName); + CheckDenyList(ssDisplayName); + } +} + +void Assembly::WriteBreadcrumb(const SString &ssDisplayName) +{ + STANDARD_VM_CONTRACT; + + WCHAR path[MAX_LONGPATH]; + HRESULT hr = WszSHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, ARRAYSIZE(path), path); + if (hr != S_OK) + { + return; + } + + if (wcscat_s(path, W("\\Microsoft\\NetFramework\\BreadcrumbStore\\")) != 0) + { + return; + } + + size_t dirPathLen = wcslen(path); + + // Validate the display name. E.g., we don't want the display name to start with "..\\". + bool inSimpleName = true; + for (SString::CIterator it = ssDisplayName.Begin(); it != ssDisplayName.End(); ++it) + { + WCHAR c = *it; + + // The following characters are always allowed: a-zA-Z0-9_ + if (c >= W('a') && c <= W('z') || c >= W('A') && c <= W('Z') || c >= W('0') && c <= W('9') || c == W('_')) continue; + + // The period is allowed except as the first char. + if (c == W('.') && it != ssDisplayName.Begin()) continue; + + // A comma terminates the assembly simple name, and we are in key=value portion of the display name. + if (c == W(',')) + { + inSimpleName = false; + continue; + } + + // In key=value portion, space and equal sign are also allowed. + if (!inSimpleName && (c == W(' ') || c == W('='))) continue; + + // If we reach here, we have an invalid assembly display name. Return without writing breadcrumb. + return; + } + + // Log a breadcrumb using full display name. + if (wcscat_s(path, ssDisplayName.GetUnicode()) == 0) + { + HandleHolder hFile = WszCreateFile(path, 0, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + } + + // Log another breadcrumb using display name without version. + // First make a copy of the display name, and look for its version part. + StackSString ssNoVersion(ssDisplayName); + SString::Iterator itVersion = ssNoVersion.Begin(); + if (!ssNoVersion.Find(itVersion, W(", Version="))) + { + return; + } + + // Start from the comma before Version=, advance past the comma, then look for the next comma. + SString::Iterator itVersionEnd = itVersion; + ++itVersionEnd; + if (!ssNoVersion.Find(itVersionEnd, W(','))) + { + // Version is the last key=value pair. + itVersionEnd = ssNoVersion.End(); + } + + // Erase the version. + ssNoVersion.Delete(itVersion, itVersionEnd - itVersion); + + // Generate the full path string and create the file. + path[dirPathLen] = W('\0'); + if (wcscat_s(path, ssNoVersion.GetUnicode()) == 0) + { + HandleHolder hFile = WszCreateFile(path, 0, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + } + +} + +bool Assembly::HasServiceableAttribute() +{ + STANDARD_VM_CONTRACT; + + IMDInternalImport *pImport = GetManifestImport(); + MDEnumHolder hEnum(pImport); + HRESULT hr = pImport->EnumCustomAttributeByNameInit(GetManifestToken(), ASSEMBLY_METADATA_TYPE, &hEnum); + if (hr != S_OK) + { + return false; + } + + mdCustomAttribute tkAttribute; + while (pImport->EnumNext(&hEnum, &tkAttribute)) + { + // Get raw custom attribute. + const BYTE *pbAttr = NULL; // Custom attribute data as a BYTE*. + ULONG cbAttr = 0; // Size of custom attribute data. + if (FAILED(pImport->GetCustomAttributeAsBlob(tkAttribute, reinterpret_cast<const void **>(&pbAttr), &cbAttr))) + { + THROW_BAD_FORMAT(BFA_INVALID_TOKEN, GetManifestModule()); + } + + CustomAttributeParser cap(pbAttr, cbAttr); + if (FAILED(cap.ValidateProlog())) + { + THROW_BAD_FORMAT(BFA_BAD_CA_HEADER, GetManifestModule()); + } + + // Get the metadata key. It is not null terminated. + LPCUTF8 key; + ULONG cbKey; + if (FAILED(cap.GetString(&key, &cbKey))) + { + THROW_BAD_FORMAT(BFA_BAD_CA_HEADER, GetManifestModule()); + } + + const LPCUTF8 szServiceable = "Serviceable"; + const ULONG cbServiceable = 11; + if (cbKey != cbServiceable || strncmp(key, szServiceable, cbKey) != 0) + { + continue; + } + + // Get the metadata value. It is not null terminated. + if (FAILED(cap.GetString(&key, &cbKey))) + { + THROW_BAD_FORMAT(BFA_BAD_CA_HEADER, GetManifestModule()); + } + + const LPCUTF8 szTrue = "True"; + const ULONG cbTrue = 4; + if (cbKey == cbTrue && strncmp(key, szTrue, cbKey) == 0) + { + return true; + } + } + + return false; +} + +bool Assembly::IsExistingOobAssembly() +{ + WRAPPER_NO_CONTRACT; + + return ExistingOobAssemblyList::Instance()->IsOnlist(this); +} + +void Assembly::CheckDenyList(const SString &ssDisplayName) +{ + STANDARD_VM_CONTRACT; + + StackSString ssKeyName(W("SOFTWARE\\Microsoft\\.NETFramework\\Policy\\DenyList\\")); + + ssKeyName.Append(ssDisplayName); + + RegKeyHolder hKey; + LONG status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, ssKeyName.GetUnicode(), 0, KEY_WOW64_64KEY | GENERIC_READ, &hKey); + + if (status != ERROR_SUCCESS) + { + return; + } + + StackSString ssFwlink; + HRESULT hr = Clr::Util::Reg::ReadStringValue(hKey, NULL, NULL, ssFwlink); + if (FAILED(hr) || ssFwlink.GetCount() == 0) + { + ssFwlink.Set(W("http://go.microsoft.com/fwlink/?LinkID=286319")); + } + + StackSString ssMessageTemplate; + if(!ssMessageTemplate.LoadResource(CCompRC::Optional, IDS_EE_ASSEMBLY_ON_DENY_LIST)) + { + ssMessageTemplate.Set(W("The assembly %1 that the application tried to load has a known vulnerability. Please go to %2 to find a fix for this issue.")); + } + + StackSString ssMessage; + ssMessage.FormatMessage(FORMAT_MESSAGE_FROM_STRING, ssMessageTemplate.GetUnicode(), 0, 0, ssDisplayName, ssFwlink); + + ClrReportEvent( + W(".NET Runtime"), // Event source + EVENTLOG_ERROR_TYPE, // Type + 0, // Category + SecurityConfig, // Event ID + NULL, // User SID + ssMessage.GetUnicode()); // Message + + NewHolder<EEMessageException> pEx(new EEMessageException(kSecurityException, IDS_EE_ASSEMBLY_ON_DENY_LIST, ssDisplayName.GetUnicode(), ssFwlink.GetUnicode())); + EEFileLoadException::Throw(m_pManifestFile, pEx->GetHR(), pEx); +} + +BOOL IsReportableAssembly(PEAssembly *pPEAssembly) +{ + STANDARD_VM_CONTRACT; + + // If the assembly could have used a native image, but did not, report the IL image + BOOL fCanUseNativeImage = (pPEAssembly->HasHostAssembly() || pPEAssembly->IsContextLoad()) && + pPEAssembly->CanUseNativeImage() && + !IsNativeImageOptedOut(pPEAssembly->GetFusionAssemblyName()); + + return fCanUseNativeImage; +} + +BOOL Assembly::SupportsAutoNGenWorker() +{ + STANDARD_VM_CONTRACT; + + PEAssembly *pPEAssembly = GetManifestFile(); + + if (pPEAssembly->IsSourceGAC() && Fusion::Util::IsUnifiedAssembly(pPEAssembly->GetFusionAssemblyName()) == S_OK) + { + // Assemblies in the .NET Framework supports Auto NGen. + return TRUE; + } + + if (IsAfContentType_WindowsRuntime(GetFlags())) + { + // WinMD files support Auto NGen. + return TRUE; + } + + if (pPEAssembly->HasHostAssembly()) + { + // Auto NGen is enabled on all Metro app assemblies. + return TRUE; + } + + if (pPEAssembly->IsSourceGAC()) + { + // For non-framework assemblies in GAC, look for TargetFrameworkAttriute. + const BYTE *pbAttr; // Custom attribute data as a BYTE*. + ULONG cbAttr; // Size of custom attribute data. + HRESULT hr = GetManifestImport()->GetCustomAttributeByName(GetManifestToken(), TARGET_FRAMEWORK_TYPE, (const void**)&pbAttr, &cbAttr); + if (hr != S_OK) + { + return FALSE; + } + + CustomAttributeParser cap(pbAttr, cbAttr); + if (FAILED(cap.ValidateProlog())) + { + THROW_BAD_FORMAT(BFA_BAD_CA_HEADER, GetManifestModule()); + } + LPCUTF8 lpTargetFramework; + ULONG cbTargetFramework; + if (FAILED(cap.GetString(&lpTargetFramework, &cbTargetFramework))) + { + THROW_BAD_FORMAT(BFA_BAD_CA_HEADER, GetManifestModule()); + } + + if (lpTargetFramework == NULL || cbTargetFramework == 0) + { + return FALSE; + } + + SString ssTargetFramework(SString::Utf8, lpTargetFramework, cbTargetFramework); + + // Look for two special TargetFramework values that disables AutoNGen. To guard against future + // variations of the string values, we do prefix matches. + SString ssFramework40(SString::Literal, W(".NETFramework,Version=v4.0")); + SString ssPortableLib(SString::Literal, W(".NETPortable,")); + if (ssTargetFramework.BeginsWithCaseInsensitive(ssFramework40) || ssTargetFramework.BeginsWithCaseInsensitive(ssPortableLib)) + { + return FALSE; + } + + // If TargetFramework doesn't match one of the two special values, we enable Auto NGen. + return TRUE; + } + + return FALSE; +} + +void Assembly::ReportAssemblyUse() +{ + STANDARD_VM_CONTRACT; + + // Do not log if we don't have a global gac logger object + if (g_pIAssemblyUsageLogGac != NULL) + { + // Only consider reporting for loads that could possibly use native images. + PEAssembly *pPEAssembly = this->GetManifestFile(); + if (IsReportableAssembly(pPEAssembly) && !pPEAssembly->IsReportedToUsageLog()) + { + // Do not log repeatedly + pPEAssembly->SetReportedToUsageLog(); + + ReleaseHolder<IAssemblyUsageLog> pRefCountedUsageLog; + IAssemblyUsageLog *pUsageLog = NULL; + if (SupportsAutoNGen()) + { + if (pPEAssembly->IsSourceGAC()) + { + pUsageLog = g_pIAssemblyUsageLogGac; + } + else if (pPEAssembly->HasHostAssembly()) + { + UINT_PTR binderId; + IfFailThrow(pPEAssembly->GetHostAssembly()->GetBinderID(&binderId)); + pRefCountedUsageLog = AssemblyUsageLogManager::GetUsageLogForBinder(binderId); + pUsageLog = pRefCountedUsageLog; + } + } + + if (pUsageLog) + { + PEAssembly *pPEAssembly = GetManifestFile(); + StackSString name; + // GAC Assemblies are reported by assembly name + if (pUsageLog == g_pIAssemblyUsageLogGac) + { + this->GetDisplayName(name); + } + // Other assemblies (AppX...) are reported by file path + else + { + name.Set(pPEAssembly->GetILimage()->GetPath().GetUnicode()); + } + + if (pPEAssembly->HasNativeImage()) + { + if(!IsSystem()) + { + // If the assembly used a native image, report it + ReleaseHolder<PEImage> pNativeImage = pPEAssembly->GetNativeImageWithRef(); + pUsageLog->LogFile(name.GetUnicode(), pNativeImage->GetPath().GetUnicode(), ASSEMBLY_USAGE_LOG_FLAGS_NI); + } + } + else + { + // If the assembly could have used a native image, but did not, report the IL image + pUsageLog->LogFile(name.GetUnicode(), NULL, ASSEMBLY_USAGE_LOG_FLAGS_IL); + } + } + } + } +} +#endif // FEATURE_CORECLR && !CROSSGEN_COMPILE + +#endif // #ifndef DACCESS_COMPILE + +#ifndef DACCESS_COMPILE +void Assembly::EnsureActive() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + GetDomainAssembly()->EnsureActive(); +} +#endif //!DACCESS_COMPILE + +CHECK Assembly::CheckActivated() +{ +#ifndef DACCESS_COMPILE + WRAPPER_NO_CONTRACT; + + CHECK(GetDomainAssembly()->CheckActivated()); +#endif + CHECK_OK; +} + + + +#ifdef DACCESS_COMPILE + +void +Assembly::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) +{ + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + + // We don't need Assembly info in triage dumps. + if (flags == CLRDATA_ENUM_MEM_TRIAGE) + { + return; + } + + DAC_ENUM_DTHIS(); + EMEM_OUT(("MEM: %p Assembly\n", dac_cast<TADDR>(this))); + + if (m_pDomain.IsValid()) + { + m_pDomain->EnumMemoryRegions(flags, true); + } + if (m_pClassLoader.IsValid()) + { + m_pClassLoader->EnumMemoryRegions(flags); + } + if (m_pManifest.IsValid()) + { + m_pManifest->EnumMemoryRegions(flags, true); + } + if (m_pManifestFile.IsValid()) + { + m_pManifestFile->EnumMemoryRegions(flags); + } +} + +#endif + +#ifndef DACCESS_COMPILE + +FriendAssemblyDescriptor::FriendAssemblyDescriptor() +{ +} + +FriendAssemblyDescriptor::~FriendAssemblyDescriptor() +{ + CONTRACTL + { + DESTRUCTOR_CHECK; + } + CONTRACTL_END; + + ArrayList::Iterator itFullAccessAssemblies = m_alFullAccessFriendAssemblies.Iterate(); + while (itFullAccessAssemblies.Next()) + { + FriendAssemblyName_t *pFriendAssemblyName = static_cast<FriendAssemblyName_t *>(itFullAccessAssemblies.GetElement()); +#ifdef FEATURE_FUSION + pFriendAssemblyName->Release(); +#else // FEATURE_FUSION + delete pFriendAssemblyName; +#endif // FEATURE_FUSION + } +} + + +//--------------------------------------------------------------------------------------- +// +// Builds a FriendAssemblyDescriptor for a given assembly +// +// Arguments: +// pAssembly - assembly to get friend assembly information for +// +// Return Value: +// A friend assembly descriptor if the assembly declares any friend assemblies, otherwise NULL +// + +// static +FriendAssemblyDescriptor *FriendAssemblyDescriptor::CreateFriendAssemblyDescriptor(PEAssembly *pAssembly) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + PRECONDITION(CheckPointer(pAssembly)); + } + CONTRACTL_END + + NewHolder<FriendAssemblyDescriptor> pFriendAssemblies = new FriendAssemblyDescriptor; + + // We're going to do this twice, once for InternalsVisibleTo and once for IgnoresAccessChecks + ReleaseHolder<IMDInternalImport> pImport(pAssembly->GetMDImportWithRef()); + for(int count = 0 ; count < 2 ; ++count) + { + _ASSERTE(pImport != NULL); + MDEnumHolder hEnum(pImport); + HRESULT hr = S_OK; + + if (count == 0) + { + hr = pImport->EnumCustomAttributeByNameInit(TokenFromRid(1, mdtAssembly), FRIEND_ASSEMBLY_TYPE, &hEnum); + } + else + { + hr = pImport->EnumCustomAttributeByNameInit(TokenFromRid(1, mdtAssembly), SUBJECT_ASSEMBLY_TYPE, &hEnum); + } + + IfFailThrow(hr); + + // Nothing to do if there are no attributes + if (hr == S_FALSE) + { + continue; + } + + // Enumerate over the declared friends + mdCustomAttribute tkAttribute; + while (pImport->EnumNext(&hEnum, &tkAttribute)) + { + // Get raw custom attribute. + const BYTE *pbAttr = NULL; // Custom attribute data as a BYTE*. + ULONG cbAttr = 0; // Size of custom attribute data. + if (FAILED(pImport->GetCustomAttributeAsBlob(tkAttribute, reinterpret_cast<const void **>(&pbAttr), &cbAttr))) + { + THROW_BAD_FORMAT(BFA_INVALID_TOKEN, pAssembly); + } + + CustomAttributeParser cap(pbAttr, cbAttr); + if (FAILED(cap.ValidateProlog())) + { + THROW_BAD_FORMAT(BFA_BAD_CA_HEADER, pAssembly); + } + + // Get the name of the friend assembly. + LPCUTF8 szString; + ULONG cbString; + if (FAILED(cap.GetNonNullString(&szString, &cbString))) + { + THROW_BAD_FORMAT(BFA_BAD_CA_HEADER, pAssembly); + } + + // Convert the string to Unicode. + StackSString displayName(SString::Utf8, szString, cbString); + + // Create an AssemblyNameObject from the string. + FriendAssemblyNameHolder pFriendAssemblyName; +#ifdef FEATURE_FUSION + hr = CreateAssemblyNameObject(&pFriendAssemblyName, displayName.GetUnicode(), CANOF_PARSE_FRIEND_DISPLAY_NAME, NULL); +#else // FEATURE_FUSION + StackScratchBuffer buffer; + pFriendAssemblyName = new FriendAssemblyName_t; + hr = pFriendAssemblyName->Init(displayName.GetUTF8(buffer)); + + if (SUCCEEDED(hr)) + { + hr = pFriendAssemblyName->CheckFriendAssemblyName(); + } +#endif // FEATURE_FUSION + + if (FAILED(hr)) + { + THROW_HR_ERROR_WITH_INFO(hr, pAssembly); + } + + if (count == 1) + { + pFriendAssemblies->AddSubjectAssembly(pFriendAssemblyName); + pFriendAssemblyName.SuppressRelease(); + // Below checks are unnecessary for IgnoresAccessChecks + continue; + } + + // CoreCLR does not have a valid scenario for strong-named assemblies requiring their dependencies + // to be strong-named as well. +#if !defined(FEATURE_CORECLR) + // If this assembly has a strong name, then its friends declarations need to have strong names too + if (pAssembly->IsStrongNamed()) + { +#ifdef FEATURE_FUSION + DWORD dwSize = 0; + if (SUCCEEDED(hr = pFriendAssemblyName->GetProperty(ASM_NAME_PUBLIC_KEY, NULL, &dwSize))) + { + // If this call succeeds with an empty buffer, then the supplied name doesn't have a public key. + THROW_HR_ERROR_WITH_INFO(META_E_CA_FRIENDS_SN_REQUIRED, pAssembly); + } + else if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) + { + IfFailThrow(hr); + } +#else // FEATURE_FUSION + // Desktop crossgen comes here + if (!pFriendAssemblyName->IsStrongNamed()) + { + // If this call succeeds with an empty buffer, then the supplied name doesn't have a public key. + THROW_HR_ERROR_WITH_INFO(META_E_CA_FRIENDS_SN_REQUIRED, pAssembly); + } +#endif // FEATURE_FUSION + } +#endif // !defined(FEATURE_CORECLR) + + pFriendAssemblies->AddFriendAssembly(pFriendAssemblyName); + + pFriendAssemblyName.SuppressRelease(); + } + } + + pFriendAssemblies.SuppressRelease(); + return pFriendAssemblies.Extract(); +} + +//--------------------------------------------------------------------------------------- +// +// Adds an assembly to the list of friend assemblies for this descriptor +// +// Arguments: +// pFriendAssembly - friend assembly to add to the list +// fAllInternalsVisible - true if all internals are visible to the friend, false if only specifically +// marked internals are visible +// +// Notes: +// This method takes ownership of the friend assembly name. It is not thread safe and does not check to +// see if an assembly has already been added to the friend assembly list. +// + +void FriendAssemblyDescriptor::AddFriendAssembly(FriendAssemblyName_t *pFriendAssembly) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + PRECONDITION(CheckPointer(pFriendAssembly)); + } + CONTRACTL_END + + m_alFullAccessFriendAssemblies.Append(pFriendAssembly); +} + +void FriendAssemblyDescriptor::AddSubjectAssembly(FriendAssemblyName_t *pFriendAssembly) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + PRECONDITION(CheckPointer(pFriendAssembly)); + } + CONTRACTL_END + + m_subjectAssemblies.Append(pFriendAssembly); +} + +// static +bool FriendAssemblyDescriptor::IsAssemblyOnList(PEAssembly *pAssembly, const ArrayList &alAssemblyNames) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + PRECONDITION(CheckPointer(pAssembly)); + } + CONTRACTL_END; + +#ifndef FEATURE_FUSION + AssemblySpec asmDef; + asmDef.InitializeSpec(pAssembly); +#endif + + ArrayList::ConstIterator itAssemblyNames = alAssemblyNames.Iterate(); + while (itAssemblyNames.Next()) + { + const FriendAssemblyName_t *pFriendAssemblyName = static_cast<const FriendAssemblyName_t *>(itAssemblyNames.GetElement()); +#ifdef FEATURE_FUSION + // This is a const operation on the pointer, but Fusion is not const-correct. + // @TODO - propigate const correctness through Fusion and remove this cast + HRESULT hr = const_cast<FriendAssemblyName_t *>(pFriendAssemblyName)->IsEqual(pAssembly->GetFusionAssemblyName(), ASM_CMPF_DEFAULT); + IfFailThrow(hr); +#else + HRESULT hr = AssemblySpec::RefMatchesDef(pFriendAssemblyName, &asmDef) ? S_OK : S_FALSE; +#endif + + if (hr == S_OK) + { + return true; + } + } + + return false; +} + +#endif // !DACCESS_COMPILE + + +#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) && !defined(DACCESS_COMPILE) + +ExistingOobAssemblyList::ExistingOobAssemblyList() +{ + STANDARD_VM_CONTRACT; + + RegKeyHolder hKey; + LONG status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, W("SOFTWARE\\Microsoft\\.NETFramework\\Policy\\Servicing"), 0, KEY_WOW64_64KEY | GENERIC_READ, &hKey); + if (status != ERROR_SUCCESS) + { + return; + } + + for (DWORD i = 0; ; i++) + { + WCHAR name[MAX_PATH_FNAME + 1]; + DWORD cchName = ARRAYSIZE(name); + status = RegEnumKeyExW(hKey, i, name, &cchName, NULL, NULL, NULL, NULL); + + if (status == ERROR_NO_MORE_ITEMS) + { + break; + } + + if (status == ERROR_SUCCESS) + { + NonVMComHolder<IAssemblyName> pAssemblyName; + HRESULT hr = CreateAssemblyNameObject(&pAssemblyName, name, CANOF_PARSE_DISPLAY_NAME, NULL); + if (SUCCEEDED(hr)) + { + hr = m_alExistingOobAssemblies.Append(pAssemblyName.GetValue()); + if (SUCCEEDED(hr)) + { + pAssemblyName.SuppressRelease(); + } + } + } + } +} + +bool ExistingOobAssemblyList::IsOnlist(Assembly *pAssembly) +{ + STANDARD_VM_CONTRACT; + + ArrayList::Iterator itAssemblyNames = m_alExistingOobAssemblies.Iterate(); + while (itAssemblyNames.Next()) + { + IAssemblyName *pAssemblyName = static_cast<IAssemblyName *>(itAssemblyNames.GetElement()); + HRESULT hr = pAssemblyName->IsEqual(pAssembly->GetFusionAssemblyName(), ASM_CMPF_DEFAULT); + if (hr == S_OK) + { + return true; + } + } + + return false; +} + +void ExistingOobAssemblyList::Init() +{ + STANDARD_VM_CONTRACT; + + s_pInstance = new ExistingOobAssemblyList(); +} + +ExistingOobAssemblyList *ExistingOobAssemblyList::s_pInstance; +#endif // !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE) && !defined(DACCESS_COMPILE) |